From 0b117abd880a3f4f6996c71388a582d1b222eaed Mon Sep 17 00:00:00 2001 From: juan Date: Tue, 15 Jul 2025 12:26:16 -0400 Subject: [PATCH] deep linking added for only the first email in a thread --- lib/augment.dart | 118 +++++++++++++++++-- lib/collapsableEmailsWeb.dart | 21 +++- lib/routingHandler.dart | 205 ++++++++++++++++++++++++++++++++++ 3 files changed, 329 insertions(+), 15 deletions(-) create mode 100644 lib/routingHandler.dart diff --git a/lib/augment.dart b/lib/augment.dart index 42f2d3a..992f118 100644 --- a/lib/augment.dart +++ b/lib/augment.dart @@ -3,9 +3,11 @@ import 'package:crab_ui/attachmentDownload.dart'; import 'package:crab_ui/collapsableEmails.dart'; import 'package:crab_ui/structs.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'attachmentWidget.dart'; import 'package:flutter/services.dart'; +import 'routingHandler.dart'; class EmailToolbar extends StatefulWidget { final Function(String) onJumpToNumbering; @@ -14,6 +16,7 @@ class EmailToolbar extends StatefulWidget { final Function(String) onFiltering; final List emails; final String subject; + late AugmentTree? rootAugment; EmailToolbar({ Key? key, @@ -23,6 +26,7 @@ class EmailToolbar extends StatefulWidget { required this.onFiltering, required this.emails, required this.subject, + required this.rootAugment, }) : super(key: key); @override @@ -33,7 +37,7 @@ class _DynamicClassesAugment extends State { String selectedClass = 'Class 1'; TextEditingController _jumpController = TextEditingController(); TextEditingController _viewspecsController = TextEditingController(); - AugmentClasses? localAugment; + AugmentTree? localAugment; List? emailsInThread; // late final FocusNode _JumpItemfocusNode; @@ -55,6 +59,7 @@ class _DynamicClassesAugment extends State { // _viewSpecsfocusNode.addListener(() { // setState(() => _viewSpecsHasFocus = _viewSpecsfocusNode.hasFocus); // }); + localAugment = widget.rootAugment; _serializableData(widget.emails); } @@ -236,7 +241,9 @@ class _DynamicClassesAugment extends State { child: Text('Create Link'), ), ElevatedButton( - onPressed: AugmentClasses.handleFind, + // onPressed: () => localAugment!.handlePaste(context), + onPressed: () => + AugmentClasses().handlePaste(context, widget.emails[0]), child: Text('Paste Link'), ), ], @@ -249,6 +256,11 @@ class _DynamicClassesAugment extends State { class AugmentClasses { CollapsableEmails? localCollapsable; String? nameOfDocument; + AugmentTree? rootTree; + + void setRootTree(AugmentTree aTree) { + rootTree = aTree; + } // AugmentClasses(CollapsableEmails localCollapsable) { // localCollapsable = localCollapsable; @@ -519,6 +531,26 @@ class AugmentClasses { print("Find button pressed"); } + AugmentTree? _findAugmentNode(String target, AugmentTree node, int index) { + // so the ideqa is that since the numbering its quite linear, meaning that it tells you where it goes, + // thus i've thought the amount of max moves are only the length of the string of the target + //e.g. if we have a target of 1e9, its steps are the same or time complexity as if it were 1a1, or 99z99 + // since each number or letter tells us which is the index in this array, genius ik + // thus first one needs another function from converting from alphabetical to numbers + if (node.numbering[index] == target[index]) { + _findAugmentNode(target, node.children[index++], index++); + } + } + + bool _checkValidTarget(String target) { + target = target.trim(); + //find if the target exists, + //recursive? + _findAugmentNode(target, rootTree!, 0); + + return false; + } + _copyLink(String anchor, String target, String format, String viewspecs, String nameOfDocument) { String form = "$anchor < $nameOfDocument, $target :$viewspecs >"; @@ -631,7 +663,13 @@ class AugmentClasses { actions: [ ElevatedButton( onPressed: () => { - _copyLink(anchorController.text, targetController.text, format, viewspecsController.text, + // _checkValidTarget(targetController.text), + + _copyLink( + anchorController.text, + targetController.text, + format, + viewspecsController.text, nameOfDocument), Navigator.of(context).pop() }, @@ -644,6 +682,72 @@ class AugmentClasses { )); } + Future handlePaste(BuildContext context, String emailID) async { + final TextEditingController gotoLink = TextEditingController(); + + Routinghandler localRouting; + await showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text("GOTO Link"), + content: SizedBox( + height: 400, + child: Column( + children: [ + Row( + children: [ + Text("Paste link to go: "), + SizedBox( + width: 350, + child: TextField( + controller: gotoLink, + maxLines: 1, + autofocus: true, + decoration: const InputDecoration( + border: OutlineInputBorder(), + ), + )) + ], + ) + ], + ), + ), + actions: [ + ElevatedButton( + onPressed: () { + print('pressed'); + // i need this shit to be processed into args + print("email_id given $emailID"); + Navigator.of(context).pop(); + final localRouting = + Routinghandler(gotoLink.text, emailID); + final String subject = + localRouting.docName; // This is your :subject + final String target = + localRouting.target; // This is your :target + final String viewspecs = + localRouting.viewspecs; // This is your :viewspecs + final String finalEmailID = emailID; + + final encodedSubject = Uri.encodeComponent(subject); + final encodedTarget = Uri.encodeComponent(target); + final encodedViewspecs = Uri.encodeComponent(viewspecs); + final encodedEmailID = Uri.encodeComponent(finalEmailID); + print("emailID $encodedEmailID"); + String link = + "/email/$encodedSubject/$encodedTarget/$encodedViewspecs/$encodedEmailID"; + GoRouter.of(context).go(link); + }, + child: Text("OK")), + ElevatedButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text("Cancel")) + ], + )); + } + static void handleStop() { print("Stop button pressed"); } @@ -655,12 +759,8 @@ class AugmentClasses { static void invisibility(String htmlClass) {} static Future JumpButton(BuildContext context) async { - // FocusNode textFieldFocusNode = FocusNode(); - - // AugmentClasses.disableIframePointerEvents(); await showDialog( barrierDismissible: true, - // barrierColor: Colors.yellow, context: context, builder: (context) => AlertDialog( title: Text('Jump Item:'), @@ -892,10 +992,6 @@ class AugmentClasses { await showDialog( context: context, builder: (BuildContext dialogContext) { - // => Container( - // height: 150, - // width: 300, - // child: return StatefulBuilder(builder: (BuildContext statefulBuilderContext, StateSetter setState) { return AlertDialog( diff --git a/lib/collapsableEmailsWeb.dart b/lib/collapsableEmailsWeb.dart index 73b8c2d..3300e5e 100644 --- a/lib/collapsableEmailsWeb.dart +++ b/lib/collapsableEmailsWeb.dart @@ -1,8 +1,6 @@ -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; @@ -14,6 +12,7 @@ class CollapsableEmails extends StatefulWidget { final String? targetJumpNumbering; final String? targetViewspecs; final String? targetFiltering; + final String? nameOfDocument; const CollapsableEmails({ required this.thread, @@ -23,10 +22,15 @@ class CollapsableEmails extends StatefulWidget { this.targetJumpNumbering, this.targetViewspecs, this.targetFiltering, + this.nameOfDocument, }); @override State createState() => _CollapsableEmailsState(); + + AugmentTree? getAugmentRoot() { + return _CollapsableEmailsState().getAugmentRoot(); + } } class _CollapsableEmailsState extends State { @@ -63,7 +67,7 @@ class _CollapsableEmailsState extends State { static bool leftNumbering = true; static bool rightNumbering = true; bool showWhole = false; - List queryResults = []; + List queryResults = []; //results of conducting filtering bool _isFilteringActive = false; @override @@ -99,6 +103,14 @@ class _CollapsableEmailsState extends State { super.dispose(); } + List getThreads() { + return emailsInThread; + } + + AugmentTree getAugmentRoot() { + return zoomTreeRoot; + } + void _add2Tree(AugmentTree tree, md.Element node2add) { // adds node to its corresponding place AugmentTree newNode = AugmentTree(); @@ -252,6 +264,8 @@ class _CollapsableEmailsState extends State { } Widget _buildForZooms(int indexThread) { + // index of email in thread, currentZoomTree, + // if (!_isLoaded) { return const Center(child: CircularProgressIndicator()); // loading screen } @@ -568,7 +582,6 @@ class _CollapsableEmailsState extends State { maxHeight: MediaQuery.of(context).size.height * 0.6, ), child: _buildForZooms(index), //show the tree - // child: _buildForZooms(key: ValueKey(currentZoomNode)), ), Divider(), ], diff --git a/lib/routingHandler.dart b/lib/routingHandler.dart new file mode 100644 index 0000000..f3052a6 --- /dev/null +++ b/lib/routingHandler.dart @@ -0,0 +1,205 @@ +import 'package:crab_ui/collapsableEmails.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:markdown/markdown.dart' as md; +import 'package:markdown_widget/markdown_widget.dart'; +import 'api_service.dart'; + +class Routinghandler extends StatefulWidget { + Routinghandler(String link, emailID) { + bool anchorDone = false; + + bool docNameDone = false; + + bool viewspecsDone = false; + + bool targetDone = false; + + for (int letter = 0; letter < link.length; letter++) { + if (!anchorDone) { + if (link[letter] != '<') { + //when the anchor hasnt been dissected + anchor += link[letter]; + } else { + anchorDone = true; + } + } else if (!docNameDone) { + if (link[letter] != ',') { + //when the docName hasnt been dissected + docName += link[letter]; + } else { + docNameDone = true; + } + } else if (!targetDone) { + if (link[letter] != ':') { + target += link[letter]; + } else { + targetDone = true; + } + } else if (!viewspecsDone) { + if (link[letter] != '>') { + //when the docName hasnt been dissected + viewspecs += link[letter]; + } else { + viewspecsDone = true; + } + } + } + anchor = anchor.trim(); + docName = docName.trim(); + target = target.trim(); + viewspecs = viewspecs.trim(); + print("inside constructor uwu $emailID"); + emailID = emailID.trim(); + } + Routinghandler.fromParameters(String anchor, String docName, String target, + String viewspecs, String emailID) { + this.anchor = anchor; + this.docName = docName; + this.viewspecs = viewspecs; + this.target = target; + this.emailID = emailID; + } + Routinghandler.copyConstructor(Routinghandler other) { + anchor = other.anchor; + docName = other.docName; + viewspecs = other.viewspecs; + target = other.target; + emailID = other.emailID; + } + + String anchor = ''; + String docName = ''; + String viewspecs = ''; + String target = ''; + String emailID = ''; + + void goToLink() { + // bool anchorDone = false; + + // bool docNameDone = false; + + // bool viewspecsDone = false; + + // bool targetDone = false; + + // for (int letter = 0; letter < link.length; letter++) { + // if (!anchorDone) { + // if (link[letter] != '<') { + // //when the anchor hasnt been dissected + // anchor += link[letter]; + // } else { + // anchorDone = true; + // } + // } else if (!docNameDone) { + // if (link[letter] != ',') { + // //when the docName hasnt been dissected + // docName += link[letter]; + // } else { + // docNameDone = true; + // } + // } else if (!targetDone) { + // if (link[letter] != ':') { + // target += link[letter]; + // } else { + // targetDone = true; + // } + // } else if (!viewspecsDone) { + // if (link[letter] != '>') { + // //when the docName hasnt been dissected + // viewspecs += link[letter]; + // } else { + // viewspecsDone = true; + // } + // } + // } + print("anchor $anchor"); + print("docName $docName"); + print("target $target"); + print("viewspecs $viewspecs"); + print("emailID $emailID"); + //now it should open a widget in that part + //maybe i need a rewrite + } + + String getEmailID() { + return emailID; + } + + @override + State createState() => _RoutingHandlerState(); +} + +class _RoutingHandlerState extends State { + List markdownContent = []; + bool _isLoaded = false; + + @override + void initState() { + // TODO: implement initState + super.initState(); + _loadMarkdown(); + } + + Future _loadMarkdown() async { + String folder = ApiService.currFolder; + // print(folder); + print(widget.getEmailID()); + String emailID = widget.emailID; + print("inside _loadMarkdown in routinghandler $emailID"); + markdownContent = + await ApiService().fetchMarkdownContent([emailID], "INBOX"); + // print(markdownContent); + setState(() { + _isLoaded = true; + }); + } + + @override + Widget build(BuildContext context) { + if (!_isLoaded) { + return const Center( + child: CircularProgressIndicator(), + ); + } + return Scaffold( + appBar: AppBar( + title: Text("Routing Handler"), + leading: IconButton( + onPressed: () { + GoRouter.of(context).go('/home'); + }, + icon: const Icon(Icons.arrow_back_ios)), + ), + body: ConstrainedBox( + constraints: BoxConstraints( + minHeight: 100, + maxHeight: MediaQuery.of(context).size.height * 0.7, + ), + child: SingleChildScrollView( + child:MarkdownBlock(data: markdownContent[0]))), + ); + } +} + +class LinkViewer extends StatefulWidget { + const LinkViewer({super.key}); + + @override + State createState() => _LinkViewerState(); +} + +class _LinkViewerState extends State { + @override + Widget build(BuildContext context) { + // this should be a class that opens a popup of the email on the view it wants + + return Scaffold( + appBar: AppBar( + title: Text('url viewer'), + ), + body: Column( + children: [], + )); + } +}