currentContent) { // i think this doesnt work anymore
- // setState(() { //update to do item per item
- // // each item to have itsviewtype ID
- // // is this necessarey here??
-
- // //could just move to collapsable
-
- // viewTypeId = 'iframe-${DateTime.now().millisecondsSinceEpoch}';
- // final emailHTML = web.document.createElement('div') as web.HTMLDivElement
- // ..id = viewTypeId
- // ..innerHTML = currentContent[0].toJS; // temporarily index because it has to do all of them
- // emailHTML.style
- // ..width = '100%'
- // ..height = '100%'
- // ..overflow = 'auto'
- // ..scrollBehavior = 'smooth';
-
- // ui.platformViewRegistry.registerViewFactory(
- // viewTypeId,
- // (int viewId) => emailHTML,
- // );
- // });
- // }
-
- void _scrollToNumber(String spanId) {
- AugmentClasses.handleJump(spanId);
- }
-
- // TODO: void _invisibility(String ) //to make purple numbers not visible
-
- @override
- Widget build(BuildContext context) {
- // print("thread id ${widget.id}");
- ApiService.currThreadID = widget.id;
- return Scaffold(
- appBar: AppBar(
- title: Text(widget.name),
- ),
- body: Stack(
- children: [
- Column(
- children: [
- EmailToolbar(
- onJumpToSpan: _scrollToNumber,
- onButtonPressed: () => {},
- // AugmentClasses.handleJump(viewTypeId, '1');
- // print("button got pressed?");
-
- // _registerViewFactory(r"""
- // Welcome to My Website
- // This is a simple HTML page.
- // What is HTML?
- // HTML (HyperText Markup Language) is the most basic building~ block of the Web. It defines the meaning and structure of web content. Other technologies besides HTML are generally used to describe a web page's appearance/presentation (CSS) or functionality/behavior (JavaScript).
- // Here's a simple list:
- //
- // - HTML elements are the building blocks of HTML pages
- // - HTML uses tags like
<tag>
to organize and format content
- // - CSS is used with HTML to style pages
- //
- // Copyright © 2023
- // """);
- // print("change");
- // widget.emailContent = r"
-
- //
- ),
- Row(
- // title of email
- children: [
- Text(
- widget.subject,
- style: TextStyle(fontSize: 30),
- ),
- ],
- ),
- Row(
- children: [
- Text(
- 'from ${widget.name}',
- style: TextStyle(fontSize: 18),
- ),
- Text(
- '<${widget.from}>',
- style: TextStyle(fontSize: 18),
- ),
- Spacer(),
- Text(
- '${widget.date}',
- textAlign: TextAlign.right,
- )
- ],
- ),
- // TODO: make a case where if one of these is the user's email it just says me :)))))
- Row(
- children: [
- Text(
- 'to ${widget.to.toString()}',
- style: TextStyle(fontSize: 15),
- )
- ],
- ),
- Expanded(
- child: CollapsableEmails(
- //change here
- thread: widget.messages, //this wont work in serializable
- threadHTML: widget.emailContent,
- threadIDs: widget.id,
- ),
- ),
- // Expanded(
- // child: HtmlElementView(
- // key: UniqueKey(),
- // viewType: viewTypeId,
- // ),
- // ),
- ],
- ),
-
- // Overlay widgets dynamically based on marker positions
- // FutureBuilder>>(
- // future: _markerPositionsFuture,
- // builder: (context, snapshot) {
- // print("FutureBuilder state: ${snapshot.connectionState}");
- // if (snapshot.connectionState == ConnectionState.waiting) {
- // return Center(child: CircularProgressIndicator());
- // }
- // if (snapshot.hasError) {
- // print("Error in FutureBuilder: ${snapshot.error}");
- // return Center(child: Text('error loading markers'));
- // }
- // if (snapshot.hasData && snapshot.data != null) {
- // final markers = snapshot.data!;
- // return Stack(
- // children: markers.map((marker) {
- // return Positioned(
- // left: marker['x'].toDouble(),
- // top: marker['y'].toDouble(),
- // child: GestureDetector(
- // onTap: () {
- // print('Tapped on ${marker['id']}');
- // },
- // child: Container(
- // width: 50,
- // height: 50,
- // color: Colors.red,
- // child: Center(
- // child: Text(
- // marker['id'],
- // style: TextStyle(color: Colors.white),
- // ),
- // ),
- // ),
- // ),
- // );
- // }).toList(),
- // );
- // }
-
- // return SizedBox.shrink(); // No markers found
- // },
- // ),
- // Red widget overlay
- // Positioned(
- // left: 8, // Adjust based on your desired position
- // top: 100 + 44 + 5, // Adjust based on your desired position
- // child: IgnorePointer(
- // ignoring: true, // Ensures the iframe remains interactive
- // child: Container(
- // color: Colors.red,
- // width: 100,
- // height: 50,
- // child: Center(
- // child: Text(
- // 'Overlay',
- // style: TextStyle(color: Colors.white),
- // ),
- // ),
- // ),
- // ),
- // ),
- ],
- ));
+ try {
+ //attaches email after email from a thread
+ for (var id in IDsString) {
+ var url = Uri.http('$ip:$port', 'email_md', {'id': id});
+ print(url);
+ var response = await http.get(url);
+ currThread.add(id);
+ if (response.statusCode == 200) {
+ counter += 1;
+ Map json = jsonDecode(response.body);
+
+ MDofThread.add(json['md'] ?? '');
+ try {
+ List attachments =
+ await getAttachmentsInfo(emailFolder, id);
+ for (var attachment in attachments) {
+ //TODO: for each attachment creaate at the bottom a widget for each individual one
+ threadAttachments
+ .add(await getAttachment(emailFolder, id, attachment.name));
+ }
+ } catch (innerError) {
+ print('_getAttachment info caught error $innerError');
+ }
+ }
+ }
+ } catch (e) {
+ print('_getMDContent caught error: $e');
+ }
+
+ return MDofThread;
}
}
diff --git a/lib/attachamentDownloadStub.dart b/lib/attachamentDownloadStub.dart
new file mode 100644
index 0000000..5daf854
--- /dev/null
+++ b/lib/attachamentDownloadStub.dart
@@ -0,0 +1,7 @@
+import 'structs.dart';
+
+class Attachmentdownload {
+ Future saveFile(AttachmentResponse attachment) async {
+ print("stub attachment download");
+ }
+}
diff --git a/lib/attachmentDownload.dart b/lib/attachmentDownload.dart
index 63364ba..f1f8f86 100644
--- a/lib/attachmentDownload.dart
+++ b/lib/attachmentDownload.dart
@@ -1,15 +1,3 @@
-import 'dart:html' as html;
-import 'package:web/web.dart' as web;
-import 'dart:io';
-import 'structs.dart';
-import 'package:file_saver/file_saver.dart';
-
-class Attachmentdownload {
- Future saveFile(AttachmentResponse attachment) async {
- await FileSaver.instance.saveFile(
- name: attachment.name.toString().substring(0, attachment.name.toString().lastIndexOf('.')),
- bytes: attachment.data,
- ext: attachment.name.toString().substring(attachment.name.toString().lastIndexOf('.')+1)
- );
- }
-}
+export 'attachamentDownloadStub.dart'
+ if (dart.library.io) 'attachmentDownloadAndroid.dart';
+ // if (dart.library.js_interop) 'attachmentDownloadWeb.dart';
\ No newline at end of file
diff --git a/lib/attachmentDownloadAndroid.dart b/lib/attachmentDownloadAndroid.dart
new file mode 100644
index 0000000..603253c
--- /dev/null
+++ b/lib/attachmentDownloadAndroid.dart
@@ -0,0 +1,7 @@
+import 'structs.dart';
+
+class Attachmentdownload {
+ Future saveFile(AttachmentResponse attachment) async {
+ print("android attachment download");
+ }
+}
diff --git a/lib/attachmentDownloadWeb.dart b/lib/attachmentDownloadWeb.dart
new file mode 100644
index 0000000..cb4954c
--- /dev/null
+++ b/lib/attachmentDownloadWeb.dart
@@ -0,0 +1,12 @@
+// import 'structs.dart';
+// import 'package:file_saver/file_saver.dart';
+
+// class Attachmentdownload {
+// Future saveFile(AttachmentResponse attachment) async {
+// await FileSaver.instance.saveFile(
+// name: attachment.name.toString().substring(0, attachment.name.toString().lastIndexOf('.')),
+// bytes: attachment.data,
+// ext: attachment.name.toString().substring(attachment.name.toString().lastIndexOf('.')+1)
+// );
+// }
+// }
diff --git a/lib/attachmentWidget.dart b/lib/attachmentWidget.dart
index 5146d9c..c40eae1 100644
--- a/lib/attachmentWidget.dart
+++ b/lib/attachmentWidget.dart
@@ -1,103 +1,3 @@
-import "dart:typed_data";
-
-import "package:crab_ui/attachmentDownload.dart";
-import "package:crab_ui/structs.dart";
-import "package:flutter/material.dart";
-import 'package:pdfrx/pdfrx.dart';
-import 'package:photo_view/photo_view.dart';
-
-class AttachmentWidget extends StatelessWidget {
- final AttachmentResponse attachment;
- AttachmentWidget({required this.attachment});
-
- Widget attachmentViewer(AttachmentResponse att) {
- String extension = att.name
- .toString()
- .substring(att.name.toString().indexOf(".") + 1)
- .toLowerCase();
- if (extension == "jpg" || extension == "png") {
- return Image.memory(att.data);
- } else if (extension == "pdf") {
- return PdfViewer.data(Uint8List.fromList(att.data),
- sourceName: att.name,
- params: PdfViewerParams(
- enableTextSelection: true,
- scrollByMouseWheel: 0.5,
- annotationRenderingMode:
- PdfAnnotationRenderingMode.annotationAndForms,
- ));
- }
- return Center(
- child: Container(
- padding: EdgeInsets.all(20),
- decoration: BoxDecoration(
- color: Color(0xff6C63FF),
- borderRadius: BorderRadius.circular(16),
- boxShadow: [
- BoxShadow(
- color: Colors.black26,
- blurRadius: 10,
- ),
- ],
- ),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- Text(
- "No preview available",
- style: TextStyle(
- color: Colors.white, fontSize: 18, decoration: TextDecoration.none),
- ),
- SizedBox(
- height: 5,
- ),
- GestureDetector(
- child: ElevatedButton(
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- Text("Download", style: TextStyle(color: Color(0xff2c3e50)),),
- Icon(Icons.download,
- color: Color(0xffb0b0b0),),
- ]),
- onPressed: () => Attachmentdownload().saveFile(att),
- )),
- ]),
- ));
- }
-
- @override
- Widget build(BuildContext context) {
- return Container(
- color: Colors.black38,
- child: Stack(children: [
- Container(
- color: Colors.white,
- child: Padding(
- padding: EdgeInsets.fromLTRB(10, 20, 0, 10),
- child: Column(
- children: [
- Row(
- children: [
- CloseButton(onPressed: () => {Navigator.pop(context)}),
- Text(
- attachment.name
- .toString(), //its alr a string but incase ¯\(ツ)/¯ //update: i did that everywhere lol
- style: TextStyle(
- color: Colors.black,
- fontSize: 20,
- decoration: TextDecoration
- .none), //TODO: personalize your fonts
- ),
- ],
- ),
- Expanded(
- child: attachmentViewer(attachment),
- )
- ],
- ),
- ),
- ),
- ]));
- }
-}
+export 'attachmentWidgetStub.dart'
+ if (dart.library.js_interop) 'attachmentWidgetWeb.dart'
+ if (dart.library.io) 'attachmentWidgetAndroid.dart';
\ No newline at end of file
diff --git a/lib/attachmentWidgetAndroid.dart b/lib/attachmentWidgetAndroid.dart
new file mode 100644
index 0000000..72c3cd8
--- /dev/null
+++ b/lib/attachmentWidgetAndroid.dart
@@ -0,0 +1,16 @@
+import "package:crab_ui/structs.dart";
+import "package:flutter/material.dart";
+
+
+class AttachmentWidget extends StatelessWidget {
+ final AttachmentResponse attachment;
+ AttachmentWidget({required this.attachment});
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ body: Text("PDF EVENTUALLY ANDROID")
+ );
+ }
+}
+
diff --git a/lib/attachmentWidgetStub.dart b/lib/attachmentWidgetStub.dart
new file mode 100644
index 0000000..5bb6c14
--- /dev/null
+++ b/lib/attachmentWidgetStub.dart
@@ -0,0 +1,14 @@
+import 'package:flutter/material.dart';
+import 'structs.dart';
+
+class AttachmentWidget extends StatelessWidget{
+ final AttachmentResponse attachment;
+ AttachmentWidget({required this.attachment});
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ body: Text("PDF EVENTUALLY, STUB")
+ );
+ }
+}
\ No newline at end of file
diff --git a/lib/attachmentWidgetWeb.dart b/lib/attachmentWidgetWeb.dart
new file mode 100644
index 0000000..b8b260b
--- /dev/null
+++ b/lib/attachmentWidgetWeb.dart
@@ -0,0 +1,101 @@
+import "dart:typed_data";
+import "package:crab_ui/attachmentDownload.dart";
+import "package:crab_ui/structs.dart";
+import "package:flutter/material.dart";
+import 'package:pdfrx/pdfrx.dart';
+
+class AttachmentWidget extends StatelessWidget {
+ final AttachmentResponse attachment;
+ AttachmentWidget({required this.attachment});
+
+ Widget attachmentViewer(AttachmentResponse att) {
+ String extension = att.name
+ .toString()
+ .substring(att.name.toString().indexOf(".") + 1)
+ .toLowerCase();
+ if (extension == "jpg" || extension == "png") {
+ return Image.memory(att.data);
+ } else if (extension == "pdf") {
+ return PdfViewer.data(Uint8List.fromList(att.data),
+ sourceName: att.name,
+ params: PdfViewerParams(
+ enableTextSelection: true,
+ scrollByMouseWheel: 0.5,
+ annotationRenderingMode:
+ PdfAnnotationRenderingMode.annotationAndForms,
+ ));
+ }
+ return Center(
+ child: Container(
+ padding: EdgeInsets.all(20),
+ decoration: BoxDecoration(
+ color: Color(0xff6C63FF),
+ borderRadius: BorderRadius.circular(16),
+ boxShadow: [
+ BoxShadow(
+ color: Colors.black26,
+ blurRadius: 10,
+ ),
+ ],
+ ),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ "No preview available",
+ style: TextStyle(
+ color: Colors.white, fontSize: 18, decoration: TextDecoration.none),
+ ),
+ SizedBox(
+ height: 5,
+ ),
+ GestureDetector(
+ child: ElevatedButton(
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text("Download", style: TextStyle(color: Color(0xff2c3e50)),),
+ Icon(Icons.download,
+ color: Color(0xffb0b0b0),),
+ ]),
+ onPressed: () => Attachmentdownload().saveFile(att),
+ )),
+ ]),
+ ));
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ color: Colors.black38,
+ child: Stack(children: [
+ Container(
+ color: Colors.white,
+ child: Padding(
+ padding: EdgeInsets.fromLTRB(10, 20, 0, 10),
+ child: Column(
+ children: [
+ Row(
+ children: [
+ CloseButton(onPressed: () => {Navigator.pop(context)}),
+ Text(
+ attachment.name
+ .toString(), //its alr a string but incase ¯\(ツ)/¯ //update: i did that everywhere lol
+ style: TextStyle(
+ color: Colors.black,
+ fontSize: 20,
+ decoration: TextDecoration
+ .none), //TODO: personalize your fonts
+ ),
+ ],
+ ),
+ Expanded(
+ child: attachmentViewer(attachment),
+ )
+ ],
+ ),
+ ),
+ ),
+ ]));
+ }
+}
diff --git a/lib/augment.dart b/lib/augment.dart
index b957740..8b8fa60 100644
--- a/lib/augment.dart
+++ b/lib/augment.dart
@@ -1,23 +1,17 @@
-// import 'dart:ffi';
-
import 'package:crab_ui/api_service.dart';
import 'package:crab_ui/attachmentDownload.dart';
import 'package:crab_ui/structs.dart';
import 'package:flutter/material.dart';
-import 'package:pdfrx/pdfrx.dart';
-import 'package:pointer_interceptor/pointer_interceptor.dart';
-// import 'dart:html' as html;
-// import 'dart:js' as js;
-import 'package:web/web.dart' as web;
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'attachmentWidget.dart';
class EmailToolbar extends StatefulWidget {
- final Function(String) onJumpToSpan;
+ final Function(String) onJumpToNumbering;
+ final Function(String) onViewspecs;
final VoidCallback onButtonPressed;
EmailToolbar(
- {Key? key, required this.onButtonPressed, required this.onJumpToSpan})
+ {Key? key, required this.onButtonPressed, required this.onJumpToNumbering, required this.onViewspecs})
: super(key: key);
@override
@@ -26,7 +20,8 @@ class EmailToolbar extends StatefulWidget {
class _DynamicClassesAugment extends State {
String selectedClass = 'Class 1';
- // TextEditingController _jumpController = TextEditingController();
+ TextEditingController _jumpController = TextEditingController();
+ TextEditingController _viewspecsController = TextEditingController();
// late final FocusNode _JumpItemfocusNode;
// late final FocusNode _viewSpecsfocusNode;
@@ -53,7 +48,7 @@ class _DynamicClassesAugment extends State {
void dispose() {
// _JumpItemfocusNode.dispose();
// _viewSpecsfocusNode.dispose();
- // _jumpController.dispose();
+ _jumpController.dispose();
super.dispose();
}
@@ -78,20 +73,20 @@ class _DynamicClassesAugment extends State {
child: Text('Attachments'),
),
SizedBox(width: 8),
- ElevatedButton(
- onPressed: AugmentClasses.handleOpen,
- child: Text('Open'),
- ),
+ // ElevatedButton(
+ // onPressed: AugmentClasses.handleOpen,
+ // child: Text('Open'),
+ // ),
// SizedBox(width: 8),
ElevatedButton(
onPressed: AugmentClasses.handleFind,
child: Text('Find'),
),
// SizedBox(width: 8),
- ElevatedButton(
- onPressed: AugmentClasses.handleStop,
- child: Text('Stop'),
- ),
+ // ElevatedButton(
+ // onPressed: AugmentClasses.handleStop,
+ // child: Text('Stop'),
+ // ),
ElevatedButton(
onPressed: () {
AugmentClasses.handleMove(context);
@@ -138,10 +133,10 @@ class _DynamicClassesAugment extends State {
// width: 8,
// ),
Container(
- width: 50,
+ width: 100,
height: 30,
child: TextField(
- // controller: _jumpController,
+ controller: _jumpController,
decoration: InputDecoration(
border: OutlineInputBorder(),
// suffixIcon: Icon(Icons.search)
@@ -149,7 +144,7 @@ class _DynamicClassesAugment extends State {
onSubmitted: (value) {
print("onSubmitted");
if (value.isNotEmpty) {
- widget.onJumpToSpan(value);
+ widget.onJumpToNumbering(value);
}
},
),
@@ -186,14 +181,18 @@ class _DynamicClassesAugment extends State {
onPressed: () => AugmentClasses.ViewSpecsButton(context),
child: Text('ViewSpecs:')),
Container(
- width: 50,
+ width: 100,
height: 30,
child: TextField(
+ controller: _viewspecsController,
decoration: InputDecoration(
labelText: '',
border: OutlineInputBorder(),
// suffixIcon: Icon(Icons.style_rounded)
),
+ onSubmitted: (value) {
+ widget.onViewspecs(value);
+ },
),
),
ElevatedButton(
@@ -491,30 +490,8 @@ class AugmentClasses {
print("Stop button pressed");
}
- static void handleJump(String spanId) {
- String js_code = '''
- var iframe = document.getElementsByTagName('iframe')[0]; // 0 for the first iframe, 1 for the second, etc.
-
- // Check if the iframe is loaded and has content
- if (iframe && iframe.contentDocument) {
- // Access the document inside the iframe
- var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
-
- // Find the element with the specific id inside the iframe
- var targetElement = iframeDoc.getElementById("$spanId"); // Replace '36 ' with the actual id of the target element
-
- // If the element exists, scroll to it
- if (targetElement) {
- targetElement.scrollIntoView();
- console.log('Scrolled to element with id "$spanId" inside the iframe.');
- } else {
- console.log('Element with id "$spanId" not found inside the iframe.');
- }
- } else {
- console.log('Iframe not found or not loaded.');
- }
- ''';
- // js.context.callMethod('eval', [js_code]);
+ static void handleJump(String value) {
+ print(value);
}
static void invisibility(String htmlClass) {}
diff --git a/lib/collapsableEmails.dart b/lib/collapsableEmails.dart
index ceed92a..e8b144d 100644
--- a/lib/collapsableEmails.dart
+++ b/lib/collapsableEmails.dart
@@ -1,132 +1,3 @@
-import 'dart:js_interop';
-import 'package:web/web.dart' as web;
-import 'package:flutter/material.dart';
-import 'dart:ui_web' as ui;
-import 'api_service.dart';
-import 'structs.dart';
-
-class CollapsableEmails extends StatefulWidget {
- final List thread; // email id's in the form xyz@gmail.com
- final List threadHTML;
- final String threadIDs;
-
- CollapsableEmails(
- {required this.thread,
- required this.threadHTML,
- required this.threadIDs});
-
- @override
- State createState() => _CollapsableEmailsState();
-}
-
-class _CollapsableEmailsState extends State {
- List emailsHTML = []; //html of the emails in the thread
- // build attachments with the forldar name and id
- Set _expandedEmails = {}; //open emails
- List viewtypeIDs = []; //IDs of the viewtypes, order matters
- List heightOfViewTypes = []; //the height of each viewtype
- List emailsInThread = [];
- bool _isLoaded = false;
-
- @override
- void initState() {
- // TODO: implement initState
- super.initState();
- _registerViewFactory(widget.threadHTML);
- _serializableData(widget.threadIDs);
- }
-
- void _registerViewFactory(List currentContent) async {
- // setState(() { //update to do item per item
- // each item to have itsviewtype ID
- // is this necessarey here??
-
- //could just move to collapsable
-
- for (var emailHTML in widget.threadHTML) {
- String viewTypeId = 'email-${DateTime.now().millisecondsSinceEpoch}';
-
- final ghost = web.document.createElement('div') as web.HTMLDivElement
- ..style.visibility = 'hidden'
- ..style.position = 'absolute'
- ..style.width = '100%'
- ..style.overflow = 'auto'
- ..innerHTML = emailHTML
- .toJS; // temporarily index because it has to do all of them
- web.document.body?.append(ghost);
- await Future.delayed(Duration(milliseconds: 10));
-
- final heightOfEmail = ghost.scrollHeight;
- ghost.remove();
-
- final HTMLsnippet = web.document.createElement('div')
- as web.HTMLDivElement
- ..id = viewTypeId
- ..innerHTML = emailHTML
- .toJS; // temporarily index because it has to do all of them
- HTMLsnippet.style
- ..width = '100%'
- ..height = '${heightOfEmail}px'
- ..overflow = 'auto'
- ..scrollBehavior = 'smooth';
-
- ui.platformViewRegistry.registerViewFactory(
- viewTypeId,
- (int viewId) => HTMLsnippet,
- );
- viewtypeIDs.add(viewTypeId);
- heightOfViewTypes.add(heightOfEmail);
- }
- }
-
- void _serializableData(String threadID) async {
- emailsInThread = await ApiService().threadsInSerializable(threadID);
- print("done thread serializable");
- if (!mounted) return;
- setState(() {
- _isLoaded = true;
- });
- }
-
- @override
- Widget build(BuildContext context) {
- return _isLoaded
- ?Column(children: [
- Expanded(
- child: ListView.builder(
- itemCount: widget.thread.length,
- itemBuilder: (context, index) {
- final isExpanded =
- _expandedEmails.contains(index); //check if email is expanded
- return Column(
- children: [
- ListTile(
- title: Text(emailsInThread[index].from),
- trailing: Text(emailsInThread[index].date),
- onTap: () {
- setState(() {
- if (isExpanded) {
- _expandedEmails.remove(index);
- } else {
- _expandedEmails.add(index);
- }
- });
- },
- ),
- if (isExpanded)
- // if(viewtypeIDs[index] == null || heightOfViewTypes[index] == null)
- // const SizedBox(height: 100, child: Center(child: CircularProgressIndicator())),
- SizedBox(
- height: heightOfViewTypes[index].toDouble(),
- child: HtmlElementView(
- key: UniqueKey(), viewType: viewtypeIDs[index]),
- ),
- Divider(),
- ],
- );
- },
- ),
- )
- ]): const Center(child:CircularProgressIndicator());
- }
-}
+export 'collapsableEmailsStub.dart'
+ if (dart.library.io) 'collapsableEmailsAndroid.dart'
+ if (dart.library.js_interop) 'collapsableEmailsWeb.dart';
\ No newline at end of file
diff --git a/lib/collapsableEmailsAndroid.dart b/lib/collapsableEmailsAndroid.dart
new file mode 100644
index 0000000..d89afb4
--- /dev/null
+++ b/lib/collapsableEmailsAndroid.dart
@@ -0,0 +1,435 @@
+import 'package:flutter/material.dart';
+import 'api_service.dart';
+import 'structs.dart';
+import 'package:html2md/html2md.dart' as html2md;
+import 'package:markdown_widget/markdown_widget.dart';
+import 'package:markdown/markdown.dart' as md;
+
+class CollapsableEmails extends StatefulWidget {
+ final List thread; // email id's in the form xyz@gmail.com
+ // final List threadHTML;
+ final List threadMarkdown;
+ final String threadIDs;
+ final String? targetJumpNumbering;
+ final String? targetViewspecs;
+
+ CollapsableEmails(
+ {required this.thread,
+ required this.threadMarkdown,
+ required this.threadIDs,
+ this.targetJumpNumbering,
+ this.targetViewspecs,
+ });
+
+ @override
+ State createState() => _CollapsableEmailsState();
+}
+
+class _CollapsableEmailsState extends State {
+ List emailsHTML = []; //html of the emails in the thread
+ // build attachments with the forldar name and id
+ Set _expandedEmails = {}; //open emails
+ List emailsInThread = [];
+ bool _isLoaded = false;
+
+ List hirarchy = ["h1", "h2", "h3", "h4", "h5", "h6", "p"];
+ Map hirarchyDict = {
+ "h1": 1,
+ "h2": 2,
+ "h3": 3,
+ "h4": 4,
+ "h5": 6,
+ "h6": 7,
+ "p": 8,
+ "ul": 8,
+ "li": 8,
+ };
+
+ List tagsCollected = [];
+ List allMarkdown = [];
+ List> sentinel = [];
+ int level = 0;
+ AugmentTree zoomTreeRoot = AugmentTree();
+ // late AugmentTree currentZoomNode;
+ late List currentZoomTree = [];
+ bool zoomOut = false;
+ bool zoomIn = true;
+ late List threadNodes = [];
+ static bool leftNumbering = false;
+ static bool rightNumbering = true;
+ bool showWhole = false;
+
+
+ @override
+ void initState() {
+ super.initState();
+ threadNodes = [];
+ currentZoomTree = [];
+ // _markdownConverter();
+ _serializableData(widget.threadIDs); // this
+ _markdown2Tree(widget.threadMarkdown);
+ }
+ @override
+ void didUpdateWidget(covariant CollapsableEmails oldWidget) {
+ // TODO: implement didUpdateWidget
+ super.didUpdateWidget(oldWidget);
+ if (widget.targetJumpNumbering != null &&
+ widget.targetJumpNumbering != oldWidget.targetJumpNumbering) {
+ _handleJump(widget.targetJumpNumbering!);
+ }
+ if (widget.targetViewspecs != null &&
+ widget.targetViewspecs != oldWidget.targetViewspecs) {
+ _handleViewspecs(widget.targetViewspecs!);
+ }
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ }
+
+ void _add2Tree(AugmentTree tree, md.Element node2add) {
+ // adds node to its corresponding place
+ AugmentTree newNode = AugmentTree();
+ newNode.setData(node2add.textContent);
+ newNode.ogTag = node2add.tag;
+ // cases,
+ //1. a node that comes is lower than the root.children last, if so it goes beneath it
+ if (tree.children.isEmpty) {
+ // new level to be created when totally empty
+ tree.children.add(newNode);
+ newNode.parent = tree;
+ } else if (tree.children.isNotEmpty &&
+ tree.children.last.ogTag.isNotEmpty) {
+ if ((hirarchyDict[node2add.tag] ??
+ -1) < // e.g. new node is h1 and old is h2, heapify
+ (hirarchyDict[tree.children.last.ogTag] ?? -1)) {
+ //have to figure out the borthers
+ //assuming it all goes right
+ if ((hirarchyDict[node2add.tag] ?? -1) == -1 ||
+ (hirarchyDict[tree.children.last.ogTag] ?? -1) == -1) {
+ print(
+ 'failed and got -1 at _add2Tree \n ${hirarchyDict[node2add.tag] ?? -1} < ${hirarchyDict[tree.children.last.ogTag] ?? -1}');
+ return;
+ } else if (tree.children.last.parent == null) {
+ // becomes the new top level
+ for (AugmentTree brother in tree.children) {
+ brother.parent = newNode;
+ }
+ tree.children = [newNode];
+ } else {
+ newNode.parent = tree;
+ tree.children.add(newNode);
+ }
+ } else if ((hirarchyDict[node2add.tag] ??
+ -1) > // go down e.g. new node is h3 and old is h2 or something
+ (hirarchyDict[tree.children.last.ogTag] ?? -1)) {
+ if ((hirarchyDict[node2add.tag] ?? -1) == -1 ||
+ (hirarchyDict[tree.children.last.ogTag] ?? -1) == -1) {
+ print(
+ 'failed and got -1 at _add2Tree \n ${hirarchyDict[node2add.tag] ?? -1} > ${hirarchyDict[tree.children.last.ogTag] ?? -1}');
+ print("-1 ${tree.children.last.ogTag}");
+ return;
+ }
+
+ _add2Tree(tree.children.last, node2add);
+ } else if ((hirarchyDict[node2add.tag] ?? -1) ==
+ (hirarchyDict[tree.children.last.ogTag] ?? -1)) {
+ tree.children.add(newNode);
+ newNode.parent = tree;
+ }
+ }
+ }
+
+ void _markdown2Tree(List text) {
+ print("started markdown2tree");
+ for (int emailsMD = 0; emailsMD < text.length; emailsMD++) {
+ final List nakedList =
+ md.Document().parseLines(text[emailsMD].split('\n'));
+ zoomTreeRoot = AugmentTree();
+ for (var node in nakedList) {
+ //maybe do an add function, but isn't this it?
+ if (node is md.Element) {
+ AugmentTree temp = AugmentTree();
+ temp.data = node.textContent;
+ temp.ogTag = node.tag;
+ if (node.tag == 'h1') {
+ // make this O(1)
+ _add2Tree(zoomTreeRoot, node);
+ } else if (node.tag == 'h2') {
+ // i dont add any since i dont have it, maybe the function makes sense
+ _add2Tree(zoomTreeRoot, node); // fix this
+ } else if (node.tag == 'h3') {
+ _add2Tree(zoomTreeRoot, node);
+ } else if (node.tag == 'h4') {
+ _add2Tree(zoomTreeRoot, node); // change to temp
+ } else if (node.tag == 'h5') {
+ _add2Tree(zoomTreeRoot, node);
+ } else if (node.tag == 'h6') {
+ _add2Tree(zoomTreeRoot, node); // fix this
+ } else if (node.tag == 'p' || node.tag == 'ul' || node.tag == 'li') {
+ _add2Tree(zoomTreeRoot, node); // fix this
+ }
+ }
+ }
+ zoomTreeRoot.addNumbering();
+ threadNodes.add(zoomTreeRoot);
+ currentZoomTree.add(zoomTreeRoot);
+ }
+
+ if (!mounted) return;
+ setState(() {
+ _isLoaded = true;
+ });
+ }
+
+ void _goToChildren(int indexThread, int index) async {
+ final target = currentZoomTree[indexThread].children[index];
+ if (target.children.isNotEmpty) {
+ setState(() {
+ currentZoomTree[indexThread] = target;
+ });
+ } else {
+ print("This child has no further children.");
+ }
+ }
+
+ void _goToParent(int indexThread) async {
+ if (currentZoomTree[indexThread].parent != null) {
+ setState(() {
+ currentZoomTree[indexThread] = currentZoomTree[indexThread].parent!;
+ });
+ } else {
+ print("Already at root.");
+ }
+ }
+
+ void _serializableData(String threadID) async {
+ emailsInThread = await ApiService().threadsInSerializable(threadID);
+ print("done thread serializable");
+ if (!mounted) return;
+ setState(() {
+ _isLoaded = true;
+ });
+ }
+
+ Widget _buildForZooms(int indexThread) {
+ // IF I GIVE IT THE INDEX????
+ if (!_isLoaded) {
+ return const Center(child: CircularProgressIndicator()); // loading screen
+ }
+
+ final AugmentTree currentZoomNodeForThisEmail =
+ currentZoomTree[indexThread];
+
+ final canZoomOut = currentZoomNodeForThisEmail.parent != null;
+
+ return ListView.builder(
+ itemCount: currentZoomNodeForThisEmail.children.length,
+ itemBuilder: (context, index) {
+ final childNode = currentZoomNodeForThisEmail.children[index];
+ final canZoomIn = childNode.children.isNotEmpty;
+ // currentZoomNodeForThisEmail.addNumbering();
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0),
+ child: Material(
+ elevation: 1,
+ borderRadius: BorderRadius.circular(12),
+ color: Theme.of(context).colorScheme.surface,
+ surfaceTintColor: Theme.of(context).colorScheme.surfaceBright,
+ child: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Wrap(
+ spacing: 4.0,
+ children: [
+ OutlinedButton(
+ onPressed:
+ canZoomOut ? () => _goToParent(indexThread) : null,
+ child: Icon(Icons.north_west_sharp),
+ ),
+ OutlinedButton(
+ onPressed: canZoomIn
+ ? () => _goToChildren(indexThread, index)
+ : null,
+ child: Icon(Icons.south_east_sharp),
+ ),
+ ],
+ ),
+ SizedBox(width: 12.0),
+ if (leftNumbering)
+ Padding(
+ padding: const EdgeInsets.fromLTRB(0, 10, 5, 0),
+ child: Text(
+ childNode.numbering,
+ style:
+ TextStyle(color: Color(Colors.purple[400]!.value)),
+ ),
+ ),
+ Expanded(
+ child: MarkdownBlock(
+ data: childNode.data,
+ // data: currentZoomNode
+ // .children[index].data, // one string of markdown
+ config: MarkdownConfig
+ .darkConfig, // or lightConfig depending on theme
+ ),
+ ),
+ if (rightNumbering)
+ Padding(
+ padding: const EdgeInsets.fromLTRB(5, 10, 5, 0),
+ child: Text(
+ childNode.numbering,
+ style:
+ TextStyle(color: Color(Colors.purple[400]!.value)),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ },
+ );
+ }
+ void _handleJump(String queryNumbering) {
+ print(queryNumbering);
+ if (queryNumbering.isEmpty) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text('Please enter a numbering to jump to.')),
+ );
+ return;
+ }
+
+ final int targetEmailIndex = _expandedEmails.first;
+ if (targetEmailIndex >= threadNodes.length) {
+ // Error handling
+ return;
+ }
+
+ final AugmentTree rootOfCurrentEmail = threadNodes[targetEmailIndex];
+ final AugmentTree? foundNode =
+ _findNodeByNumbering(rootOfCurrentEmail, queryNumbering);
+
+ if (foundNode != null) {
+ setState(() {
+ currentZoomTree[targetEmailIndex] = foundNode; // Update the state
+ });
+ } else {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text('Numbering "$queryNumbering" not found.')),
+ );
+ }
+ }
+
+ void _handleViewspecs(String viewspecsQuery) {
+ print(viewspecsQuery);
+ if (viewspecsQuery.isEmpty) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text('Please enter the viewspecs.')),
+ );
+ return;
+ }
+
+ final int targetEmailIndex = _expandedEmails.first;
+ if (targetEmailIndex >= threadNodes.length) {
+ // Error handling
+ return;
+ }
+
+ if (viewspecsQuery.contains('n')) {
+ setState(() {
+ leftNumbering = false; // Update the state
+ rightNumbering = false;
+ });
+ }
+ if (viewspecsQuery.contains('m')) {
+ setState(() {
+ rightNumbering = true;
+ leftNumbering = true;
+ });
+ }
+ if (viewspecsQuery.contains('H')) {
+ setState(() {
+ leftNumbering = !leftNumbering;
+ });
+ }
+ if (viewspecsQuery.contains('G')) {
+ setState(() {
+ rightNumbering = !rightNumbering;
+ });
+ }
+ if (viewspecsQuery.contains('w')) {
+ setState(() {
+ showWhole = true;
+ });
+ }
+
+ // else {
+ // ScaffoldMessenger.of(context).showSnackBar(
+ // SnackBar(content: Text('Numbering "$viewspecsQuery" not found.')),
+ // );
+ // }
+ }
+
+ AugmentTree? _findNodeByNumbering(AugmentTree root, String numbering) {
+ //recursively finds the node you mentioned
+ // to find the AugmentTree node corresponding to the `numbering`.
+ if (root.numbering == numbering) {
+ return root;
+ }
+ for (var child in root.children) {
+ final found = _findNodeByNumbering(child, numbering);
+ if (found != null) {
+ return found;
+ }
+ }
+ return null;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return _isLoaded
+ ? Column(children: [
+ Expanded(
+ child: ListView.builder(
+ itemCount: emailsInThread.length,
+ itemBuilder: (context, index) {
+ final isExpanded = _expandedEmails
+ .contains(index); //check if email is expanded
+ return Column(
+ children: [
+ ListTile(
+ title: Text(emailsInThread[index].from),
+ trailing: Text(emailsInThread[index].date),
+ onTap: () {
+ setState(() {
+ if (isExpanded) {
+ _expandedEmails.remove(index);
+ } else {
+ _expandedEmails.add(index);
+ }
+ });
+ },
+ ),
+ if (isExpanded)
+ ConstrainedBox(
+ constraints: BoxConstraints(
+ minHeight: 100,
+ maxHeight:
+ MediaQuery.of(context).size.height * 0.6,
+ ),
+ child: _buildForZooms(index),
+ ),
+ Divider(),
+ ],
+ );
+ },
+ ),
+ ),
+ ])
+ : const Center(child: CircularProgressIndicator());
+ }
+}
diff --git a/lib/collapsableEmailsStub.dart b/lib/collapsableEmailsStub.dart
new file mode 100644
index 0000000..dd9168f
--- /dev/null
+++ b/lib/collapsableEmailsStub.dart
@@ -0,0 +1,23 @@
+import 'structs.dart';
+import 'package:flutter/material.dart';
+
+class CollapsableEmails extends StatefulWidget {
+ final List thread; // email id's in the form xyz@gmail.com
+ final List threadMarkdown;
+ final String threadIDs;
+
+ CollapsableEmails(
+ {required this.thread,
+ required this.threadMarkdown,
+ required this.threadIDs, String? targetJumpNumbering, String? targetViewspecs});
+
+ @override
+ State createState() => _CollapsableEmailsState();
+}
+
+class _CollapsableEmailsState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(body: Text("collapsable stud"));
+ }
+}
diff --git a/lib/collapsableEmailsWeb.dart b/lib/collapsableEmailsWeb.dart
new file mode 100644
index 0000000..1183259
--- /dev/null
+++ b/lib/collapsableEmailsWeb.dart
@@ -0,0 +1,450 @@
+import 'package:english_words/english_words.dart';
+import 'package:flutter/material.dart';
+import 'api_service.dart';
+import 'structs.dart';
+import 'package:html2md/html2md.dart' as html2md;
+import 'package:markdown_widget/markdown_widget.dart';
+import 'package:markdown/markdown.dart' as md;
+
+class CollapsableEmails extends StatefulWidget {
+ final List thread; // email id's in the form xyz@gmail.com
+ // final List threadHTML; to be replaced with the MD
+ final List threadMarkdown;
+ final String threadIDs;
+ final String? targetJumpNumbering;
+ final String? targetViewspecs;
+
+ const CollapsableEmails({
+ required this.thread,
+ // required this.threadHTML,
+ required this.threadMarkdown,
+ required this.threadIDs,
+ this.targetJumpNumbering,
+ this.targetViewspecs,
+ });
+
+ @override
+ State createState() => _CollapsableEmailsState();
+}
+
+class _CollapsableEmailsState extends State {
+ List emailsHTML = []; //html of the emails in the thread
+ // build attachments with the forldar name and id
+ Set _expandedEmails = {}; //open emails
+
+ List emailsInThread = [];
+ bool _isLoaded = false;
+ List hirarchy = ["h1", "h2", "h3", "h4", "h5", "h6", "p"];
+ Map hirarchyDict = {
+ "h1": 1,
+ "h2": 2,
+ "h3": 3,
+ "h4": 4,
+ "h5": 5,
+ "h6": 6,
+ "p": 8,
+ "ul": 8,
+ "li": 8,
+ };
+
+ List tagsCollected = [];
+ List allMarkdown = [];
+ List> sentinel = [];
+ int level = 0;
+ AugmentTree zoomTreeRoot = AugmentTree();
+ // late AugmentTree currentZoomNode;
+ late List currentZoomTree = [];
+ bool zoomOut = false;
+ bool zoomIn = true;
+ late List threadNodes = [];
+ static bool leftNumbering = true;
+ static bool rightNumbering = true;
+ bool showWhole = false;
+
+ @override
+ void initState() {
+ super.initState();
+ threadNodes = [];
+ currentZoomTree = [];
+ // _markdownConverter();
+ _serializableData(widget.threadIDs); // this
+ _markdown2Tree(widget.threadMarkdown);
+ }
+
+ @override
+ void didUpdateWidget(covariant CollapsableEmails oldWidget) {
+ // TODO: implement didUpdateWidget
+ super.didUpdateWidget(oldWidget);
+ if (widget.targetJumpNumbering != null &&
+ widget.targetJumpNumbering != oldWidget.targetJumpNumbering) {
+ _handleJump(widget.targetJumpNumbering!);
+ }
+ if (widget.targetViewspecs != null &&
+ widget.targetViewspecs != oldWidget.targetViewspecs) {
+ _handleViewspecs(widget.targetViewspecs!);
+ }
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ }
+
+ // void _markdownConverter() async {
+ // // to list of markdown
+ // // for (int email = 0; email < widget.threadHTML.length; email++) {
+ // // String markdown = html2md.convert(widget.threadHTML[email]);
+ // // allMarkdown.add(markdown);
+ // // }
+ // for (int email = 0; email < widget.threadMarkdown.length; email++) {
+ // allMarkdown.add(email);
+ // }
+ // }
+
+ void _add2Tree(AugmentTree tree, md.Element node2add) {
+ // adds node to its corresponding place
+ AugmentTree newNode = AugmentTree();
+ newNode.setData(node2add.textContent);
+ newNode.ogTag = node2add.tag;
+ // cases,
+ //1. a node that comes is lower than the root.children last, if so it goes beneath it
+ if (tree.children.isEmpty) {
+ // new level to be created when totally empty
+ tree.children.add(newNode);
+ newNode.parent = tree;
+ } else if (tree.children.isNotEmpty &&
+ tree.children.last.ogTag.isNotEmpty) {
+ if ((hirarchyDict[node2add.tag] ??
+ -1) < // e.g. new node is h1 and old is h2, heapify
+ (hirarchyDict[tree.children.last.ogTag] ?? -1)) {
+ //have to figure out the borthers
+ //assuming it all goes right
+ if ((hirarchyDict[node2add.tag] ?? -1) == -1 ||
+ (hirarchyDict[tree.children.last.ogTag] ?? -1) == -1) {
+ print(
+ 'failed and got -1 at _add2Tree \n ${hirarchyDict[node2add.tag] ?? -1} < ${hirarchyDict[tree.children.last.ogTag] ?? -1}');
+ return;
+ } else if (tree.children.last.parent == null) {
+ // becomes the new top level
+ for (AugmentTree brother in tree.children) {
+ brother.parent = newNode;
+ }
+ tree.children = [newNode];
+ } else {
+ newNode.parent = tree;
+ tree.children.add(newNode);
+ }
+ } else if ((hirarchyDict[node2add.tag] ??
+ -1) > // go down e.g. new node is h3 and old is h2 or something
+ (hirarchyDict[tree.children.last.ogTag] ?? -1)) {
+ if ((hirarchyDict[node2add.tag] ?? -1) == -1 ||
+ (hirarchyDict[tree.children.last.ogTag] ?? -1) == -1) {
+ print(
+ 'failed and got -1 at _add2Tree \n ${hirarchyDict[node2add.tag] ?? -1} > ${hirarchyDict[tree.children.last.ogTag] ?? -1}');
+ print("-1 ${tree.children.last.ogTag}");
+ return;
+ }
+
+ _add2Tree(tree.children.last, node2add);
+ } else if ((hirarchyDict[node2add.tag] ?? -1) ==
+ (hirarchyDict[tree.children.last.ogTag] ?? -1)) {
+ tree.children.add(newNode);
+ newNode.parent = tree;
+ }
+ }
+ }
+
+ void _markdown2Tree(List text) {
+ print("started markdown2tree");
+ for (int emailsMD = 0; emailsMD < text.length; emailsMD++) {
+ final List nakedList =
+ md.Document().parseLines(text[emailsMD].split('\n'));
+ zoomTreeRoot = AugmentTree();
+ for (var node in nakedList) {
+ //maybe do an add function, but isn't this it?
+ if (node is md.Element) {
+ AugmentTree temp = AugmentTree();
+ temp.data = node.textContent;
+ temp.ogTag = node.tag;
+ if (node.tag == 'h1') {
+ // make this O(1)
+ _add2Tree(zoomTreeRoot, node);
+ } else if (node.tag == 'h2') {
+ // i dont add any since i dont have it, maybe the function makes sense
+ _add2Tree(zoomTreeRoot, node); // fix this
+ } else if (node.tag == 'h3') {
+ _add2Tree(zoomTreeRoot, node);
+ } else if (node.tag == 'h4') {
+ _add2Tree(zoomTreeRoot, node); // change to temp
+ } else if (node.tag == 'h5') {
+ _add2Tree(zoomTreeRoot, node);
+ } else if (node.tag == 'h6') {
+ _add2Tree(zoomTreeRoot, node); // fix this
+ } else if (node.tag == 'p' || node.tag == 'ul' || node.tag == 'li') {
+ _add2Tree(zoomTreeRoot, node); // fix this
+ }
+ }
+ }
+ zoomTreeRoot.addNumbering();
+ threadNodes.add(zoomTreeRoot);
+ currentZoomTree.add(zoomTreeRoot);
+ }
+
+ if (!mounted) return;
+ setState(() {
+ _isLoaded = true;
+ });
+ }
+
+ void _goToChildren(int indexThread, int index) async {
+ final target = currentZoomTree[indexThread].children[index];
+ if (target.children.isNotEmpty) {
+ setState(() {
+ currentZoomTree[indexThread] = target;
+ });
+ } else {
+ print("This child has no further children.");
+ }
+ }
+
+ void _goToParent(int indexThread) async {
+ if (currentZoomTree[indexThread].parent != null) {
+ setState(() {
+ currentZoomTree[indexThread] = currentZoomTree[indexThread].parent!;
+ });
+ } else {
+ print("Already at root.");
+ }
+ }
+
+ void _serializableData(String threadID) async {
+ emailsInThread = await ApiService().threadsInSerializable(threadID);
+ print("done thread serializable");
+
+ if (!mounted) return;
+ setState(() {
+ _isLoaded = true;
+ });
+ }
+
+ Widget _buildForZooms(int indexThread) {
+ // IF I GIVE IT THE INDEX????
+ if (!_isLoaded) {
+ return const Center(child: CircularProgressIndicator()); // loading screen
+ }
+
+ final AugmentTree currentZoomNodeForThisEmail =
+ currentZoomTree[indexThread];
+
+ final canZoomOut = currentZoomNodeForThisEmail.parent != null;
+
+ return ListView.builder(
+ itemCount: currentZoomNodeForThisEmail.children.length,
+ itemBuilder: (context, index) {
+ final childNode = currentZoomNodeForThisEmail.children[index];
+ final canZoomIn = childNode.children.isNotEmpty;
+ // currentZoomNodeForThisEmail.addNumbering();
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0),
+ child: Material(
+ elevation: 1,
+ borderRadius: BorderRadius.circular(12),
+ color: Theme.of(context).colorScheme.surface,
+ surfaceTintColor: Theme.of(context).colorScheme.surfaceBright,
+ child: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Wrap(
+ spacing: 4.0,
+ children: [
+ OutlinedButton(
+ onPressed:
+ canZoomOut ? () => _goToParent(indexThread) : null,
+ child: Icon(Icons.north_west_sharp),
+ ),
+ OutlinedButton(
+ onPressed: canZoomIn
+ ? () => _goToChildren(indexThread, index)
+ : null,
+ child: Icon(Icons.south_east_sharp),
+ ),
+ ],
+ ),
+ SizedBox(width: 12.0),
+ if (leftNumbering)
+ Padding(
+ padding: const EdgeInsets.fromLTRB(0, 10, 5, 0),
+ child: Text(
+ childNode.numbering,
+ style:
+ TextStyle(color: Color(Colors.purple[400]!.value)),
+ ),
+ ),
+ Expanded(
+ child: MarkdownBlock(
+ data: childNode.data,
+ // data: currentZoomNode
+ // .children[index].data, // one string of markdown
+ config: MarkdownConfig
+ .darkConfig, // or lightConfig depending on theme
+ ),
+ ),
+ if (rightNumbering)
+ Padding(
+ padding: const EdgeInsets.fromLTRB(5, 10, 5, 0),
+ child: Text(
+ childNode.numbering,
+ style:
+ TextStyle(color: Color(Colors.purple[400]!.value)),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ },
+ );
+ }
+
+ void _handleJump(String queryNumbering) {
+ print(queryNumbering);
+ if (queryNumbering.isEmpty) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text('Please enter a numbering to jump to.')),
+ );
+ return;
+ }
+
+ final int targetEmailIndex = _expandedEmails.first;
+ if (targetEmailIndex >= threadNodes.length) {
+ // Error handling
+ return;
+ }
+
+ final AugmentTree rootOfCurrentEmail = threadNodes[targetEmailIndex];
+ final AugmentTree? foundNode =
+ _findNodeByNumbering(rootOfCurrentEmail, queryNumbering);
+
+ if (foundNode != null) {
+ setState(() {
+ currentZoomTree[targetEmailIndex] = foundNode; // Update the state
+ });
+ } else {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text('Numbering "$queryNumbering" not found.')),
+ );
+ }
+ }
+
+ void _handleViewspecs(String viewspecsQuery) {
+ print(viewspecsQuery);
+ if (viewspecsQuery.isEmpty) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text('Please enter the viewspecs.')),
+ );
+ return;
+ }
+
+ final int targetEmailIndex = _expandedEmails.first;
+ if (targetEmailIndex >= threadNodes.length) {
+ // Error handling
+ return;
+ }
+
+ if (viewspecsQuery.contains('n')) {
+ setState(() {
+ leftNumbering = false; // Update the state
+ rightNumbering = false;
+ });
+ }
+ if (viewspecsQuery.contains('m')) {
+ setState(() {
+ rightNumbering = true;
+ leftNumbering = true;
+ });
+ }
+ if (viewspecsQuery.contains('H')) {
+ setState(() {
+ leftNumbering = !leftNumbering;
+ });
+ }
+ if (viewspecsQuery.contains('G')) {
+ setState(() {
+ rightNumbering = !rightNumbering;
+ });
+ }
+ if (viewspecsQuery.contains('w')) {
+ setState(() {
+ showWhole = true;
+ });
+ }
+
+ // else {
+ // ScaffoldMessenger.of(context).showSnackBar(
+ // SnackBar(content: Text('Numbering "$viewspecsQuery" not found.')),
+ // );
+ // }
+ }
+
+ AugmentTree? _findNodeByNumbering(AugmentTree root, String numbering) {
+ //recursively finds the node you mentioned
+ // to find the AugmentTree node corresponding to the `numbering`.
+ if (root.numbering == numbering) {
+ return root;
+ }
+ for (var child in root.children) {
+ final found = _findNodeByNumbering(child, numbering);
+ if (found != null) {
+ return found;
+ }
+ }
+ return null;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return _isLoaded
+ ? Column(children: [
+ Expanded(
+ child: ListView.builder(
+ itemCount: emailsInThread.length,
+ itemBuilder: (context, index) {
+ final isExpanded = _expandedEmails
+ .contains(index); //check if email is expanded
+ return Column(
+ children: [
+ ListTile(
+ title: Text(emailsInThread[index].from),
+ trailing: Text(emailsInThread[index].date),
+ onTap: () {
+ setState(() {
+ if (isExpanded) {
+ _expandedEmails.remove(index);
+ } else {
+ _expandedEmails.add(index);
+ }
+ });
+ },
+ ),
+ if (isExpanded)
+ ConstrainedBox(
+ constraints: BoxConstraints(
+ minHeight: 100,
+ maxHeight: MediaQuery.of(context).size.height * 0.6,
+ ),
+ child: _buildForZooms(index), //show the tree
+ // child: _buildForZooms(key: ValueKey(currentZoomNode)),
+ ),
+ Divider(),
+ ],
+ );
+ },
+ ),
+ ),
+ ])
+ : const Center(child: CircularProgressIndicator());
+ }
+}
diff --git a/lib/contact.dart b/lib/contact.dart
index 73d60fd..32b4dae 100644
--- a/lib/contact.dart
+++ b/lib/contact.dart
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
-import 'package:http/http.dart' as http;
-import 'package:flutter_html/flutter_html.dart';
+// import 'package:http/http.dart' as http;
+// import 'package:flutter_html/flutter_html.dart';
class ContactsPage extends StatefulWidget {
const ContactsPage({super.key});
diff --git a/lib/email.dart b/lib/email.dart
index 5dbf1a6..0b1ded3 100644
--- a/lib/email.dart
+++ b/lib/email.dart
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
-import 'package:flutter/widgets.dart';
import 'api_service.dart';
import 'structs.dart';
+import 'emailView.dart';
class EmailListScreen extends StatelessWidget {
final List emails;
@@ -140,7 +140,8 @@ class EmailPageState extends State {
return Scaffold(
body: EmailListScreen(
emails: emails,
- getEmailContent: apiService.fetchEmailContent,
+ // getEmailContent: apiService.fetchEmailContent,
+ getEmailContent: apiService.fetchMarkdownContent,
folder: widget.selectedFolder, //try to grab from it directly
),
);
diff --git a/lib/emailView.dart b/lib/emailView.dart
new file mode 100644
index 0000000..61c9bf8
--- /dev/null
+++ b/lib/emailView.dart
@@ -0,0 +1,3 @@
+export 'emailViewStub.dart'
+ if (dart.library.io) 'emailViewAndroid.dart'
+ if (dart.library.js_interop) 'emailViewWeb.dart';
\ No newline at end of file
diff --git a/lib/emailViewAndroid.dart b/lib/emailViewAndroid.dart
new file mode 100644
index 0000000..7b257a2
--- /dev/null
+++ b/lib/emailViewAndroid.dart
@@ -0,0 +1,115 @@
+import 'package:crab_ui/augment.dart';
+import 'package:crab_ui/collapsableEmailsAndroid.dart';
+import 'package:flutter/material.dart';
+// import 'dart:ui_web' as ui;
+// import 'augment.dart';
+// // import 'dart:js_interop' as js; //eventually for manipulating css
+// import 'package:pointer_interceptor/pointer_interceptor.dart';
+// import 'collapsableEmails.dart';
+// import 'api_service.dart';
+
+class EmailView extends StatefulWidget {
+ final List emailContent;
+ final String from;
+ final String name;
+ final String to;
+ final String subject;
+ final String date;
+ final String id;
+ final List messages;
+
+ const EmailView({
+ Key? key,
+ required this.emailContent,
+ required this.from,
+ required this.name, // tf is name
+ required this.to,
+ required this.subject,
+ required this.date,
+ required this.id,
+ required this.messages,
+ }) : super(key: key);
+ @override
+ _EmailViewState createState() => _EmailViewState();
+}
+
+class _EmailViewState extends State {
+
+ @override
+ void initState() {
+ super.initState();
+ }
+
+ void _scrollToNumber(String spanId) {
+ // AugmentClasses.handleJump(spanId);
+ }
+ void _viewSpecs(String command){
+
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(widget.name),
+ ),
+ body: Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: Column(
+ children: [
+ EmailToolbar(
+ onButtonPressed: () => {},
+ onJumpToNumbering: _scrollToNumber,
+ onViewspecs: _viewSpecs,
+
+ ),
+ Row(
+ children: [
+ Expanded(
+ child: Text(
+ widget.subject,
+ style: TextStyle(fontSize: 15),
+ overflow: TextOverflow.visible,
+ softWrap: true,
+ ),
+ ),
+ ],
+ ),
+ Row(
+ children: [
+ Text(
+ 'from ${widget.name}',
+ style: TextStyle(fontSize: 8),
+ ),
+ Text(
+ '<${widget.from}>',
+ style: TextStyle(fontSize: 8),
+ ),
+ Spacer(),
+ Text(
+ widget.date,
+ textAlign: TextAlign.right,
+ )
+ ],
+ ),
+ Row(
+ children: [
+ Text(
+ 'to ${widget.to.toString()}',
+ style: TextStyle(fontSize: 8),
+ )
+ ],
+ ),
+ Expanded(
+ child: CollapsableEmails(
+ thread: widget.messages,
+ threadMarkdown: widget.emailContent,
+ threadIDs: widget.id,
+ ),
+ ),
+ ],
+ ),
+ )
+ );
+ }
+}
\ No newline at end of file
diff --git a/lib/emailViewStub.dart b/lib/emailViewStub.dart
new file mode 100644
index 0000000..5c5f657
--- /dev/null
+++ b/lib/emailViewStub.dart
@@ -0,0 +1,39 @@
+import 'package:flutter/material.dart';
+
+
+class EmailView extends StatefulWidget {
+ final List emailContent;
+ final String from;
+ final String name;
+ final String to;
+ final String subject;
+ final String date;
+ final String id;
+ final List messages;
+
+ const EmailView({
+ Key? key,
+ required this.emailContent,
+ required this.from,
+ required this.name,
+ required this.to,
+ required this.subject,
+ required this.date,
+ required this.id,
+ required this.messages,
+ }) : super(key: key);
+ @override
+ _EmailViewState createState() => _EmailViewState();
+}
+
+class _EmailViewState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ body: Center(
+ child: Text(" emailview stub, not supported")
+ )
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/lib/emailViewWeb.dart b/lib/emailViewWeb.dart
new file mode 100644
index 0000000..ceb4ef1
--- /dev/null
+++ b/lib/emailViewWeb.dart
@@ -0,0 +1,143 @@
+import 'package:flutter/material.dart';
+import 'dart:ui_web' as ui;
+import 'augment.dart';
+// import 'dart:js_interop' as js; //eventually for manipulating css
+import 'collapsableEmails.dart';
+import 'api_service.dart';
+
+class EmailView extends StatefulWidget {
+ final List emailContent;
+ final String from;
+ final String name;
+ final String to;
+ final String subject;
+ final String date;
+ final String id;
+ final List messages;
+
+ const EmailView({
+ Key? key,
+ required this.emailContent,
+ required this.from,
+ required this.name,
+ required this.to,
+ required this.subject,
+ required this.date,
+ required this.id,
+ required this.messages,
+ }) : super(key: key);
+ @override
+ _EmailViewState createState() => _EmailViewState();
+}
+
+class _EmailViewState extends State {
+ //html css rendering thing
+ late Key iframeKey;
+ late String currentContent;
+ late String viewTypeId; //make this a list too???
+ Future>>? _markerPositionsFuture;
+ // TextEditingController _jumpController = TextEditingController();
+ final hardcodedMarkers = [
+ {'id': 'marker1', 'x': 50, 'y': 100},
+ {'id': 'marker2', 'x': 150, 'y': 200},
+ {'id': 'marker3', 'x': 250, 'y': 300},
+ ];
+ String? _targetJumpNumbering;
+ String? _targetViewspecs;
+
+ @override
+ void initState() {
+ super.initState();
+ print("thread id? ${widget.id}");
+ List currentContent = widget
+ .emailContent; //html of the email/ actually entire thread, gives me little space to play in between
+ // i wonder if the other attributes change? because if so i have to add like some zooms in and out of the emails, as in collapse
+ // _registerViewFactory(currentContent);
+ }
+
+ void _scrollToNumber(String spanId) {
+ AugmentClasses.handleJump(spanId);
+ }
+
+ void _handleJumpRequest(String numbering) {
+ setState(() {
+ _targetJumpNumbering = numbering;
+ });
+ }
+
+ void _handleViewspecsRequest(String viewspecsCommand) {
+ setState(() {
+ _targetViewspecs = viewspecsCommand;
+ });
+ }
+
+ // TODO: void _invisibility(String ) //to make purple numbers not visible
+
+ @override
+ Widget build(BuildContext context) {
+ ApiService.currThreadID = widget.id;
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(widget.name),
+ ),
+ body: Stack(
+ children: [
+ Column(
+ children: [
+ EmailToolbar(
+ onJumpToNumbering: _handleJumpRequest,
+ onViewspecs: _handleViewspecsRequest,
+ onButtonPressed: () => {print("email tool bar pressed")},
+ ),
+ Row(
+ // title of email
+ children: [
+ Text(
+ widget.subject,
+ style: TextStyle(fontSize: 30),
+ ),
+ ],
+ ),
+ Row(
+ children: [
+ Text(
+ 'from ${widget.name}',
+ style: TextStyle(fontSize: 18),
+ ),
+ Text(
+ '<${widget.from}>',
+ style: TextStyle(fontSize: 18),
+ ),
+ Spacer(),
+ Text(
+ '${widget.date}',
+ textAlign: TextAlign.right,
+ )
+ ],
+ ),
+ // TODO: make a case where if one of these is the user's email it just says me :)))))
+ Row(
+ children: [
+ Text(
+ 'to ${widget.to.toString()}',
+ style: TextStyle(fontSize: 15),
+ )
+ ],
+ ),
+ Expanded(
+ child: CollapsableEmails(
+ //change here
+ thread: widget.messages, //this wont work in serializable
+ // threadHTML: widget.emailContent, // old html
+ threadMarkdown: widget.emailContent,
+ threadIDs: widget.id,
+ targetJumpNumbering: _targetJumpNumbering,
+ targetViewspecs: _targetViewspecs,
+ ),
+ ),
+ ],
+ ),
+ ],
+ ));
+ }
+}
diff --git a/lib/home_page.dart b/lib/home_page.dart
index b4878b0..2849444 100644
--- a/lib/home_page.dart
+++ b/lib/home_page.dart
@@ -2,7 +2,7 @@ import 'package:crab_ui/sonicEmailView.dart';
import 'folder_drawer.dart';
import 'structs.dart';
-import 'package:flutter/widgets.dart';
+// import 'package:flutter/widgets.dart';
import 'api_service.dart';
import 'package:flutter/material.dart';
import 'email.dart';
@@ -148,16 +148,6 @@ class _HomeScreenState extends State with TickerProviderStateMixin {
builder: (context) =>SonicEmailView(
email: email,
emailHTML: emailContent[0])
- // builder: (context) => EmailView(
- // emailContent: emailContent,
- // from: email.from,
- // name: email.name,
- // to: email.to.toString(),
- // subject: email.subject,
- // date: email.date.toString(),
- // id: email.id.toString(),
- // messages: [email.id],
- // ),
),
);
},
@@ -165,28 +155,8 @@ class _HomeScreenState extends State with TickerProviderStateMixin {
},
separatorBuilder: (context, index) => Divider(),
),
- // child: Column(
- // mainAxisAlignment: MainAxisAlignment.center,
- // children: [
- // Text("Results for: $query", style: TextStyle(fontSize: 24)),
- // // Display the actual data
- // Text(result[0].name), // Accessing the first result safely
- // Text(result[0].from), // Displaying the 'from' field as an example
- // Text(result[0].hash),
- // Text(result[0].subject),
- // Text(result[0].uid.toString()),
- // Text(result[0].list),
- // Text(result[0].id),
-
- // // Add more fields or customize the display
- // // SerializableEmailListScreen(emails: result, getEmailContent: getEmailContent)
- // // Expanded(
-
- // // child:
- // // ),
- // ],
+
);
- // );
}
},
);
@@ -201,265 +171,253 @@ class _HomeScreenState extends State with TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
return Scaffold(
- key: _scaffoldKey,
- drawer: FolderDrawer(
- apiService: apiService,
- onFolderTap: (folder) {
- _emailPageKey.currentState?.updateSelectedFolder(folder);
- },
- ),
- body: Stack(
- children: [
- Row(
- children: [
- // Sidebar
- if (_isSidebarOpen)
- Container(
- width: 70,
- color: Color.fromARGB(17, 96, 122, 135),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
+ backgroundColor: Theme.of(context).colorScheme.onPrimary,
+ body: Padding(
+ padding: const EdgeInsets.fromLTRB(0, 20, 0 , 20),
+ child: Scaffold(
+ key: _scaffoldKey,
+ drawer: FolderDrawer(
+ apiService: apiService,
+ onFolderTap: (folder) {
+ _emailPageKey.currentState?.updateSelectedFolder(folder);
+ },
+ ),
+ body: Scaffold(
+ backgroundColor: Theme.of(context).colorScheme.onPrimary,
+ body: Padding(
+ padding: const EdgeInsets.fromLTRB(0, 20, 0, 0),
+ child: Stack(
+ children: [
+ Row(
children: [
- ListTile(
- leading: Icon(Icons.home),
- onTap: () {
- // Navigate to Home
- },
- ),
- ListTile(
- leading: Icon(Icons.settings),
- onTap: () {
- // Navigate to Settings
- },
- ),
- ListTile(
- leading: Icon(Icons.email),
- onTap: () {
- _scaffoldKey.currentState?.openDrawer();
- },
- ),
- Spacer(),
- Padding(
- padding: const EdgeInsets.all(8.0),
- child: Align(
- alignment: Alignment.bottomLeft,
- child: IconButton(
- icon: Icon(Icons.close, color: Colors.white),
- onPressed: () {
- setState(() {
- _isSidebarOpen = false;
- });
- },
+ // Sidebar
+ if (_isSidebarOpen)
+ Container(
+ width: 70,
+ color: Color.fromARGB(17, 96, 122, 135),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ ListTile(
+ leading: Icon(Icons.home),
+ onTap: () {
+ // Navigate to Home
+ },
+ ),
+ ListTile(
+ leading: Icon(Icons.settings),
+ onTap: () {
+ // Navigate to Settings
+ },
+ ),
+ ListTile(
+ leading: Icon(Icons.email),
+ onTap: () {
+ _scaffoldKey.currentState?.openDrawer();
+ },
+ ),
+ Spacer(),
+ Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: Align(
+ alignment: Alignment.bottomLeft,
+ child: IconButton(
+ icon: Icon(Icons.close, color: Colors.white),
+ onPressed: () {
+ setState(() {
+ _isSidebarOpen = false;
+ });
+ },
+ ),
+ ),
+ ),
+ ],
),
),
+ // Main content
+ Expanded(
+ child: Column(
+ children: [
+ Container(
+ padding: EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 4.0),
+ color: Color.fromARGB(42, 36, 102, 132),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Flexible(
+ child: ConstrainedBox(
+ constraints: BoxConstraints(
+ maxWidth: 800,
+ ),
+ child: SizedBox(
+ height: 40,
+ child: TextField(
+ decoration: InputDecoration(
+ hintText: 'Search...',
+ border: OutlineInputBorder(),
+ prefixIcon: Icon(Icons.search),
+ ),
+ onSubmitted: (value) {
+ if (value.isNotEmpty) {
+ _performSearch(value, _selectedOption);
+ }
+ //this is the input box i mentioned
+ // if (value == '') {
+ // setState(() {
+ // querySearches = false;
+ // });
+ // }
+ // Future> results = apiService
+ // .sonicSearch('INBOX', 20, 0, value);
+ // // print(value);
+ // print(results);
+ // setState(() {
+ // querySearches = true;
+ // });
+ },
+ ),
+ ),
+ ),
+ ),
+ SizedBox(
+ width: 8,
+ ),
+ Container(
+ height: 40,
+ child: ElevatedButton(
+ onPressed: _showOptionsSearchDialog,
+ child: Icon(Icons.manage_search),
+ ),
+ )
+ ],
+ ),
+ ),
+ Container(
+ padding: EdgeInsets.all(0.0),
+ color: Color.fromARGB(42, 36, 102, 132),
+ child: Row(
+ children: [
+ Container(
+ height: 2,
+ )
+ ],
+ ),
+ ),
+ Container(
+ color: Color.fromARGB(255, 131, 110, 143),
+ child: TabBar(
+ controller: _tabController,
+ isScrollable: true,
+ tabs: _tabs
+ .asMap()
+ .entries
+ .map((entry) => Tab(
+ child: Row(
+ children: [
+ Text(entry.value),
+ if (entry.value != 'Emails')
+ GestureDetector(
+ onTap: () => _removeTab(entry.key),
+ child: Icon(Icons.close, size: 16),
+ ),
+ ],
+ ),
+ ))
+ .toList(),
+ labelColor: Colors.white,
+ indicatorColor: Colors.white,
+ ),
+ ),
+ Container(
+ // alignment: Alignment.topLeft,
+ padding: EdgeInsets.all(8.0),
+ color: Colors.white,
+ child: Row(
+ children: [
+ ElevatedButton(
+ onPressed: () {
+ _emailPageKey.currentState!.isBackDisabled ? null: _emailPageKey.currentState
+ ?.updatePagenation('back');
+ },
+ child: Icon(Icons.navigate_before),
+ ),
+ Builder(
+ builder: (context) {
+ final emailState = _emailPageKey.currentState;
+ if (emailState == null) {
+ // Schedule a rebuild once the state is available
+ Future.microtask(() => setState(() {}));
+ return Text('Loading...');
+ }
+
+ return ValueListenableBuilder(
+ valueListenable: emailState.currentPageNotifier,
+ builder: (context, value, _) => Text('$value'),
+ );
+ },
+ ),
+ ElevatedButton(
+ onPressed: () {
+ _emailPageKey.currentState
+ ?.updatePagenation('next');
+ },
+ child: Icon(Icons.navigate_next),
+ ),
+ ],
+ ),
+ ),
+ Expanded(
+ child: TabBarView(
+ controller: _tabController,
+ children: _tabs.map((tab) {
+ return _tabWidgets[tab] ??
+ Center(child: Text("No content found"));
+ // return Center(
+ // child: EmailPage(
+ // key: _emailPageKey,
+ // ));
+ }).toList(),
+ ),
+ ),
+
+ // if (_tabs.isEmpty)
+ // Expanded(
+ // child: EmailPage(key: _emailPageKey),
+ // ),
+ // if (_tabs.isNotEmpty)
+ // Expanded(
+ // // child: Text('supposed to be mails'),
+ // child: TabBarView(
+ // controller: _tabController,
+ // children: _tabs
+ // .map((tab) => Center(child: Text('Results for $tab')))
+ // .toList(),
+ // ),
+ // ),
+ ],
+ ),
),
],
),
- ),
- // Main content
- Expanded(
- child: Column(
- children: [
- Container(
- padding: EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 4.0),
- color: Color.fromARGB(42, 36, 102, 132),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Container(
- width: 800,
- height: 40,
- child: TextField(
- decoration: InputDecoration(
- hintText: 'Search...',
- border: OutlineInputBorder(),
- prefixIcon: Icon(Icons.search),
- ),
- onSubmitted: (value) {
- if (value.isNotEmpty) {
- _performSearch(value, _selectedOption);
- }
- //this is the input box i mentioned
- // if (value == '') {
- // setState(() {
- // querySearches = false;
- // });
- // }
- // Future> results = apiService
- // .sonicSearch('INBOX', 20, 0, value);
- // // print(value);
- // print(results);
- // setState(() {
- // querySearches = true;
- // });
- },
- ),
- ),
- SizedBox(
- width: 16,
- ),
- Container(
- width: 80,
- height: 40,
- child: ElevatedButton(
- onPressed: _showOptionsSearchDialog,
- child: Icon(Icons.manage_search),
- ),
- )
- ],
+ if (!_isSidebarOpen)
+ Positioned(
+ bottom: 16,
+ left: 16,
+ child: FloatingActionButton(
+ child: Icon(Icons.menu),
+ onPressed: () {
+ setState(() {
+ _isSidebarOpen = true;
+ });
+ },
),
),
- Container(
- padding: EdgeInsets.all(0.0),
- color: Color.fromARGB(42, 36, 102, 132),
- child: Row(
- children: [
- Container(
- height: 2,
- )
- ],
- ),
- ),
- Container(
- color: Color.fromARGB(255, 131, 110, 143),
- child: TabBar(
- controller: _tabController,
- isScrollable: true,
- tabs: _tabs
- .asMap()
- .entries
- .map((entry) => Tab(
- child: Row(
- children: [
- Text(entry.value),
- if (entry.value != 'Emails')
- GestureDetector(
- onTap: () => _removeTab(entry.key),
- child: Icon(Icons.close, size: 16),
- ),
- ],
- ),
- ))
- .toList(),
- labelColor: Colors.white,
- indicatorColor: Colors.white,
- ),
- ),
- Container(
- // alignment: Alignment.topLeft,
- padding: EdgeInsets.all(8.0),
- color: Colors.white,
- child: Row(
- children: [
- ElevatedButton(
- onPressed: () {
- _emailPageKey.currentState!.isBackDisabled ? null: _emailPageKey.currentState
- ?.updatePagenation('back');
- },
- child: Icon(Icons.navigate_before),
- ),
- Builder(
- builder: (context) {
- final emailState = _emailPageKey.currentState;
- if (emailState == null) {
- // Schedule a rebuild once the state is available
- Future.microtask(() => setState(() {}));
- return Text('Loading...');
- }
-
- return ValueListenableBuilder(
- valueListenable: emailState.currentPageNotifier,
- builder: (context, value, _) => Text('$value'),
- );
- },
- ),
- ElevatedButton(
- onPressed: () {
- _emailPageKey.currentState
- ?.updatePagenation('next');
- },
- child: Icon(Icons.navigate_next),
- ),
- ],
- ),
- ),
- Expanded(
- child: TabBarView(
- controller: _tabController,
- children: _tabs.map((tab) {
- return _tabWidgets[tab] ??
- Center(child: Text("No content found"));
- // return Center(
- // child: EmailPage(
- // key: _emailPageKey,
- // ));
- }).toList(),
- ),
- ),
-
- // if (_tabs.isEmpty)
- // Expanded(
- // child: EmailPage(key: _emailPageKey),
- // ),
- // if (_tabs.isNotEmpty)
- // Expanded(
- // // child: Text('supposed to be mails'),
- // child: TabBarView(
- // controller: _tabController,
- // children: _tabs
- // .map((tab) => Center(child: Text('Results for $tab')))
- // .toList(),
- // ),
- // ),
- ],
- ),
- ),
- ],
- ),
- if (!_isSidebarOpen)
- Positioned(
- bottom: 16,
- left: 16,
- child: FloatingActionButton(
- child: Icon(Icons.menu),
- onPressed: () {
- setState(() {
- _isSidebarOpen = true;
- });
- },
+ ],
),
),
- ],
+ ),
+ ),
),
);
}
}
-// void _showPopupMenu(BuildContext context, Offset position) async {
-// final RenderBox overlay =
-// Overlay.of(context).context.findRenderObject() as RenderBox;
-
-// await showMenu(
-// context: context,
-// position: RelativeRect.fromLTRB(
-// position.dx,
-// position.dy,
-// overlay.size.width - position.dx,
-// overlay.size.height - position.dy,
-// ),
-// items: >[
-// PopupMenuItem(
-// value: 'Open',
-// child: Text('Open'),
-// ),
-// PopupMenuItem(
-// value: 'Reply',
-// child: Text('Reply'),
-// ),
-// PopupMenuItem(
-// value: 'Delete',
-// child: Text('Delete'),
-// ),
-// ],
-// );
-// }
-// }
diff --git a/lib/main.dart b/lib/main.dart
index da90a50..7790390 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -17,7 +17,10 @@ class HyM extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
- theme: ThemeData.light(),
+ theme: ThemeData(
+ colorScheme: ColorScheme.light(),
+ useMaterial3: true,
+ ),
title: 'HyM',
// home: HomeScreen(),
initialRoute: "/",
diff --git a/lib/sonicEmailView.dart b/lib/sonicEmailView.dart
index bfe0bbe..aeb7301 100644
--- a/lib/sonicEmailView.dart
+++ b/lib/sonicEmailView.dart
@@ -1,145 +1,3 @@
-import 'package:crab_ui/augment.dart';
-import 'package:web/web.dart' as web;
-import 'dart:ui_web' as ui;
-import 'dart:js_interop';
-import 'structs.dart';
-import 'package:flutter/material.dart';
-
-class SonicEmailView extends StatefulWidget {
- SerializableMessage email;
- String emailHTML;
-
- SonicEmailView({required this.email, required this.emailHTML});
-
- @override
- _SonicEmailViewState createState() => _SonicEmailViewState();
-}
-
-class _SonicEmailViewState extends State {
- String viewTypeIDs = "";
- int heightOFViewtype = 0;
- bool _isLoaded = false;
-
- void _scrollToNumber(String spanId) {
- AugmentClasses.handleJump(spanId);
- }
-
- @override
- void initState() {
- super.initState();
- _init();
- }
-
- Future _init() async {
- await _registerViewFactory(widget.emailHTML);
- if (!mounted) return;
- setState(() {
- _isLoaded = true;
- });
- }
-
- Future _registerViewFactory(String currentContent) async {
- // setState(() { //update to do item per item
- // each item to have itsviewtype ID
- // is this necessarey here??
-
- //could just move to collapsable
-
- // for (var emailHTML in widget.threadHTML) {
- String viewTypeId = 'email-${DateTime.now().millisecondsSinceEpoch}';
-
- final ghost = web.document.createElement('div') as web.HTMLDivElement
- ..style.visibility = 'hidden'
- ..style.position = 'absolute'
- ..style.width = '100%'
- ..style.overflow = 'auto'
- ..innerHTML = currentContent.toJS;
- web.document.body?.append(ghost);
- await Future.delayed(Duration(milliseconds: 10));
-
- final heightOfEmail = ghost.scrollHeight;
- ghost.remove();
-
- final HTMLsnippet = web.document.createElement('div') as web.HTMLDivElement
- ..id = viewTypeId
- ..innerHTML = widget
- .emailHTML.toJS; // temporarily index because it has to do all of them
- HTMLsnippet.style
- ..width = '100%'
- ..height = '${heightOfEmail}px'
- ..overflow = 'auto'
- ..scrollBehavior = 'smooth';
-
- ui.platformViewRegistry.registerViewFactory(
- viewTypeId,
- (int viewId) => HTMLsnippet,
- );
- this.viewTypeIDs = viewTypeId;
- this.heightOFViewtype = heightOfEmail;
- print(viewTypeIDs);
- }
-
- @override
- Widget build(BuildContext context) {
- return _isLoaded
- ? Scaffold(
- appBar: AppBar(title: Text(widget.email.subject)),
- body: Stack(
- children: [
- Column(
- children: [
- EmailToolbar(
- onButtonPressed: () => {},
- onJumpToSpan: _scrollToNumber),
- Row(
- // title of email
- children: [
- Text(
- widget.email.subject,
- style: TextStyle(fontSize: 30),
- ),
- ],
- ),
- Row(
- children: [
- Text(
- 'from ${widget.email.name}',
- style: TextStyle(fontSize: 18),
- ),
- Text(
- '<${widget.email.from}>',
- style: TextStyle(fontSize: 18),
- ),
- Spacer(),
- Text(
- '${widget.email.date}',
- textAlign: TextAlign.right,
- )
- ],
- ),
- // TODO: make a case where if one of these is the user's email it just says me :)))))
- Row(
- children: [
- Text(
- 'to ${widget.email.to.toString()}',
- style: TextStyle(fontSize: 15),
- )
- ],
- ),
- Expanded(
- // child: SizedBox(
- // height: heightOFViewtype.toDouble(),
- child: HtmlElementView(
- key: UniqueKey(), viewType: this.viewTypeIDs,
- // ),
- ))
- ],
- ),
- ],
- ),
- )
- : const Center(
- child: CircularProgressIndicator(),
- );
- }
-}
+export 'SonicEmailViewStub.dart'
+ if (dart.library.js_interop) 'SonicEmailViewWeb.dart'
+ if (dart.library.io) 'SonicEmailViewAndroid.dart';
\ No newline at end of file
diff --git a/lib/structs.dart b/lib/structs.dart
index c7e9da4..e99c0d2 100644
--- a/lib/structs.dart
+++ b/lib/structs.dart
@@ -127,7 +127,8 @@ class AttachmentInfoList extends Iterable {
AttachmentInfoList(this._attachments);
factory AttachmentInfoList.fromJsonList(List