Compare commits

...

6 Commits

27 changed files with 1337 additions and 677 deletions

View File

@ -0,0 +1,19 @@
import 'structs.dart';
import 'package:flutter/material.dart';
class SonicEmailView extends StatefulWidget {
SerializableMessage email;
String emailHTML;
SonicEmailView({required this.email, required this.emailHTML});
@override
_SonicEmailViewState createState() => _SonicEmailViewState();
}
class _SonicEmailViewState extends State<SonicEmailView> {
@override
Widget build(BuildContext context) {
return Scaffold(body: Text("sonic email android"));
}
}

View File

@ -0,0 +1,22 @@
import 'structs.dart';
import 'package:flutter/material.dart';
class SonicEmailView extends StatefulWidget {
SerializableMessage email;
String emailHTML;
SonicEmailView({required this.email, required this.emailHTML});
@override
_SonicEmailViewState createState() => _SonicEmailViewState();
}
class _SonicEmailViewState extends State<SonicEmailView> {
@override
Widget build(BuildContext context) {
return Scaffold(
body:Text("sonic email stub")
);
}
}

145
lib/SonicEmailViewWeb.dart Normal file
View File

@ -0,0 +1,145 @@
import 'package:crab_ui/augment.dart';
import 'package:web/web.dart' as web;
import 'dart:ui_web' as ui;
import 'dart:js_interop';
import 'structs.dart';
import 'package:flutter/material.dart';
class SonicEmailView extends StatefulWidget {
SerializableMessage email;
String emailHTML;
SonicEmailView({required this.email, required this.emailHTML});
@override
_SonicEmailViewState createState() => _SonicEmailViewState();
}
class _SonicEmailViewState extends State<SonicEmailView> {
String viewTypeIDs = "";
int heightOFViewtype = 0;
bool _isLoaded = false;
void _scrollToNumber(String spanId) {
AugmentClasses.handleJump(spanId);
}
@override
void initState() {
super.initState();
_init();
}
Future<void> _init() async {
await _registerViewFactory(widget.emailHTML);
if (!mounted) return;
setState(() {
_isLoaded = true;
});
}
Future<void> _registerViewFactory(String 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 = currentContent.toJS;
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 = widget
.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,
);
this.viewTypeIDs = viewTypeId;
this.heightOFViewtype = heightOfEmail;
print(viewTypeIDs);
}
@override
Widget build(BuildContext context) {
return _isLoaded
? Scaffold(
appBar: AppBar(title: Text(widget.email.subject)),
body: Stack(
children: [
Column(
children: [
EmailToolbar(
onButtonPressed: () => {},
onJumpToSpan: _scrollToNumber),
Row(
// title of email
children: [
Text(
widget.email.subject,
style: TextStyle(fontSize: 30),
),
],
),
Row(
children: [
Text(
'from ${widget.email.name}',
style: TextStyle(fontSize: 18),
),
Text(
'<${widget.email.from}>',
style: TextStyle(fontSize: 18),
),
Spacer(),
Text(
'${widget.email.date}',
textAlign: TextAlign.right,
)
],
),
// TODO: make a case where if one of these is the user's email it just says me :)))))
Row(
children: [
Text(
'to ${widget.email.to.toString()}',
style: TextStyle(fontSize: 15),
)
],
),
Expanded(
// child: SizedBox(
// height: heightOFViewtype.toDouble(),
child: HtmlElementView(
key: UniqueKey(), viewType: this.viewTypeIDs,
// ),
))
],
),
],
),
)
: const Center(
child: CircularProgressIndicator(),
);
}
}

View File

@ -4,19 +4,12 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'collapsableEmails.dart';
import 'structs.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:ui_web' as ui;
import 'augment.dart';
// import 'dart:html' as html;
// import 'dart:js' as js;
import 'package:web/web.dart' as web;
import 'dart:js_interop' as js;
class ApiService {
static String ip = "";
@ -422,237 +415,4 @@ class ApiService {
// return [];
// }
}
class EmailView extends StatefulWidget {
final List<String> emailContent;
final String from;
final String name;
final String to;
final String subject;
final String date;
final String id;
final List<String> messages;
const EmailView({
Key? key,
required this.emailContent,
required this.from,
required this.name,
required this.to,
required this.subject,
required this.date,
required this.id,
required this.messages,
}) : super(key: key);
@override
_EmailViewState createState() => _EmailViewState();
}
class _EmailViewState extends State<EmailView> {
//html css rendering thing
late Key iframeKey;
late String currentContent;
late String viewTypeId; //make this a list too???
Future<List<Map<String, dynamic>>>? _markerPositionsFuture;
// TextEditingController _jumpController = TextEditingController();
final hardcodedMarkers = [
{'id': 'marker1', 'x': 50, 'y': 100},
{'id': 'marker2', 'x': 150, 'y': 200},
{'id': 'marker3', 'x': 250, 'y': 300},
];
@override
void initState() {
super.initState();
print("thread id? ${widget.id}");
List<String> currentContent = widget
.emailContent; //html of the email/ actually entire thread, gives me little space to play in between
// i wonder if the other attributes change? because if so i have to add like some zooms in and out of the emails, as in collapse
// _registerViewFactory(currentContent);
}
// void _registerViewFactory(List<String> currentContent) { // i think this doesnt work anymore
// setState(() { //update to do item per item
// // each item to have itsviewtype ID
// // is this necessarey here??
// //could just move to collapsable
// viewTypeId = 'iframe-${DateTime.now().millisecondsSinceEpoch}';
// final emailHTML = web.document.createElement('div') as web.HTMLDivElement
// ..id = viewTypeId
// ..innerHTML = currentContent[0].toJS; // temporarily index because it has to do all of them
// emailHTML.style
// ..width = '100%'
// ..height = '100%'
// ..overflow = 'auto'
// ..scrollBehavior = 'smooth';
// ui.platformViewRegistry.registerViewFactory(
// viewTypeId,
// (int viewId) => emailHTML,
// );
// });
// }
void _scrollToNumber(String spanId) {
AugmentClasses.handleJump(spanId);
}
// TODO: void _invisibility(String ) //to make purple numbers not visible
@override
Widget build(BuildContext context) {
// print("thread id ${widget.id}");
ApiService.currThreadID = widget.id;
return Scaffold(
appBar: AppBar(
title: Text(widget.name),
),
body: Stack(
children: [
Column(
children: [
EmailToolbar(
onJumpToSpan: _scrollToNumber,
onButtonPressed: () => {},
// AugmentClasses.handleJump(viewTypeId, '1');
// print("button got pressed?");
// _registerViewFactory(r"""
// <h1>Welcome to My Website</h1>
// <p>This is a simple HTML page.</p>
// <h2>What is HTML?</h2>
// <p>HTML (HyperText Markup Language) is the most basic building~ block of the Web. It defines the meaning and structure of web content. Other technologies besides HTML are generally used to describe a web page's appearance/presentation (CSS) or functionality/behavior (JavaScript).</p>
// <h3>Here's a simple list:</h3>
// <ul>
// <li>HTML elements are the building blocks of HTML pages</li>
// <li>HTML uses tags like <code>&lt;tag&gt;</code> to organize and format content</li>
// <li>CSS is used with HTML to style pages</li>
// </ul>
// <p>Copyright © 2023</p>
// """);
// print("change");
// widget.emailContent = r"
//
),
Row(
// title of email
children: [
Text(
widget.subject,
style: TextStyle(fontSize: 30),
),
],
),
Row(
children: [
Text(
'from ${widget.name}',
style: TextStyle(fontSize: 18),
),
Text(
'<${widget.from}>',
style: TextStyle(fontSize: 18),
),
Spacer(),
Text(
'${widget.date}',
textAlign: TextAlign.right,
)
],
),
// TODO: make a case where if one of these is the user's email it just says me :)))))
Row(
children: [
Text(
'to ${widget.to.toString()}',
style: TextStyle(fontSize: 15),
)
],
),
Expanded(
child: CollapsableEmails(
//change here
thread: widget.messages, //this wont work in serializable
threadHTML: widget.emailContent,
threadIDs: widget.id,
),
),
// Expanded(
// child: HtmlElementView(
// key: UniqueKey(),
// viewType: viewTypeId,
// ),
// ),
],
),
// Overlay widgets dynamically based on marker positions
// FutureBuilder<List<Map<String, dynamic>>>(
// future: _markerPositionsFuture,
// builder: (context, snapshot) {
// print("FutureBuilder state: ${snapshot.connectionState}");
// if (snapshot.connectionState == ConnectionState.waiting) {
// return Center(child: CircularProgressIndicator());
// }
// if (snapshot.hasError) {
// print("Error in FutureBuilder: ${snapshot.error}");
// return Center(child: Text('error loading markers'));
// }
// if (snapshot.hasData && snapshot.data != null) {
// final markers = snapshot.data!;
// return Stack(
// children: markers.map((marker) {
// return Positioned(
// left: marker['x'].toDouble(),
// top: marker['y'].toDouble(),
// child: GestureDetector(
// onTap: () {
// print('Tapped on ${marker['id']}');
// },
// child: Container(
// width: 50,
// height: 50,
// color: Colors.red,
// child: Center(
// child: Text(
// marker['id'],
// style: TextStyle(color: Colors.white),
// ),
// ),
// ),
// ),
// );
// }).toList(),
// );
// }
// return SizedBox.shrink(); // No markers found
// },
// ),
// Red widget overlay
// Positioned(
// left: 8, // Adjust based on your desired position
// top: 100 + 44 + 5, // Adjust based on your desired position
// child: IgnorePointer(
// ignoring: true, // Ensures the iframe remains interactive
// child: Container(
// color: Colors.red,
// width: 100,
// height: 50,
// child: Center(
// child: Text(
// 'Overlay',
// style: TextStyle(color: Colors.white),
// ),
// ),
// ),
// ),
// ),
],
));
}
}
}

View File

@ -0,0 +1,7 @@
import 'structs.dart';
class Attachmentdownload {
Future<void> saveFile(AttachmentResponse attachment) async {
print("stub attachment download");
}
}

View File

@ -1,15 +1,3 @@
import 'dart:html' as html;
import 'package:web/web.dart' as web;
import 'dart:io';
import 'structs.dart';
import 'package:file_saver/file_saver.dart';
class Attachmentdownload {
Future<void> saveFile(AttachmentResponse attachment) async {
await FileSaver.instance.saveFile(
name: attachment.name.toString().substring(0, attachment.name.toString().lastIndexOf('.')),
bytes: attachment.data,
ext: attachment.name.toString().substring(attachment.name.toString().lastIndexOf('.')+1)
);
}
}
export 'attachamentDownloadStub.dart'
if (dart.library.io) 'attachmentDownloadAndroid.dart';
// if (dart.library.js_interop) 'attachmentDownloadWeb.dart';

View File

@ -0,0 +1,7 @@
import 'structs.dart';
class Attachmentdownload {
Future<void> saveFile(AttachmentResponse attachment) async {
print("android attachment download");
}
}

View File

@ -0,0 +1,12 @@
// import 'structs.dart';
// import 'package:file_saver/file_saver.dart';
// class Attachmentdownload {
// Future<void> saveFile(AttachmentResponse attachment) async {
// await FileSaver.instance.saveFile(
// name: attachment.name.toString().substring(0, attachment.name.toString().lastIndexOf('.')),
// bytes: attachment.data,
// ext: attachment.name.toString().substring(attachment.name.toString().lastIndexOf('.')+1)
// );
// }
// }

View File

@ -1,103 +1,3 @@
import "dart:typed_data";
import "package:crab_ui/attachmentDownload.dart";
import "package:crab_ui/structs.dart";
import "package:flutter/material.dart";
import 'package:pdfrx/pdfrx.dart';
import 'package:photo_view/photo_view.dart';
class AttachmentWidget extends StatelessWidget {
final AttachmentResponse attachment;
AttachmentWidget({required this.attachment});
Widget attachmentViewer(AttachmentResponse att) {
String extension = att.name
.toString()
.substring(att.name.toString().indexOf(".") + 1)
.toLowerCase();
if (extension == "jpg" || extension == "png") {
return Image.memory(att.data);
} else if (extension == "pdf") {
return PdfViewer.data(Uint8List.fromList(att.data),
sourceName: att.name,
params: PdfViewerParams(
enableTextSelection: true,
scrollByMouseWheel: 0.5,
annotationRenderingMode:
PdfAnnotationRenderingMode.annotationAndForms,
));
}
return Center(
child: Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: Color(0xff6C63FF),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black26,
blurRadius: 10,
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"No preview available",
style: TextStyle(
color: Colors.white, fontSize: 18, decoration: TextDecoration.none),
),
SizedBox(
height: 5,
),
GestureDetector(
child: ElevatedButton(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text("Download", style: TextStyle(color: Color(0xff2c3e50)),),
Icon(Icons.download,
color: Color(0xffb0b0b0),),
]),
onPressed: () => Attachmentdownload().saveFile(att),
)),
]),
));
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.black38,
child: Stack(children: <Widget>[
Container(
color: Colors.white,
child: Padding(
padding: EdgeInsets.fromLTRB(10, 20, 0, 10),
child: Column(
children: [
Row(
children: [
CloseButton(onPressed: () => {Navigator.pop(context)}),
Text(
attachment.name
.toString(), //its alr a string but incase ¯\()/¯ //update: i did that everywhere lol
style: TextStyle(
color: Colors.black,
fontSize: 20,
decoration: TextDecoration
.none), //TODO: personalize your fonts
),
],
),
Expanded(
child: attachmentViewer(attachment),
)
],
),
),
),
]));
}
}
export 'attachmentWidgetStub.dart'
if (dart.library.js_interop) 'attachmentWidgetWeb.dart'
if (dart.library.io) 'attachmentWidgetAndroid.dart';

View File

@ -0,0 +1,16 @@
import "package:crab_ui/structs.dart";
import "package:flutter/material.dart";
class AttachmentWidget extends StatelessWidget {
final AttachmentResponse attachment;
AttachmentWidget({required this.attachment});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Text("PDF EVENTUALLY ANDROID")
);
}
}

View File

@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
import 'structs.dart';
class AttachmentWidget extends StatelessWidget{
final AttachmentResponse attachment;
AttachmentWidget({required this.attachment});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Text("PDF EVENTUALLY, STUB")
);
}
}

View File

@ -0,0 +1,101 @@
import "dart:typed_data";
import "package:crab_ui/attachmentDownload.dart";
import "package:crab_ui/structs.dart";
import "package:flutter/material.dart";
import 'package:pdfrx/pdfrx.dart';
class AttachmentWidget extends StatelessWidget {
final AttachmentResponse attachment;
AttachmentWidget({required this.attachment});
Widget attachmentViewer(AttachmentResponse att) {
String extension = att.name
.toString()
.substring(att.name.toString().indexOf(".") + 1)
.toLowerCase();
if (extension == "jpg" || extension == "png") {
return Image.memory(att.data);
} else if (extension == "pdf") {
return PdfViewer.data(Uint8List.fromList(att.data),
sourceName: att.name,
params: PdfViewerParams(
enableTextSelection: true,
scrollByMouseWheel: 0.5,
annotationRenderingMode:
PdfAnnotationRenderingMode.annotationAndForms,
));
}
return Center(
child: Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: Color(0xff6C63FF),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black26,
blurRadius: 10,
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"No preview available",
style: TextStyle(
color: Colors.white, fontSize: 18, decoration: TextDecoration.none),
),
SizedBox(
height: 5,
),
GestureDetector(
child: ElevatedButton(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text("Download", style: TextStyle(color: Color(0xff2c3e50)),),
Icon(Icons.download,
color: Color(0xffb0b0b0),),
]),
onPressed: () => Attachmentdownload().saveFile(att),
)),
]),
));
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.black38,
child: Stack(children: <Widget>[
Container(
color: Colors.white,
child: Padding(
padding: EdgeInsets.fromLTRB(10, 20, 0, 10),
child: Column(
children: [
Row(
children: [
CloseButton(onPressed: () => {Navigator.pop(context)}),
Text(
attachment.name
.toString(), //its alr a string but incase ¯\()/¯ //update: i did that everywhere lol
style: TextStyle(
color: Colors.black,
fontSize: 20,
decoration: TextDecoration
.none), //TODO: personalize your fonts
),
],
),
Expanded(
child: attachmentViewer(attachment),
)
],
),
),
),
]));
}
}

View File

@ -1,14 +1,7 @@
// import 'dart:ffi';
import 'package:crab_ui/api_service.dart';
import 'package:crab_ui/attachmentDownload.dart';
import 'package:crab_ui/structs.dart';
import 'package:flutter/material.dart';
import 'package:pdfrx/pdfrx.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
// import 'dart:html' as html;
// import 'dart:js' as js;
import 'package:web/web.dart' as web;
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'attachmentWidget.dart';

View File

@ -1,132 +1,3 @@
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<String> thread; // email id's in the form xyz@gmail.com
final List<String> threadHTML;
final String threadIDs;
CollapsableEmails(
{required this.thread,
required this.threadHTML,
required this.threadIDs});
@override
State<CollapsableEmails> createState() => _CollapsableEmailsState();
}
class _CollapsableEmailsState extends State<CollapsableEmails> {
List<String> emailsHTML = []; //html of the emails in the thread
// build attachments with the forldar name and id
Set<int> _expandedEmails = {}; //open emails
List viewtypeIDs = []; //IDs of the viewtypes, order matters
List heightOfViewTypes = []; //the height of each viewtype
List<SerializableMessage> emailsInThread = [];
bool _isLoaded = false;
@override
void initState() {
// TODO: implement initState
super.initState();
_registerViewFactory(widget.threadHTML);
_serializableData(widget.threadIDs);
}
void _registerViewFactory(List<String> 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;
});
}
@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)
// if(viewtypeIDs[index] == null || heightOfViewTypes[index] == null)
// const SizedBox(height: 100, child: Center(child: CircularProgressIndicator())),
SizedBox(
height: heightOfViewTypes[index].toDouble(),
child: HtmlElementView(
key: UniqueKey(), viewType: viewtypeIDs[index]),
),
Divider(),
],
);
},
),
)
]): const Center(child:CircularProgressIndicator());
}
}
export 'collapsableEmailsStub.dart'
if (dart.library.io) 'collapsableEmailsAndroid.dart'
if (dart.library.js_interop) 'collapsableEmailsWeb.dart';

View File

@ -0,0 +1,48 @@
import 'structs.dart';
import 'package:flutter/material.dart';
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
class CollapsableEmails extends StatefulWidget {
final List<String> thread; // email id's in the form xyz@gmail.com
final List<String> threadHTML;
final String threadIDs;
CollapsableEmails(
{required this.thread,
required this.threadHTML,
required this.threadIDs});
@override
State<CollapsableEmails> createState() => _CollapsableEmailsState();
}
class _CollapsableEmailsState extends State<CollapsableEmails> {
List<String> emailsHTML = []; //html of the emails in the thread
// build attachments with the forldar name and id
Set<int> _expandedEmails = {}; //open emails
List viewtypeIDs = []; //IDs of the viewtypes, order matters
List heightOfViewTypes = []; //the height of each viewtype
List<SerializableMessage> emailsInThread = [];
bool _isLoaded = false;
@override
void initState() {
super.initState();
//html
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: [
HtmlWidget(
widget.threadHTML[0],
// renderMode: RenderMode.listView,
)
]
)
);
}
}

View File

@ -0,0 +1,23 @@
import 'structs.dart';
import 'package:flutter/material.dart';
class CollapsableEmails extends StatefulWidget {
final List<String> thread; // email id's in the form xyz@gmail.com
final List<String> threadHTML;
final String threadIDs;
CollapsableEmails(
{required this.thread,
required this.threadHTML,
required this.threadIDs});
@override
State<CollapsableEmails> createState() => _CollapsableEmailsState();
}
class _CollapsableEmailsState extends State<CollapsableEmails> {
@override
Widget build(BuildContext context) {
return Scaffold(body: Text("collapsable stud"));
}
}

View File

@ -0,0 +1,132 @@
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<String> thread; // email id's in the form xyz@gmail.com
final List<String> threadHTML;
final String threadIDs;
CollapsableEmails(
{required this.thread,
required this.threadHTML,
required this.threadIDs});
@override
State<CollapsableEmails> createState() => _CollapsableEmailsState();
}
class _CollapsableEmailsState extends State<CollapsableEmails> {
List<String> emailsHTML = []; //html of the emails in the thread
// build attachments with the forldar name and id
Set<int> _expandedEmails = {}; //open emails
List viewtypeIDs = []; //IDs of the viewtypes, order matters
List heightOfViewTypes = []; //the height of each viewtype
List<SerializableMessage> emailsInThread = [];
bool _isLoaded = false;
@override
void initState() {
// TODO: implement initState
super.initState();
_registerViewFactory(widget.threadHTML);
_serializableData(widget.threadIDs);
}
void _registerViewFactory(List<String> 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;
});
}
@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)
// if(viewtypeIDs[index] == null || heightOfViewTypes[index] == null)
// const SizedBox(height: 100, child: Center(child: CircularProgressIndicator())),
SizedBox(
height: heightOfViewTypes[index].toDouble(),
child: HtmlElementView(
key: UniqueKey(), viewType: viewtypeIDs[index]),
),
Divider(),
],
);
},
),
)
]): const Center(child:CircularProgressIndicator());
}
}

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_html/flutter_html.dart';
// import 'package:http/http.dart' as http;
// import 'package:flutter_html/flutter_html.dart';
class ContactsPage extends StatefulWidget {
const ContactsPage({super.key});

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'api_service.dart';
import 'structs.dart';
import 'emailView.dart';
class EmailListScreen extends StatelessWidget {
final List<GetThreadResponse> emails;

3
lib/emailView.dart Normal file
View File

@ -0,0 +1,3 @@
export 'emailViewStub.dart'
if (dart.library.io) 'emailViewAndroid.dart'
if (dart.library.js_interop) 'emailViewWeb.dart';

95
lib/emailViewAndroid.dart Normal file
View File

@ -0,0 +1,95 @@
import 'package:crab_ui/collapsableEmailsAndroid.dart';
import 'package:flutter/material.dart';
// import 'dart:ui_web' as ui;
// import 'augment.dart';
// // import 'dart:js_interop' as js; //eventually for manipulating css
// import 'package:pointer_interceptor/pointer_interceptor.dart';
// import 'collapsableEmails.dart';
// import 'api_service.dart';
class EmailView extends StatefulWidget {
final List<String> emailContent;
final String from;
final String name;
final String to;
final String subject;
final String date;
final String id;
final List<String> messages;
const EmailView({
Key? key,
required this.emailContent,
required this.from,
required this.name, // tf is name
required this.to,
required this.subject,
required this.date,
required this.id,
required this.messages,
}) : super(key: key);
@override
_EmailViewState createState() => _EmailViewState();
}
class _EmailViewState extends State<EmailView> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.name),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Row(
children: [
Expanded(
child: Text(
widget.subject,
style: TextStyle(fontSize: 15),
overflow: TextOverflow.visible,
softWrap: true,
),
),
],
),
Row(
children: [
Text(
'from ${widget.name}',
style: TextStyle(fontSize: 8),
),
Text(
'<${widget.from}>',
style: TextStyle(fontSize: 8),
),
Spacer(),
Text(
widget.date,
textAlign: TextAlign.right,
)
],
),
Row(
children: [
Text(
'to ${widget.to.toString()}',
style: TextStyle(fontSize: 8),
)
],
),
Expanded(
child: CollapsableEmails(
thread: widget.messages,
threadHTML: widget.emailContent,
threadIDs: widget.id,
),
),
],
),
)
);
}
}

39
lib/emailViewStub.dart Normal file
View File

@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
class EmailView extends StatefulWidget {
final List<String> emailContent;
final String from;
final String name;
final String to;
final String subject;
final String date;
final String id;
final List<String> messages;
const EmailView({
Key? key,
required this.emailContent,
required this.from,
required this.name,
required this.to,
required this.subject,
required this.date,
required this.id,
required this.messages,
}) : super(key: key);
@override
_EmailViewState createState() => _EmailViewState();
}
class _EmailViewState extends State<EmailView> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text(" emailview stub, not supported")
)
);
}
}

241
lib/emailViewWeb.dart Normal file
View File

@ -0,0 +1,241 @@
import 'package:flutter/material.dart';
import 'dart:ui_web' as ui;
import 'augment.dart';
// import 'dart:js_interop' as js; //eventually for manipulating css
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'collapsableEmails.dart';
import 'api_service.dart';
class EmailView extends StatefulWidget {
final List<String> emailContent;
final String from;
final String name;
final String to;
final String subject;
final String date;
final String id;
final List<String> messages;
const EmailView({
Key? key,
required this.emailContent,
required this.from,
required this.name,
required this.to,
required this.subject,
required this.date,
required this.id,
required this.messages,
}) : super(key: key);
@override
_EmailViewState createState() => _EmailViewState();
}
class _EmailViewState extends State<EmailView> {
//html css rendering thing
late Key iframeKey;
late String currentContent;
late String viewTypeId; //make this a list too???
Future<List<Map<String, dynamic>>>? _markerPositionsFuture;
// TextEditingController _jumpController = TextEditingController();
final hardcodedMarkers = [
{'id': 'marker1', 'x': 50, 'y': 100},
{'id': 'marker2', 'x': 150, 'y': 200},
{'id': 'marker3', 'x': 250, 'y': 300},
];
@override
void initState() {
super.initState();
print("thread id? ${widget.id}");
List<String> currentContent = widget
.emailContent; //html of the email/ actually entire thread, gives me little space to play in between
// i wonder if the other attributes change? because if so i have to add like some zooms in and out of the emails, as in collapse
// _registerViewFactory(currentContent);
}
// void _registerViewFactory(List<String> currentContent) { // i think this doesnt work anymore
// setState(() { //update to do item per item
// // each item to have itsviewtype ID
// // is this necessarey here??
// //could just move to collapsable
// viewTypeId = 'iframe-${DateTime.now().millisecondsSinceEpoch}';
// final emailHTML = web.document.createElement('div') as web.HTMLDivElement
// ..id = viewTypeId
// ..innerHTML = currentContent[0].toJS; // temporarily index because it has to do all of them
// emailHTML.style
// ..width = '100%'
// ..height = '100%'
// ..overflow = 'auto'
// ..scrollBehavior = 'smooth';
// ui.platformViewRegistry.registerViewFactory(
// viewTypeId,
// (int viewId) => emailHTML,
// );
// });
// }
void _scrollToNumber(String spanId) {
AugmentClasses.handleJump(spanId);
}
// TODO: void _invisibility(String ) //to make purple numbers not visible
@override
Widget build(BuildContext context) {
// print("thread id ${widget.id}");
ApiService.currThreadID = widget.id;
return Scaffold(
appBar: AppBar(
title: Text(widget.name),
),
body: Stack(
children: [
Column(
children: [
EmailToolbar(
onJumpToSpan: _scrollToNumber,
onButtonPressed: () => {},
// AugmentClasses.handleJump(viewTypeId, '1');
// print("button got pressed?");
// _registerViewFactory(r"""
// <h1>Welcome to My Website</h1>
// <p>This is a simple HTML page.</p>
// <h2>What is HTML?</h2>
// <p>HTML (HyperText Markup Language) is the most basic building~ block of the Web. It defines the meaning and structure of web content. Other technologies besides HTML are generally used to describe a web page's appearance/presentation (CSS) or functionality/behavior (JavaScript).</p>
// <h3>Here's a simple list:</h3>
// <ul>
// <li>HTML elements are the building blocks of HTML pages</li>
// <li>HTML uses tags like <code>&lt;tag&gt;</code> to organize and format content</li>
// <li>CSS is used with HTML to style pages</li>
// </ul>
// <p>Copyright © 2023</p>
// """);
// print("change");
// widget.emailContent = r"
//
),
Row(
// title of email
children: [
Text(
widget.subject,
style: TextStyle(fontSize: 30),
),
],
),
Row(
children: [
Text(
'from ${widget.name}',
style: TextStyle(fontSize: 18),
),
Text(
'<${widget.from}>',
style: TextStyle(fontSize: 18),
),
Spacer(),
Text(
'${widget.date}',
textAlign: TextAlign.right,
)
],
),
// TODO: make a case where if one of these is the user's email it just says me :)))))
Row(
children: [
Text(
'to ${widget.to.toString()}',
style: TextStyle(fontSize: 15),
)
],
),
Expanded(
child: CollapsableEmails(
//change here
thread: widget.messages, //this wont work in serializable
threadHTML: widget.emailContent,
threadIDs: widget.id,
),
),
// Expanded(
// child: HtmlElementView(
// key: UniqueKey(),
// viewType: viewTypeId,
// ),
// ),
],
),
// Overlay widgets dynamically based on marker positions
// FutureBuilder<List<Map<String, dynamic>>>(
// future: _markerPositionsFuture,
// builder: (context, snapshot) {
// print("FutureBuilder state: ${snapshot.connectionState}");
// if (snapshot.connectionState == ConnectionState.waiting) {
// return Center(child: CircularProgressIndicator());
// }
// if (snapshot.hasError) {
// print("Error in FutureBuilder: ${snapshot.error}");
// return Center(child: Text('error loading markers'));
// }
// if (snapshot.hasData && snapshot.data != null) {
// final markers = snapshot.data!;
// return Stack(
// children: markers.map((marker) {
// return Positioned(
// left: marker['x'].toDouble(),
// top: marker['y'].toDouble(),
// child: GestureDetector(
// onTap: () {
// print('Tapped on ${marker['id']}');
// },
// child: Container(
// width: 50,
// height: 50,
// color: Colors.red,
// child: Center(
// child: Text(
// marker['id'],
// style: TextStyle(color: Colors.white),
// ),
// ),
// ),
// ),
// );
// }).toList(),
// );
// }
// return SizedBox.shrink(); // No markers found
// },
// ),
// Red widget overlay
// Positioned(
// left: 8, // Adjust based on your desired position
// top: 100 + 44 + 5, // Adjust based on your desired position
// child: IgnorePointer(
// ignoring: true, // Ensures the iframe remains interactive
// child: Container(
// color: Colors.red,
// width: 100,
// height: 50,
// child: Center(
// child: Text(
// 'Overlay',
// style: TextStyle(color: Colors.white),
// ),
// ),
// ),
// ),
// ),
],
));
}
}

View File

@ -2,7 +2,7 @@ import 'package:crab_ui/sonicEmailView.dart';
import 'folder_drawer.dart';
import 'structs.dart';
import 'package:flutter/widgets.dart';
// import 'package:flutter/widgets.dart';
import 'api_service.dart';
import 'package:flutter/material.dart';
import 'email.dart';
@ -266,40 +266,45 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 800,
height: 40,
child: TextField(
decoration: InputDecoration(
hintText: 'Search...',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.search),
Flexible(
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: 800,
),
child: SizedBox(
height: 40,
child: TextField(
decoration: InputDecoration(
hintText: 'Search...',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.search),
),
onSubmitted: (value) {
if (value.isNotEmpty) {
_performSearch(value, _selectedOption);
}
//this is the input box i mentioned
// if (value == '') {
// setState(() {
// querySearches = false;
// });
// }
// Future<List<String>> results = apiService
// .sonicSearch('INBOX', 20, 0, value);
// // print(value);
// print(results);
// setState(() {
// querySearches = true;
// });
},
),
),
onSubmitted: (value) {
if (value.isNotEmpty) {
_performSearch(value, _selectedOption);
}
//this is the input box i mentioned
// if (value == '') {
// setState(() {
// querySearches = false;
// });
// }
// Future<List<String>> results = apiService
// .sonicSearch('INBOX', 20, 0, value);
// // print(value);
// print(results);
// setState(() {
// querySearches = true;
// });
},
),
),
SizedBox(
width: 16,
width: 8,
),
Container(
width: 80,
height: 40,
child: ElevatedButton(
onPressed: _showOptionsSearchDialog,

View File

@ -1,145 +1,3 @@
import 'package:crab_ui/augment.dart';
import 'package:web/web.dart' as web;
import 'dart:ui_web' as ui;
import 'dart:js_interop';
import 'structs.dart';
import 'package:flutter/material.dart';
class SonicEmailView extends StatefulWidget {
SerializableMessage email;
String emailHTML;
SonicEmailView({required this.email, required this.emailHTML});
@override
_SonicEmailViewState createState() => _SonicEmailViewState();
}
class _SonicEmailViewState extends State<SonicEmailView> {
String viewTypeIDs = "";
int heightOFViewtype = 0;
bool _isLoaded = false;
void _scrollToNumber(String spanId) {
AugmentClasses.handleJump(spanId);
}
@override
void initState() {
super.initState();
_init();
}
Future<void> _init() async {
await _registerViewFactory(widget.emailHTML);
if (!mounted) return;
setState(() {
_isLoaded = true;
});
}
Future<void> _registerViewFactory(String 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 = currentContent.toJS;
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 = widget
.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,
);
this.viewTypeIDs = viewTypeId;
this.heightOFViewtype = heightOfEmail;
print(viewTypeIDs);
}
@override
Widget build(BuildContext context) {
return _isLoaded
? Scaffold(
appBar: AppBar(title: Text(widget.email.subject)),
body: Stack(
children: [
Column(
children: [
EmailToolbar(
onButtonPressed: () => {},
onJumpToSpan: _scrollToNumber),
Row(
// title of email
children: [
Text(
widget.email.subject,
style: TextStyle(fontSize: 30),
),
],
),
Row(
children: [
Text(
'from ${widget.email.name}',
style: TextStyle(fontSize: 18),
),
Text(
'<${widget.email.from}>',
style: TextStyle(fontSize: 18),
),
Spacer(),
Text(
'${widget.email.date}',
textAlign: TextAlign.right,
)
],
),
// TODO: make a case where if one of these is the user's email it just says me :)))))
Row(
children: [
Text(
'to ${widget.email.to.toString()}',
style: TextStyle(fontSize: 15),
)
],
),
Expanded(
// child: SizedBox(
// height: heightOFViewtype.toDouble(),
child: HtmlElementView(
key: UniqueKey(), viewType: this.viewTypeIDs,
// ),
))
],
),
],
),
)
: const Center(
child: CircularProgressIndicator(),
);
}
}
export 'SonicEmailViewStub.dart'
if (dart.library.js_interop) 'SonicEmailViewWeb.dart'
if (dart.library.io) 'SonicEmailViewAndroid.dart';

View File

@ -25,6 +25,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.13.0"
audio_session:
dependency: transitive
description:
name: audio_session
sha256: "2b7fff16a552486d078bfc09a8cde19f426dc6d6329262b684182597bec5b1ac"
url: "https://pub.dev"
source: hosted
version: "0.1.25"
boolean_selector:
dependency: transitive
description:
@ -33,6 +41,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.2"
cached_network_image:
dependency: transitive
description:
name: cached_network_image
sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916"
url: "https://pub.dev"
source: hosted
version: "3.4.1"
cached_network_image_platform_interface:
dependency: transitive
description:
name: cached_network_image_platform_interface
sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829"
url: "https://pub.dev"
source: hosted
version: "4.1.1"
cached_network_image_web:
dependency: transitive
description:
name: cached_network_image_web
sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
characters:
dependency: transitive
description:
@ -41,6 +73,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
chewie:
dependency: transitive
description:
name: chewie
sha256: "4d9554a8f87cc2dc6575dfd5ad20a4375015a29edd567fd6733febe6365e2566"
url: "https://pub.dev"
source: hosted
version: "1.11.3"
clock:
dependency: transitive
description:
@ -73,6 +113,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.6"
csslib:
dependency: transitive
description:
name: csslib
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
cupertino_icons:
dependency: transitive
description:
name: cupertino_icons
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
url: "https://pub.dev"
source: hosted
version: "1.0.8"
dbus:
dependency: transitive
description:
name: dbus
sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c"
url: "https://pub.dev"
source: hosted
version: "0.7.11"
encrypt:
dependency: "direct main"
description:
@ -113,11 +177,27 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.1"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
url: "https://pub.dev"
source: hosted
version: "1.1.1"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_cache_manager:
dependency: transitive
description:
name: flutter_cache_manager
sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386"
url: "https://pub.dev"
source: hosted
version: "3.4.1"
flutter_layout_grid:
dependency: "direct overridden"
description:
@ -160,6 +240,78 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_widget_from_html:
dependency: "direct main"
description:
name: flutter_widget_from_html
sha256: "0dfebf7417df2149de93926520c703db9be0c9017e60dc5cf43cebed37f4d11e"
url: "https://pub.dev"
source: hosted
version: "0.16.0"
flutter_widget_from_html_core:
dependency: transitive
description:
name: flutter_widget_from_html_core
sha256: f77ea1aa1ba29a38fcce04483f44f12382f541b9e8c2150df37166c23bbbd30f
url: "https://pub.dev"
source: hosted
version: "0.16.0"
fwfh_cached_network_image:
dependency: transitive
description:
name: fwfh_cached_network_image
sha256: "8f4896109ff3e42424ccacf9058ba3afe5d575b58946c8ac646ac85ae882ce23"
url: "https://pub.dev"
source: hosted
version: "0.16.0"
fwfh_chewie:
dependency: transitive
description:
name: fwfh_chewie
sha256: "1ce7c56894db19881a997813b933835dec142878431370c0eb40f1f878396a25"
url: "https://pub.dev"
source: hosted
version: "0.16.0"
fwfh_just_audio:
dependency: transitive
description:
name: fwfh_just_audio
sha256: "17816168de1fd180fd3d1fd4500e23136630a248a6889b553e2d2067e133c1a6"
url: "https://pub.dev"
source: hosted
version: "0.16.0"
fwfh_svg:
dependency: transitive
description:
name: fwfh_svg
sha256: "82f3eb378186fe39b3e2e01ed48a1830d34b0b9a237d951077e74ff0d99e2ac3"
url: "https://pub.dev"
source: hosted
version: "0.16.0"
fwfh_url_launcher:
dependency: transitive
description:
name: fwfh_url_launcher
sha256: "5cf1b1baa16740abaef8eb41a8e16ba430295d5ec20b880e4cb94e2924774f0a"
url: "https://pub.dev"
source: hosted
version: "0.16.0"
fwfh_webview:
dependency: transitive
description:
name: fwfh_webview
sha256: "894aa7d98ebdc2d86d79ac2309173043dec7f102575de87bf9626ddb26104e49"
url: "https://pub.dev"
source: hosted
version: "0.15.4"
html:
dependency: transitive
description:
name: html
sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602"
url: "https://pub.dev"
source: hosted
version: "0.15.6"
http:
dependency: "direct main"
description:
@ -192,6 +344,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.2"
just_audio:
dependency: transitive
description:
name: just_audio
sha256: f978d5b4ccea08f267dae0232ec5405c1b05d3f3cd63f82097ea46c015d5c09e
url: "https://pub.dev"
source: hosted
version: "0.9.46"
just_audio_platform_interface:
dependency: transitive
description:
name: just_audio_platform_interface
sha256: "4cd94536af0219fa306205a58e78d67e02b0555283c1c094ee41e402a14a5c4a"
url: "https://pub.dev"
source: hosted
version: "4.5.0"
just_audio_web:
dependency: transitive
description:
name: just_audio_web
sha256: "6ba8a2a7e87d57d32f0f7b42856ade3d6a9fbe0f1a11fabae0a4f00bb73f0663"
url: "https://pub.dev"
source: hosted
version: "0.4.16"
leak_tracker:
dependency: transitive
description:
@ -224,6 +400,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
logging:
dependency: transitive
description:
name: logging
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
url: "https://pub.dev"
source: hosted
version: "1.3.0"
matcher:
dependency: transitive
description:
@ -264,6 +448,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.0"
octo_image:
dependency: transitive
description:
name: octo_image
sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
package_info_plus:
dependency: transitive
description:
name: package_info_plus
sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191"
url: "https://pub.dev"
source: hosted
version: "8.3.0"
package_info_plus_platform_interface:
dependency: transitive
description:
name: package_info_plus_platform_interface
sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c"
url: "https://pub.dev"
source: hosted
version: "3.2.0"
path:
dependency: transitive
description:
@ -501,6 +709,54 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.1"
sprintf:
dependency: transitive
description:
name: sprintf
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
sqflite:
dependency: transitive
description:
name: sqflite
sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03
url: "https://pub.dev"
source: hosted
version: "2.4.2"
sqflite_android:
dependency: transitive
description:
name: sqflite_android
sha256: "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b"
url: "https://pub.dev"
source: hosted
version: "2.5.5"
sqflite_darwin:
dependency: transitive
description:
name: sqflite_darwin
sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
sqflite_platform_interface:
dependency: transitive
description:
name: sqflite_platform_interface
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
stack_trace:
dependency: transitive
description:
@ -629,6 +885,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.4"
uuid:
dependency: transitive
description:
name: uuid
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
url: "https://pub.dev"
source: hosted
version: "4.5.1"
vector_graphics:
dependency: transitive
description:
@ -661,6 +925,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
video_player:
dependency: transitive
description:
name: video_player
sha256: "7d78f0cfaddc8c19d4cb2d3bebe1bfef11f2103b0a03e5398b303a1bf65eeb14"
url: "https://pub.dev"
source: hosted
version: "2.9.5"
video_player_android:
dependency: transitive
description:
name: video_player_android
sha256: "28dcc4122079f40f93a0965b3679aff1a5f4251cf79611bd8011f937eb6b69de"
url: "https://pub.dev"
source: hosted
version: "2.8.4"
video_player_avfoundation:
dependency: transitive
description:
name: video_player_avfoundation
sha256: "9ee764e5cd2fc1e10911ae8ad588e1a19db3b6aa9a6eb53c127c42d3a3c3f22f"
url: "https://pub.dev"
source: hosted
version: "2.7.1"
video_player_platform_interface:
dependency: transitive
description:
name: video_player_platform_interface
sha256: df534476c341ab2c6a835078066fc681b8265048addd853a1e3c78740316a844
url: "https://pub.dev"
source: hosted
version: "6.3.0"
video_player_web:
dependency: transitive
description:
name: video_player_web
sha256: e8bba2e5d1e159d5048c9a491bb2a7b29c535c612bb7d10c1e21107f5bd365ba
url: "https://pub.dev"
source: hosted
version: "2.3.5"
vm_service:
dependency: transitive
description:
@ -669,6 +973,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "15.0.0"
wakelock_plus:
dependency: transitive
description:
name: wakelock_plus
sha256: a474e314c3e8fb5adef1f9ae2d247e57467ad557fa7483a2b895bc1b421c5678
url: "https://pub.dev"
source: hosted
version: "1.3.2"
wakelock_plus_platform_interface:
dependency: transitive
description:
name: wakelock_plus_platform_interface
sha256: e10444072e50dbc4999d7316fd303f7ea53d31c824aa5eb05d7ccbdd98985207
url: "https://pub.dev"
source: hosted
version: "1.2.3"
web:
dependency: "direct main"
description:
@ -677,6 +997,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.1"
webview_flutter:
dependency: transitive
description:
name: webview_flutter
sha256: c3e4fe614b1c814950ad07186007eff2f2e5dd2935eba7b9a9a1af8e5885f1ba
url: "https://pub.dev"
source: hosted
version: "4.13.0"
webview_flutter_android:
dependency: transitive
description:
name: webview_flutter_android
sha256: f6e6afef6e234801da77170f7a1847ded8450778caf2fe13979d140484be3678
url: "https://pub.dev"
source: hosted
version: "4.7.0"
webview_flutter_platform_interface:
dependency: transitive
description:
name: webview_flutter_platform_interface
sha256: "7cb32b21825bd65569665c32bb00a34ded5779786d6201f5350979d2d529940d"
url: "https://pub.dev"
source: hosted
version: "2.13.0"
webview_flutter_wkwebview:
dependency: transitive
description:
name: webview_flutter_wkwebview
sha256: a3d461fe3467014e05f3ac4962e5fdde2a4bf44c561cb53e9ae5c586600fdbc3
url: "https://pub.dev"
source: hosted
version: "3.22.0"
win32:
dependency: transitive
description:
name: win32
sha256: "329edf97fdd893e0f1e3b9e88d6a0e627128cc17cc316a8d67fda8f1451178ba"
url: "https://pub.dev"
source: hosted
version: "5.13.0"
xdg_directories:
dependency: transitive
description:

View File

@ -26,6 +26,7 @@ dependencies:
pdfrx: ^1.0.94
photo_view: ^0.15.0
web: ^1.1.1
flutter_widget_from_html: ^0.16.0
dev_dependencies:
flutter_test: