Reviewed-on: #3
This commit is contained in:
commit
4fa8e5b6fe
@ -1,360 +1,549 @@
|
|||||||
// this file should handle most of the API calls
|
// this file should handle most of the API calls
|
||||||
// it also builds some widgets, but it will be modulated later
|
// it also builds some widgets, but it will be modulated later
|
||||||
|
|
||||||
import 'package:crab_ui/structs.dart';
|
import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
import 'dart:typed_data';
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'dart:convert';
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
import 'dart:ui_web' as ui;
|
|
||||||
import 'augment.dart';
|
import 'structs.dart';
|
||||||
import 'dart:html' as html;
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
class ApiService {
|
import 'dart:convert';
|
||||||
static String ip = "";
|
import 'dart:ui_web' as ui;
|
||||||
static String port = "";
|
import 'augment.dart';
|
||||||
Future<List<GetThreadResponse>> fetchEmailsFromFolder(
|
import 'dart:html' as html;
|
||||||
String folder, int pagenitaion) async {
|
import 'dart:js' as js;
|
||||||
// print(ip + " " + port);
|
|
||||||
try {
|
class ApiService {
|
||||||
var url = Uri.http('$ip:$port', 'sorted_threads_by_date', {
|
static String ip = "";
|
||||||
'folder': folder,
|
static String port = "";
|
||||||
'limit': '50',
|
static List<AttachmentResponse> threadAttachments = [];
|
||||||
'offset': pagenitaion.toString(),
|
static String currFolder = "";
|
||||||
});
|
static List<String> currThread = [];
|
||||||
var response = await http.get(url);
|
|
||||||
// print(response);
|
Future<List<GetThreadResponse>> fetchEmailsFromFolder(
|
||||||
List<GetThreadResponse> allEmails = [];
|
String folder, int pagenitaion) async {
|
||||||
|
try {
|
||||||
if (response.statusCode == 200) {
|
var url = Uri.http('$ip:$port', 'sorted_threads_by_date', {
|
||||||
List json = jsonDecode(response.body);
|
'folder': folder,
|
||||||
for (var item in json) {
|
'limit': '50',
|
||||||
//each item in the json is a date
|
'offset': pagenitaion.toString(),
|
||||||
if (item.length > 1 && item[0] is String && item[1] is List) {
|
});
|
||||||
List<int> threadIDs = List<int>.from(item[1]);
|
var response = await http.get(url);
|
||||||
for (var threadId in threadIDs) {
|
// print(response);
|
||||||
await fetchThreads(threadId, allEmails);
|
List<GetThreadResponse> allEmails = [];
|
||||||
}
|
|
||||||
}
|
if (response.statusCode == 200) {
|
||||||
}
|
List json = jsonDecode(response.body);
|
||||||
return allEmails;
|
for (var item in json) {
|
||||||
} else {
|
//each item in the json is a date
|
||||||
throw Exception('Failed to load threads');
|
if (item.length > 1 && item[0] is String && item[1] is List) {
|
||||||
}
|
List<int> threadIDs = List<int>.from(item[1]);
|
||||||
} catch (e) {
|
for (var threadId in threadIDs) {
|
||||||
print('_displayEmailsFromFolder caught error: $e');
|
await fetchThreads(threadId, allEmails);
|
||||||
return [];
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return allEmails;
|
||||||
Future<void> fetchThreads(
|
} else {
|
||||||
//populates allEmails, which is the List that contains all the emails in a thread
|
throw Exception('Failed to load threads');
|
||||||
int threadId,
|
}
|
||||||
List<GetThreadResponse> allEmails) async {
|
} catch (e) {
|
||||||
try {
|
print('_displayEmailsFromFolder caught error: $e');
|
||||||
var url =
|
return [];
|
||||||
Uri.http('$ip:$port', 'get_thread', {'id': threadId.toString()});
|
}
|
||||||
var response = await http.get(url);
|
}
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
Future<void> fetchThreads(
|
||||||
Map<String, dynamic> messagesJson = jsonDecode(response.body);
|
//populates allEmails, which is the List that contains all the emails in a thread
|
||||||
GetThreadResponse threadResponse =
|
int threadId,
|
||||||
GetThreadResponse.fromJson(messagesJson);
|
List<GetThreadResponse> allEmails) async {
|
||||||
|
try {
|
||||||
allEmails.add(threadResponse);
|
var url = Uri.http('${ApiService.ip}:${ApiService.port}', 'get_thread',
|
||||||
} else {
|
{'id': threadId.toString()});
|
||||||
throw Exception(
|
var response = await http.get(url);
|
||||||
'Failed to fetch thread messages for thread ID: $threadId');
|
|
||||||
}
|
if (response.statusCode == 200) {
|
||||||
} catch (e) {
|
Map<String, dynamic> messagesJson = jsonDecode(response.body);
|
||||||
print('Error fetching thread messages: $e');
|
GetThreadResponse threadResponse =
|
||||||
}
|
GetThreadResponse.fromJson(messagesJson);
|
||||||
}
|
|
||||||
|
allEmails.add(threadResponse);
|
||||||
Future<List<SerializableMessage>> sonicSearch(
|
} else {
|
||||||
String list, int limit, int offset, String query) async {
|
throw Exception(
|
||||||
try {
|
'Failed to fetch thread messages for thread ID: $threadId');
|
||||||
var url = Uri.http('$ip:$port', 'search_emails', {
|
}
|
||||||
'list': list,
|
} catch (e) {
|
||||||
'limit': limit.toString(),
|
print('Error fetching thread messages: $e');
|
||||||
'offset': offset.toString(),
|
}
|
||||||
'query': query
|
}
|
||||||
});
|
|
||||||
print(url);
|
Future<List<SerializableMessage>> sonicSearch(
|
||||||
|
String list, int limit, int offset, String query) async {
|
||||||
var response = await http.get(url);
|
try {
|
||||||
print(response);
|
var url = Uri.http('$ip:$port', 'search_emails', {
|
||||||
if (response.statusCode == 200) {
|
'list': list,
|
||||||
List<dynamic> messagesJson = json.decode(response.body);
|
'limit': limit.toString(),
|
||||||
List<SerializableMessage> messages =
|
'offset': offset.toString(),
|
||||||
messagesJson.map((mj) => SerializableMessage.fromJson(mj)).toList();
|
'query': query
|
||||||
|
});
|
||||||
return messages;
|
print(url);
|
||||||
}
|
|
||||||
print(response.statusCode);
|
var response = await http.get(url);
|
||||||
} catch (e) {
|
print(response);
|
||||||
print("caught $e");
|
if (response.statusCode == 200) {
|
||||||
}
|
List<dynamic> messagesJson = json.decode(response.body);
|
||||||
return [];
|
List<SerializableMessage> messages =
|
||||||
}
|
messagesJson.map((mj) => SerializableMessage.fromJson(mj)).toList();
|
||||||
|
|
||||||
Future<String> fetchEmailContent(List<String> IDs) async {
|
return messages;
|
||||||
String content = r"""
|
}
|
||||||
""";
|
print(response.statusCode);
|
||||||
|
} catch (e) {
|
||||||
try {
|
print("caught $e");
|
||||||
//attaches email after email from a thread
|
}
|
||||||
for (var id in IDs) {
|
return [];
|
||||||
var url = Uri.http('$ip:$port', 'email', {'id': id});
|
}
|
||||||
|
|
||||||
var response = await http.get(url);
|
//returns the html for the email, it gets used in emailView
|
||||||
|
Future<String> fetchEmailContent(
|
||||||
if (response.statusCode == 200) {
|
List<String> IDsString, String emailFolder) async {
|
||||||
content += response.body;
|
String content = r"""
|
||||||
try {
|
""";
|
||||||
getAttachmentsInfo("INBOX", id);
|
threadAttachments = [];
|
||||||
} catch (innerError) {
|
|
||||||
print('_getAttachment info caught error $innerError');
|
try {
|
||||||
}
|
//attaches email after email from a thread
|
||||||
content += "<hr>";
|
for (var id in IDsString) {
|
||||||
}
|
var url = Uri.http('$ip:$port', 'email', {'id': id});
|
||||||
}
|
var response = await http.get(url);
|
||||||
} catch (e) {
|
currThread.add(id);
|
||||||
print('_getEmailContent caught error: $e');
|
if (response.statusCode == 200) {
|
||||||
}
|
content += response.body;
|
||||||
return content;
|
try {
|
||||||
}
|
List<AttachmentInfo> attachments = await getAttachmentsInfo(
|
||||||
|
emailFolder, id);
|
||||||
// void _addMailBox async(BuildContext context){
|
for (var attachment in attachments) {
|
||||||
// //add email folder
|
//TODO: for each attachment creaate at the bottom a widget for each individual one
|
||||||
// showDialog(context: context, builder: builder)
|
threadAttachments
|
||||||
// }
|
.add(await getAttachment(emailFolder, id, attachment.name));
|
||||||
|
}
|
||||||
Future<List<String>> fetchFolders() async {
|
} catch (innerError) {
|
||||||
try {
|
print('_getAttachment info caught error $innerError');
|
||||||
var url = Uri.http('$ip:$port', 'folders');
|
}
|
||||||
var response = await http.get(url);
|
content +=
|
||||||
return List<String>.from(json.decode(response.body));
|
"""<div id="JuanBedarramarker" style="width: 10px; height: 30px;"></div>""";
|
||||||
} catch (e) {
|
content += "<hr>";
|
||||||
print('fetchFolders caught error: $e');
|
}
|
||||||
return [];
|
}
|
||||||
}
|
} catch (e) {
|
||||||
}
|
print('_getEmailContent caught error: $e');
|
||||||
|
}
|
||||||
Future<void> createFolder(String folderName) async {
|
return content;
|
||||||
var url = Uri.http('$ip:$port', 'create_folder');
|
}
|
||||||
|
|
||||||
Map<String, String> requestBody = {'name': folderName};
|
// void _addMailBox async(BuildContext context){
|
||||||
|
// //add email folder
|
||||||
try {
|
// showDialog(context: context, builder: builder)
|
||||||
var response = await http.post(
|
// }
|
||||||
url,
|
|
||||||
headers: {
|
Future<List<String>> fetchFolders() async {
|
||||||
'Content-Type': 'application/json',
|
try {
|
||||||
},
|
var url = Uri.http('$ip:$port', 'folders');
|
||||||
body: jsonEncode(requestBody),
|
var response = await http.get(url);
|
||||||
);
|
return List<String>.from(json.decode(response.body));
|
||||||
if (response.statusCode == 200) {
|
} catch (e) {
|
||||||
print('response body: ${response.body}');
|
print('fetchFolders caught error: $e');
|
||||||
} else {
|
return [];
|
||||||
print('Error: ${response.statusCode}, response body: ${response.body}');
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
print('error making post req: $e');
|
Future<void> createFolder(String folderName) async {
|
||||||
}
|
var url = Uri.http('$ip:$port', 'create_folder');
|
||||||
}
|
|
||||||
|
Map<String, String> requestBody = {'name': folderName};
|
||||||
Future<void> deleteFolder(String folderName) async {
|
|
||||||
var url = Uri.http('$ip:$port', 'delete_folder');
|
try {
|
||||||
|
var response = await http.post(
|
||||||
Map<String, String> requestBody = {'name': folderName};
|
url,
|
||||||
|
headers: {
|
||||||
try {
|
'Content-Type': 'application/json',
|
||||||
var response = await http.post(
|
},
|
||||||
url,
|
body: jsonEncode(requestBody),
|
||||||
headers: {
|
);
|
||||||
'Content-Type': 'application/json',
|
if (response.statusCode == 200) {
|
||||||
},
|
print('response body: ${response.body}');
|
||||||
body: jsonEncode(requestBody),
|
} else {
|
||||||
);
|
print('Error: ${response.statusCode}, response body: ${response.body}');
|
||||||
if (response.statusCode == 200) {
|
}
|
||||||
print('response body: ${response.body}');
|
} catch (e) {
|
||||||
} else {
|
print('error making post req: $e');
|
||||||
print('Error: ${response.statusCode}, response body: ${response.body}');
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
print('error making post req: $e');
|
Future<void> deleteFolder(String folderName) async {
|
||||||
}
|
var url = Uri.http('$ip:$port', 'delete_folder');
|
||||||
}
|
|
||||||
|
Map<String, String> requestBody = {'name': folderName};
|
||||||
Future<bool> logIn(String json) async {
|
|
||||||
// var url = Uri.https('')
|
try {
|
||||||
// try{
|
var response = await http.post(
|
||||||
// String response = await http.post(
|
url,
|
||||||
// url
|
headers: {
|
||||||
// );
|
'Content-Type': 'application/json',
|
||||||
// }
|
},
|
||||||
|
body: jsonEncode(requestBody),
|
||||||
return false;
|
);
|
||||||
}
|
if (response.statusCode == 200) {
|
||||||
|
print('response body: ${response.body}');
|
||||||
Future<List<AttachmentInfo>> getAttachmentsInfo(
|
} else {
|
||||||
String folder, String email_id) async {
|
print('Error: ${response.statusCode}, response body: ${response.body}');
|
||||||
try {
|
}
|
||||||
var url = Uri.http('127.0.0.1:3001', 'get_attachments_info',
|
} catch (e) {
|
||||||
{'folder': folder, 'email_id': email_id});
|
print('error making post req: $e');
|
||||||
print(url);
|
}
|
||||||
var response = await http.get(url);
|
}
|
||||||
print("response $response");
|
|
||||||
if (response.statusCode == 200) {
|
Future<bool> logIn(String json) async {
|
||||||
var result = response.body;
|
// var url = Uri.https('')
|
||||||
List<dynamic> attachmentList = json.decode(result);
|
// try{
|
||||||
print("attachment list $attachmentList");
|
// String response = await http.post(
|
||||||
List<AttachmentInfo> attachments =
|
// url
|
||||||
attachmentList.map((al) => AttachmentInfo.fromJson(al)).toList();
|
// );
|
||||||
print("attachments $attachments");
|
// }
|
||||||
|
|
||||||
return attachments;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
print(e);
|
Future<List<AttachmentInfo>> getAttachmentsInfo(
|
||||||
}
|
String folder, String email_id) async {
|
||||||
return [];
|
try {
|
||||||
}
|
var url = Uri.http('$ip:$port', 'get_attachments_info',
|
||||||
}
|
{'folder': folder, 'id': email_id});
|
||||||
|
// print(url);
|
||||||
class EmailView extends StatefulWidget {
|
var response = await http.get(url);
|
||||||
final String emailContent;
|
// print("response $response");
|
||||||
final String from;
|
if (response.statusCode == 200) {
|
||||||
final String name;
|
var result = response.body;
|
||||||
final String to;
|
// print(result);
|
||||||
final String subject;
|
List<dynamic> attachmentList = json.decode(result);
|
||||||
final String date;
|
// Map<String, dynamic> attachmentList = json.decode(result);
|
||||||
final String id;
|
|
||||||
|
// print("attachment list $attachmentList");
|
||||||
const EmailView({
|
List<AttachmentInfo> attachments =
|
||||||
Key? key,
|
attachmentList.map((al) => AttachmentInfo.fromJson(al)).toList();
|
||||||
required this.emailContent,
|
// print("attachments $attachments");
|
||||||
required this.from,
|
|
||||||
required this.name,
|
return attachments;
|
||||||
required this.to,
|
}
|
||||||
required this.subject,
|
} catch (e) {
|
||||||
required this.date,
|
print(e);
|
||||||
required this.id,
|
}
|
||||||
}) : super(key: key);
|
return [];
|
||||||
@override
|
}
|
||||||
_EmailViewState createState() => _EmailViewState();
|
|
||||||
}
|
Future<AttachmentResponse> getAttachment(
|
||||||
|
String folder, String email_id, String name) async {
|
||||||
class _EmailViewState extends State<EmailView> {
|
try {
|
||||||
late Key iframeKey;
|
var url = Uri.http('$ip:$port', 'get_attachment',
|
||||||
late String currentContent;
|
{'folder': folder, 'id': email_id, 'name': name});
|
||||||
late String viewTypeId;
|
var response = await http.get(url);
|
||||||
// TextEditingController _jumpController = TextEditingController();
|
if (response.statusCode == 200) {
|
||||||
|
var result = response.body;
|
||||||
@override
|
// print(result);
|
||||||
void initState() {
|
Map<String, dynamic> attachmentData = json.decode(result);
|
||||||
super.initState();
|
AttachmentResponse data = AttachmentResponse.fromJson(attachmentData);
|
||||||
String currentContent = widget.emailContent;
|
print("data $data");
|
||||||
viewTypeId = "iframe-${DateTime.now().millisecondsSinceEpoch}";
|
return data;
|
||||||
_registerViewFactory(currentContent);
|
}
|
||||||
}
|
} catch (e) {
|
||||||
|
print("getAttachment failed $e");
|
||||||
void _registerViewFactory(String currentContent) {
|
}
|
||||||
setState(() {
|
return AttachmentResponse(name: "error", data: Uint8List(0));
|
||||||
viewTypeId = 'iframe-${DateTime.now().millisecondsSinceEpoch}';
|
}
|
||||||
ui.platformViewRegistry.registerViewFactory(
|
|
||||||
viewTypeId,
|
Future<List<Map<String, dynamic>>> getMarkerPosition() async {
|
||||||
(int viewId) => html.IFrameElement()
|
//this is so we can put a widget right below each email, but the way how the email content is generated
|
||||||
..width = '100%'
|
//leads to problems as for a) the html is added one right after the other in one iframe, b)
|
||||||
..height = '100%'
|
// if it was multiple iframes then the scrolling to jump would not work as expected
|
||||||
..srcdoc = currentContent
|
|
||||||
..style.border = 'none');
|
|
||||||
});
|
print("marker called");
|
||||||
}
|
// JavaScript code embedded as a string
|
||||||
|
String jsCode = '''
|
||||||
void _scrollToNumber(String spanId) {
|
(async function waitForIframeAndMarkers() {
|
||||||
AugmentClasses.handleJump(spanId);
|
try {
|
||||||
}
|
return await new Promise((resolve) => {
|
||||||
|
const interval = setInterval(() => {
|
||||||
// TODO: void _invisibility(String )
|
console.log("⏳ Checking for iframe...");
|
||||||
|
var iframe = document.getElementsByTagName('iframe')[0];
|
||||||
@override
|
if (iframe && iframe.contentDocument) {
|
||||||
Widget build(BuildContext context) {
|
console.log("✅ Iframe found!");
|
||||||
// print(currentContent);
|
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
|
||||||
return Scaffold(
|
var markers = iframeDoc.querySelectorAll('[id^="JuanBedarramarker"]');
|
||||||
appBar: AppBar(
|
if (markers.length > 0) {
|
||||||
title: Text(widget.name),
|
console.log(`✅ Found markers in the iframe.`);
|
||||||
),
|
var positions = [];
|
||||||
body: Column(
|
markers.forEach((marker) => {
|
||||||
children: [
|
var rect = marker.getBoundingClientRect();
|
||||||
EmailToolbar(
|
positions.push({
|
||||||
onJumpToSpan: _scrollToNumber,
|
id: marker.id,
|
||||||
onButtonPressed: () => {},
|
x: rect.left + window.scrollX,
|
||||||
// AugmentClasses.handleJump(viewTypeId, '1');
|
y: rect.top + window.scrollY,
|
||||||
// print("button got pressed?");
|
});
|
||||||
|
});
|
||||||
// _registerViewFactory(r"""
|
console.log("📌 Marker positions:", positions);
|
||||||
// <h1>Welcome to My Website</h1>
|
clearInterval(interval);
|
||||||
// <p>This is a simple HTML page.</p>
|
resolve(JSON.stringify(positions)); // Ensure proper JSON string
|
||||||
// <h2>What is HTML?</h2>
|
} else {
|
||||||
// <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>
|
console.log("❌ No markers found yet.");
|
||||||
// <h3>Here's a simple list:</h3>
|
}
|
||||||
// <ul>
|
} else {
|
||||||
// <li>HTML elements are the building blocks of HTML pages</li>
|
console.log("❌ Iframe not found or not loaded yet.");
|
||||||
// <li>HTML uses tags like <code><tag></code> to organize and format content</li>
|
}
|
||||||
// <li>CSS is used with HTML to style pages</li>
|
}, 200);
|
||||||
// </ul>
|
});
|
||||||
// <p>Copyright © 2023</p>
|
} catch (error) {
|
||||||
// """);
|
console.error("JS Error:", error);
|
||||||
// print("change");
|
throw error; // Propagate error to Dart
|
||||||
// widget.emailContent = r"
|
}
|
||||||
|
})();
|
||||||
// "
|
''';
|
||||||
// },
|
|
||||||
),
|
try {
|
||||||
Row(
|
// Execute the JavaScript code using eval
|
||||||
// title of email
|
final result = await js.context.callMethod('eval', [jsCode]);
|
||||||
children: [
|
|
||||||
Text(
|
if (result != null && result is String) {
|
||||||
widget.subject,
|
print("Result received: $result");
|
||||||
style: TextStyle(fontSize: 30),
|
|
||||||
),
|
// Parse the JSON string returned by JavaScript into a Dart list of maps
|
||||||
],
|
final List<dynamic> parsedResult = jsonDecode(result);
|
||||||
),
|
var positions = List<Map<String, dynamic>>.from(parsedResult);
|
||||||
Row(
|
print("positions put on");
|
||||||
children: [
|
print(positions);
|
||||||
Text(
|
return positions;
|
||||||
'from ${widget.name}',
|
} else {
|
||||||
style: TextStyle(fontSize: 18),
|
print("result is null or not a string");
|
||||||
),
|
}
|
||||||
Text(
|
} catch (e, stackTrace) {
|
||||||
'<${widget.from}>',
|
print("Error executing JavaScript: $e");
|
||||||
style: TextStyle(fontSize: 18),
|
print(stackTrace);
|
||||||
),
|
}
|
||||||
Spacer(),
|
|
||||||
Text(
|
return [];
|
||||||
'${widget.date}',
|
}
|
||||||
textAlign: TextAlign.right,
|
}
|
||||||
)
|
|
||||||
],
|
class EmailView extends StatefulWidget {
|
||||||
),
|
final String emailContent;
|
||||||
// TODO: make a case where if one of these is the user's email it just says me :)))))
|
final String from;
|
||||||
Row(
|
final String name;
|
||||||
children: [
|
final String to;
|
||||||
Text(
|
final String subject;
|
||||||
'to ${widget.to.toString()}',
|
final String date;
|
||||||
style: TextStyle(fontSize: 15),
|
final String id;
|
||||||
)
|
|
||||||
],
|
const EmailView({
|
||||||
),
|
Key? key,
|
||||||
|
required this.emailContent,
|
||||||
Expanded(
|
required this.from,
|
||||||
child: HtmlElementView(
|
required this.name,
|
||||||
key: UniqueKey(),
|
required this.to,
|
||||||
viewType: viewTypeId,
|
required this.subject,
|
||||||
),
|
required this.date,
|
||||||
),
|
required this.id,
|
||||||
],
|
}) : 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;
|
||||||
|
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},
|
||||||
|
];
|
||||||
|
ApiService _apiService = ApiService();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
String currentContent = widget.emailContent;
|
||||||
|
viewTypeId = "iframe-${DateTime.now().millisecondsSinceEpoch}";
|
||||||
|
_registerViewFactory(currentContent);
|
||||||
|
_markerPositionsFuture = ApiService().getMarkerPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _registerViewFactory(String currentContent) {
|
||||||
|
setState(() {
|
||||||
|
viewTypeId = 'iframe-${DateTime.now().millisecondsSinceEpoch}';
|
||||||
|
ui.platformViewRegistry.registerViewFactory(
|
||||||
|
viewTypeId,
|
||||||
|
(int viewId) => html.IFrameElement()
|
||||||
|
..width = '100%'
|
||||||
|
..height = '100%'
|
||||||
|
..srcdoc = currentContent
|
||||||
|
..style.border = 'none');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _scrollToNumber(String spanId) {
|
||||||
|
AugmentClasses.handleJump(spanId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: void _invisibility(String ) //to make purple numbers not visible
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// print(currentContent);
|
||||||
|
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><tag></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: 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),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
14
lib/attachmentDownload.dart
Normal file
14
lib/attachmentDownload.dart
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import 'dart:html' as html;
|
||||||
|
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)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
103
lib/attachmentWidget.dart
Normal file
103
lib/attachmentWidget.dart
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
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),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
}
|
105
lib/augment.dart
105
lib/augment.dart
@ -1,6 +1,12 @@
|
|||||||
|
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:flutter/material.dart';
|
||||||
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
import 'dart:html' as html;
|
import 'dart:html' as html;
|
||||||
import 'dart:js' as js;
|
import 'dart:js' as js;
|
||||||
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
|
import 'attachmentWidget.dart';
|
||||||
|
|
||||||
class EmailToolbar extends StatefulWidget {
|
class EmailToolbar extends StatefulWidget {
|
||||||
final Function(String) onJumpToSpan;
|
final Function(String) onJumpToSpan;
|
||||||
@ -64,8 +70,8 @@ class _DynamicClassesAugment extends State<EmailToolbar> {
|
|||||||
child: Text('Reload'),
|
child: Text('Reload'),
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: AugmentClasses.handleImages,
|
onPressed: () => AugmentClasses.handleImages(context),
|
||||||
child: Text('Images'),
|
child: Text('Attachments'),
|
||||||
),
|
),
|
||||||
SizedBox(width: 8),
|
SizedBox(width: 8),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
@ -206,6 +212,8 @@ class _DynamicClassesAugment extends State<EmailToolbar> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AugmentClasses {
|
class AugmentClasses {
|
||||||
|
ApiService _apiService = ApiService();
|
||||||
|
static OverlayEntry? _overlayEntry;
|
||||||
static void handleHome(BuildContext context) {
|
static void handleHome(BuildContext context) {
|
||||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||||
}
|
}
|
||||||
@ -214,8 +222,96 @@ class AugmentClasses {
|
|||||||
print("reload");
|
print("reload");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleImages() {
|
static void handleImages(BuildContext context) {
|
||||||
print("Images button pressed");
|
print("Images button pressed");
|
||||||
|
final overlay = Overlay.of(context);
|
||||||
|
final renderBox = context.findRenderObject() as RenderBox;
|
||||||
|
final offset = renderBox.localToGlobal(Offset.zero);
|
||||||
|
|
||||||
|
_overlayEntry = OverlayEntry(
|
||||||
|
builder: (context) => Stack(
|
||||||
|
children: [
|
||||||
|
// Dimmed background
|
||||||
|
Container(
|
||||||
|
color: Colors.black54,
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
height: MediaQuery.of(context).size.height,
|
||||||
|
),
|
||||||
|
// Focused content window
|
||||||
|
PointerInterceptor(
|
||||||
|
child: Center(
|
||||||
|
child: Material(
|
||||||
|
elevation: 8,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
maxWidth: 400,
|
||||||
|
maxHeight: 500,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_buildHeader(context),
|
||||||
|
const Divider(height: 1),
|
||||||
|
Expanded(
|
||||||
|
child: ListView(
|
||||||
|
children: _buildMenuItem(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (_overlayEntry != null) {
|
||||||
|
overlay.insert(_overlayEntry!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add missing widget builder methods
|
||||||
|
static Widget _buildHeader(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.all(16.0),
|
||||||
|
child:
|
||||||
|
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
|
||||||
|
Text(
|
||||||
|
'Thread Attachments',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
CloseButton(
|
||||||
|
onPressed: () {
|
||||||
|
_overlayEntry?.remove();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<Widget> _buildMenuItem(BuildContext context) {
|
||||||
|
List<Widget> listOfFiles = [];
|
||||||
|
for (AttachmentResponse file in ApiService.threadAttachments) {
|
||||||
|
listOfFiles.add(
|
||||||
|
ListTile (
|
||||||
|
leading: Icon(Icons.file_present),
|
||||||
|
title: Text(file.name.toString()),
|
||||||
|
trailing: GestureDetector(
|
||||||
|
child: Icon(Icons.download),
|
||||||
|
onTap: () => Attachmentdownload().saveFile(file),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
_overlayEntry?.remove();
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => AttachmentWidget(attachment: file)));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return listOfFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleOpen() {
|
static void handleOpen() {
|
||||||
@ -485,7 +581,7 @@ class AugmentClasses {
|
|||||||
void handleFilter() {}
|
void handleFilter() {}
|
||||||
static Future<void> FilterButton(context) async {
|
static Future<void> FilterButton(context) async {
|
||||||
//this is literally ctrl+F :skull:
|
//this is literally ctrl+F :skull:
|
||||||
//idea is to search in file, extract the <p> tags that contain these
|
//idea is to search in file, extract the <p> tags that contain these
|
||||||
//words and highlight, then when zoom, you just jump to that paragraph
|
//words and highlight, then when zoom, you just jump to that paragraph
|
||||||
|
|
||||||
AugmentClasses.disableIframePointerEvents();
|
AugmentClasses.disableIframePointerEvents();
|
||||||
@ -517,6 +613,7 @@ class AugmentClasses {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void disableIframePointerEvents() {
|
static void disableIframePointerEvents() {
|
||||||
|
//pretty sure these dont work
|
||||||
final iframes = html.document.getElementsByTagName('iframe');
|
final iframes = html.document.getElementsByTagName('iframe');
|
||||||
for (var iframe in iframes) {
|
for (var iframe in iframes) {
|
||||||
if (iframe is html.Element) {
|
if (iframe is html.Element) {
|
||||||
|
@ -4,10 +4,12 @@ import 'structs.dart';
|
|||||||
|
|
||||||
class EmailListScreen extends StatelessWidget {
|
class EmailListScreen extends StatelessWidget {
|
||||||
final List<GetThreadResponse> emails;
|
final List<GetThreadResponse> emails;
|
||||||
final Future<String> Function(List<String>) getEmailContent;
|
final Future<String> Function(List<String>, String) getEmailContent;
|
||||||
|
final String folder;
|
||||||
|
|
||||||
EmailListScreen({required this.emails, required this.getEmailContent});
|
|
||||||
|
|
||||||
|
EmailListScreen({required this.emails, required this.getEmailContent, required this.folder});
|
||||||
|
//fix the email list
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -24,7 +26,7 @@ class EmailListScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
trailing: Text(email.date.toString()),
|
trailing: Text(email.date.toString()),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
String emailContent = await getEmailContent(email.messages);
|
String emailContent = await getEmailContent(email.messages, folder);
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
@ -51,7 +53,7 @@ class EmailListScreen extends StatelessWidget {
|
|||||||
// ignore: must_be_immutable
|
// ignore: must_be_immutable
|
||||||
class EmailPage extends StatefulWidget {
|
class EmailPage extends StatefulWidget {
|
||||||
EmailPage({Key? key}) : super(key: key);
|
EmailPage({Key? key}) : super(key: key);
|
||||||
String selectedFolder = "INBOX";
|
String selectedFolder = "INBOX"; //starter
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
int page = 1;
|
int page = 1;
|
||||||
|
|
||||||
@ -62,11 +64,14 @@ class EmailPage extends StatefulWidget {
|
|||||||
class EmailPageState extends State<EmailPage> {
|
class EmailPageState extends State<EmailPage> {
|
||||||
final ApiService apiService = ApiService();
|
final ApiService apiService = ApiService();
|
||||||
List<GetThreadResponse> emails = [];
|
List<GetThreadResponse> emails = [];
|
||||||
|
int page = 1;
|
||||||
|
bool isBackDisabled = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
widget.page = widget.page;
|
widget.page = page;
|
||||||
|
isBackDisabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateSelectedFolder(String folder) {
|
void updateSelectedFolder(String folder) {
|
||||||
@ -86,11 +91,16 @@ class EmailPageState extends State<EmailPage> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
widget.offset += 50;
|
widget.offset += 50;
|
||||||
widget.page += 1;
|
widget.page += 1;
|
||||||
|
isBackDisabled = false;
|
||||||
});
|
});
|
||||||
} else if (option == "back") {
|
} else if (option == "back") {
|
||||||
setState(() {
|
setState(() {
|
||||||
widget.offset -= 50;
|
widget.offset -= 50;
|
||||||
widget.page -= 1;
|
widget.page -= 1;
|
||||||
|
if (widget.page == 1) {
|
||||||
|
isBackDisabled = true;
|
||||||
|
print("back dis");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// print(currentPage);
|
// print(currentPage);
|
||||||
@ -119,6 +129,7 @@ class EmailPageState extends State<EmailPage> {
|
|||||||
body: EmailListScreen(
|
body: EmailListScreen(
|
||||||
emails: emails,
|
emails: emails,
|
||||||
getEmailContent: apiService.fetchEmailContent,
|
getEmailContent: apiService.fetchEmailContent,
|
||||||
|
folder: widget.selectedFolder,//try to grab from it directly
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import 'package:crab_ui/folder_drawer.dart';
|
import 'folder_drawer.dart';
|
||||||
import 'package:crab_ui/structs.dart';
|
import 'structs.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.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 'package:shared_preferences/shared_preferences.dart';
|
||||||
// import 'serialize.dart';
|
// import 'serialize.dart';
|
||||||
|
|
||||||
class HomeScreen extends StatefulWidget {
|
class HomeScreen extends StatefulWidget {
|
||||||
@ -119,7 +120,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 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)),
|
||||||
@ -131,7 +132,7 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
|
|||||||
onTap: () async {
|
onTap: () async {
|
||||||
// print('tapped');
|
// print('tapped');
|
||||||
String emailContent =
|
String emailContent =
|
||||||
await apiService.fetchEmailContent([email.id]);
|
await apiService.fetchEmailContent([email.id], email.list);
|
||||||
// print('content below');
|
// print('content below');
|
||||||
// print(emailContent);
|
// print(emailContent);
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
@ -341,7 +342,7 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
|
|||||||
children: [
|
children: [
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_emailPageKey.currentState
|
_emailPageKey.currentState!.isBackDisabled ? null: _emailPageKey.currentState
|
||||||
?.updatePagenation('back');
|
?.updatePagenation('back');
|
||||||
},
|
},
|
||||||
child: Icon(Icons.navigate_before),
|
child: Icon(Icons.navigate_before),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:crab_ui/api_service.dart';
|
// import 'package:crab_ui/api_service.dart';
|
||||||
import 'package:crab_ui/home_page.dart';
|
import 'api_service.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;
|
||||||
@ -11,7 +12,9 @@ class AuthService {
|
|||||||
Future<bool> isUserLoggedIn() async {
|
Future<bool> isUserLoggedIn() async {
|
||||||
try {
|
try {
|
||||||
final response =
|
final response =
|
||||||
await http.get(Uri.parse('http://localhost:6823/read-config'));
|
await http.get(Uri.http('localhost:6823', 'read-config'));
|
||||||
|
// await http.get(Uri.parse('http://localhost:6823/read-config'));
|
||||||
|
|
||||||
print(response.statusCode);
|
print(response.statusCode);
|
||||||
print(response.body);
|
print(response.body);
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
@ -27,7 +30,9 @@ class AuthService {
|
|||||||
Map<String, dynamic> json = jsonDecode(jsonOuter);
|
Map<String, dynamic> json = jsonDecode(jsonOuter);
|
||||||
// print(json["is_logged_in"]);
|
// print(json["is_logged_in"]);
|
||||||
ApiService.ip = data['ip'];
|
ApiService.ip = data['ip'];
|
||||||
|
print("setting ip " + ApiService.ip);
|
||||||
ApiService.port = data['port'];
|
ApiService.port = data['port'];
|
||||||
|
print("setting port " + data['port']);
|
||||||
return json["is_logged_in"];
|
return json["is_logged_in"];
|
||||||
}
|
}
|
||||||
} catch (er) {
|
} catch (er) {
|
||||||
@ -35,6 +40,7 @@ class AuthService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
print('caughtther');
|
||||||
print(e);
|
print(e);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -61,7 +61,7 @@ class _SerializableMessageListScreenState extends State<SerializableMessageListS
|
|||||||
),
|
),
|
||||||
trailing: Text(message.date),
|
trailing: Text(message.date),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
String emailContent = await apiService.fetchEmailContent([message.id]);
|
String emailContent = await apiService.fetchEmailContent([message.id], message.list);
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
//data structures
|
//data structures
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
class GetThreadResponse {
|
class GetThreadResponse {
|
||||||
final int id;
|
final int id;
|
||||||
final List<String> messages;
|
final List<String> messages;
|
||||||
@ -76,7 +78,7 @@ class SerializableMessage {
|
|||||||
required this.subject,
|
required this.subject,
|
||||||
required this.date,
|
required this.date,
|
||||||
required this.uid,
|
required this.uid,
|
||||||
required this.list,
|
required this.list, //email list???
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.in_reply_to,
|
required this.in_reply_to,
|
||||||
});
|
});
|
||||||
@ -118,3 +120,29 @@ class AttachmentInfo {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AttachmentInfoList extends Iterable<AttachmentInfo> {
|
||||||
|
final List<AttachmentInfo> _attachments;
|
||||||
|
|
||||||
|
AttachmentInfoList(this._attachments);
|
||||||
|
|
||||||
|
factory AttachmentInfoList.fromJsonList(List<Map<String, dynamic>> jsonList) {
|
||||||
|
return AttachmentInfoList(jsonList.map((json) => AttachmentInfo.fromJson(json)).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterator<AttachmentInfo> get iterator => _attachments.iterator;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => _attachments.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
class AttachmentResponse {
|
||||||
|
final name;
|
||||||
|
final Uint8List data;
|
||||||
|
AttachmentResponse({required this.name, required this.data});
|
||||||
|
|
||||||
|
factory AttachmentResponse.fromJson(Map<String, dynamic> json) {
|
||||||
|
return AttachmentResponse(name: json["name"], data: Uint8List.fromList(List<int>.from(json["data"])));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
87
pubspec.yaml
87
pubspec.yaml
@ -1,42 +1,47 @@
|
|||||||
name: crab_ui
|
name: crab_ui
|
||||||
description: A new Flutter project.
|
description: A new Flutter project.
|
||||||
|
|
||||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
|
|
||||||
version: 0.0.1+1
|
version: 0.0.1+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.1.1
|
sdk: ^3.1.1
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
http: 1.2.2
|
http: 1.2.2
|
||||||
flutter_html_all: 3.0.0-beta.2
|
flutter_html_all: 3.0.0-beta.2
|
||||||
flutter_widget_from_html: ^0.10.0
|
flutter_widget_from_html: ^0.10.0
|
||||||
shared_preferences: ^2.0.6
|
shared_preferences: ^2.0.6
|
||||||
encrypt: ^5.0.0
|
encrypt: ^5.0.0
|
||||||
pointycastle: ^3.4.0
|
pointycastle: ^3.4.0
|
||||||
mime: ^1.0.3
|
mime: ^1.0.3
|
||||||
|
pointer_interceptor: ^0.10.1+2
|
||||||
english_words: ^4.0.0
|
file_saver: ^0.2.14
|
||||||
provider: ^6.0.0
|
|
||||||
intl: ^0.19.0
|
|
||||||
|
english_words: ^4.0.0
|
||||||
dev_dependencies:
|
provider: ^6.0.0
|
||||||
flutter_test:
|
intl: ^0.19.0
|
||||||
sdk: flutter
|
pdfrx: ^1.0.94
|
||||||
|
photo_view: ^0.15.0
|
||||||
flutter_lints: ^2.0.0
|
|
||||||
|
dev_dependencies:
|
||||||
dependency_overrides:
|
flutter_test:
|
||||||
flutter_layout_grid: 2.0.7
|
sdk: flutter
|
||||||
flutter_math_fork: 0.7.2
|
|
||||||
|
flutter_lints: ^2.0.0
|
||||||
flutter:
|
|
||||||
uses-material-design: true
|
dependency_overrides:
|
||||||
assets:
|
flutter_layout_grid: 2.0.7
|
||||||
- assets/back.png
|
flutter_math_fork: 0.7.2
|
||||||
- assets/communications.png
|
|
||||||
- assets/contact-book.png
|
flutter:
|
||||||
|
uses-material-design: true
|
||||||
|
assets:
|
||||||
|
- assets/back.png
|
||||||
|
- assets/communications.png
|
||||||
|
- assets/contact-book.png
|
||||||
- assets/email.png
|
- assets/email.png
|
Loading…
Reference in New Issue
Block a user