diff --git a/lib/api_service.dart b/lib/api_service.dart index 3257d94..75d0fad 100644 --- a/lib/api_service.dart +++ b/lib/api_service.dart @@ -1,93 +1,401 @@ -//api file +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}); -// import 'package:http/http.dart' as http; -// import 'dart:convert'; + factory MailAddress.fromJson(Map json) { + return MailAddress( + name: json['name'], + address: json['address'], + ); + } +} -// class SerializableMessage { -// final String name; -// final String from; -// final String path; -// final String subject; -// final String date; +class SerializableMessage { + final String name; + final String from; + final List to; + final List cc; + final String hash; -// SerializableMessage({ -// required this.name, -// required this.from, -// required this.path, -// required this.subject, -// required this.date, -// }); + // 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 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 createState() => _EmailPageState(); +} + +class _EmailPageState extends State { + List emails = []; + + void _displayEmailsFromFolder(String folder) async { + // Map> messagesMap = {}; + List 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 json = jsonDecode(response.body); original + + // json.forEach((key, value) { + // List messages = (value as List) + // .map((item) => SerializableMessage.fromJson(item)) + // .toList(); + // messagesMap[key] = messages; + // }); + + // new shit + if (response.statusCode == 200) { + List 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 threadIDs = List.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 fetchThreadMessages( + int threadId, List 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 messagesJson = jsonDecode(response.body); + List 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 _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> _getDrawerItems() async { + List drawerItems = []; + + try { + var url = Uri.http('127.0.0.1:3001', 'folders'); + var response = await http.get(url); + drawerItems = List.from(json.decode(response.body)); + } catch (e) { + print('_getDrawerItems caught error: $e'); + } + + List drawerWidgets = []; + + for (String item in drawerItems) { + drawerWidgets.add( + ListTile( + leading: Icon(Icons.mail), + title: Text(item), + onTap: () { + _displayEmailsFromFolder(item); + Navigator.pop(context); + }, + ), + ); + } + + return drawerWidgets; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + title: Text(widget.title), + ), + drawer: Drawer( + child: FutureBuilder>( + future: + _getDrawerItems(), // call the async function to get the future + builder: + (BuildContext context, AsyncSnapshot> 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), + ); + } +} + +class EmailListScreen extends StatelessWidget { + List emails; + final Future 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',), +// ), -// factory SerializableMessage.fromJson(Map json) { -// return SerializableMessage( -// name: json['name'], -// from: json['from'], -// path: json['path'], -// subject: json['subject'], -// date: json['date'], // ); // } // } -// class ApiService { -// static const String baseUrl = '127.0.0.1:3001'; +// class HtmlContentWidget -// Future>> fetchEmailsByFolder(String folder) async { -// Map> messagesMap = {}; +// class HtmlIFrameView extends StatelessWidget { +// final String emailContent; -// try { -// var url = Uri.http(baseUrl, '/sorted_threads_by_date', {'folder': folder}); -// var response = await http.get(url); +// const HtmlIFrameView({required this.emailContent}); -// if (response.statusCode == 200) { -// Map json = jsonDecode(response.body); - -// json.forEach((key, value) { -// List messages = (value as List) -// .map((item) => SerializableMessage.fromJson(item)) -// .toList(); -// messagesMap[key] = messages; -// }); -// } -// } catch (e) { -// print('fetchEmailsByFolder caught error: $e'); -// } - -// return messagesMap; -// } - -// Future> fetchFolders() async { -// List folders = []; - -// try { -// var url = Uri.http(baseUrl, '/folders'); -// var response = await http.get(url); - -// if (response.statusCode == 200) { -// folders = List.from(json.decode(response.body)); -// } -// } catch (e) { -// print('fetchFolders caught error: $e'); -// } - -// return folders; -// } - -// Future 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) { +// return HtmlElementView(viewType: 'html-view'); // } // } + +// class HtmlElementView extends StatelessWidget { +// final String emailContent; + +// const HtmlElementView({required this.emailContent}); + +// @override +// Widget build(BuildContext context) { +// return IFrameElementWidget(emailContent: emailContent); +// } +// } + +// class IFrameElementWidget extends StatelessWidget { +// final String emailContent; + +// const IFrameElementWidget({required this.emailContent}); + +// @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 { + @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', + ), + ); + } +}