4 Angajamente 42e31f63c5 ... 254972d2af

Autor SHA1 Permisiunea de a trimite mesaje. Dacă este dezactivată, utilizatorul nu va putea trimite nici un fel de mesaj Data
  juan 254972d2af shorter time to collect threads due to new endpoint, and fixed the view for emails (needs improvement tho) 2 luni în urmă
  juan 04129f6da0 Viewspecs button WIP 2 luni în urmă
  juan 84c50d7b97 made this file to keep the model of emails (soon to be more moved here) 2 luni în urmă
  juan 9645db2fbe major refactor because of new endpoint supposed to bring runtime significantly (broke the opening of emails) 2 luni în urmă
3 a modificat fișierele cu 298 adăugiri și 132 ștergeri
  1. 173 125
      lib/api_service.dart
  2. 78 7
      lib/augment.dart
  3. 47 0
      lib/structs.dart

+ 173 - 125
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;
+// //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;
+//   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,
-  });
+//   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;
+//   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'],
-    );
-  }
-}
+//     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}");
+
+    // Convert allEmails to a list
   }
+// }
 
-  // Future<void> fetchThreads(AboutDialog){
-  // }
-
-  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;
+  // 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);
+  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(),
+    );
+  }
+}