1
0

4 Komitmen 42e31f63c5 ... 254972d2af

Pembuat SHA1 Pesan Tanggal
  juan 254972d2af shorter time to collect threads due to new endpoint, and fixed the view for emails (needs improvement tho) 4 jam lalu
  juan 04129f6da0 Viewspecs button WIP 1 hari lalu
  juan 84c50d7b97 made this file to keep the model of emails (soon to be more moved here) 1 hari lalu
  juan 9645db2fbe major refactor because of new endpoint supposed to bring runtime significantly (broke the opening of emails) 1 hari lalu
3 mengubah file dengan 303 tambahan dan 137 penghapusan
  1. 178 130
      lib/api_service.dart
  2. 78 7
      lib/augment.dart
  3. 47 0
      lib/structs.dart

+ 178 - 130
lib/api_service.dart

@@ -1,3 +1,5 @@
+import 'package:crab_ui/structs.dart';
+import 'package:english_words/english_words.dart';
 import 'package:flutter/material.dart';
 import 'package:http/http.dart' as http;
 import 'dart:convert';
@@ -26,57 +28,56 @@ class MailAddress {
   }
 }
 
-//data structure
-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'],
-    );
-  }
-}
+// //data structure
+// class SerializableMessage {
+//   final String name;
+//   final String from;
+//   final List<MailAddress> to;
+//   final List<MailAddress> cc;
+//   final String hash;
+
+//   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});
@@ -91,31 +92,23 @@ class _EmailPageState extends State<EmailPage> {
 
   void _displayEmailsFromFolder(String folder) async {
     // Map<String, List<SerializableMessage>> messagesMap = {};
-    List<SerializableMessage> allEmails = [];
+    List<GetThreadResponse> allEmails = []; //all the emails
 
     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.take(1)) { //each item in the json is a date
+        List json = jsonDecode(response.body);
+        for (var item in json.take(1)) {
+          //each item in the json is a date
           if (item.length > 1 && item[0] is String && item[1] is List) {
             List<int> threadIDs = List<int>.from(item[1]);
             for (var threadId in threadIDs) {
-              print(threadId);
-              await fetchThreadMessages(threadId, allEmails);
+              // print(threadId);
+              // await fetchThreadMessages(threadId, allEmails);
+              await fetchThreads(threadId, allEmails);
             }
             //TODO: get exact thread with new api endpoint from chosen thread?
           }
@@ -127,30 +120,43 @@ class _EmailPageState extends State<EmailPage> {
       print('_displayEmailsFromFolder caught error: $e');
     }
     print("Done");
+
     setState(() {
-      emails.clear();
-      // emails = messagesMap.values.toList().expand((list) => list).toList();
-      emails.addAll(allEmails);
-      ;
+      emails = allEmails;
     });
-  }
+    // print(emails[0]);
+    // Print allEmails to debug its structure
+    // print("allEmails: ${allEmails[0].messages}");
 
-  // Future<void> fetchThreads(AboutDialog){
-  // }
+    // Convert allEmails to a list
+  }
+// }
 
-  Future<void> fetchThreadMessages(
-      int threadId, List<SerializableMessage> allEmails) async {
+  Future<void> fetchThreads(
+      //complete
+      int threadId,
+      List<GetThreadResponse> allEmails) async {
     try {
-      var url = Uri.http(
-          '127.0.0.1:3001', 'get_thread_messages', {'id': threadId.toString()});
-
+      var url =
+          Uri.http('127.0.0.1:3001', 'get_thread', {'id': threadId.toString()});
       var response = await http.get(url);
-
+      // print(response.body);
       if (response.statusCode == 200) {
-        List<dynamic> messagesJson = jsonDecode(response.body);
-        List<SerializableMessage> messages =
-            messagesJson.map((mj) => SerializableMessage.fromJson(mj)).toList();
-        allEmails.addAll(messages);
+        Map<String, dynamic> messagesJson = jsonDecode(response.body);
+        // print(messagesJson);
+        // List<String> messagesofThread = messagesJson['messages'];
+        // messagesJson.map((mj) => GetThreadResponse.fromJson(mj)).toList();
+        // List<GetThreadResponse> messages = messagesJson.values.map((mj) {
+        //   return GetThreadResponse.fromJson(mj as Map<String, dynamic>);
+        // }).toList();
+        // List<GetThreadResponse> thread =
+        //     messagesJson.map((mj) => GetThreadResponse.fromJson(mj)).toList();
+        GetThreadResponse threadResponse =
+            GetThreadResponse.fromJson(messagesJson);
+
+        allEmails.add(
+            threadResponse); //adds all the messages of the thread into all emails
+        //perhaps should change
       } else {
         throw Exception(
             'Failed to fetch thread messages for thread ID: $threadId');
@@ -160,23 +166,46 @@ class _EmailPageState extends State<EmailPage> {
     }
   }
 
-  Future<String> _getEmailContent(String id) async {
-    String content = r"""
+  // 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(List<String> IDs) async {
+    String content = r"""
     """;
 
     try {
-      var url = Uri.http('127.0.0.1:3001', 'email', {'id': id});
+      for (var id in IDs) {
+        var url = Uri.http('127.0.0.1:3001', 'email', {'id': id});
 
-      var response = await http.get(url);
+        var response = await http.get(url);
 
-      if (response.statusCode == 200) {
-        content = response.body;
+        if (response.statusCode == 200) {
+          content += response.body;
+        }
       }
     } catch (e) {
       print('_getEmailContent caught error: $e');
     }
-
+    print(content);
     return content;
   }
 
@@ -249,14 +278,16 @@ class _EmailPageState extends State<EmailPage> {
 }
 
 class EmailListScreen extends StatelessWidget {
+  //this is the bulding of the drawer with all emails
+  // try to only get the subject and id, date, sender to make it faster
   final List emails;
-  final Future<String> Function(String) getEmailContent;
+  final Future<String> Function(List<String>) getEmailContent;
 
   EmailListScreen({
     required this.emails,
     required this.getEmailContent,
   });
-
+  // instead of getting the entire email, just the from, text, subject, and id
   @override
   Widget build(BuildContext context) {
     return Scaffold(
@@ -267,7 +298,7 @@ class EmailListScreen extends StatelessWidget {
         itemCount: emails.length,
         itemBuilder: (context, index) {
           return ListTile(
-              title: Text(emails[index].from,
+              title: Text(emails[index].from_name,
                   style: TextStyle(fontWeight: FontWeight.bold)),
               subtitle: Column(
                 crossAxisAlignment: CrossAxisAlignment.start,
@@ -276,31 +307,43 @@ class EmailListScreen extends StatelessWidget {
                 ],
               ),
               trailing: Text(emails[index].date.toString()),
+              //here we assign each part of json to a var, this could be changed so it only happens,
+              // when clicking on email for modularity
               onTap: () async {
-                String emailContent = await getEmailContent(emails[index].id);
-                String from = emails[index].from.toString();
-                String name = emails[index].name.toString();
+                String emailContent =
+                    await getEmailContent(emails[index].messages);
+                String messages = emails[index].messages.toString();
+                String fromName = emails[index].from_name.toString();
+                String fromAddress = emails[index].from_address.toString();
                 String to = emails[index].to.toString();
-                String cc = emails[index].cc.toString();
-                String hash = emails[index].hash.toString();
+                // String cc = emails[index].cc.toString();
+                // String hash = emails[index].hash.toString();
                 String subject = emails[index].subject.toString();
                 String date = emails[index].date.toString();
-                String uid = emails[index].uid.toString();
-                String list = emails[index].list.toString();
+                // String uid = emails[index].uid.toString();
+                // String list = emails[index].list.toString();
                 String id = emails[index].id.toString();
-                String in_reply_to = emails[index].in_reply_to.toString();
-                // String jsonbuilt =
+                // String in_reply_to = emails[index].in_reply_to.toString();
+
                 Navigator.push(
                   context,
                   MaterialPageRoute(
                       builder: (context) => EmailView(
                             emailContent: emailContent,
-                            // jsonEmail: jsonContent,
-                            from: from, name: name, to: to, cc: cc, hash: hash,
-                            subject: subject, date: date,
-                            uid: uid, list: list, id: id,
-                            in_reply_to: in_reply_to,
-                          )),
+                            from: fromAddress,
+                            name: fromName,
+                            to: to,
+                            // cc: cc,
+                            // hash: hash,
+                            subject: subject,
+                            date: date,
+                            // uid: uid,
+                            // list: list,
+                            id: id,
+                            // in_reply_to: in_reply_to,
+                          )
+                      //nada hpta
+                      ),
                 );
               });
         },
@@ -317,32 +360,37 @@ class EmailView extends StatefulWidget {
   // final String jsonEmail;
   final String from;
   final String name;
+
   final String to;
-  final String cc;
-  final String hash;
+
+  // final String to;
+  // final String cc;
+  // final String hash;
   final String subject;
   final String date;
-  final String uid;
-  final String list;
+  // final String uid;
+  // final String list;
   final String id;
-  final String in_reply_to;
-
-  const EmailView(
-      {Key? key,
-      required this.emailContent,
-      //  required this.jsonEmail,
-      required this.from,
-      required this.name,
-      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})
-      : super(key: key);
+  // final String in_reply_to;
+
+  const EmailView({
+    Key? key,
+    required this.emailContent,
+    //  required this.jsonEmail,
+    required this.from,
+    required this.name,
+    required this.to,
+
+    // 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
+  }) : super(key: key);
   @override
   _EmailViewState createState() => _EmailViewState();
 }

+ 78 - 7
lib/augment.dart

@@ -187,7 +187,7 @@ class _DynamicClassesAugment extends State<EmailToolbar> {
                 ),
               ),
               ElevatedButton(
-                onPressed: AugmentClasses.handleImages,
+                onPressed: () => AugmentClasses.FilterButton(context),
                 child: Text('Filter'),
               ),
               SizedBox(width: 8),
@@ -275,8 +275,8 @@ class AugmentClasses {
       builder: (context) => AlertDialog(
         title: Text('Jump Item:'),
         content: Container(
-          width: 200,
-          height: 120,
+          width: 300,
+          height: 170,
           child: Column(
             mainAxisSize: MainAxisSize.min,
             children: [
@@ -296,20 +296,56 @@ class AugmentClasses {
                   }
                 },
               ),
+              Spacer(
+                flex: 5,
+              ),
+              Row(
+                mainAxisSize: MainAxisSize.min,
+                children: [
+                  ElevatedButton(
+                    onPressed: () => AugmentClasses.ViewSpecsButton(context),
+                    child: Text("Viewspecs:"),
+                  ),
+                  SizedBox(
+                    width: 150,
+                    child: TextField(
+                      maxLines: 1,
+                      decoration: InputDecoration(
+                          labelText: '',
+                          border: OutlineInputBorder(),
+                          suffixIcon: Icon(Icons.search)),
+                      onSubmitted: (value) {
+                        print("onSubmitted: $value");
+                        if (value.isNotEmpty) {
+                          handleJump(value);
+                          Navigator.of(context).pop();
+                        }
+                      },
+                    ),
+                  ),
+                ],
+              ),
             ],
           ),
         ),
         actions: [
+          ElevatedButton(
+              onPressed: () {
+                //TODO: Grab both textfields and call both of the functions handles
+              },
+              child: Text('OK')),
           TextButton(
             onPressed: () {
               Navigator.of(context).pop();
               // print('close pressed');
             },
-            child: Text('close'),
+            child: Text('Cancel'),
           ),
           ElevatedButton(
-              onPressed: () => ViewSpecsButton(context),
-              child: Text('viewspecs'))
+              onPressed: () {
+                //TODO: in the ui demo didn't see it
+              },
+              child: Text('Help'))
         ],
       ),
     ).then((_) {
@@ -434,6 +470,7 @@ class AugmentClasses {
                           ElevatedButton(onPressed: () {}, child: Text('OK')),
                           ElevatedButton(
                               onPressed: () {
+                                AugmentClasses.disableIframePointerEvents();
                                 Navigator.of(context).pop();
                               },
                               child: Text('Cancel')),
@@ -447,10 +484,44 @@ class AugmentClasses {
                 ),
               ),
             )).then((_) {
-      AugmentClasses.enableIframePointerEvents();
+      AugmentClasses.enableIframePointerEvents(); // may be useless?
     });
   }
 
+  void handleFilter() {}
+  static Future<void> FilterButton(context) async {
+    //this is literally ctrl+F :skull:
+    //idea is to search in file, extract the <p> tags that contain these 
+    //words and highlight, then when zoom, you just jump to that paragraph
+
+    AugmentClasses.disableIframePointerEvents();
+    await showDialog(
+        context: context,
+        builder: (context) => Container(
+            height: 150,
+            width: 300,
+            child: AlertDialog(
+                title: Text('Filter'),
+                content: Container(
+                    width: 400, // Set the width to simulate the Windows style
+                    child: Column(
+                      mainAxisSize: MainAxisSize.min,
+                      crossAxisAlignment: CrossAxisAlignment.start,
+                      children: [
+                        Text('Set filter:'),
+                        SizedBox(
+                          width: 175,
+                          child: TextField(
+                            maxLines: 1,
+                            decoration: InputDecoration(
+                              border: OutlineInputBorder(),
+                            ),
+                          ),
+                        )
+                      ],
+                    )))));
+  }
+
   static void disableIframePointerEvents() {
     final iframes = html.document.getElementsByTagName('iframe');
     for (var iframe in iframes) {

+ 47 - 0
lib/structs.dart

@@ -0,0 +1,47 @@
+import 'package:flutter/material.dart';
+import 'package:http/http.dart' as http;
+import 'dart:convert';
+import 'dart:ui_web' as ui;
+import 'dart:html' as html;
+import 'augment.dart';
+import 'dart:js' as js;
+import 'api_service.dart';
+import 'dart:convert';  // For JSON decoding
+import 'package:intl/intl.dart'; 
+
+
+class GetThreadResponse {
+  final int id;
+  final List<String> messages;
+  final String subject;
+  final DateTime date;
+  final String from_name;
+  final String from_address;
+  final List<MailAddress> to;
+
+  GetThreadResponse({
+    required this.id,
+    required this.messages,
+    required this.subject,
+    required this.date,
+    required this.from_name,
+    required this.from_address,
+    required this.to,
+  });
+  factory GetThreadResponse.fromJson(Map<String, dynamic> json) {
+    var messagesList = json['messages'] as List<dynamic>;
+    var toList = json['to'] as List<dynamic>;
+    // var ccList = json['cc'] as List;
+
+    return GetThreadResponse (
+      id: json['id'],
+      // messages: messagesList.map((message) => message.toString()).toList(),
+      messages: List<String>.from(json['messages']),
+      subject: json['subject'],
+      date: DateTime.parse(json['date']),
+      from_name: json['from_name'],
+      from_address: json['from_address'],
+      to: toList.map((i)=> MailAddress.fromJson(i)).toList(),
+    );
+  }
+}