import 'dart:js_interop'; import 'package:web/web.dart' as web; import 'package:flutter/material.dart'; import 'dart:ui_web' as ui; import 'api_service.dart'; import 'structs.dart'; class CollapsableEmails extends StatefulWidget { final List thread; // email id's in the form xyz@gmail.com final List threadHTML; final String threadIDs; CollapsableEmails( {required this.thread, required this.threadHTML, required this.threadIDs}); @override State createState() => _CollapsableEmailsState(); } 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; static bool _isListenerRegistered = false; @override void initState() { super.initState(); _registerViewFactory(widget.threadHTML); _serializableData(widget.threadIDs); // this _keyListener(); } void _registerViewFactory(List currentContent) async { // setState(() { //update to do item per item // each item to have itsviewtype ID // is this necessarey here?? //could just move to collapsable for (var emailHTML in widget.threadHTML) { String viewTypeId = 'email-${DateTime.now().millisecondsSinceEpoch}'; final ghost = web.document.createElement('div') as web.HTMLDivElement ..style.visibility = 'hidden' ..style.position = 'absolute' ..style.width = '100%' ..style.overflow = 'auto' ..innerHTML = emailHTML .toJS; // temporarily index because it has to do all of them web.document.body?.append(ghost); await Future.delayed(Duration(milliseconds: 10)); final heightOfEmail = ghost.scrollHeight; ghost.remove(); final HTMLsnippet = web.document.createElement('div') as web.HTMLDivElement ..id = viewTypeId ..innerHTML = emailHTML .toJS; // temporarily index because it has to do all of them HTMLsnippet.style ..width = '100%' ..height = '${heightOfEmail}px' ..overflow = 'auto' ..scrollBehavior = 'smooth'; ui.platformViewRegistry.registerViewFactory( viewTypeId, (int viewId) => HTMLsnippet, ); viewtypeIDs.add(viewTypeId); heightOfViewTypes.add(heightOfEmail); } } void _serializableData(String threadID) async { emailsInThread = await ApiService().threadsInSerializable(threadID); print("done thread serializable"); if (!mounted) return; setState(() { _isLoaded = true; }); } void _keyListener() { if (_isListenerRegistered) return; _isListenerRegistered = true; web.window.document.addEventListener( 'keydown', ((web.Event event) { final keyEvent = event as web.KeyboardEvent; if (keyEvent.key.toLowerCase() == 'k') { print('You pressed the "k" key!'); final leftPurpleNums = web.document.getElementsByClassName("right"); for (int i = 0; i < leftPurpleNums.length; i++) { final currentElement = leftPurpleNums.item(i) as web.HTMLElement; final opacity = web.window.getComputedStyle(currentElement).opacity; currentElement.style.opacity = (opacity == '0') ? '1.0' : '0.0'; // works mostly } } }).toJS, ); } @override Widget build(BuildContext context) { 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(), ], ); }, ), ) ]) : const Center(child: CircularProgressIndicator()); } }