works, and optimized time for checking the node type
This commit is contained in:
		
							parent
							
								
									69b5408f73
								
							
						
					
					
						commit
						160fe25be3
					
				
					 1 changed files with 276 additions and 15 deletions
				
			
		| 
						 | 
					@ -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: [
 | 
				
			||||||
 | 
					            Expanded(
 | 
				
			||||||
 | 
					              child: ListView.builder(
 | 
				
			||||||
 | 
					                itemCount: emailsInThread.length,
 | 
				
			||||||
 | 
					                itemBuilder: (context, index) {
 | 
				
			||||||
 | 
					                  final isExpanded = _expandedEmails
 | 
				
			||||||
 | 
					                      .contains(index); //check if email is expanded
 | 
				
			||||||
 | 
					                  return Column(
 | 
				
			||||||
                    children: [
 | 
					                    children: [
 | 
				
			||||||
          HtmlWidget(
 | 
					                      ListTile(
 | 
				
			||||||
            widget.threadHTML[0],
 | 
					                        title: Text(emailsInThread[index].from),
 | 
				
			||||||
            // renderMode: RenderMode.listView,
 | 
					                        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());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue