Compare commits

...

4 Commits

3 changed files with 271 additions and 55 deletions

View File

@ -1,3 +1,5 @@
import 'package:crab_ui/api_service.dart';
import 'package:crab_ui/structs.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:super_editor/super_editor.dart'; import 'package:super_editor/super_editor.dart';
import 'package:super_editor_markdown/super_editor_markdown.dart'; import 'package:super_editor_markdown/super_editor_markdown.dart';
@ -6,12 +8,14 @@ class ComposeEmail extends StatefulWidget {
final VoidCallback onClose; final VoidCallback onClose;
final Function(String) onMinimize; final Function(String) onMinimize;
final Function(String) onSendMessage; final Function(String) onSendMessage;
const ComposeEmail({ GetThreadResponse? emailDraftID;
Key? key, ComposeEmail(
{Key? key,
required this.onMinimize, required this.onMinimize,
required this.onClose, required this.onClose,
required this.onSendMessage, required this.onSendMessage,
}) : super(key: key); this.emailDraftID})
: super(key: key);
@override @override
_ComposeEmailState createState() => _ComposeEmailState(); _ComposeEmailState createState() => _ComposeEmailState();
@ -23,20 +27,38 @@ class _ComposeEmailState extends State<ComposeEmail> {
late final MutableDocument _document; late final MutableDocument _document;
late final MutableDocumentComposer _composer; late final MutableDocumentComposer _composer;
TextEditingController _emailRecipientController = TextEditingController(); TextEditingController _emailRecipientController = TextEditingController();
TextEditingController _emailSubjectController = TextEditingController();
List<String>? contentOfDraft;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_loadDraftContent();
}
void _loadDraftContent() async {
if (widget.emailDraftID != null) {
String? drafted = widget.emailDraftID?.messages.last;
if (drafted != null) {
contentOfDraft =
await ApiService().fetchMarkdownContent([drafted!], "Drafts");
setState(() {
_document = MutableDocument(nodes: [ _document = MutableDocument(nodes: [
ParagraphNode( ParagraphNode(
id: Editor.createNodeId(), id: Editor.createNodeId(),
text: AttributedText("hello world!"), text: AttributedText(contentOfDraft?[0] ??
""), // NOW THIS SHOULD BE WTV ITS IN DRAFTS
) )
]); ]);
_composer = MutableDocumentComposer(); _composer = MutableDocumentComposer();
_editor = _editor = createDefaultDocumentEditor(
createDefaultDocumentEditor(document: _document!, composer: _composer!); document: _document, composer: _composer);
_emailRecipientController.text =
widget.emailDraftID!.to[0].address;
_emailSubjectController.text = widget.emailDraftID!.subject;
});
}
}
} }
@override @override
@ -70,7 +92,7 @@ class _ComposeEmailState extends State<ComposeEmail> {
icon: Icon(Icons.minimize, color: Colors.grey[600])), icon: Icon(Icons.minimize, color: Colors.grey[600])),
IconButton( IconButton(
onPressed: () { onPressed: () {
//TODO: implement that maximizing //TODO: implement maximizing the window or widget
}, },
icon: Icon(Icons.maximize, color: Colors.grey[600])), icon: Icon(Icons.maximize, color: Colors.grey[600])),
IconButton( IconButton(
@ -82,8 +104,8 @@ class _ComposeEmailState extends State<ComposeEmail> {
), ),
Container( Container(
// TODO: WHEN NOT CLICKED ITS ONLY A TEXTFIELD WITH A HINT, AND THEN WHEN CLICKED THIS // TODO: WHEN NOT CLICKED ITS ONLY A TEXTFIELD WITH A HINT, AND THEN WHEN CLICKED THIS
width: 500.0, // width: 500.0,
height: 40.0, // height: 40.0,
child: Row( child: Row(
children: [ children: [
TextButton(onPressed: () {}, child: Text("To:")), TextButton(onPressed: () {}, child: Text("To:")),
@ -100,24 +122,94 @@ class _ComposeEmailState extends State<ComposeEmail> {
], ],
), ),
), ),
SizedBox(
height: 4,
),
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: [
Expanded(
child: TextField(
controller: _emailSubjectController,
decoration: InputDecoration(
hintText: "Subject",
),
),
)
],
),
),
Expanded( Expanded(
//here the widget goes //here the widget goes
child: SuperEditor( child: SuperEditor(
//make this its own
editor: _editor!, editor: _editor!,
plugins: {MarkdownInlineUpstreamSyntaxPlugin()}, plugins: {MarkdownInlineUpstreamSyntaxPlugin()},
stylesheet: Stylesheet(
rules: [StyleRule(BlockSelector.all, (doc, docNode) { // stylesheet: Stylesheet(
return { // rules: [StyleRule(BlockSelector.all, (doc, docNode) {
Styles.maxWidth: 640.0, // return {
Styles.padding: const CascadingPadding.symmetric(horizontal: 24), // Styles.maxWidth: 640.0,
Styles.textStyle: const TextStyle( // Styles.padding: const CascadingPadding.symmetric(horizontal: 24),
color: Colors.black, // Styles.textStyle: const TextStyle(
fontSize: 15, // color: Colors.black,
height: 1.4, // // fontSize: 15,
// height: 1.4,
// ),
// };
// }),],
// inlineTextStyler: defaultInlineTextStyler)
), ),
}; ),
}),], Container(
inlineTextStyler: defaultInlineTextStyler) padding: EdgeInsets.symmetric(horizontal: 20, vertical: 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
ElevatedButton(
onPressed: () {
print('sent');
String markdown =
serializeDocumentToMarkdown(_editor.document);
print(_emailRecipientController.text);
print(_emailSubjectController.text);
print(markdown);
ApiService().sendEmail(_emailRecipientController.text,
_emailSubjectController.text, markdown);
},
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30)),
elevation: 4,
foregroundColor: Colors.white,
backgroundColor: Colors.blueAccent),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'Send',
style: TextStyle(fontWeight: FontWeight.bold),
),
// const SizedBox(
// width: 8,
// ),
// Container(
// height: 30, width: 1.0, color: Colors.white),
// const SizedBox(
// width: 8,
// ),
// const Icon(
// Icons.arrow_drop_down,
// size: 24,
// )
],
),
),
],
), ),
) )
])), ])),
@ -131,6 +223,7 @@ class OverlayService {
factory OverlayService() => _instance; factory OverlayService() => _instance;
OverlayService._internal(); OverlayService._internal();
OverlayEntry? _overlayEntry; OverlayEntry? _overlayEntry;
GetThreadResponse? draftID;
void showPersistentWidget(BuildContext context) { void showPersistentWidget(BuildContext context) {
if (_overlayEntry != null) { if (_overlayEntry != null) {
@ -148,7 +241,7 @@ class OverlayService {
onSendMessage: (message) { onSendMessage: (message) {
print('msg senf form overlay $message'); print('msg senf form overlay $message');
}, },
)); emailDraftID: draftID));
Navigator.of(context).overlay?.insert(_overlayEntry!); Navigator.of(context).overlay?.insert(_overlayEntry!);
print("inserted into tree"); print("inserted into tree");
} }

View File

@ -9,8 +9,8 @@ import 'package:http/http.dart' as http;
import 'dart:convert'; import 'dart:convert';
class ApiService { class ApiService {
static String ip = ""; static String ip = '127.0.0.1';
static String port = ""; static String port = "3001";
static List<AttachmentResponse> threadAttachments = static List<AttachmentResponse> threadAttachments =
[]; //holds attachments of the thread []; //holds attachments of the thread
static String currFolder = ""; static String currFolder = "";
@ -169,7 +169,7 @@ class ApiService {
Future<bool> moveEmail( Future<bool> moveEmail(
//only moves the first email of the thread //or perhaps should do the last //only moves the first email of the thread //or perhaps should do the last
String fromFolder, String fromFolder,
String thread_id, String thread_id, //uid
String toFolder) async { String toFolder) async {
var url = Uri.http('$ip:$port', 'move_email'); var url = Uri.http('$ip:$port', 'move_email');
@ -185,7 +185,7 @@ class ApiService {
Map<String, String> requestBody = { Map<String, String> requestBody = {
'from': fromFolder, 'from': fromFolder,
'uid': firstMail.uid.toString(), 'uid': firstMail.uid.toString(),
'to': toFolder, 'to': "Deleted Crabmail",
}; };
try { try {
@ -374,7 +374,110 @@ class ApiService {
} catch (e) { } catch (e) {
print('_getMDContent caught error: $e'); print('_getMDContent caught error: $e');
} }
print("IDS inside fetch md content $IDsString");
return MDofThread; return MDofThread;
} }
Future<void> markAsSeen(int thread_id) async {
try {
var url = Uri.http(
'$ip:$port', 'post_seen_thread', {'id': thread_id.toString()});
var response = await http.get(url);
if (response.statusCode == 200) {
var result = response.body;
print("data $result");
}
} catch (e) {
print("markAsSeen failed $e");
}
}
Future<void> markAsUnseen(int thread_id) async {
try {
var url = Uri.http(
'$ip:$port', 'post_unseen_thread', {'id': thread_id.toString()});
var response = await http.get(url);
if (response.statusCode == 200) {
var result = response.body;
print("data $result");
}
} catch (e) {
print("markAsUnseen failed $e");
}
}
Future<bool> deleteEmail(String from_folder, int thread_id) async {
// post
try {
List<SerializableMessage> mailsInSerializable =
await this.threadsInSerializable(thread_id.toString());
if (mailsInSerializable.isEmpty) {
return false;
}
Map<String, String> requestBody = {
"from": from_folder,
"uid": mailsInSerializable.first.uid.toString(),
"to": "not used"
};
//delete the email that is given to the
var url = Uri.http("$ip:$port", 'delete_email');
var response = await http.post(url,
headers: {
"Content-Type": "application/json",
},
body: jsonEncode(requestBody));
if (response.statusCode == 200) {
print("response body: ${response.body}");
return true;
} else {
print("not 200: ${response.body}");
return false;
}
} catch (e) {
print("error in deleteEmail $e");
return false;
}
}
Future<bool> sendEmail(
String? to, String? subject, String? emailContent) async {
try {
var url = Uri.http('$ip:$port', 'send_email');
Map<String, dynamic> requestBody = {
"to": [to ?? ""],
"cc": [],
"bcc": [],
"subject": subject ?? "Untitled",
"in_reply_to": "",
"messages": [
{
"message": emailContent ?? "",
"is_html": false}],
"attachments": [],
"inline_images": [],
};
var response = await http.post(
url,
headers: {
'Content-Type': 'application/json',
},
body: jsonEncode(requestBody),
);
if (response.statusCode == 200) {
print("response body: ${response.body}");
} else {
print('error: ${response.statusCode}, response body: ${response.body}');
return false;
}
return true;
} catch (e) {
print("error in post send email $e");
return false;
}
}
} }

View File

@ -91,6 +91,20 @@ class _EmailListScreenState extends State<EmailListScreen>
return false; return false;
} }
bool moveOfSelected(String destinyFolder) {
//this should be called from a widget
print("move of folder");
setState(() {
for (int email = 0; email < selectedEmails.length; email++) {
ApiService().moveEmail(
widget.folder, selectedEmails[email].id.toString(), destinyFolder);
}
});
return false;
}
// Widget moveOfFolderWidget()
List<GetThreadResponse> listOfSelectedThreads() { List<GetThreadResponse> listOfSelectedThreads() {
return selectedEmails; return selectedEmails;
} }
@ -153,7 +167,7 @@ class _EmailListScreenState extends State<EmailListScreen>
IconButton( IconButton(
icon: Icon(Icons.mark_email_read_outlined), icon: Icon(Icons.mark_email_read_outlined),
onPressed: () { onPressed: () {
//mark email as read // mark email as read
setState(() { setState(() {
widget.emails[index].seen = true; widget.emails[index].seen = true;
ApiService().markAsSeen(email.id); ApiService().markAsSeen(email.id);
@ -163,7 +177,8 @@ class _EmailListScreenState extends State<EmailListScreen>
IconButton( IconButton(
icon: Icon(Icons.delete_outline), icon: Icon(Icons.delete_outline),
onPressed: () { onPressed: () {
//delete email // delete email
ApiService().deleteEmail(widget.folder, email.id);
}, },
), ),
], ],
@ -297,8 +312,13 @@ class EmailPageState extends State<EmailPage> {
return selectionType; return selectionType;
} }
bool moveSelectedOfFolder(String folder) {
emailListKey.currentState?.moveOfSelected(folder);
return false;
}
List<GetThreadResponse> getListOfSelected() { List<GetThreadResponse> getListOfSelected() {
return emailListKey.currentState!.listOfSelectedThreads() ?? []; return emailListKey.currentState!.listOfSelectedThreads();
} }
// return [GetThreadResponse(id: 1, messages: [], subject: "subject", date: DateTime(2025), from_name: "from_name", from_address: "from_address", to: [], seen: false)]; // return [GetThreadResponse(id: 1, messages: [], subject: "subject", date: DateTime(2025), from_name: "from_name", from_address: "from_address", to: [], seen: false)];