|
@@ -1,93 +1,401 @@
|
|
-//api file
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-// import 'package:http/http.dart' as http;
|
|
|
|
-// import 'dart:convert';
|
|
|
|
-
|
|
|
|
-// class SerializableMessage {
|
|
|
|
-// final String name;
|
|
|
|
-// final String from;
|
|
|
|
-// final String path;
|
|
|
|
-// final String subject;
|
|
|
|
-// final String date;
|
|
|
|
-
|
|
|
|
-// SerializableMessage({
|
|
|
|
-// required this.name,
|
|
|
|
-// required this.from,
|
|
|
|
-// required this.path,
|
|
|
|
-// required this.subject,
|
|
|
|
-// required this.date,
|
|
|
|
-// });
|
|
|
|
-
|
|
|
|
-// factory SerializableMessage.fromJson(Map<String, dynamic> json) {
|
|
|
|
-// return SerializableMessage(
|
|
|
|
-// name: json['name'],
|
|
|
|
-// from: json['from'],
|
|
|
|
-// path: json['path'],
|
|
|
|
-// subject: json['subject'],
|
|
|
|
-// date: json['date'],
|
|
|
|
-// );
|
|
|
|
-// }
|
|
|
|
-// }
|
|
|
|
|
|
+import 'package:flutter/material.dart';
|
|
|
|
+import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
|
|
|
+// import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
|
|
|
+import 'package:http/http.dart' as http;
|
|
|
|
+import 'dart:convert';
|
|
|
|
+//TODO: copy hope_page.dart impl of iframe
|
|
|
|
+import 'dart:ui_web' as ui;
|
|
|
|
+import 'dart:html' as html;
|
|
|
|
+// import 'package:flutter_html/flutter_html.dart';
|
|
|
|
+
|
|
|
|
+class MailAddress {
|
|
|
|
+ final String? name;
|
|
|
|
+ final String address;
|
|
|
|
+ MailAddress({this.name, required this.address});
|
|
|
|
+
|
|
|
|
+ factory MailAddress.fromJson(Map<String, dynamic> json) {
|
|
|
|
+ return MailAddress(
|
|
|
|
+ name: json['name'],
|
|
|
|
+ address: json['address'],
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+class SerializableMessage {
|
|
|
|
+ final String name;
|
|
|
|
+ final String from;
|
|
|
|
+ final List<MailAddress> to;
|
|
|
|
+ final List<MailAddress> cc;
|
|
|
|
+ final String hash;
|
|
|
|
+
|
|
|
|
+ // final String path;
|
|
|
|
+ final String subject;
|
|
|
|
+ final String date;
|
|
|
|
+ final int uid;
|
|
|
|
+ final String list;
|
|
|
|
+ final String id;
|
|
|
|
+ final String in_reply_to;
|
|
|
|
+
|
|
|
|
+ SerializableMessage({
|
|
|
|
+ required this.name,
|
|
|
|
+ required this.from,
|
|
|
|
+ required this.to,
|
|
|
|
+ required this.cc,
|
|
|
|
+ required this.hash,
|
|
|
|
+ required this.subject,
|
|
|
|
+ required this.date,
|
|
|
|
+ required this.uid,
|
|
|
|
+ required this.list,
|
|
|
|
+ required this.id,
|
|
|
|
+ required this.in_reply_to,
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ factory SerializableMessage.fromJson(Map<String, dynamic> json) {
|
|
|
|
+ var toList = json['to'] as List;
|
|
|
|
+ var ccList = json['cc'] as List;
|
|
|
|
+
|
|
|
|
+ return SerializableMessage(
|
|
|
|
+ name: json['name'],
|
|
|
|
+ from: json['from'],
|
|
|
|
+ // to: json['name', 'address']
|
|
|
|
+ to: toList.map((i) => MailAddress.fromJson(i)).toList(),
|
|
|
|
+ cc: ccList.map((i) => MailAddress.fromJson(i)).toList(),
|
|
|
|
+ // path: json['path'],
|
|
|
|
+ hash: json['hash'],
|
|
|
|
+ subject: json['subject'],
|
|
|
|
+ date: json['date'],
|
|
|
|
+ uid: json['uid'],
|
|
|
|
+ list: json['list'],
|
|
|
|
+ id: json['id'],
|
|
|
|
+ in_reply_to: json['in_reply_to'],
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+class EmailPage extends StatefulWidget {
|
|
|
|
+ const EmailPage({super.key});
|
|
|
|
+ final String title = 'Emails';
|
|
|
|
+
|
|
|
|
+ @override
|
|
|
|
+ State<EmailPage> createState() => _EmailPageState();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+class _EmailPageState extends State<EmailPage> {
|
|
|
|
+ List emails = [];
|
|
|
|
+
|
|
|
|
+ void _displayEmailsFromFolder(String folder) async {
|
|
|
|
+ // Map<String, List<SerializableMessage>> messagesMap = {};
|
|
|
|
+ List<SerializableMessage> allEmails = [];
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ var url = Uri.http(
|
|
|
|
+ '127.0.0.1:3001', 'sorted_threads_by_date', {'folder': folder});
|
|
|
|
+ var response = await http.get(url);
|
|
|
|
+ // print(response.body);
|
|
|
|
+ // Map<String, dynamic> json = jsonDecode(response.body); original
|
|
|
|
+
|
|
|
|
+ // json.forEach((key, value) {
|
|
|
|
+ // List<SerializableMessage> messages = (value as List)
|
|
|
|
+ // .map((item) => SerializableMessage.fromJson(item))
|
|
|
|
+ // .toList();
|
|
|
|
+ // messagesMap[key] = messages;
|
|
|
|
+ // });
|
|
|
|
+
|
|
|
|
+ // new shit
|
|
|
|
+ if (response.statusCode == 200) {
|
|
|
|
+ List<dynamic> json = jsonDecode(response.body);
|
|
|
|
+ for (var item in json) {
|
|
|
|
+ if (item.length > 1 && item[0] is String && item[1] is List) {
|
|
|
|
+ // print('Date: ${item[0]}, Threads: ${item[1]}');
|
|
|
|
+ List<int> threadIDs = List<int>.from(item[1]);
|
|
|
|
+ for (var threadId in threadIDs) {
|
|
|
|
+ await fetchThreadMessages(threadId, allEmails);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ throw Exception('Failed to load threads');
|
|
|
|
+ }
|
|
|
|
+ } catch (e) {
|
|
|
|
+ print('_displayEmailsFromFolder caught error: $e');
|
|
|
|
+ }
|
|
|
|
+ setState(() {
|
|
|
|
+ emails.clear();
|
|
|
|
+ // emails = messagesMap.values.toList().expand((list) => list).toList();
|
|
|
|
+ emails.addAll(allEmails);
|
|
|
|
+ print(emails);
|
|
|
|
+ ;
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Future<void> fetchThreadMessages(
|
|
|
|
+ int threadId, List<SerializableMessage> allEmails) async {
|
|
|
|
+ try {
|
|
|
|
+ var url = Uri.http(
|
|
|
|
+ '127.0.0.1:3001', 'get_thread_messages', {'id': threadId.toString()});
|
|
|
|
+ var response = await http.get(url);
|
|
|
|
+ if (response.statusCode == 200) {
|
|
|
|
+ List<dynamic> messagesJson = jsonDecode(response.body);
|
|
|
|
+ List<SerializableMessage> messages =
|
|
|
|
+ messagesJson.map((mj) => SerializableMessage.fromJson(mj)).toList();
|
|
|
|
+ allEmails.addAll(messages);
|
|
|
|
+ } else {
|
|
|
|
+ throw Exception(
|
|
|
|
+ 'Failed to fetch thread messages for thread ID: $threadId');
|
|
|
|
+ }
|
|
|
|
+ } catch (e) {
|
|
|
|
+ print('Error fetching thread messages: $e');
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Future<String> _getEmailContent(String relativePath) async {
|
|
|
|
+ String content = r"""
|
|
|
|
+
|
|
|
|
+ """;
|
|
|
|
+ try {
|
|
|
|
+ var url = Uri.http('127.0.0.1:3001', 'email', {'path': relativePath});
|
|
|
|
+ var response = await http.get(url);
|
|
|
|
+ if (response.statusCode == 200) {
|
|
|
|
+ print('ok');
|
|
|
|
+ content = response.body;
|
|
|
|
+ }
|
|
|
|
+ } catch (e) {
|
|
|
|
+ print('_getEmailContent caught error: $e');
|
|
|
|
+ }
|
|
|
|
+ // print(content);
|
|
|
|
+
|
|
|
|
+ return content;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Future<List<Widget>> _getDrawerItems() async {
|
|
|
|
+ List<String> drawerItems = [];
|
|
|
|
|
|
-// class ApiService {
|
|
|
|
-// static const String baseUrl = '127.0.0.1:3001';
|
|
|
|
|
|
+ try {
|
|
|
|
+ var url = Uri.http('127.0.0.1:3001', 'folders');
|
|
|
|
+ var response = await http.get(url);
|
|
|
|
+ drawerItems = List<String>.from(json.decode(response.body));
|
|
|
|
+ } catch (e) {
|
|
|
|
+ print('_getDrawerItems caught error: $e');
|
|
|
|
+ }
|
|
|
|
|
|
-// Future<Map<String, List<SerializableMessage>>> fetchEmailsByFolder(String folder) async {
|
|
|
|
-// Map<String, List<SerializableMessage>> messagesMap = {};
|
|
|
|
|
|
+ List<Widget> drawerWidgets = [];
|
|
|
|
|
|
-// try {
|
|
|
|
-// var url = Uri.http(baseUrl, '/sorted_threads_by_date', {'folder': folder});
|
|
|
|
-// var response = await http.get(url);
|
|
|
|
|
|
+ for (String item in drawerItems) {
|
|
|
|
+ drawerWidgets.add(
|
|
|
|
+ ListTile(
|
|
|
|
+ leading: Icon(Icons.mail),
|
|
|
|
+ title: Text(item),
|
|
|
|
+ onTap: () {
|
|
|
|
+ _displayEmailsFromFolder(item);
|
|
|
|
+ Navigator.pop(context);
|
|
|
|
+ },
|
|
|
|
+ ),
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
|
|
-// if (response.statusCode == 200) {
|
|
|
|
-// Map<String, dynamic> json = jsonDecode(response.body);
|
|
|
|
|
|
+ return drawerWidgets;
|
|
|
|
+ }
|
|
|
|
|
|
-// json.forEach((key, value) {
|
|
|
|
-// List<SerializableMessage> messages = (value as List)
|
|
|
|
-// .map((item) => SerializableMessage.fromJson(item))
|
|
|
|
-// .toList();
|
|
|
|
-// messagesMap[key] = messages;
|
|
|
|
-// });
|
|
|
|
-// }
|
|
|
|
-// } catch (e) {
|
|
|
|
-// print('fetchEmailsByFolder caught error: $e');
|
|
|
|
-// }
|
|
|
|
|
|
+ @override
|
|
|
|
+ Widget build(BuildContext context) {
|
|
|
|
+ return Scaffold(
|
|
|
|
+ appBar: AppBar(
|
|
|
|
+ backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
|
|
|
+ title: Text(widget.title),
|
|
|
|
+ ),
|
|
|
|
+ drawer: Drawer(
|
|
|
|
+ child: FutureBuilder<List<Widget>>(
|
|
|
|
+ future:
|
|
|
|
+ _getDrawerItems(), // call the async function to get the future
|
|
|
|
+ builder:
|
|
|
|
+ (BuildContext context, AsyncSnapshot<List<Widget>> snapshot) {
|
|
|
|
+ if (snapshot.connectionState == ConnectionState.waiting) {
|
|
|
|
+ // While data is loading, show a progress indicator
|
|
|
|
+ return Center(child: CircularProgressIndicator());
|
|
|
|
+ } else if (snapshot.hasError) {
|
|
|
|
+ // If something went wrong, show an error message
|
|
|
|
+ return Center(child: Text('Error: ${snapshot.error}'));
|
|
|
|
+ } else {
|
|
|
|
+ // When data is fetched successfully, display the items
|
|
|
|
+ return ListView(
|
|
|
|
+ padding: EdgeInsets.zero,
|
|
|
|
+ children:
|
|
|
|
+ snapshot.data!, // Unwrap the data once confirmed it's there
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ ),
|
|
|
|
+ ),
|
|
|
|
+ body: EmailListScreen(emails: emails, getEmailContent: _getEmailContent),
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
|
|
-// return messagesMap;
|
|
|
|
|
|
+class EmailListScreen extends StatelessWidget {
|
|
|
|
+ List emails;
|
|
|
|
+ final Future<String> Function(String) getEmailContent;
|
|
|
|
+
|
|
|
|
+ EmailListScreen({required this.emails, required this.getEmailContent});
|
|
|
|
+
|
|
|
|
+ @override
|
|
|
|
+ Widget build(BuildContext context) {
|
|
|
|
+ print(emails);
|
|
|
|
+ return Scaffold(
|
|
|
|
+ appBar: AppBar(
|
|
|
|
+ title: Text('Emails'),
|
|
|
|
+ ),
|
|
|
|
+ body: ListView.separated(
|
|
|
|
+ itemCount: emails.length,
|
|
|
|
+ itemBuilder: (context, index) {
|
|
|
|
+ return ListTile(
|
|
|
|
+ title: Text(emails[index].from,
|
|
|
|
+ style: TextStyle(fontWeight: FontWeight.bold)),
|
|
|
|
+ subtitle: Column(
|
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
+ children: [
|
|
|
|
+ Text(emails[index].subject),
|
|
|
|
+ ],
|
|
|
|
+ ),
|
|
|
|
+ trailing: Text(emails[index].date.toString()),
|
|
|
|
+ onTap: () async {
|
|
|
|
+ String emailContent = await getEmailContent(emails[index].path);
|
|
|
|
+ Navigator.push(
|
|
|
|
+ context,
|
|
|
|
+ MaterialPageRoute(
|
|
|
|
+ builder: (context) =>
|
|
|
|
+ EmailView(emailContent: emailContent)),
|
|
|
|
+ );
|
|
|
|
+ });
|
|
|
|
+ },
|
|
|
|
+ separatorBuilder: (context, index) {
|
|
|
|
+ return Divider();
|
|
|
|
+ },
|
|
|
|
+ ),
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+class EmailView extends StatefulWidget {
|
|
|
|
+ final String emailContent;
|
|
|
|
+
|
|
|
|
+ const EmailView({Key? key, required this.emailContent}) : super(key: key);
|
|
|
|
+ @override
|
|
|
|
+ _EmailViewState createState() => _EmailViewState();
|
|
|
|
+
|
|
|
|
+ // @override
|
|
|
|
+ // void initState(){
|
|
|
|
+ // ui.platformViewRegistry.registerViewFactory(
|
|
|
|
+ // 'html-view33',
|
|
|
|
+ // (int viewId) => html.IFrameElement()
|
|
|
|
+ // ..width = '100%'
|
|
|
|
+ // ..height = '100%'
|
|
|
|
+ // ..srcdoc = emailContent
|
|
|
|
+ // ..style.border = 'none');
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// @override
|
|
|
|
+// Widget build(BuildContext context) {
|
|
|
|
+// return Scaffold(
|
|
|
|
+// appBar: AppBar(
|
|
|
|
+// title: Text('Email Content'),
|
|
|
|
+// ),
|
|
|
|
+// // body: SingleChildScrollView(
|
|
|
|
+// // child: Padding(
|
|
|
|
+// // padding: const EdgeInsets.all(16.0),
|
|
|
|
+// // child: HtmlWidget(
|
|
|
|
+// // emailContent,
|
|
|
|
+// // onErrorBuilder: (context, element, error) =>
|
|
|
|
+// // Text('$element error: $error'),
|
|
|
|
+// // onLoadingBuilder: (context, element, loadingProgress) =>
|
|
|
|
+// // CircularProgressIndicator(),
|
|
|
|
+// // renderMode: RenderMode.column,
|
|
|
|
+// // // webView: true,
|
|
|
|
+// // ),
|
|
|
|
+// // ),
|
|
|
|
+// // ),
|
|
|
|
+// // body: Center(
|
|
|
|
+// // child: Html(
|
|
|
|
+// // data: emailContent
|
|
|
|
+// // )
|
|
|
|
+// // ,
|
|
|
|
+// // ),
|
|
|
|
+// body: Center(
|
|
|
|
+// child: HtmlElementView(viewType: 'html-view33',),
|
|
|
|
+// ),
|
|
|
|
+
|
|
|
|
+// );
|
|
// }
|
|
// }
|
|
|
|
+// }
|
|
|
|
+
|
|
|
|
+// class HtmlContentWidget
|
|
|
|
+
|
|
|
|
+// class HtmlIFrameView extends StatelessWidget {
|
|
|
|
+// final String emailContent;
|
|
|
|
|
|
-// Future<List<String>> fetchFolders() async {
|
|
|
|
-// List<String> folders = [];
|
|
|
|
|
|
+// const HtmlIFrameView({required this.emailContent});
|
|
|
|
|
|
-// try {
|
|
|
|
-// var url = Uri.http(baseUrl, '/folders');
|
|
|
|
-// var response = await http.get(url);
|
|
|
|
|
|
+// @override
|
|
|
|
+// Widget build(BuildContext context) {
|
|
|
|
+// return HtmlElementView(viewType: 'html-view');
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
+
|
|
|
|
+// class HtmlElementView extends StatelessWidget {
|
|
|
|
+// final String emailContent;
|
|
|
|
|
|
-// if (response.statusCode == 200) {
|
|
|
|
-// folders = List<String>.from(json.decode(response.body));
|
|
|
|
-// }
|
|
|
|
-// } catch (e) {
|
|
|
|
-// print('fetchFolders caught error: $e');
|
|
|
|
-// }
|
|
|
|
|
|
+// const HtmlElementView({required this.emailContent});
|
|
|
|
|
|
-// return folders;
|
|
|
|
|
|
+// @override
|
|
|
|
+// Widget build(BuildContext context) {
|
|
|
|
+// return IFrameElementWidget(emailContent: emailContent);
|
|
// }
|
|
// }
|
|
|
|
+// }
|
|
|
|
+
|
|
|
|
+// class IFrameElementWidget extends StatelessWidget {
|
|
|
|
+// final String emailContent;
|
|
|
|
+
|
|
|
|
+// const IFrameElementWidget({required this.emailContent});
|
|
|
|
|
|
-// Future<String> fetchEmailContent(String path) async {
|
|
|
|
-// try {
|
|
|
|
-// var url = Uri.http(baseUrl, path);
|
|
|
|
-// var response = await http.get(url);
|
|
|
|
-
|
|
|
|
-// if (response.statusCode == 200) {
|
|
|
|
-// return response.body;
|
|
|
|
-// } else {
|
|
|
|
-// print('Failed to load email content');
|
|
|
|
-// }
|
|
|
|
-// } catch (e) {
|
|
|
|
-// print('fetchEmailContent caught error: $e');
|
|
|
|
-// }
|
|
|
|
-
|
|
|
|
-// return '';
|
|
|
|
|
|
+// @override
|
|
|
|
+// Widget build(BuildContext context) {
|
|
|
|
+// ui.platformViewRegistry.registerViewFactory(
|
|
|
|
+// 'html-view',
|
|
|
|
+// (int viewId) => html.IFrameElement()
|
|
|
|
+// ..width = '100%'
|
|
|
|
+// ..height = '100%'
|
|
|
|
+// ..srcdoc = emailContent
|
|
|
|
+// ..style.border = 'none',
|
|
|
|
+// );
|
|
|
|
+// return HtmlElementView(
|
|
|
|
+// viewType: 'html-view',
|
|
|
|
+// );
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
+
|
|
|
|
+class _EmailViewState extends State<EmailView> {
|
|
|
|
+ @override
|
|
|
|
+ void initState() {
|
|
|
|
+ super.initState();
|
|
|
|
+
|
|
|
|
+ ui.platformViewRegistry.registerViewFactory(
|
|
|
|
+ 'html-view33',
|
|
|
|
+ (int viewId) => html.IFrameElement()
|
|
|
|
+ ..width = '100%'
|
|
|
|
+ ..height = '100%'
|
|
|
|
+ ..srcdoc = widget.emailContent
|
|
|
|
+ ..style.border = 'none',
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @override
|
|
|
|
+ Widget build(BuildContext context) {
|
|
|
|
+ return Scaffold(
|
|
|
|
+ appBar: AppBar(
|
|
|
|
+ title: Text('email content'),
|
|
|
|
+ ),
|
|
|
|
+ body: HtmlElementView(
|
|
|
|
+ viewType: 'html-view33',
|
|
|
|
+ ),
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+}
|