diff --git a/lib/collapsableEmailsAndroid.dart b/lib/collapsableEmailsAndroid.dart index f2cdf01..64f2cc8 100644 --- a/lib/collapsableEmailsAndroid.dart +++ b/lib/collapsableEmailsAndroid.dart @@ -1,6 +1,9 @@ -import 'structs.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_widget_from_html/flutter_widget_from_html.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 @@ -20,29 +23,287 @@ 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; + 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 = []; + String markdown = ''; + List> sentinel = []; + int level = 0; + AugmentTree zoomTreeRoot = AugmentTree(); + late AugmentTree currentZoomNode; + bool zoomOut = false; + bool zoomIn = true; + @override void initState() { super.initState(); - //html + _markdownConverter(); + _serializableData(widget.threadIDs); // this + _markdown2Tree(markdown); + _buildForZooms(); } + @override + void dispose() { + 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(); + 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(String text) { + print("started markdown2tree"); + final List 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); + } + } + + currentZoomNode = zoomTreeRoot; + if (!mounted) return; + setState(() { + _isLoaded = true; + }); + } + } + + void _goToChildren(int index) async { + final target = currentZoomNode.children[index]; + if (target.children.isNotEmpty) { + setState(() { + currentZoomNode = 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) { + setState(() { + currentZoomNode = currentZoomNode.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 { + emailsInThread = await ApiService().threadsInSerializable(threadID); + print("done thread serializable"); + if (!mounted) return; + setState(() { + _isLoaded = true; + }); + } + + Widget _buildForZooms({Key? key}) { + if (!_isLoaded) { + return const Center(child: CircularProgressIndicator()); // loading screen + } + final canZoomOut = currentZoomNode.parent != null; + + return ListView.builder( + key: key, + itemCount: currentZoomNode.children.length, + itemBuilder: (context, index) { + final childNode = currentZoomNode.children[index]; + final canZoomIn = childNode.children.isNotEmpty; + + 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() : null, + child: Icon(Icons.north_west_sharp), + ), + OutlinedButton( + onPressed: + canZoomIn ? () => _goToChildren(index) : null, + child: Icon(Icons.south_east_sharp), + ), + ], + ), + SizedBox(width: 12.0), + Expanded( + child: MarkdownBlock( + data: currentZoomNode + .children[index].data, // one string of markdown + config: MarkdownConfig + .darkConfig, // or lightConfig depending on theme + ), + ), + ], + ), + ), + ), + ); + }, + ); + } @override Widget build(BuildContext context) { - return Scaffold( - body: ListView( - children: [ - HtmlWidget( - widget.threadHTML[0], - // renderMode: RenderMode.listView, - ) - ] - ) - ); + 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(key: ValueKey(currentZoomNode))), + Divider(), + ], + ); + }, + ), + ), + ]) + : const Center(child: CircularProgressIndicator()); } }