Compare commits
2 Commits
de3d7f8bfc
...
160fe25be3
Author | SHA1 | Date | |
---|---|---|---|
160fe25be3 | |||
69b5408f73 |
@ -1,6 +1,9 @@
|
|||||||
import 'structs.dart';
|
|
||||||
import 'package:flutter/material.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 {
|
class CollapsableEmails extends StatefulWidget {
|
||||||
final List<String> thread; // email id's in the form xyz@gmail.com
|
final List<String> thread; // email id's in the form xyz@gmail.com
|
||||||
@ -20,29 +23,287 @@ 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;
|
||||||
|
|
||||||
|
List<String> hirarchy = ["h1", "h2", "h3", "h4", "h5", "h6", "p"];
|
||||||
|
Map<String, int> hirarchyDict = {
|
||||||
|
"h1": 1,
|
||||||
|
"h2": 2,
|
||||||
|
"h3": 3,
|
||||||
|
"h4": 4,
|
||||||
|
"h5": 6,
|
||||||
|
"h6": 7,
|
||||||
|
"p": 8,
|
||||||
|
"ul": 8,
|
||||||
|
"li": 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
List<String> tagsCollected = [];
|
||||||
|
String markdown = '';
|
||||||
|
List<List<String>> sentinel = [];
|
||||||
|
int level = 0;
|
||||||
|
AugmentTree zoomTreeRoot = AugmentTree();
|
||||||
|
late AugmentTree currentZoomNode;
|
||||||
|
bool zoomOut = false;
|
||||||
|
bool zoomIn = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.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<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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return _isLoaded
|
||||||
body: ListView(
|
? Column(children: [
|
||||||
children: [
|
Expanded(
|
||||||
HtmlWidget(
|
child: ListView.builder(
|
||||||
widget.threadHTML[0],
|
itemCount: emailsInThread.length,
|
||||||
// renderMode: RenderMode.listView,
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
import 'dart:collection';
|
|
||||||
import 'dart:js_interop';
|
import 'dart:js_interop';
|
||||||
import 'dart:js_interop_unsafe';
|
|
||||||
import 'package:web/web.dart' as web;
|
import 'package:web/web.dart' as web;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'dart:ui_web' as ui;
|
|
||||||
import 'api_service.dart';
|
import 'api_service.dart';
|
||||||
import 'structs.dart';
|
import 'structs.dart';
|
||||||
import 'package:html2md/html2md.dart' as html2md;
|
import 'package:html2md/html2md.dart' as html2md;
|
||||||
@ -117,13 +114,6 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
|
|||||||
newNode.parent = tree;
|
newNode.parent = tree;
|
||||||
tree.children.add(newNode);
|
tree.children.add(newNode);
|
||||||
}
|
}
|
||||||
// } else{ // so the node should go high
|
|
||||||
// _add2Tree(tree.children.last, node2add);
|
|
||||||
// }
|
|
||||||
|
|
||||||
//maybe?
|
|
||||||
// } else {
|
|
||||||
// }
|
|
||||||
} else if ((hirarchyDict[node2add.tag] ??
|
} else if ((hirarchyDict[node2add.tag] ??
|
||||||
-1) > // go down e.g. new node is h3 and old is h2 or something
|
-1) > // go down e.g. new node is h3 and old is h2 or something
|
||||||
(hirarchyDict[tree.children.last.ogTag] ?? -1)) {
|
(hirarchyDict[tree.children.last.ogTag] ?? -1)) {
|
||||||
@ -135,8 +125,6 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// print("> ${node2add.tag}");
|
|
||||||
|
|
||||||
_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)) {
|
||||||
@ -194,32 +182,6 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
|
|||||||
|
|
||||||
this.sentinel = [h1List, h2List, h3List, h4List, h5List, h6List, pList];
|
this.sentinel = [h1List, h2List, h3List, h4List, h5List, h6List, pList];
|
||||||
sentinel.removeWhere((hList) => hList.isEmpty);
|
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);
|
|
||||||
// }
|
|
||||||
currentZoomNode = zoomTreeRoot;
|
currentZoomNode = zoomTreeRoot;
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -253,50 +215,16 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
|
|||||||
} else {
|
} else {
|
||||||
print("This child has no further children.");
|
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 {
|
void _goToParent() async {
|
||||||
if (currentZoomNode.parent != null) {
|
if (currentZoomNode.parent != null) {
|
||||||
setState(() {
|
setState(() {
|
||||||
currentZoomNode = currentZoomNode.parent!;
|
currentZoomNode = currentZoomNode.parent!;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
print("Already at root.");
|
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 {
|
void _serializableData(String threadID) async {
|
||||||
@ -449,16 +377,19 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (isExpanded)
|
if (isExpanded)
|
||||||
SizedBox(
|
ConstrainedBox(
|
||||||
height: 800,
|
constraints: BoxConstraints(
|
||||||
child:
|
minHeight: 100,
|
||||||
_buildForZooms(key: ValueKey(currentZoomNode))),
|
maxHeight: MediaQuery.of(context).size.height * 0.6,
|
||||||
|
),
|
||||||
|
child: _buildForZooms(key: ValueKey(currentZoomNode)),
|
||||||
|
),
|
||||||
Divider(),
|
Divider(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
])
|
])
|
||||||
: const Center(child: CircularProgressIndicator());
|
: const Center(child: CircularProgressIndicator());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user