From a1fde46aec454cc57bed49c940415da5fe7d792d Mon Sep 17 00:00:00 2001 From: juan Date: Mon, 9 Jun 2025 16:37:38 -0400 Subject: [PATCH] data structure works --- lib/collapsableEmailsWeb.dart | 430 +++++++++++++++++++++++++++------- 1 file changed, 340 insertions(+), 90 deletions(-) diff --git a/lib/collapsableEmailsWeb.dart b/lib/collapsableEmailsWeb.dart index 966a10e..b748daf 100644 --- a/lib/collapsableEmailsWeb.dart +++ b/lib/collapsableEmailsWeb.dart @@ -1,3 +1,4 @@ +import 'dart:collection'; import 'dart:js_interop'; import 'dart:js_interop_unsafe'; import 'package:web/web.dart' as web; @@ -5,6 +6,9 @@ import 'package:flutter/material.dart'; import 'dart:ui_web' as ui; 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 @@ -33,22 +37,234 @@ class _CollapsableEmailsState extends State { static bool right = true; web.EventListener? _listener; 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 root = AugmentTree(); @override void initState() { super.initState(); - _registerViewFactory(widget.threadHTML); - _serializableData(widget.threadIDs); // this + _markdownConverter(); + // _registerViewFactory(widget.threadHTML); + // _serializableData(widget.threadIDs); // this + _markdown2Tree(markdown); _keyListener(); + _buildForZooms(level); + } + + @override + void dispose() { + if (_listener != null) { + web.window.document.removeEventListener('keydown', _listener!); + _listener = null; + _isListenerRegistered = false; + } + 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 + print('is 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 { + tree.children.add(newNode); + } + // } else{ // so the node should go high + // _add2Tree(tree.children.last, node2add); + // } + + //maybe? + // } else { + // } + } 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; + } + + print("> ${node2add.tag}"); + + _add2Tree(tree.children.last, node2add); + } else if ((hirarchyDict[node2add.tag] ?? -1) == + (hirarchyDict[tree.children.last.ogTag] ?? -1)) { + print("equals??"); + tree.children.add(newNode); + newNode.parent = tree; + } + } + } + + void _markdown2Tree(String text) { + print("started markdown2tree"); + int highest = 0; + AugmentTree zoomTreeRoot = AugmentTree(); + final List nakedList = md.Document().parseLines(text.split('\n')); + List pList = []; + List h1List = []; + List h2List = []; + List h3List = []; + List h4List = []; + List h5List = []; + List h6List = []; + + 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 (node.tag == 'h1') { + h1List.add(node.textContent); + _add2Tree(zoomTreeRoot, node); + } else if (node.tag == 'h2') { + // i dont add any since i dont have it, maybe the function makes sense + h2List.add(node.textContent); + } else if (node.tag == 'h3') { + h3List.add(node.textContent); + root.children.add(temp); + _add2Tree(zoomTreeRoot, node); + } else if (node.tag == 'h4') { + h4List.add(node.textContent); //this broke it + _add2Tree(zoomTreeRoot, node); // change to temp + if (root.children.isNotEmpty) { + root.children.last.children.add(temp); + } + } else if (node.tag == 'h5') { + h5List.add(node.textContent); + print(node.textContent); + _add2Tree(zoomTreeRoot, node); + if (root.children.last.children.isNotEmpty) { + print("h5 index ${root.children.last.children.length}"); + root.children.last.children.last.children.add(temp); + print( + "h5 after index length ${root.children.last.children.last.children.length}"); + } + } else if (node.tag == 'h6') { + h6List.add(node.textContent); + if (root.children.last.children.isNotEmpty) { + print( + "h6 index ${root.children.last.children.last.children.length}"); + root.children.last.children.last.children.add(temp); + print(node.textContent); + _add2Tree(zoomTreeRoot, node); + } + // root.children.last.children.last.children.add(temp); + } else if (node.tag == 'p' || node.tag == 'ul' || node.tag == 'li') { + pList.add(node.textContent); + _add2Tree(zoomTreeRoot, node); // fix this + + if (root.children.isEmpty) { + root.children.add(temp); + } else if (root.children.last.children.isNotEmpty) { + //perhaps recursive + root.children.last.children.last.children.add(temp); + } + } + } + } + + this.sentinel = [h1List, h2List, h3List, h4List, h5List, h6List, pList]; + sentinel.removeWhere((hList) => hList.isEmpty); + + print('algorithm adding: '); + print("first layer: ${zoomTreeRoot.children}"); + print("first node: ${zoomTreeRoot.children[0].data}"); //good + print("second layer: ${zoomTreeRoot.children.last.children}"); //good + print( + "first node: ${zoomTreeRoot.children.last.children.first.data}"); //good + for (int n = 1; n < zoomTreeRoot.children.last.children.length - 1; n++) { + //good + print(zoomTreeRoot.children.last.children[n].data); + } + print("last node: ${zoomTreeRoot.children.last.children.last.data}"); //good + print("third layer"); + for (int thirdLayer = 0; + thirdLayer < zoomTreeRoot.children.last.children.length; + thirdLayer++) { + print(zoomTreeRoot.children.last.children[thirdLayer].children); + } + print("third layer contents first"); //not sure + + print(zoomTreeRoot.children.last.children[5].children[0].data); //good + for (int contentsOf4 = 1; + contentsOf4 < zoomTreeRoot.children.last.children[5].children.length; + contentsOf4++) { + print(zoomTreeRoot.children.last.children[5].children[contentsOf4].data); + } + if (!mounted) return; + setState(() { + _isLoaded = true; + }); + } + + void handleKeyDownMD(web.Event event) async { + final keyEvent = event as web.KeyboardEvent; + + if (!mounted) return; + if (keyEvent.key == 'a') { + print("key a"); + setState(() { + level = (level - 1).clamp(0, sentinel.length - 1); + }); + // _buildForZooms(level + 1); //probably need a level? + } else if (keyEvent.key == "b") { + print("b"); + setState(() { + level = (level + 1).clamp(0, sentinel.length - 1); + }); + // _buildForZooms(level - 1); //probably need a level? + } } - // @override - // void dispose() { - // if (_listener != null) { - // web.window.document.removeEventListener('keydown', _listener!); - // } - // super.dispose(); - // } void _registerViewFactory(List currentContent) async { // setState(() { //update to do item per item @@ -102,7 +318,7 @@ class _CollapsableEmailsState extends State { }); } - void handleKeyDown(web.Event event) { + void handleKeyDownHTML(web.Event event) { final keyEvent = event as web.KeyboardEvent; if (keyEvent.key == 'G') { @@ -145,7 +361,7 @@ class _CollapsableEmailsState extends State { } } else if (keyEvent.key == 'w') { print("you pressed 'w'"); - getTopLevel(); + // getTopLevel(); } } @@ -154,62 +370,91 @@ class _CollapsableEmailsState extends State { _isListenerRegistered = true; // Convert the top-level function to JS-compatible - _listener = handleKeyDown.toJS; + // _listener = handleKeyDownHTML.toJS; + _listener = handleKeyDownMD.toJS; + web.window.document.addEventListener('keydown', _listener!); } - void getTopLevel() { - print("started top"); - int highest = 0; - AugmentTree zoomTreeRoot = AugmentTree(); - // zoomTreeRoot.data = emailsHTML[0]; // whole thing + // void getTopLevel() { + // print("started top"); + // int highest = 0; + // AugmentTree zoomTreeRoot = AugmentTree(); + // // zoomTreeRoot.data = emailsHTML[0]; // whole thing - while (highest < hirarchy.length - 1) { - var highestElement = web.document.querySelectorAll(hirarchy[highest]); - print("nodelist $highestElement"); + // while (highest < hirarchy.length - 1) { + // var highestElement = web.document.querySelectorAll(hirarchy[highest]); + // print("nodelist $highestElement"); - if (highestElement.isNull || highestElement.length == 0) { - //from h1, h2, h3, ..., p. - highest += 1; - print(hirarchy[highest]); - } else { - AugmentTree newLevel = AugmentTree(); // list of children of each level - for (int i = 0; i < highestElement.length; i++) { - print(highestElement.item(i)?.textContent); + // if (highestElement.isNull || highestElement.length == 0) { + // //from h1, h2, h3, ..., p. + // highest += 1; + // print(hirarchy[highest]); + // } else { + // AugmentTree newLevel = AugmentTree(); // list of children of each level + // for (int i = 0; i < highestElement.length; i++) { + // print(highestElement.item(i)?.textContent); - tagsCollected - .add(highestElement.item(i)?.textContent ?? "nameless subtitle"); - newLevel.children - .add(highestElement.item(i)?.textContent ?? "nameless subtitle"); - } - // traverse to last node and add new level to it - // next - if (zoomTreeRoot.node == null) { - zoomTreeRoot.node = newLevel; - } else { - AugmentTree temp = zoomTreeRoot; - while (true) { - //get to the last and assign node = last node - if (temp.node != null) { - temp = temp.node!; - } else { - temp.node = newLevel; - break; - } - } - } + // tagsCollected + // .add(highestElement.item(i)?.textContent ?? "nameless subtitle"); + // newLevel.children + // .add(highestElement.item(i)?.textContent ?? "nameless subtitle"); + // } + // // traverse to last node and add new level to it + // // next + // if (zoomTreeRoot.node == null) { + // zoomTreeRoot.node = newLevel; + // } else { + // AugmentTree temp = zoomTreeRoot; + // while (true) { + // //get to the last and assign node = last node + // if (temp.node != null) { + // temp = temp.node!; + // } else { + // temp.node = newLevel; + // break; + // } + // } + // } - highest += 1; - } + // highest += 1; + // } + // } + // print("out safely"); + // print(tagsCollected); //instead of a list make a tree + // print(zoomTreeRoot.children); + // print(zoomTreeRoot.node?.children); + // print(zoomTreeRoot.node?.node?.children); + // print(zoomTreeRoot.node?.node?.node?.children); + // } + + Widget _buildForZooms(int lvl) { + this.level = lvl; + if (lvl < 0 || lvl >= sentinel.length) { + return Center(child: Text("No content at level $lvl")); } - print("out safely"); - print(tagsCollected); //instead of a list make a tree - print(zoomTreeRoot.children); - print(zoomTreeRoot.node?.children); - print(zoomTreeRoot.node?.node?.children); - print(zoomTreeRoot.node?.node?.node?.children); - - + return ListView.builder( + itemCount: this.sentinel[level].length, + itemBuilder: (context, index) { + 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.surfaceTint, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: MarkdownBlock( + data: sentinel[level][index], // one string of markdown + config: MarkdownConfig + .darkConfig, // or lightConfig depending on theme + ), + ), + ), + ); + }, + ); } @override @@ -217,38 +462,43 @@ class _CollapsableEmailsState extends State { 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) - SizedBox( - height: heightOfViewTypes[index].toDouble(), - child: HtmlElementView( - key: UniqueKey(), viewType: viewtypeIDs[index]), - ), - Divider(), - ], - ); - }, - ), + // child: MarkdownWidget(data: markdown), //hmmm + child: _buildForZooms(level), ) + + // 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) + // SizedBox( + // height: heightOfViewTypes[index].toDouble(), + // child: HtmlElementView( + // key: UniqueKey(), viewType: viewtypeIDs[index]), + // ), + // Divider(), + // ], + // ); + // }, + // ), + // ) ]) : const Center(child: CircularProgressIndicator()); }