WIP: android/ios-adaption feature, markdown, and augment #6

Draft
Juan wants to merge 28 commits from android-adaption into main
Showing only changes of commit 654520ad3a - Show all commits

View File

@ -1,5 +1,3 @@
import 'dart:js_interop';
import 'package:web/web.dart' as web;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'api_service.dart'; import 'api_service.dart';
import 'structs.dart'; import 'structs.dart';
@ -25,59 +23,53 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
List<String> emailsHTML = []; //html of the emails in the thread List<String> emailsHTML = []; //html of the emails in the thread
// build attachments with the forldar name and id // build attachments with the forldar name and id
Set<int> _expandedEmails = {}; //open emails Set<int> _expandedEmails = {}; //open emails
List viewtypeIDs = []; //IDs of the viewtypes, order matters
List heightOfViewTypes = []; //the height of each viewtype
List<SerializableMessage> emailsInThread = []; List<SerializableMessage> emailsInThread = [];
bool _isLoaded = false; bool _isLoaded = false;
static bool _isListenerRegistered = false;
static bool left = true;
static bool right = true;
web.EventListener? _listener;
List<String> hirarchy = ["h1", "h2", "h3", "h4", "h5", "h6", "p"]; List<String> hirarchy = ["h1", "h2", "h3", "h4", "h5", "h6", "p"];
Map<String, int> hirarchyDict = { Map<String, int> hirarchyDict = {
"h1": 1, "h1": 1,
"h2": 2, "h2": 2,
"h3": 3, "h3": 3,
"h4": 4, "h4": 4,
"h5": 6, "h5": 5,
"h6": 7, "h6": 6,
"p": 8, "p": 8,
"ul": 8, "ul": 8,
"li": 8, "li": 8,
}; };
List<String> tagsCollected = []; List<String> tagsCollected = [];
String markdown = ''; List<String> allMarkdown = [];
List<List<String>> sentinel = []; List<List<String>> sentinel = [];
int level = 0; int level = 0;
AugmentTree zoomTreeRoot = AugmentTree(); AugmentTree zoomTreeRoot = AugmentTree();
late AugmentTree currentZoomNode; // late AugmentTree currentZoomNode;
late List<AugmentTree> currentZoomTree = [];
bool zoomOut = false; bool zoomOut = false;
bool zoomIn = true; bool zoomIn = true;
late List<AugmentTree> threadNodes = [];
@override @override
void initState() { void initState() {
super.initState(); super.initState();
threadNodes = [];
currentZoomTree = [];
_markdownConverter(); _markdownConverter();
// _registerViewFactory(widget.threadHTML);
_serializableData(widget.threadIDs); // this _serializableData(widget.threadIDs); // this
_markdown2Tree(markdown); _markdown2Tree(allMarkdown);
_keyListener();
_buildForZooms();
} }
@override @override
void dispose() { void dispose() {
if (_listener != null) {
web.window.document.removeEventListener('keydown', _listener!);
_listener = null;
_isListenerRegistered = false;
}
super.dispose(); super.dispose();
} }
void _markdownConverter() async { void _markdownConverter() async {
markdown = html2md.convert(widget.threadHTML[0]); for (int email = 0; email < widget.threadHTML.length; email++) {
String markdown = html2md.convert(widget.threadHTML[email]);
allMarkdown.add(markdown);
}
} }
void _add2Tree(AugmentTree tree, md.Element node2add) { void _add2Tree(AugmentTree tree, md.Element node2add) {
@ -89,7 +81,6 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
//1. a node that comes is lower than the root.children last, if so it goes beneath it //1. a node that comes is lower than the root.children last, if so it goes beneath it
if (tree.children.isEmpty) { if (tree.children.isEmpty) {
// new level to be created when totally empty // new level to be created when totally empty
print('is empty');
tree.children.add(newNode); tree.children.add(newNode);
newNode.parent = tree; newNode.parent = tree;
} else if (tree.children.isNotEmpty && } else if (tree.children.isNotEmpty &&
@ -128,99 +119,69 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
_add2Tree(tree.children.last, node2add); _add2Tree(tree.children.last, node2add);
} else if ((hirarchyDict[node2add.tag] ?? -1) == } else if ((hirarchyDict[node2add.tag] ?? -1) ==
(hirarchyDict[tree.children.last.ogTag] ?? -1)) { (hirarchyDict[tree.children.last.ogTag] ?? -1)) {
// print("equals??");
tree.children.add(newNode); tree.children.add(newNode);
newNode.parent = tree; newNode.parent = tree;
} }
} }
} }
void _markdown2Tree(String text) { void _markdown2Tree(List<String> text) {
print("started markdown2tree"); print("started markdown2tree");
final List<md.Node> nakedList = md.Document().parseLines(text.split('\n')); for (int emailsMD = 0; emailsMD < text.length; emailsMD++) {
List<String> pList = []; final List<md.Node> nakedList =
List<String> h1List = []; md.Document().parseLines(text[emailsMD].split('\n'));
List<String> h2List = []; zoomTreeRoot = AugmentTree();
List<String> h3List = []; for (var node in nakedList) {
List<String> h4List = []; //maybe do an add function, but isn't this it?
List<String> h5List = []; if (node is md.Element) {
List<String> h6List = []; AugmentTree temp = AugmentTree();
temp.data = node.textContent;
for (var node in nakedList) { temp.ogTag = node.tag;
//maybe do an add function, but isn't this it? if (node.tag == 'h1') {
if (node is md.Element) { // make this O(1)
// print(node.textContent); _add2Tree(zoomTreeRoot, node);
AugmentTree temp = AugmentTree(); } else if (node.tag == 'h2') {
temp.data = node.textContent; // i dont add any since i dont have it, maybe the function makes sense
temp.ogTag = node.tag; _add2Tree(zoomTreeRoot, node); // fix this
if (node.tag == 'h1') { } else if (node.tag == 'h3') {
h1List.add(node.textContent); _add2Tree(zoomTreeRoot, node);
_add2Tree(zoomTreeRoot, node); } else if (node.tag == 'h4') {
} else if (node.tag == 'h2') { _add2Tree(zoomTreeRoot, node); // change to temp
// i dont add any since i dont have it, maybe the function makes sense } else if (node.tag == 'h5') {
h2List.add(node.textContent); _add2Tree(zoomTreeRoot, node);
_add2Tree(zoomTreeRoot, node); // fix this } else if (node.tag == 'h6') {
} else if (node.tag == 'h3') { _add2Tree(zoomTreeRoot, node); // fix this
h3List.add(node.textContent); } else if (node.tag == 'p' || node.tag == 'ul' || node.tag == 'li') {
_add2Tree(zoomTreeRoot, node); _add2Tree(zoomTreeRoot, node); // fix this
} else if (node.tag == 'h4') { }
h4List.add(node.textContent); //this broke it
_add2Tree(zoomTreeRoot, node); // change to temp
} else if (node.tag == 'h5') {
h5List.add(node.textContent);
print(node.textContent);
_add2Tree(zoomTreeRoot, node);
} else if (node.tag == 'h6') {
h6List.add(node.textContent);
_add2Tree(zoomTreeRoot, node); // fix this
} else if (node.tag == 'p' || node.tag == 'ul' || node.tag == 'li') {
pList.add(node.textContent);
_add2Tree(zoomTreeRoot, node); // fix this
} }
} }
zoomTreeRoot.addNumbering();
threadNodes.add(zoomTreeRoot);
currentZoomTree.add(zoomTreeRoot);
} }
this.sentinel = [h1List, h2List, h3List, h4List, h5List, h6List, pList];
sentinel.removeWhere((hList) => hList.isEmpty);
currentZoomNode = zoomTreeRoot;
if (!mounted) return; if (!mounted) return;
setState(() { setState(() {
_isLoaded = true; _isLoaded = true;
}); });
} }
void handleKeyDownMD(web.Event event) async { void _goToChildren(int indexThread, int index) async {
final keyEvent = event as web.KeyboardEvent; final target = currentZoomTree[indexThread].children[index];
if (!mounted) return;
if (keyEvent.key == 'a') {
print("key a");
setState(() {
level = (level - 1).clamp(0, sentinel.length - 1);
});
} else if (keyEvent.key == "b") {
print("b");
setState(() {
level = (level + 1).clamp(0, sentinel.length - 1);
});
}
}
void _goToChildren(int index) async {
final target = currentZoomNode.children[index];
if (target.children.isNotEmpty) { if (target.children.isNotEmpty) {
setState(() { setState(() {
currentZoomNode = target; currentZoomTree[indexThread] = target;
}); });
} else { } else {
print("This child has no further children."); print("This child has no further children.");
} }
} }
void _goToParent() async { void _goToParent(int indexThread) async {
if (currentZoomNode.parent != null) { if (currentZoomTree[indexThread].parent != null) {
setState(() { setState(() {
currentZoomNode = currentZoomNode.parent!; currentZoomTree[indexThread] = currentZoomTree[indexThread].parent!;
}); });
} else { } else {
print("Already at root."); print("Already at root.");
@ -230,82 +191,30 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
void _serializableData(String threadID) async { void _serializableData(String threadID) async {
emailsInThread = await ApiService().threadsInSerializable(threadID); emailsInThread = await ApiService().threadsInSerializable(threadID);
print("done thread serializable"); print("done thread serializable");
if (!mounted) return; if (!mounted) return;
setState(() { setState(() {
_isLoaded = true; _isLoaded = true;
}); });
} }
void handleKeyDownHTML(web.Event event) { Widget _buildForZooms(int indexThread) {
final keyEvent = event as web.KeyboardEvent; // IF I GIVE IT THE INDEX????
if (keyEvent.key == 'G') {
print('You pressed the "G" key!');
final rightPurpleNums = web.document.getElementsByClassName("right");
_CollapsableEmailsState.right = !_CollapsableEmailsState.right;
final newOpacity = _CollapsableEmailsState.right ? '1.0' : '0.0';
for (int i = 0; i < rightPurpleNums.length; i++) {
final currentElement = rightPurpleNums.item(i) as web.HTMLElement;
currentElement.style.opacity = newOpacity;
}
} else if (keyEvent.key == 'H') {
print('You pressed the "H" key!');
final leftPurpleNums = web.document.getElementsByClassName("left");
_CollapsableEmailsState.left = !_CollapsableEmailsState.left;
final newOpacity = _CollapsableEmailsState.left ? '1.0' : '0.0';
for (int i = 0; i < leftPurpleNums.length; i++) {
final currentElement = leftPurpleNums.item(i) as web.HTMLElement;
currentElement.style.opacity = newOpacity;
}
} else if (keyEvent.key == 'm') {
print("you pressed 'm'");
final purpleNums = web.document.getElementsByClassName("purplenumber");
_CollapsableEmailsState.left = true;
_CollapsableEmailsState.right = true;
for (int i = 0; i < purpleNums.length; i++) {
final currentElement = purpleNums.item(i) as web.HTMLElement;
currentElement.style.opacity = '1.0';
}
} else if (keyEvent.key == 'n') {
print("you pressed 'n'");
final purpleNums = web.document.getElementsByClassName("purplenumber");
_CollapsableEmailsState.left = false;
_CollapsableEmailsState.right = false;
for (int i = 0; i < purpleNums.length; i++) {
final currentElement = purpleNums.item(i) as web.HTMLElement;
currentElement.style.opacity = '0.0';
}
} else if (keyEvent.key == 'w') {
print("you pressed 'w'");
}
}
void _keyListener() {
if (_isListenerRegistered) return;
_isListenerRegistered = true;
// Convert the top-level function to JS-compatible
// _listener = handleKeyDownHTML.toJS;
_listener = handleKeyDownMD.toJS;
web.window.document.addEventListener('keydown', _listener!);
}
Widget _buildForZooms({Key? key}) {
if (!_isLoaded) { if (!_isLoaded) {
return const Center(child: CircularProgressIndicator()); // loading screen 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( return ListView.builder(
key: key, itemCount: currentZoomNodeForThisEmail.children.length,
itemCount: currentZoomNode.children.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final childNode = currentZoomNode.children[index]; final childNode = currentZoomNodeForThisEmail.children[index];
final canZoomIn = childNode.children.isNotEmpty; final canZoomIn = childNode.children.isNotEmpty;
// currentZoomNodeForThisEmail.addNumbering();
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0), padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0),
child: Material( child: Material(
@ -323,12 +232,13 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
children: [ children: [
OutlinedButton( OutlinedButton(
onPressed: onPressed:
canZoomOut ? () => _goToParent() : null, canZoomOut ? () => _goToParent(indexThread) : null,
child: Icon(Icons.north_west_sharp), child: Icon(Icons.north_west_sharp),
), ),
OutlinedButton( OutlinedButton(
onPressed: onPressed: canZoomIn
canZoomIn ? () => _goToChildren(index) : null, ? () => _goToChildren(indexThread, index)
: null,
child: Icon(Icons.south_east_sharp), child: Icon(Icons.south_east_sharp),
), ),
], ],
@ -336,12 +246,17 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
SizedBox(width: 12.0), SizedBox(width: 12.0),
Expanded( Expanded(
child: MarkdownBlock( child: MarkdownBlock(
data: currentZoomNode data: childNode.data,
.children[index].data, // one string of markdown // data: currentZoomNode
// .children[index].data, // one string of markdown
config: MarkdownConfig config: MarkdownConfig
.darkConfig, // or lightConfig depending on theme .darkConfig, // or lightConfig depending on theme
), ),
), ),
Text(
childNode.numbering,
style: TextStyle(color: Color(Colors.purple[400]!.value)),
)
], ],
), ),
), ),
@ -382,7 +297,8 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
minHeight: 100, minHeight: 100,
maxHeight: MediaQuery.of(context).size.height * 0.6, maxHeight: MediaQuery.of(context).size.height * 0.6,
), ),
child: _buildForZooms(key: ValueKey(currentZoomNode)), child: _buildForZooms(index), //show the tree
// child: _buildForZooms(key: ValueKey(currentZoomNode)),
), ),
Divider(), Divider(),
], ],