api_service.dart 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
  3. // import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
  4. import 'package:http/http.dart' as http;
  5. import 'dart:convert';
  6. //TODO: copy hope_page.dart impl of iframe
  7. import 'dart:ui_web' as ui;
  8. import 'dart:html' as html;
  9. // import 'package:flutter_html/flutter_html.dart';
  10. class MailAddress {
  11. final String? name;
  12. final String address;
  13. MailAddress({this.name, required this.address});
  14. factory MailAddress.fromJson(Map<String, dynamic> json) {
  15. return MailAddress(
  16. name: json['name'],
  17. address: json['address'],
  18. );
  19. }
  20. }
  21. class SerializableMessage {
  22. final String name;
  23. final String from;
  24. final List<MailAddress> to;
  25. final List<MailAddress> cc;
  26. final String hash;
  27. // final String path;
  28. final String subject;
  29. final String date;
  30. final int uid;
  31. final String list;
  32. final String id;
  33. final String in_reply_to;
  34. SerializableMessage({
  35. required this.name,
  36. required this.from,
  37. required this.to,
  38. required this.cc,
  39. required this.hash,
  40. required this.subject,
  41. required this.date,
  42. required this.uid,
  43. required this.list,
  44. required this.id,
  45. required this.in_reply_to,
  46. });
  47. factory SerializableMessage.fromJson(Map<String, dynamic> json) {
  48. var toList = json['to'] as List;
  49. var ccList = json['cc'] as List;
  50. return SerializableMessage(
  51. name: json['name'],
  52. from: json['from'],
  53. // to: json['name', 'address']
  54. to: toList.map((i) => MailAddress.fromJson(i)).toList(),
  55. cc: ccList.map((i) => MailAddress.fromJson(i)).toList(),
  56. // path: json['path'],
  57. hash: json['hash'],
  58. subject: json['subject'],
  59. date: json['date'],
  60. uid: json['uid'],
  61. list: json['list'],
  62. id: json['id'],
  63. in_reply_to: json['in_reply_to'],
  64. );
  65. }
  66. }
  67. class EmailPage extends StatefulWidget {
  68. const EmailPage({super.key});
  69. final String title = 'Emails';
  70. @override
  71. State<EmailPage> createState() => _EmailPageState();
  72. }
  73. class _EmailPageState extends State<EmailPage> {
  74. List emails = [];
  75. void _displayEmailsFromFolder(String folder) async {
  76. // Map<String, List<SerializableMessage>> messagesMap = {};
  77. List<SerializableMessage> allEmails = [];
  78. try {
  79. var url = Uri.http(
  80. '127.0.0.1:3001', 'sorted_threads_by_date', {'folder': folder});
  81. var response = await http.get(url);
  82. // print(response.body);
  83. // Map<String, dynamic> json = jsonDecode(response.body); original
  84. // json.forEach((key, value) {
  85. // List<SerializableMessage> messages = (value as List)
  86. // .map((item) => SerializableMessage.fromJson(item))
  87. // .toList();
  88. // messagesMap[key] = messages;
  89. // });
  90. // new shit
  91. if (response.statusCode == 200) {
  92. List<dynamic> json = jsonDecode(response.body);
  93. for (var item in json) {
  94. if (item.length > 1 && item[0] is String && item[1] is List) {
  95. // print('Date: ${item[0]}, Threads: ${item[1]}');
  96. List<int> threadIDs = List<int>.from(item[1]);
  97. for (var threadId in threadIDs) {
  98. await fetchThreadMessages(threadId, allEmails);
  99. }
  100. }
  101. }
  102. } else {
  103. throw Exception('Failed to load threads');
  104. }
  105. } catch (e) {
  106. print('_displayEmailsFromFolder caught error: $e');
  107. }
  108. setState(() {
  109. emails.clear();
  110. // emails = messagesMap.values.toList().expand((list) => list).toList();
  111. emails.addAll(allEmails);
  112. print(emails);
  113. ;
  114. });
  115. }
  116. Future<void> fetchThreadMessages(
  117. int threadId, List<SerializableMessage> allEmails) async {
  118. try {
  119. var url = Uri.http(
  120. '127.0.0.1:3001', 'get_thread_messages', {'id': threadId.toString()});
  121. var response = await http.get(url);
  122. if (response.statusCode == 200) {
  123. List<dynamic> messagesJson = jsonDecode(response.body);
  124. List<SerializableMessage> messages =
  125. messagesJson.map((mj) => SerializableMessage.fromJson(mj)).toList();
  126. allEmails.addAll(messages);
  127. } else {
  128. throw Exception(
  129. 'Failed to fetch thread messages for thread ID: $threadId');
  130. }
  131. } catch (e) {
  132. print('Error fetching thread messages: $e');
  133. }
  134. }
  135. Future<String> _getEmailContent(String relativePath) async {
  136. String content = r"""
  137. """;
  138. try {
  139. var url = Uri.http('127.0.0.1:3001', 'email', {'path': relativePath});
  140. var response = await http.get(url);
  141. if (response.statusCode == 200) {
  142. print('ok');
  143. content = response.body;
  144. }
  145. } catch (e) {
  146. print('_getEmailContent caught error: $e');
  147. }
  148. // print(content);
  149. return content;
  150. }
  151. Future<List<Widget>> _getDrawerItems() async {
  152. List<String> drawerItems = [];
  153. try {
  154. var url = Uri.http('127.0.0.1:3001', 'folders');
  155. var response = await http.get(url);
  156. drawerItems = List<String>.from(json.decode(response.body));
  157. } catch (e) {
  158. print('_getDrawerItems caught error: $e');
  159. }
  160. List<Widget> drawerWidgets = [];
  161. for (String item in drawerItems) {
  162. drawerWidgets.add(
  163. ListTile(
  164. leading: Icon(Icons.mail),
  165. title: Text(item),
  166. onTap: () {
  167. _displayEmailsFromFolder(item);
  168. Navigator.pop(context);
  169. },
  170. ),
  171. );
  172. }
  173. return drawerWidgets;
  174. }
  175. @override
  176. Widget build(BuildContext context) {
  177. return Scaffold(
  178. appBar: AppBar(
  179. backgroundColor: Theme.of(context).colorScheme.inversePrimary,
  180. title: Text(widget.title),
  181. ),
  182. drawer: Drawer(
  183. child: FutureBuilder<List<Widget>>(
  184. future:
  185. _getDrawerItems(), // call the async function to get the future
  186. builder:
  187. (BuildContext context, AsyncSnapshot<List<Widget>> snapshot) {
  188. if (snapshot.connectionState == ConnectionState.waiting) {
  189. // While data is loading, show a progress indicator
  190. return Center(child: CircularProgressIndicator());
  191. } else if (snapshot.hasError) {
  192. // If something went wrong, show an error message
  193. return Center(child: Text('Error: ${snapshot.error}'));
  194. } else {
  195. // When data is fetched successfully, display the items
  196. return ListView(
  197. padding: EdgeInsets.zero,
  198. children:
  199. snapshot.data!, // Unwrap the data once confirmed it's there
  200. );
  201. }
  202. },
  203. ),
  204. ),
  205. body: EmailListScreen(emails: emails, getEmailContent: _getEmailContent),
  206. );
  207. }
  208. }
  209. class EmailListScreen extends StatelessWidget {
  210. List emails;
  211. final Future<String> Function(String) getEmailContent;
  212. EmailListScreen({required this.emails, required this.getEmailContent});
  213. @override
  214. Widget build(BuildContext context) {
  215. print(emails);
  216. return Scaffold(
  217. appBar: AppBar(
  218. title: Text('Emails'),
  219. ),
  220. body: ListView.separated(
  221. itemCount: emails.length,
  222. itemBuilder: (context, index) {
  223. return ListTile(
  224. title: Text(emails[index].from,
  225. style: TextStyle(fontWeight: FontWeight.bold)),
  226. subtitle: Column(
  227. crossAxisAlignment: CrossAxisAlignment.start,
  228. children: [
  229. Text(emails[index].subject),
  230. ],
  231. ),
  232. trailing: Text(emails[index].date.toString()),
  233. onTap: () async {
  234. String emailContent = await getEmailContent(emails[index].path);
  235. Navigator.push(
  236. context,
  237. MaterialPageRoute(
  238. builder: (context) =>
  239. EmailView(emailContent: emailContent)),
  240. );
  241. });
  242. },
  243. separatorBuilder: (context, index) {
  244. return Divider();
  245. },
  246. ),
  247. );
  248. }
  249. }
  250. class EmailView extends StatefulWidget {
  251. final String emailContent;
  252. const EmailView({Key? key, required this.emailContent}) : super(key: key);
  253. @override
  254. _EmailViewState createState() => _EmailViewState();
  255. // @override
  256. // void initState(){
  257. // ui.platformViewRegistry.registerViewFactory(
  258. // 'html-view33',
  259. // (int viewId) => html.IFrameElement()
  260. // ..width = '100%'
  261. // ..height = '100%'
  262. // ..srcdoc = emailContent
  263. // ..style.border = 'none');
  264. }
  265. // @override
  266. // Widget build(BuildContext context) {
  267. // return Scaffold(
  268. // appBar: AppBar(
  269. // title: Text('Email Content'),
  270. // ),
  271. // // body: SingleChildScrollView(
  272. // // child: Padding(
  273. // // padding: const EdgeInsets.all(16.0),
  274. // // child: HtmlWidget(
  275. // // emailContent,
  276. // // onErrorBuilder: (context, element, error) =>
  277. // // Text('$element error: $error'),
  278. // // onLoadingBuilder: (context, element, loadingProgress) =>
  279. // // CircularProgressIndicator(),
  280. // // renderMode: RenderMode.column,
  281. // // // webView: true,
  282. // // ),
  283. // // ),
  284. // // ),
  285. // // body: Center(
  286. // // child: Html(
  287. // // data: emailContent
  288. // // )
  289. // // ,
  290. // // ),
  291. // body: Center(
  292. // child: HtmlElementView(viewType: 'html-view33',),
  293. // ),
  294. // );
  295. // }
  296. // }
  297. // class HtmlContentWidget
  298. // class HtmlIFrameView extends StatelessWidget {
  299. // final String emailContent;
  300. // const HtmlIFrameView({required this.emailContent});
  301. // @override
  302. // Widget build(BuildContext context) {
  303. // return HtmlElementView(viewType: 'html-view');
  304. // }
  305. // }
  306. // class HtmlElementView extends StatelessWidget {
  307. // final String emailContent;
  308. // const HtmlElementView({required this.emailContent});
  309. // @override
  310. // Widget build(BuildContext context) {
  311. // return IFrameElementWidget(emailContent: emailContent);
  312. // }
  313. // }
  314. // class IFrameElementWidget extends StatelessWidget {
  315. // final String emailContent;
  316. // const IFrameElementWidget({required this.emailContent});
  317. // @override
  318. // Widget build(BuildContext context) {
  319. // ui.platformViewRegistry.registerViewFactory(
  320. // 'html-view',
  321. // (int viewId) => html.IFrameElement()
  322. // ..width = '100%'
  323. // ..height = '100%'
  324. // ..srcdoc = emailContent
  325. // ..style.border = 'none',
  326. // );
  327. // return HtmlElementView(
  328. // viewType: 'html-view',
  329. // );
  330. // }
  331. // }
  332. class _EmailViewState extends State<EmailView> {
  333. @override
  334. void initState() {
  335. super.initState();
  336. ui.platformViewRegistry.registerViewFactory(
  337. 'html-view33',
  338. (int viewId) => html.IFrameElement()
  339. ..width = '100%'
  340. ..height = '100%'
  341. ..srcdoc = widget.emailContent
  342. ..style.border = 'none',
  343. );
  344. }
  345. @override
  346. Widget build(BuildContext context) {
  347. return Scaffold(
  348. appBar: AppBar(
  349. title: Text('email content'),
  350. ),
  351. body: HtmlElementView(
  352. viewType: 'html-view33',
  353. ),
  354. );
  355. }
  356. }