hym_ui/lib/email.dart

338 lines
11 KiB
Dart

import 'package:flutter/material.dart';
import 'package:markdown/markdown.dart' as md;
import 'api_service.dart';
import 'structs.dart';
import 'emailView.dart';
class EmailListScreen extends StatefulWidget {
final List<GetThreadResponse> emails;
final Future<List<String>> Function(List<String>, String) getEmailContent;
final String folder;
final GlobalKey<_EmailListScreenState> key;
final Function(List<GetThreadResponse>)? onSelectionChanged;
EmailListScreen({
required this.key,
required this.emails,
required this.getEmailContent,
required this.folder,
this.onSelectionChanged,
}) : super(key: key);
@override
_EmailListScreenState createState() => _EmailListScreenState();
}
class _EmailListScreenState extends State<EmailListScreen>
with TickerProviderStateMixin {
late List<bool> selectStates; // for checkboxes if its selected or not
late List<GetThreadResponse> selectedEmails =
[]; // holds the emails that are selected i.e. the emails that got the checkbox on
final Set<int> _hoveredRows = {}; //the row that is being hovered over atm
bool bulkSelectMenu = false;
final GlobalKey<EmailPageState> _emailPageKey = GlobalKey<EmailPageState>();
@override
void initState() {
super.initState();
selectStates = List<bool>.filled(widget.emails.length, false);
}
@override
void didUpdateWidget(covariant EmailListScreen oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.emails.length != widget.emails.length) {
selectStates = List<bool>.filled(widget.emails.length, false);
}
}
bool selectAllChecks(bool selectionType) {
//perhaps it should return a list of the selected
setState(() {
selectedEmails = [];
if (selectionType) {
bulkSelectMenu = true;
for (int email = 0; email < selectStates.length; email++) {
selectStates[email] = selectionType;
selectedEmails.add(widget.emails[email]);
}
} else {
for (int email = 0; email < selectStates.length; email++) {
selectStates[email] = selectionType;
}
selectedEmails = [];
}
});
widget.onSelectionChanged?.call(selectedEmails);
printTheSelected();
return false;
}
bool markAsRead(bool read) {
print("markasread $read");
setState(() {
if (read) {
//read
for (int email = 0; email < selectedEmails.length; email++) {
selectedEmails[email].seen = read;
ApiService()
.markAsSeen(selectedEmails[email].id); //the remote or .json
}
} else {
//unread
for (int email = 0; email < selectedEmails.length; email++) {
selectedEmails[email].seen = read;
ApiService()
.markAsUnseen(selectedEmails[email].id); //the remote or .json
print(selectedEmails[email].subject);
}
}
});
return false;
}
bool moveOfSelected(String destinyFolder) {
//this should be called from a widget
print("move of folder");
setState(() {
for (int email = 0; email < selectedEmails.length; email++) {
ApiService().moveEmail(
widget.folder, selectedEmails[email].id.toString(), destinyFolder);
}
});
return false;
}
// Widget moveOfFolderWidget()
List<GetThreadResponse> listOfSelectedThreads() {
return selectedEmails;
}
void printTheSelected() {
for (int i = 0; i < selectedEmails.length; i++) {
print(selectedEmails[i].subject);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.separated(
itemCount: widget.emails.length,
itemBuilder: (context, index) {
Color seenColour;
final email = widget.emails[index];
if (email.seen) {
seenColour = ThemeData().highlightColor;
} else {
seenColour = Colors.transparent;
}
return MouseRegion(
onEnter: (_) => setState(() => _hoveredRows.add(index)),
onExit: (_) => setState(() => _hoveredRows.remove(index)),
child: ListTile(
leading: Checkbox(
value: selectStates[index],
onChanged: (bool? value) {
setState(() {
//works great
selectStates[index] = value ?? false;
setState(() {
if (value!) {
selectedEmails.add(widget.emails[index]);
//here i must update the other side
_emailPageKey.currentState?.getListOfSelected();
} else {
selectedEmails.remove(widget.emails[index]);
_emailPageKey.currentState?.getListOfSelected();
}
widget.onSelectionChanged?.call(selectedEmails);
print(selectedEmails);
});
});
},
),
title: Text(email.from_name,
style: TextStyle(fontWeight: FontWeight.bold)),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [Text(email.subject)],
),
trailing: _hoveredRows.contains(index)
? Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(Icons.mark_email_read_outlined),
onPressed: () {
// mark email as read
setState(() {
widget.emails[index].seen = true;
ApiService().markAsSeen(email.id);
});
},
),
IconButton(
icon: Icon(Icons.delete_outline),
onPressed: () {
// delete email
ApiService().deleteEmail(widget.folder, email.id);
},
),
],
)
: Text(email.date.toString()),
hoverColor: Colors.transparent,
tileColor: seenColour,
onTap: () async {
List<String> emailContent = // list of the html
await widget.getEmailContent(email.messages, widget.folder);
// print("thread id? $email.id"); yes
print(email.messages); //email ids of the thread
if (widget.folder == "Drafts") {
print("IN DRAFTS MOVE THE CONTENT TO THE WRITING THING");
} else {
Navigator.push(
context,
MaterialPageRoute(
// could call collapsable and inside collable each calls email view?
builder: (context) => EmailView(
emailContent: emailContent,
from: email.from_address,
name: email.from_name,
to: email.to.toString(),
subject: email.subject,
date: email.date.toString(),
id: email.id.toString(), //i think this is thread id?
messages: email.messages,
),
),
);
ApiService().markAsSeen(email.id);
}
},
),
);
},
separatorBuilder: (context, index) => Divider(),
),
);
}
}
// ignore: must_be_immutable
class EmailPage extends StatefulWidget {
String selectedFolder = "INBOX"; //starter
int offset = 0;
int page = 1;
final Function(List<GetThreadResponse>)? onSelectionChanged;
EmailPage({Key? key, this.onSelectionChanged}) : super(key: key);
@override
EmailPageState createState() => EmailPageState();
}
class EmailPageState extends State<EmailPage> {
final ApiService apiService = ApiService();
List<GetThreadResponse> emails = [];
ValueNotifier<int> currentPageNotifier = ValueNotifier<int>(1);
int page = 1;
bool isBackDisabled = false;
final GlobalKey<_EmailListScreenState> emailListKey =
GlobalKey<_EmailListScreenState>();
@override
void initState() {
super.initState();
widget.page = page;
isBackDisabled = true;
_fetchEmails();
}
List<GetThreadResponse> get getEmails => emails;
String getPage() => widget.page.toString();
bool get backDisabled => isBackDisabled;
void updateSelectedFolder(String folder) {
setState(() {
widget.selectedFolder = folder;
});
print(folder);
_fetchEmails();
}
void updatePagenation(String option) {
if (option == "next") {
setState(() {
widget.offset += 50;
widget.page += 1;
currentPageNotifier.value = widget.page;
isBackDisabled = false;
});
} else if (option == "back") {
setState(() {
widget.offset -= 50;
widget.page -= 1;
currentPageNotifier.value = widget.page;
if (widget.page == 1) {
isBackDisabled = true;
print("back disabled");
}
});
}
print(widget.page);
_fetchEmails();
}
void _fetchEmails() async {
try {
List<GetThreadResponse> fetchedEmails = await apiService
.fetchEmailsFromFolder(widget.selectedFolder, widget.offset);
if (!mounted) return;
setState(() {
emails = fetchedEmails; // Update the list of emails
});
} catch (e) {
print('Error fetching emails: $e');
}
}
bool selectAllEmails(bool selectionType) {
emailListKey.currentState?.selectAllChecks(selectionType);
return selectionType;
}
bool markSelectedAsRead(bool selectionType) {
emailListKey.currentState?.markAsRead(selectionType);
return selectionType;
}
bool moveSelectedOfFolder(String folder) {
emailListKey.currentState?.moveOfSelected(folder);
return false;
}
List<GetThreadResponse> getListOfSelected() {
return emailListKey.currentState!.listOfSelectedThreads();
}
// return [GetThreadResponse(id: 1, messages: [], subject: "subject", date: DateTime(2025), from_name: "from_name", from_address: "from_address", to: [], seen: false)];
@override
Widget build(BuildContext context) {
return Scaffold(
body: EmailListScreen(
key: emailListKey,
emails: emails,
// getEmailContent: apiService.fetchEmailContent,
getEmailContent: apiService.fetchMarkdownContent,
folder: widget.selectedFolder, //try to grab from it directly
onSelectionChanged: widget.onSelectionChanged,
));
}
}