deep linking added for only the first email in a thread

This commit is contained in:
Juan Marulanda De Los Rios 2025-07-15 12:26:16 -04:00
parent 4177e29e8b
commit 0b117abd88
3 changed files with 329 additions and 15 deletions

View File

@ -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<String> 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<EmailToolbar> {
String selectedClass = 'Class 1';
TextEditingController _jumpController = TextEditingController();
TextEditingController _viewspecsController = TextEditingController();
AugmentClasses? localAugment;
AugmentTree? localAugment;
List<SerializableMessage>? emailsInThread;
// late final FocusNode _JumpItemfocusNode;
@ -55,6 +59,7 @@ class _DynamicClassesAugment extends State<EmailToolbar> {
// _viewSpecsfocusNode.addListener(() {
// setState(() => _viewSpecsHasFocus = _viewSpecsfocusNode.hasFocus);
// });
localAugment = widget.rootAugment;
_serializableData(widget.emails);
}
@ -236,7 +241,9 @@ class _DynamicClassesAugment extends State<EmailToolbar> {
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<EmailToolbar> {
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<void> 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<void> 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(

View File

@ -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<CollapsableEmails> createState() => _CollapsableEmailsState();
AugmentTree? getAugmentRoot() {
return _CollapsableEmailsState().getAugmentRoot();
}
}
class _CollapsableEmailsState extends State<CollapsableEmails> {
@ -63,7 +67,7 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
static bool leftNumbering = true;
static bool rightNumbering = true;
bool showWhole = false;
List<AugmentTree> queryResults = [];
List<AugmentTree> queryResults = []; //results of conducting filtering
bool _isFilteringActive = false;
@override
@ -99,6 +103,14 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
super.dispose();
}
List<SerializableMessage> 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<CollapsableEmails> {
}
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<CollapsableEmails> {
maxHeight: MediaQuery.of(context).size.height * 0.6,
),
child: _buildForZooms(index), //show the tree
// child: _buildForZooms(key: ValueKey(currentZoomNode)),
),
Divider(),
],

205
lib/routingHandler.dart Normal file
View File

@ -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<StatefulWidget> createState() => _RoutingHandlerState();
}
class _RoutingHandlerState extends State<Routinghandler> {
List<String> markdownContent = [];
bool _isLoaded = false;
@override
void initState() {
// TODO: implement initState
super.initState();
_loadMarkdown();
}
Future<void> _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<StatefulWidget> createState() => _LinkViewerState();
}
class _LinkViewerState extends State<LinkViewer> {
@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: [],
));
}
}