Compare commits
9 Commits
bde05197ae
...
71707bd1c0
Author | SHA1 | Date | |
---|---|---|---|
71707bd1c0 | |||
b961be3e8b | |||
c025fbe07a | |||
214a60ce1b | |||
07091eb708 | |||
dda581bda0 | |||
433394a74a | |||
2465201b0b | |||
4d75674e8e |
165
lib/Compose.dart
Normal file
165
lib/Compose.dart
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:super_editor/super_editor.dart';
|
||||||
|
import 'package:super_editor_markdown/super_editor_markdown.dart';
|
||||||
|
|
||||||
|
class ComposeEmail extends StatefulWidget {
|
||||||
|
final VoidCallback onClose;
|
||||||
|
final Function(String) onMinimize;
|
||||||
|
final Function(String) onSendMessage;
|
||||||
|
const ComposeEmail({
|
||||||
|
Key? key,
|
||||||
|
required this.onMinimize,
|
||||||
|
required this.onClose,
|
||||||
|
required this.onSendMessage,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ComposeEmailState createState() => _ComposeEmailState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ComposeEmailState extends State<ComposeEmail> {
|
||||||
|
// if one were to alter a mutableDocument, one should only alter the document through EditRequest to the Editor
|
||||||
|
late final Editor _editor;
|
||||||
|
late final MutableDocument _document;
|
||||||
|
late final MutableDocumentComposer _composer;
|
||||||
|
TextEditingController _emailRecipientController = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
_document = MutableDocument(nodes: [
|
||||||
|
ParagraphNode(
|
||||||
|
id: Editor.createNodeId(),
|
||||||
|
text: AttributedText("hello world!"),
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
_composer = MutableDocumentComposer();
|
||||||
|
_editor =
|
||||||
|
createDefaultDocumentEditor(document: _document!, composer: _composer!);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_editor.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Positioned(
|
||||||
|
bottom: 10.0,
|
||||||
|
right: 10.0,
|
||||||
|
child: Material(
|
||||||
|
elevation: 8.0,
|
||||||
|
child: Container(
|
||||||
|
width: 600.0,
|
||||||
|
height: 616.0,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
child: Column(children: [
|
||||||
|
AppBar(
|
||||||
|
title: const Text("new message"),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
//TODO: implement minimize, and submit email to drafts
|
||||||
|
widget.onClose();
|
||||||
|
},
|
||||||
|
icon: Icon(Icons.minimize, color: Colors.grey[600])),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
//TODO: implement that maximizing
|
||||||
|
},
|
||||||
|
icon: Icon(Icons.maximize, color: Colors.grey[600])),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
widget.onClose();
|
||||||
|
},
|
||||||
|
icon: Icon(Icons.close, color: Colors.grey[600])),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
// TODO: WHEN NOT CLICKED ITS ONLY A TEXTFIELD WITH A HINT, AND THEN WHEN CLICKED THIS
|
||||||
|
width: 500.0,
|
||||||
|
height: 40.0,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
TextButton(onPressed: () {}, child: Text("To:")),
|
||||||
|
Expanded(
|
||||||
|
child: TextField(
|
||||||
|
controller: _emailRecipientController,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton(onPressed: () {}, child: Text("Cc")),
|
||||||
|
SizedBox(
|
||||||
|
width: 4.0,
|
||||||
|
),
|
||||||
|
TextButton(onPressed: () {}, child: Text("Bcc")),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
//here the widget goes
|
||||||
|
child: SuperEditor(
|
||||||
|
editor: _editor!,
|
||||||
|
plugins: {MarkdownInlineUpstreamSyntaxPlugin()},
|
||||||
|
stylesheet: Stylesheet(
|
||||||
|
rules: [StyleRule(BlockSelector.all, (doc, docNode) {
|
||||||
|
return {
|
||||||
|
Styles.maxWidth: 640.0,
|
||||||
|
Styles.padding: const CascadingPadding.symmetric(horizontal: 24),
|
||||||
|
Styles.textStyle: const TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: 15,
|
||||||
|
height: 1.4,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}),],
|
||||||
|
inlineTextStyler: defaultInlineTextStyler)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
])),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OverlayService {
|
||||||
|
static final OverlayService _instance = OverlayService._internal();
|
||||||
|
factory OverlayService() => _instance;
|
||||||
|
OverlayService._internal();
|
||||||
|
OverlayEntry? _overlayEntry;
|
||||||
|
|
||||||
|
void showPersistentWidget(BuildContext context) {
|
||||||
|
if (_overlayEntry != null) {
|
||||||
|
print("overlay visible");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_overlayEntry = OverlayEntry(
|
||||||
|
builder: (context) => ComposeEmail(
|
||||||
|
onClose: () {
|
||||||
|
removeComposeWidget();
|
||||||
|
},
|
||||||
|
onMinimize: (String content) {
|
||||||
|
minimizeComposeWidget(content);
|
||||||
|
},
|
||||||
|
onSendMessage: (message) {
|
||||||
|
print('msg senf form overlay $message');
|
||||||
|
},
|
||||||
|
));
|
||||||
|
Navigator.of(context).overlay?.insert(_overlayEntry!);
|
||||||
|
print("inserted into tree");
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeComposeWidget() {
|
||||||
|
_overlayEntry?.remove();
|
||||||
|
_overlayEntry = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String minimizeComposeWidget(String content) {
|
||||||
|
//just hide the overlay but keep its info
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
@ -101,6 +101,7 @@ class _SonicEmailViewState extends State<SonicEmailView> {
|
|||||||
onJumpToNumbering: _scrollToNumber,
|
onJumpToNumbering: _scrollToNumber,
|
||||||
onViewspecs: _handleViewspecs,
|
onViewspecs: _handleViewspecs,
|
||||||
onFiltering: _handleFiltering,
|
onFiltering: _handleFiltering,
|
||||||
|
emails: [widget.email.name], subject: '', rootAugment: null,
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
// title of email
|
// title of email
|
||||||
|
@ -90,6 +90,11 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
List<String> getThreads() {
|
||||||
|
return widget.thread;
|
||||||
|
}
|
||||||
|
|
||||||
void _add2Tree(AugmentTree tree, md.Element node2add) {
|
void _add2Tree(AugmentTree tree, md.Element node2add) {
|
||||||
// adds node to its corresponding place
|
// adds node to its corresponding place
|
||||||
AugmentTree newNode = AugmentTree();
|
AugmentTree newNode = AugmentTree();
|
||||||
|
@ -9,13 +9,22 @@ class CollapsableEmails extends StatefulWidget {
|
|||||||
CollapsableEmails(
|
CollapsableEmails(
|
||||||
{required this.thread,
|
{required this.thread,
|
||||||
required this.threadMarkdown,
|
required this.threadMarkdown,
|
||||||
required this.threadIDs, String? targetJumpNumbering, String? targetViewspecs, String? targetFiltering});
|
required this.threadIDs, String? targetJumpNumbering, String? targetViewspecs, String? targetFiltering, required String nameOfDocument});
|
||||||
|
|
||||||
|
get getThreads => null;
|
||||||
|
|
||||||
|
get getAugmentRoot => null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CollapsableEmails> createState() => _CollapsableEmailsState();
|
State<CollapsableEmails> createState() => _CollapsableEmailsState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CollapsableEmailsState extends State<CollapsableEmails> {
|
class _CollapsableEmailsState extends State<CollapsableEmails> {
|
||||||
|
|
||||||
|
List<String> getThreads() {
|
||||||
|
return widget.thread;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(body: Text("collapsable stud"));
|
return Scaffold(body: Text("collapsable stud"));
|
||||||
|
@ -176,22 +176,9 @@ class _CollapsableEmailsState extends State<CollapsableEmails> {
|
|||||||
AugmentTree temp = AugmentTree();
|
AugmentTree temp = AugmentTree();
|
||||||
temp.data = node.textContent;
|
temp.data = node.textContent;
|
||||||
temp.ogTag = node.tag;
|
temp.ogTag = node.tag;
|
||||||
if (node.tag == 'h1') {
|
//why did i do this???
|
||||||
// make this O(1)
|
if ( hirarchyDict.containsKey(node.tag)) {
|
||||||
_add2Tree(zoomTreeRoot, node);
|
_add2Tree(zoomTreeRoot, node);
|
||||||
} else if (node.tag == 'h2') {
|
|
||||||
// i dont add any since i dont have it, maybe the function makes sense
|
|
||||||
_add2Tree(zoomTreeRoot, node); // fix this
|
|
||||||
} else if (node.tag == 'h3') {
|
|
||||||
_add2Tree(zoomTreeRoot, node);
|
|
||||||
} else if (node.tag == 'h4') {
|
|
||||||
_add2Tree(zoomTreeRoot, node); // change to temp
|
|
||||||
} else if (node.tag == 'h5') {
|
|
||||||
_add2Tree(zoomTreeRoot, node);
|
|
||||||
} else if (node.tag == 'h6') {
|
|
||||||
_add2Tree(zoomTreeRoot, node); // fix this
|
|
||||||
} else if (node.tag == 'p' || node.tag == 'ul' || node.tag == 'li') {
|
|
||||||
_add2Tree(zoomTreeRoot, node); // fix this
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,7 @@ class _EmailViewState extends State<EmailView> {
|
|||||||
onJumpToNumbering: _scrollToNumber,
|
onJumpToNumbering: _scrollToNumber,
|
||||||
onViewspecs: _viewSpecs,
|
onViewspecs: _viewSpecs,
|
||||||
onFiltering: _filteringQuery,
|
onFiltering: _filteringQuery,
|
||||||
|
emails: widget.messages, subject: '', rootAugment: null,
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'dart:ui_web' as ui;
|
import 'dart:ui_web' as ui;
|
||||||
import 'augment.dart';
|
import 'augment.dart';
|
||||||
// import 'dart:js_interop' as js; //eventually for manipulating css
|
|
||||||
import 'collapsableEmails.dart';
|
import 'collapsableEmails.dart';
|
||||||
import 'api_service.dart';
|
import 'api_service.dart';
|
||||||
|
|
||||||
@ -35,8 +34,30 @@ class _EmailViewState extends State<EmailView> {
|
|||||||
late Key iframeKey;
|
late Key iframeKey;
|
||||||
late String currentContent;
|
late String currentContent;
|
||||||
late String viewTypeId; //make this a list too???
|
late String viewTypeId; //make this a list too???
|
||||||
Future<List<Map<String, dynamic>>>? _markerPositionsFuture;
|
|
||||||
// TextEditingController _jumpController = TextEditingController();
|
// TextEditingController _jumpController = TextEditingController();
|
||||||
|
late EmailToolbar toolbarInstance = EmailToolbar(
|
||||||
|
onJumpToNumbering: _handleJumpRequest,
|
||||||
|
onViewspecs: _handleViewspecsRequest,
|
||||||
|
onButtonPressed: () => {print("email tool bar pressed")},
|
||||||
|
onFiltering: _handleFiltering,
|
||||||
|
emails: widget.messages,
|
||||||
|
subject: widget.subject,
|
||||||
|
rootAugment: localCollapsable.getAugmentRoot(),
|
||||||
|
);
|
||||||
|
|
||||||
|
late CollapsableEmails localCollapsable = CollapsableEmails(
|
||||||
|
//change here
|
||||||
|
thread: widget.messages, //this wont work in serializable
|
||||||
|
// threadHTML: widget.emailContent, // old html
|
||||||
|
threadMarkdown: widget.emailContent,
|
||||||
|
threadIDs: widget.id,
|
||||||
|
targetJumpNumbering: _targetJumpNumbering,
|
||||||
|
targetViewspecs: _targetViewspecs,
|
||||||
|
targetFiltering: _queryFiltering,
|
||||||
|
nameOfDocument: widget.subject,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
final hardcodedMarkers = [
|
final hardcodedMarkers = [
|
||||||
{'id': 'marker1', 'x': 50, 'y': 100},
|
{'id': 'marker1', 'x': 50, 'y': 100},
|
||||||
{'id': 'marker2', 'x': 150, 'y': 200},
|
{'id': 'marker2', 'x': 150, 'y': 200},
|
||||||
@ -78,11 +99,11 @@ class _EmailViewState extends State<EmailView> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: void _invisibility(String ) //to make purple numbers not visible
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
ApiService.currThreadID = widget.id;
|
ApiService.currThreadID = widget.id;
|
||||||
|
// AugmentClasses localAugment = AugmentClasses(localCollapsable);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(widget.name),
|
title: Text(widget.name),
|
||||||
@ -91,12 +112,7 @@ class _EmailViewState extends State<EmailView> {
|
|||||||
children: [
|
children: [
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
EmailToolbar(
|
toolbarInstance,
|
||||||
onJumpToNumbering: _handleJumpRequest,
|
|
||||||
onViewspecs: _handleViewspecsRequest,
|
|
||||||
onButtonPressed: () => {print("email tool bar pressed")},
|
|
||||||
onFiltering: _handleFiltering,
|
|
||||||
),
|
|
||||||
Row(
|
Row(
|
||||||
// title of email
|
// title of email
|
||||||
children: [
|
children: [
|
||||||
@ -133,16 +149,7 @@ class _EmailViewState extends State<EmailView> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: CollapsableEmails(
|
child: localCollapsable,
|
||||||
//change here
|
|
||||||
thread: widget.messages, //this wont work in serializable
|
|
||||||
// threadHTML: widget.emailContent, // old html
|
|
||||||
threadMarkdown: widget.emailContent,
|
|
||||||
threadIDs: widget.id,
|
|
||||||
targetJumpNumbering: _targetJumpNumbering,
|
|
||||||
targetViewspecs: _targetViewspecs,
|
|
||||||
targetFiltering: _queryFiltering,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -5,6 +5,7 @@ import 'structs.dart';
|
|||||||
import 'api_service.dart';
|
import 'api_service.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'email.dart';
|
import 'email.dart';
|
||||||
|
import 'Compose.dart';
|
||||||
|
|
||||||
class HomeScreen extends StatefulWidget {
|
class HomeScreen extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
@ -44,49 +45,50 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showOptionsSearchDialog () async {
|
void _showOptionsSearchDialog() async {
|
||||||
List<String> folders = await apiService.fetchFolders();
|
List<String> folders = await apiService.fetchFolders();
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: Text('Choose an Option'),
|
title: Text('Choose an Option'),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: folders.map((option) {
|
children: folders.map((option) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(option),
|
title: Text(option),
|
||||||
leading: Radio<String>(
|
leading: Radio<String>(
|
||||||
value: option,
|
value: option,
|
||||||
groupValue: _selectedOption, // Bind with _selectedOption
|
groupValue: _selectedOption, // Bind with _selectedOption
|
||||||
onChanged: (String? value) {
|
onChanged: (String? value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedOption = value;
|
_selectedOption = value;
|
||||||
});
|
});
|
||||||
Navigator.of(context).pop(); // Close the dialog on selection
|
Navigator.of(context)
|
||||||
},
|
.pop(); // Close the dialog on selection
|
||||||
),
|
},
|
||||||
);
|
),
|
||||||
}).toList(),
|
);
|
||||||
),
|
}).toList(),
|
||||||
actions: <Widget>[
|
|
||||||
ElevatedButton(
|
|
||||||
child: Text('Submit'),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop(); // Close the dialog
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
|
||||||
content: Text('You selected: $_selectedOption'),
|
|
||||||
));
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
actions: <Widget>[
|
||||||
);
|
ElevatedButton(
|
||||||
},
|
child: Text('Submit'),
|
||||||
);}
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop(); // Close the dialog
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
|
content: Text('You selected: $_selectedOption'),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Remove a tab
|
// Remove a tab
|
||||||
void _removeTab(int index) {
|
void _removeTab(int index) {
|
||||||
@ -119,7 +121,7 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
|
|||||||
body: ListView.separated(
|
body: ListView.separated(
|
||||||
itemCount: result.length,
|
itemCount: result.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final SerializableMessage email = result[index];
|
final SerializableMessage email = result[index];
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(email.from,
|
title: Text(email.from,
|
||||||
style: TextStyle(fontWeight: FontWeight.bold)),
|
style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
@ -132,27 +134,24 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
|
|||||||
// print('tapped');
|
// print('tapped');
|
||||||
// List<String> emailContent =
|
// List<String> emailContent =
|
||||||
// await apiService.fetchEmailContent([email.id], email.list);
|
// await apiService.fetchEmailContent([email.id], email.list);
|
||||||
//call the foldable
|
//call the foldable
|
||||||
|
|
||||||
List<String> emailContent = // list of the html
|
List<String> emailContent = // list of the html
|
||||||
await apiService.fetchEmailContent([email.id], email.list);
|
await apiService
|
||||||
|
.fetchEmailContent([email.id], email.list);
|
||||||
// List<String> emailIds = email.messages;
|
// List<String> emailIds = email.messages;
|
||||||
|
|
||||||
|
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) =>SonicEmailView(
|
builder: (context) => SonicEmailView(
|
||||||
email: email,
|
email: email, emailHTML: emailContent[0])),
|
||||||
emailHTML: emailContent[0])
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
separatorBuilder: (context, index) => Divider(),
|
separatorBuilder: (context, index) => Divider(),
|
||||||
),
|
),
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -170,7 +169,7 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Theme.of(context).colorScheme.onPrimary,
|
backgroundColor: Theme.of(context).colorScheme.onPrimary,
|
||||||
body: Padding(
|
body: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(0, 20, 0 , 20),
|
padding: const EdgeInsets.fromLTRB(0, 20, 0, 20),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
key: _scaffoldKey,
|
key: _scaffoldKey,
|
||||||
drawer: FolderDrawer(
|
drawer: FolderDrawer(
|
||||||
@ -195,6 +194,12 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
ListTile(
|
||||||
|
leading: Icon(Icons.edit_note_sharp),
|
||||||
|
onTap: () {
|
||||||
|
OverlayService()
|
||||||
|
.showPersistentWidget(context);
|
||||||
|
}),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Icon(Icons.home),
|
leading: Icon(Icons.home),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@ -219,7 +224,8 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
|
|||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.bottomLeft,
|
alignment: Alignment.bottomLeft,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
icon: Icon(Icons.close, color: Colors.white),
|
icon:
|
||||||
|
Icon(Icons.close, color: Colors.white),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isSidebarOpen = false;
|
_isSidebarOpen = false;
|
||||||
@ -256,7 +262,8 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
|
|||||||
),
|
),
|
||||||
onSubmitted: (value) {
|
onSubmitted: (value) {
|
||||||
if (value.isNotEmpty) {
|
if (value.isNotEmpty) {
|
||||||
_performSearch(value, _selectedOption);
|
_performSearch(
|
||||||
|
value, _selectedOption);
|
||||||
}
|
}
|
||||||
//this is the input box i mentioned
|
//this is the input box i mentioned
|
||||||
// if (value == '') {
|
// if (value == '') {
|
||||||
@ -314,8 +321,10 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
|
|||||||
Text(entry.value),
|
Text(entry.value),
|
||||||
if (entry.value != 'Emails')
|
if (entry.value != 'Emails')
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () => _removeTab(entry.key),
|
onTap: () =>
|
||||||
child: Icon(Icons.close, size: 16),
|
_removeTab(entry.key),
|
||||||
|
child: Icon(Icons.close,
|
||||||
|
size: 16),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -333,23 +342,28 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
|
|||||||
children: [
|
children: [
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_emailPageKey.currentState!.isBackDisabled ? null: _emailPageKey.currentState
|
_emailPageKey.currentState!.isBackDisabled
|
||||||
?.updatePagenation('back');
|
? null
|
||||||
|
: _emailPageKey.currentState
|
||||||
|
?.updatePagenation('back');
|
||||||
},
|
},
|
||||||
child: Icon(Icons.navigate_before),
|
child: Icon(Icons.navigate_before),
|
||||||
),
|
),
|
||||||
Builder(
|
Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final emailState = _emailPageKey.currentState;
|
final emailState =
|
||||||
|
_emailPageKey.currentState;
|
||||||
if (emailState == null) {
|
if (emailState == null) {
|
||||||
// Schedule a rebuild once the state is available
|
// Schedule a rebuild once the state is available
|
||||||
Future.microtask(() => setState(() {}));
|
Future.microtask(() => setState(() {}));
|
||||||
return Text('Loading...');
|
return Text('Loading...');
|
||||||
}
|
}
|
||||||
|
|
||||||
return ValueListenableBuilder<int>(
|
return ValueListenableBuilder<int>(
|
||||||
valueListenable: emailState.currentPageNotifier,
|
valueListenable:
|
||||||
builder: (context, value, _) => Text('$value'),
|
emailState.currentPageNotifier,
|
||||||
|
builder: (context, value, _) =>
|
||||||
|
Text('$value'),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -376,7 +390,7 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
|
|||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// if (_tabs.isEmpty)
|
// if (_tabs.isEmpty)
|
||||||
// Expanded(
|
// Expanded(
|
||||||
// child: EmailPage(key: _emailPageKey),
|
// child: EmailPage(key: _emailPageKey),
|
||||||
|
@ -1,15 +1,22 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
// import 'package:crab_ui/api_service.dart';
|
// import 'package:crab_ui/api_service.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
import 'api_service.dart';
|
import 'api_service.dart';
|
||||||
import 'home_page.dart';
|
// import 'home_page.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
// import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
// import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
// import 'package:shared_preferences/shared_preferences.dart';
|
// import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:flutter/services.dart' show rootBundle;
|
import 'package:flutter/services.dart' show rootBundle;
|
||||||
|
|
||||||
class AuthService {
|
class AuthService extends ChangeNotifier {
|
||||||
Future<bool> isUserLoggedIn() async {
|
Future<bool> isUserLoggedIn() async {
|
||||||
|
ApiService.ip = '192.168.2.38';
|
||||||
|
ApiService.port = '3001';
|
||||||
|
print("setted up");
|
||||||
|
|
||||||
|
return true;
|
||||||
try {
|
try {
|
||||||
final response =
|
final response =
|
||||||
await http.get(Uri.http('localhost:6823', 'read-config'));
|
await http.get(Uri.http('localhost:6823', 'read-config'));
|
||||||
@ -83,6 +90,7 @@ class LoginPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SplashScreen extends StatefulWidget {
|
class SplashScreen extends StatefulWidget {
|
||||||
|
//entry point
|
||||||
@override
|
@override
|
||||||
_SplashScreenState createState() => _SplashScreenState();
|
_SplashScreenState createState() => _SplashScreenState();
|
||||||
}
|
}
|
||||||
@ -92,19 +100,24 @@ class _SplashScreenState extends State<SplashScreen> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_checkLoginStatus();
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
_checkLoginStatus();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _checkLoginStatus() async {
|
Future<void> _checkLoginStatus() async {
|
||||||
// SharedPreferences prefs = await SharedPreferences.getInstance();
|
// SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
// print(prefs);
|
// print(prefs);
|
||||||
// bool isLoggedIn = prefs.getBool('isLoggedIn') ?? false;
|
// bool isLoggedIn = prefs.getBool('isLoggedIn') ?? false;
|
||||||
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
bool isLoggedIn = await _authService.isUserLoggedIn();
|
bool isLoggedIn = await _authService.isUserLoggedIn();
|
||||||
print("is loogeed in $isLoggedIn");
|
print("is logged in $isLoggedIn");
|
||||||
if (isLoggedIn) {
|
if (isLoggedIn) {
|
||||||
Navigator.pushReplacementNamed(context, '/home');
|
context.go("/home");
|
||||||
|
// Navigator.pushReplacementNamed(context, '/home');
|
||||||
} else {
|
} else {
|
||||||
Navigator.pushReplacementNamed(context, '/login');
|
context.go("/login");
|
||||||
|
// Navigator.pushReplacementNamed(context, '/login');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +125,7 @@ class _SplashScreenState extends State<SplashScreen> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
body: Center(child: CircularProgressIndicator()),
|
body: Center(child: CircularProgressIndicator()), //nothing happens
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -132,6 +145,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
Future<bool> setIp(String ip) async {
|
Future<bool> setIp(String ip) async {
|
||||||
|
//this is not done :sob: :skull:
|
||||||
// _configManager.setField("api_addr", ip);
|
// _configManager.setField("api_addr", ip);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
|
|||||||
import 'package:markdown/markdown.dart' as md;
|
import 'package:markdown/markdown.dart' as md;
|
||||||
import 'package:markdown_widget/markdown_widget.dart';
|
import 'package:markdown_widget/markdown_widget.dart';
|
||||||
import 'api_service.dart';
|
import 'api_service.dart';
|
||||||
|
import 'structs.dart';
|
||||||
|
|
||||||
class Routinghandler extends StatefulWidget {
|
class Routinghandler extends StatefulWidget {
|
||||||
Routinghandler(String link, emailID) {
|
Routinghandler(String link, emailID) {
|
||||||
@ -133,6 +134,7 @@ class Routinghandler extends StatefulWidget {
|
|||||||
class _RoutingHandlerState extends State<Routinghandler> {
|
class _RoutingHandlerState extends State<Routinghandler> {
|
||||||
List<String> markdownContent = [];
|
List<String> markdownContent = [];
|
||||||
bool _isLoaded = false;
|
bool _isLoaded = false;
|
||||||
|
AugmentTree? aug;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -143,13 +145,15 @@ class _RoutingHandlerState extends State<Routinghandler> {
|
|||||||
|
|
||||||
Future<void> _loadMarkdown() async {
|
Future<void> _loadMarkdown() async {
|
||||||
String folder = ApiService.currFolder;
|
String folder = ApiService.currFolder;
|
||||||
// print(folder);
|
|
||||||
print(widget.getEmailID());
|
print(widget.getEmailID());
|
||||||
String emailID = widget.emailID;
|
String emailID = widget.emailID;
|
||||||
print("inside _loadMarkdown in routinghandler $emailID");
|
print("inside _loadMarkdown in routinghandler $emailID");
|
||||||
markdownContent =
|
markdownContent =
|
||||||
await ApiService().fetchMarkdownContent([emailID], "INBOX");
|
await ApiService().fetchMarkdownContent([emailID], folder);
|
||||||
// print(markdownContent);
|
// print(markdownContent);
|
||||||
|
aug = AugmentTree.fromMD(markdownContent[0]);
|
||||||
|
aug!.addNumbering();
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoaded = true;
|
_isLoaded = true;
|
||||||
});
|
});
|
||||||
@ -162,44 +166,58 @@ class _RoutingHandlerState extends State<Routinghandler> {
|
|||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: Text("Routing Handler"),
|
|
||||||
leading: IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
GoRouter.of(context).go('/home');
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.arrow_back_ios)),
|
|
||||||
),
|
|
||||||
body: ConstrainedBox(
|
|
||||||
constraints: BoxConstraints(
|
|
||||||
minHeight: 100,
|
|
||||||
maxHeight: MediaQuery.of(context).size.height * 0.7,
|
|
||||||
),
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child:MarkdownBlock(data: markdownContent[0]))),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LinkViewer extends StatefulWidget {
|
|
||||||
const LinkViewer({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<StatefulWidget> createState() => _LinkViewerState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _LinkViewerState extends State<LinkViewer> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
// this should be a class that opens a popup of the email on the view it wants
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('url viewer'),
|
title: Text("Routing Handler"),
|
||||||
|
leading: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
GoRouter.of(context).go('/home');
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.arrow_back_ios)),
|
||||||
),
|
),
|
||||||
body: Column(
|
body: ConstrainedBox(
|
||||||
children: [],
|
constraints: BoxConstraints(
|
||||||
));
|
minHeight: 100,
|
||||||
|
maxHeight: MediaQuery.of(context).size.height * 0.7,
|
||||||
|
),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
//inside here put the bunch rows
|
||||||
|
//make rows of markdownBlocks, but firstly i need to conveert the content into a tree
|
||||||
|
// child:MarkdownBlock(data: markdownContent[0])
|
||||||
|
|
||||||
|
child: Column(children: [
|
||||||
|
for (int i = 0; i < this.aug!.children![0]!.children.length; i++)
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// if (leftNumbering)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 10, 5, 0),
|
||||||
|
child: Text(
|
||||||
|
aug!.children![0]!.children![i]!.numbering,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(Colors.purple[400]!.value)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Wrap(children: [
|
||||||
|
MarkdownBlock(
|
||||||
|
data: aug!.children![0]!.children![i]!.data ?? ''),
|
||||||
|
],)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(0, 10, 5, 0),
|
||||||
|
child: Text(
|
||||||
|
aug!.children![0]!.children![i]!.numbering,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(Colors.purple[400]!.value)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
]))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
100
lib/structs.dart
100
lib/structs.dart
@ -1,6 +1,7 @@
|
|||||||
//data structures
|
//data structures
|
||||||
|
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
import 'package:markdown/markdown.dart' as md;
|
||||||
|
|
||||||
class GetThreadResponse {
|
class GetThreadResponse {
|
||||||
final int id;
|
final int id;
|
||||||
@ -157,6 +158,94 @@ class AugmentTree {
|
|||||||
AugmentTree? parent;
|
AugmentTree? parent;
|
||||||
String ogTag = '';
|
String ogTag = '';
|
||||||
String numbering = '';
|
String numbering = '';
|
||||||
|
Map<String, int> hirarchyDict = {
|
||||||
|
"h1": 1,
|
||||||
|
"h2": 2,
|
||||||
|
"h3": 3,
|
||||||
|
"h4": 4,
|
||||||
|
"h5": 5,
|
||||||
|
"h6": 6,
|
||||||
|
"p": 8,
|
||||||
|
"ul": 8,
|
||||||
|
"li": 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
AugmentTree();
|
||||||
|
|
||||||
|
AugmentTree.fromMD(String rawMD) {
|
||||||
|
//makes raw MD into an augmentTree
|
||||||
|
print("started markdown2tree");
|
||||||
|
final List<md.Node> nakedList = md.Document().parseLines(rawMD.split(
|
||||||
|
'\n')); //emails md is the index of the email in the thread, since this only handles one thus it shall be removed
|
||||||
|
// AugmentTree zoomTreeRoot = AugmentTree();
|
||||||
|
for (var node in nakedList) {
|
||||||
|
//maybe do an add function, but isn't this it?
|
||||||
|
if (node is md.Element) {
|
||||||
|
AugmentTree temp = AugmentTree();
|
||||||
|
temp.data = node.textContent;
|
||||||
|
temp.ogTag = node.tag;
|
||||||
|
if (hirarchyDict.containsKey(node.tag)) {
|
||||||
|
// make this O(1)
|
||||||
|
_add2Tree(this, node);
|
||||||
|
// print(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.addNumbering();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 setData(String data) {
|
void setData(String data) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
@ -176,7 +265,7 @@ class AugmentTree {
|
|||||||
parentIsLettered = false;
|
parentIsLettered = false;
|
||||||
} else {
|
} else {
|
||||||
parentIsLettered = prefix.runes.last >= 'a'.runes.first &&
|
parentIsLettered = prefix.runes.last >= 'a'.runes.first &&
|
||||||
prefix.runes.last <= 'z'.runes.first;
|
prefix.runes.last <= 'z'.runes.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prefix.isEmpty) {
|
if (prefix.isEmpty) {
|
||||||
@ -197,14 +286,19 @@ class AugmentTree {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//perhaps make a struct that builds augment tree, since its so complex and needs to be like recursive
|
||||||
|
|
||||||
class MarkdownParsed{
|
class MarkdownParsed {
|
||||||
|
//struct for holding the MD given in endpoint //not used
|
||||||
final String text;
|
final String text;
|
||||||
MarkdownParsed({required this.text});
|
MarkdownParsed({required this.text});
|
||||||
factory MarkdownParsed.fromJson(Map<String, String> json){
|
factory MarkdownParsed.fromJson(Map<String, String> json) {
|
||||||
return MarkdownParsed(
|
return MarkdownParsed(
|
||||||
text: json['md'] ?? '',
|
text: json['md'] ?? '',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//should make an md to tree class/struct
|
||||||
|
|
||||||
|
// make a for loop of rows with markdown
|
||||||
|
@ -31,6 +31,8 @@ dependencies:
|
|||||||
markdown_widget: ^2.3.2+8
|
markdown_widget: ^2.3.2+8
|
||||||
markdown: ^7.3.0
|
markdown: ^7.3.0
|
||||||
go_router: ^16.0.0
|
go_router: ^16.0.0
|
||||||
|
super_editor: ^0.3.0-dev.27
|
||||||
|
super_editor_markdown: 0.1.8
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Loading…
Reference in New Issue
Block a user