clean, and update to match the web

This commit is contained in:
Juan Marulanda De Los Rios 2025-06-18 21:09:02 -04:00
parent c1afc8875e
commit 3410007f55
3 changed files with 217 additions and 87 deletions

View File

@ -1,6 +1,5 @@
import 'package:crab_ui/api_service.dart';
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:pointer_interceptor/pointer_interceptor.dart';

View File

@ -7,13 +7,19 @@ import 'package:markdown/markdown.dart' as md;
class CollapsableEmails extends StatefulWidget {
final List<String> thread; // email id's in the form xyz@gmail.com
final List<String> threadHTML;
// final List<String> threadHTML;
final List<String> threadMarkdown;
final String threadIDs;
final String? targetJumpNumbering;
final String? targetViewspecs;
CollapsableEmails(
{required this.thread,
required this.threadHTML,
required this.threadIDs});
required this.threadMarkdown,
required this.threadIDs,
this.targetJumpNumbering,
this.targetViewspecs,
});
@override
State<CollapsableEmails> createState() => _CollapsableEmailsState();
@ -40,21 +46,41 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
};
List<String> tagsCollected = [];
String markdown = '';
List<String> allMarkdown = [];
List<List<String>> sentinel = [];
int level = 0;
AugmentTree zoomTreeRoot = AugmentTree();
late AugmentTree currentZoomNode;
// late AugmentTree currentZoomNode;
late List<AugmentTree> currentZoomTree = [];
bool zoomOut = false;
bool zoomIn = true;
late List<AugmentTree> threadNodes = [];
static bool leftNumbering = false;
static bool rightNumbering = true;
bool showWhole = false;
@override
void initState() {
super.initState();
_markdownConverter();
threadNodes = [];
currentZoomTree = [];
// _markdownConverter();
_serializableData(widget.threadIDs); // this
_markdown2Tree(markdown);
_buildForZooms();
_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
@ -62,10 +88,6 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
super.dispose();
}
void _markdownConverter() async {
markdown = html2md.convert(widget.threadHTML[0]);
}
void _add2Tree(AugmentTree tree, md.Element node2add) {
// adds node to its corresponding place
AugmentTree newNode = AugmentTree();
@ -119,83 +141,67 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
}
}
void _markdown2Tree(String text) {
void _markdown2Tree(List<String> text) {
print("started markdown2tree");
final List<md.Node> nakedList = md.Document().parseLines(text.split('\n'));
for (var node in nakedList) {
//maybe do an add function, but isn't this it?
if (node is md.Element) {
// print(node.textContent);
AugmentTree temp = AugmentTree();
temp.data = node.textContent;
temp.ogTag = node.tag;
if (hirarchyDict.containsKey(node.tag)) {
_add2Tree(zoomTreeRoot, node);
for (int emailsMD = 0; emailsMD < text.length; emailsMD++) {
final List<md.Node> 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
}
}
}
currentZoomNode = zoomTreeRoot;
if (!mounted) return;
setState(() {
_isLoaded = true;
});
zoomTreeRoot.addNumbering();
threadNodes.add(zoomTreeRoot);
currentZoomTree.add(zoomTreeRoot);
}
if (!mounted) return;
setState(() {
_isLoaded = true;
});
}
void _goToChildren(int index) async {
final target = currentZoomNode.children[index];
void _goToChildren(int indexThread, int index) async {
final target = currentZoomTree[indexThread].children[index];
if (target.children.isNotEmpty) {
setState(() {
currentZoomNode = target;
currentZoomTree[indexThread] = target;
});
} else {
print("This child has no further children.");
}
// if (currentZoomNode.children.isNotEmpty) {
// setState(() {
// zoomIn = true;
// zoomOut = true;
// currentZoomNode = currentZoomNode.children[index];
// if (currentZoomNode.children[index].children.isEmpty) {
// print('disable in');
// setState(() {
// zoomIn = false;
// });
// }
// });
// } else {
// print("disable zoom down");
// setState(() {
// zoomIn = false;
// });
// }
}
void _goToParent() async {
if (currentZoomNode.parent != null) {
void _goToParent(int indexThread) async {
if (currentZoomTree[indexThread].parent != null) {
setState(() {
currentZoomNode = currentZoomNode.parent!;
currentZoomTree[indexThread] = currentZoomTree[indexThread].parent!;
});
} else {
print("Already at root.");
}
// print("parent ${currentZoomNode.parent}");
// print("parent ${currentZoomNode.parent!.parent}");
// if (currentZoomNode.parent != null) {
// setState(() {
// currentZoomNode = currentZoomNode.parent!;
// if (currentZoomNode.parent == null) {
// setState(() {
// zoomOut = false;
// });
// }
// });
// } else if (currentZoomNode.parent == null ||
// currentZoomNode.parent!.parent == null) {
// print("disable zoom up");
}
void _serializableData(String threadID) async {
@ -207,19 +213,23 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
});
}
Widget _buildForZooms({Key? key}) {
Widget _buildForZooms(int indexThread) {
// IF I GIVE IT THE INDEX????
if (!_isLoaded) {
return const Center(child: CircularProgressIndicator()); // loading screen
}
final canZoomOut = currentZoomNode.parent != null;
final AugmentTree currentZoomNodeForThisEmail =
currentZoomTree[indexThread];
final canZoomOut = currentZoomNodeForThisEmail.parent != null;
return ListView.builder(
key: key,
itemCount: currentZoomNode.children.length,
itemCount: currentZoomNodeForThisEmail.children.length,
itemBuilder: (context, index) {
final childNode = currentZoomNode.children[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(
@ -236,25 +246,46 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
spacing: 4.0,
children: [
OutlinedButton(
onPressed: canZoomOut ? () => _goToParent() : null,
onPressed:
canZoomOut ? () => _goToParent(indexThread) : null,
child: Icon(Icons.north_west_sharp),
),
OutlinedButton(
onPressed:
canZoomIn ? () => _goToChildren(index) : null,
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: currentZoomNode
.children[index].data, // one string of markdown
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)),
),
),
],
),
),
@ -263,6 +294,100 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
},
);
}
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) {
@ -294,9 +419,10 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
constraints: BoxConstraints(
minHeight: 100,
maxHeight:
MediaQuery.of(context).size.height * 0.6),
child:
_buildForZooms(key: ValueKey(currentZoomNode))),
MediaQuery.of(context).size.height * 0.6,
),
child: _buildForZooms(index),
),
Divider(),
],
);

View File

@ -43,6 +43,9 @@ class _EmailViewState extends State<EmailView> {
void _scrollToNumber(String spanId) {
// AugmentClasses.handleJump(spanId);
}
void _viewSpecs(String command){
}
@override
Widget build(BuildContext context) {
@ -56,7 +59,9 @@ class _EmailViewState extends State<EmailView> {
children: [
EmailToolbar(
onButtonPressed: () => {},
onJumpToSpan: _scrollToNumber
onJumpToNumbering: _scrollToNumber,
onViewspecs: _viewSpecs,
),
Row(
children: [
@ -98,7 +103,7 @@ class _EmailViewState extends State<EmailView> {
Expanded(
child: CollapsableEmails(
thread: widget.messages,
threadHTML: widget.emailContent,
threadMarkdown: widget.emailContent,
threadIDs: widget.id,
),
),