api_service.dart 12 KB

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