diff --git a/core/lib/data/model/source_type/data_source_type.dart b/core/lib/data/model/source_type/data_source_type.dart index 92e0723c7..90ab33dd9 100644 --- a/core/lib/data/model/source_type/data_source_type.dart +++ b/core/lib/data/model/source_type/data_source_type.dart @@ -2,5 +2,5 @@ enum DataSourceType { network, local, - cache + hiveCache; } \ No newline at end of file diff --git a/core/lib/utils/file_utils.dart b/core/lib/utils/file_utils.dart index 69ab9630b..438b5cf68 100644 --- a/core/lib/utils/file_utils.dart +++ b/core/lib/utils/file_utils.dart @@ -6,13 +6,11 @@ import 'package:path_provider/path_provider.dart'; class FileUtils { - Future _getInternalStorageDirPath( - String nameFile, - { - String? folderPath, - String? extensionFile - } - ) async { + Future _getInternalStorageDirPath({ + required String nameFile, + String? folderPath, + String? extensionFile + }) async { if (!BuildUtils.isWeb) { String fileDirectory = (await getApplicationDocumentsDirectory()).absolute.path; @@ -39,16 +37,14 @@ class FileUtils { } } - Future saveToFile( - String nameFile, - String content, - { - String? folderPath, - String? extensionFile - }) async { - + Future saveToFile({ + required String nameFile, + required String content, + String? folderPath, + String? extensionFile + }) async { final internalStorageDirPath = await _getInternalStorageDirPath( - nameFile, + nameFile: nameFile, folderPath: folderPath, extensionFile: extensionFile ); @@ -59,16 +55,13 @@ class FileUtils { return await file.writeAsString(content, mode: FileMode.append); } - Future getFromFile( - String nameFile, - { - String? folderPath, - String? extensionFile - } - ) async { - + Future getFromFile({ + required String nameFile, + String? folderPath, + String? extensionFile + }) async { final internalStorageDirPath = await _getInternalStorageDirPath( - nameFile, + nameFile: nameFile, folderPath: folderPath, extensionFile: extensionFile ); @@ -80,4 +73,18 @@ class FileUtils { return emailContent; } +} + +enum ExtensionType { + text, + json; + + String get value { + switch(this) { + case ExtensionType.text: + return 'txt'; + case ExtensionType.json: + return 'json'; + } + } } \ No newline at end of file diff --git a/core/test/utils/file_utils_test.dart b/core/test/utils/file_utils_test.dart index 309888696..ca19a5403 100644 --- a/core/test/utils/file_utils_test.dart +++ b/core/test/utils/file_utils_test.dart @@ -16,7 +16,7 @@ void main() { test('Store private HTLM String to File', () async { - final file = await FileUtils().saveToFile(fileName, fileContent); + final file = await FileUtils().saveToFile(nameFile: fileName, content: fileContent); expect(await file.exists(), equals(true)); @@ -28,9 +28,9 @@ void main() { test('Get HTML String from File Private', () async { /// Create a temporary file that will be deleted after `getFromFile` is done - final file = await FileUtils().saveToFile(fileName, fileContent); + final file = await FileUtils().saveToFile(nameFile: fileName, content: fileContent); - final htmlString = await FileUtils().getFromFile(fileName); + final htmlString = await FileUtils().getFromFile(nameFile: fileName); expect(htmlString.isNotEmpty, equals(true)); diff --git a/lib/features/caching/utils/caching_constants.dart b/lib/features/caching/utils/caching_constants.dart index fd7987ffe..76f9182b9 100644 --- a/lib/features/caching/utils/caching_constants.dart +++ b/lib/features/caching/utils/caching_constants.dart @@ -1,4 +1,6 @@ +import 'package:tmail_ui_user/main/utils/app_constants.dart'; + class CachingConstants { static const int MAILBOX_CACHE_IDENTIFY = 1; static const int MAILBOX_RIGHTS_CACHE_IDENTIFY = 2; @@ -22,4 +24,7 @@ class CachingConstants { static const String detailedEmailCacheBoxName = 'detailed_email_cache_box'; static const int maxNumberRecentEmails = 10; + + static const String appFolderName = AppConstants.applicationName; + static const String emailContentFolderName = 'EmailContent'; } \ No newline at end of file diff --git a/lib/features/composer/presentation/composer_bindings.dart b/lib/features/composer/presentation/composer_bindings.dart index 3efe0c891..9ab90086a 100644 --- a/lib/features/composer/presentation/composer_bindings.dart +++ b/lib/features/composer/presentation/composer_bindings.dart @@ -1,4 +1,5 @@ import 'package:core/core.dart'; +import 'package:core/utils/file_utils.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:get/get.dart'; import 'package:tmail_ui_user/features/base/base_bindings.dart'; @@ -21,6 +22,7 @@ import 'package:tmail_ui_user/features/composer/presentation/controller/rich_tex import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart'; import 'package:tmail_ui_user/features/email/data/datasource/html_datasource.dart'; import 'package:tmail_ui_user/features/email/data/datasource_impl/email_datasource_impl.dart'; +import 'package:tmail_ui_user/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart'; import 'package:tmail_ui_user/features/email/data/datasource_impl/html_datasource_impl.dart'; import 'package:tmail_ui_user/features/email/data/local/html_analyzer.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; @@ -42,6 +44,8 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_composer_cache_on_web_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart'; +import 'package:tmail_ui_user/features/offline_mode/manager/detailed_email_cache_manager.dart'; +import 'package:tmail_ui_user/features/offline_mode/manager/detailed_email_cache_worker_queue.dart'; import 'package:tmail_ui_user/features/upload/data/datasource/attachment_upload_datasource.dart'; import 'package:tmail_ui_user/features/upload/data/datasource_impl/attachment_upload_datasource_impl.dart'; import 'package:tmail_ui_user/features/upload/data/network/file_uploader.dart'; @@ -89,6 +93,11 @@ class ComposerBindings extends BaseBindings { Get.find(), Get.find())); Get.lazyPut(() => StateDataSourceImpl(Get.find(), Get.find())); + Get.lazyPut(() => EmailHiveCacheDataSourceImpl( + Get.find(), + Get.find(), + Get.find(), + Get.find())); } @override @@ -116,9 +125,12 @@ class ComposerBindings extends BaseBindings { Get.find(), )); Get.lazyPut(() => EmailRepositoryImpl( - Get.find(), - Get.find(), - Get.find(), + { + DataSourceType.network: Get.find(), + DataSourceType.hiveCache: Get.find() + }, + Get.find(), + Get.find(), )); } diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index f8c2f6f9d..47a1fd416 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -448,22 +448,6 @@ class ComposerController extends BaseController { _autoFocusFieldWhenLauncher(); } - String? getContentEmail(BuildContext context) { - if (composerArguments.value != null) { - switch(composerArguments.value!.emailActionType) { - case EmailActionType.reply: - case EmailActionType.forward: - case EmailActionType.replyAll: - return getEmailContentQuotedAsHtml(context, composerArguments.value!); - case EmailActionType.edit: - return getEmailContentDraftsAsHtml(); - default: - return ''; - } - } - return ''; - } - String? _getHeaderEmailQuoted(BuildContext context, ComposerArguments arguments) { final presentationEmail = arguments.presentationEmail; if (presentationEmail != null) { diff --git a/lib/features/email/data/datasource/email_datasource.dart b/lib/features/email/data/datasource/email_datasource.dart index c6c497856..5863e2847 100644 --- a/lib/features/email/data/datasource/email_datasource.dart +++ b/lib/features/email/data/datasource/email_datasource.dart @@ -15,6 +15,7 @@ import 'package:model/email/attachment.dart'; import 'package:model/email/mark_star_action.dart'; import 'package:model/email/read_actions.dart'; import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'; +import 'package:tmail_ui_user/features/email/domain/model/detailed_email.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; @@ -72,4 +73,6 @@ abstract class EmailDataSource { Future> deleteMultipleEmailsPermanently(Session session, AccountId accountId, List emailIds); Future deleteEmailPermanently(Session session, AccountId accountId, EmailId emailId); + + Future storeDetailedEmailToCache(Session session, AccountId accountId, DetailedEmail detailedEmail); } \ No newline at end of file diff --git a/lib/features/email/data/datasource_impl/email_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_datasource_impl.dart index ced1083cb..cb5adaf62 100644 --- a/lib/features/email/data/datasource_impl/email_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_datasource_impl.dart @@ -13,6 +13,7 @@ import 'package:model/model.dart'; import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'; import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; +import 'package:tmail_ui_user/features/email/domain/model/detailed_email.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart'; @@ -148,4 +149,9 @@ class EmailDataSourceImpl extends EmailDataSource { return await emailAPI.deleteEmailPermanently(session, accountId, emailId); }).catchError(_exceptionThrower.throwException); } + + @override + Future storeDetailedEmailToCache(Session session, AccountId accountId, DetailedEmail detailedEmail) { + throw UnimplementedError(); + } } \ No newline at end of file diff --git a/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart new file mode 100644 index 000000000..aba9fc70c --- /dev/null +++ b/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart @@ -0,0 +1,136 @@ + +import 'dart:async'; + +import 'dart:typed_data'; + +import 'package:core/data/network/download/downloaded_response.dart'; +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:core/utils/app_logger.dart'; +import 'package:core/utils/file_utils.dart'; +import 'package:dartz/dartz.dart'; +import 'package:dio/dio.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:model/account/account_request.dart'; +import 'package:model/download/download_task_id.dart'; +import 'package:model/email/attachment.dart'; +import 'package:model/email/mark_star_action.dart'; +import 'package:model/email/read_actions.dart'; +import 'package:model/extensions/email_id_extensions.dart'; +import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'; +import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart'; +import 'package:tmail_ui_user/features/email/domain/extensions/detailed_email_extension.dart'; +import 'package:tmail_ui_user/features/email/domain/model/detailed_email.dart'; +import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; +import 'package:tmail_ui_user/features/offline_mode/manager/detailed_email_cache_manager.dart'; +import 'package:tmail_ui_user/features/offline_mode/manager/detailed_email_cache_worker_queue.dart'; +import 'package:tmail_ui_user/features/offline_mode/worker/hive_task.dart'; +import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart'; + +class EmailHiveCacheDataSourceImpl extends EmailDataSource { + + final DetailedEmailCacheManager _emailCacheManager; + final DetailedEmailCacheWorkerQueue _cacheWorkerQueue; + final FileUtils _fileUtils; + final ExceptionThrower _exceptionThrower; + + EmailHiveCacheDataSourceImpl( + this._emailCacheManager, + this._cacheWorkerQueue, + this._fileUtils, + this._exceptionThrower + ); + + @override + Future deleteEmailPermanently(Session session, AccountId accountId, EmailId emailId) { + throw UnimplementedError(); + } + + @override + Future> deleteMultipleEmailsPermanently(Session session, AccountId accountId, List emailIds) { + throw UnimplementedError(); + } + + @override + Future downloadAttachmentForWeb(DownloadTaskId taskId, Attachment attachment, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest, StreamController> onReceiveController) { + throw UnimplementedError(); + } + + @override + Future> downloadAttachments(List attachments, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest) { + throw UnimplementedError(); + } + + @override + Future exportAttachment(Attachment attachment, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest, CancelToken cancelToken) { + throw UnimplementedError(); + } + + @override + Future getEmailContent(Session session, AccountId accountId, EmailId emailId) { + throw UnimplementedError(); + } + + @override + Future> markAsRead(Session session, AccountId accountId, List emails, ReadActions readActions) { + throw UnimplementedError(); + } + + @override + Future> markAsStar(Session session, AccountId accountId, List emails, MarkStarAction markStarAction) { + throw UnimplementedError(); + } + + @override + Future> moveToMailbox(Session session, AccountId accountId, MoveToMailboxRequest moveRequest) { + throw UnimplementedError(); + } + + @override + Future removeEmailDrafts(Session session, AccountId accountId, EmailId emailId) { + throw UnimplementedError(); + } + + @override + Future saveEmailAsDrafts(Session session, AccountId accountId, Email email) { + throw UnimplementedError(); + } + + @override + Future sendEmail(Session session, AccountId accountId, EmailRequest emailRequest, {CreateNewMailboxRequest? mailboxRequest}) { + throw UnimplementedError(); + } + + @override + Future storeDetailedEmailToCache(Session session, AccountId accountId, DetailedEmail detailedEmail) { + return Future.sync(() async { + final task = HiveTask( + runnable: () async { + final fileSaved = await _fileUtils.saveToFile( + nameFile: detailedEmail.emailId.asString, + content: detailedEmail.htmlEmailContent ?? '', + folderPath: detailedEmail.folderPath, + extensionFile: ExtensionType.text.value + ); + log('EmailHiveCacheDataSourceImpl::storeDetailedEmailToCache():fileSavedPath: ${fileSaved.path}'); + final detailedEmailSaved = detailedEmail.fromEmailContentPath(fileSaved.path); + + final detailedEmailCache = await _emailCacheManager.handleStoreDetailedEmailToCache( + accountId, + session.username, + detailedEmailSaved.toHiveCache()); + + return detailedEmailCache; + }); + return await _cacheWorkerQueue.addTask(task); + }).catchError(_exceptionThrower.throwException); + } + + @override + Future updateEmailDrafts(Session session, AccountId accountId, Email newEmail) { + throw UnimplementedError(); + } +} \ No newline at end of file diff --git a/lib/features/email/data/repository/email_repository_impl.dart b/lib/features/email/data/repository/email_repository_impl.dart index f184c5df0..5d69f73dd 100644 --- a/lib/features/email/data/repository/email_repository_impl.dart +++ b/lib/features/email/data/repository/email_repository_impl.dart @@ -2,17 +2,26 @@ import 'dart:async'; import 'dart:typed_data'; -import 'package:core/core.dart'; +import 'package:core/data/model/source_type/data_source_type.dart'; +import 'package:core/data/network/download/downloaded_response.dart'; +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; import 'package:dartz/dartz.dart'; import 'package:dio/dio.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; -import 'package:model/model.dart'; +import 'package:model/account/account_request.dart'; +import 'package:model/download/download_task_id.dart'; +import 'package:model/email/attachment.dart'; +import 'package:model/email/email_content.dart'; +import 'package:model/email/mark_star_action.dart'; +import 'package:model/email/read_actions.dart'; import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'; import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart'; import 'package:tmail_ui_user/features/email/data/datasource/html_datasource.dart'; +import 'package:tmail_ui_user/features/email/domain/model/detailed_email.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart'; import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart'; @@ -21,7 +30,7 @@ import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_r class EmailRepositoryImpl extends EmailRepository { - final EmailDataSource emailDataSource; + final Map emailDataSource; final HtmlDataSource _htmlDataSource; final StateDataSource _stateDataSource; @@ -33,7 +42,7 @@ class EmailRepositoryImpl extends EmailRepository { @override Future getEmailContent(Session session, AccountId accountId, EmailId emailId) { - return emailDataSource.getEmailContent(session ,accountId, emailId); + return emailDataSource[DataSourceType.network]!.getEmailContent(session ,accountId, emailId); } @override @@ -43,7 +52,7 @@ class EmailRepositoryImpl extends EmailRepository { EmailRequest emailRequest, {CreateNewMailboxRequest? mailboxRequest} ) { - return emailDataSource.sendEmail(session, accountId, emailRequest, mailboxRequest: mailboxRequest); + return emailDataSource[DataSourceType.network]!.sendEmail(session, accountId, emailRequest, mailboxRequest: mailboxRequest); } @override @@ -53,7 +62,7 @@ class EmailRepositoryImpl extends EmailRepository { List emails, ReadActions readActions ) { - return emailDataSource.markAsRead(session, accountId, emails, readActions); + return emailDataSource[DataSourceType.network]!.markAsRead(session, accountId, emails, readActions); } @override @@ -63,7 +72,7 @@ class EmailRepositoryImpl extends EmailRepository { String baseDownloadUrl, AccountRequest accountRequest ) { - return emailDataSource.downloadAttachments(attachments, accountId, baseDownloadUrl, accountRequest); + return emailDataSource[DataSourceType.network]!.downloadAttachments(attachments, accountId, baseDownloadUrl, accountRequest); } @override @@ -74,7 +83,7 @@ class EmailRepositoryImpl extends EmailRepository { AccountRequest accountRequest, CancelToken cancelToken ) { - return emailDataSource.exportAttachment( + return emailDataSource[DataSourceType.network]!.exportAttachment( attachment, accountId, baseDownloadUrl, @@ -84,7 +93,7 @@ class EmailRepositoryImpl extends EmailRepository { @override Future> moveToMailbox(Session session, AccountId accountId, MoveToMailboxRequest moveRequest) { - return emailDataSource.moveToMailbox(session, accountId, moveRequest); + return emailDataSource[DataSourceType.network]!.moveToMailbox(session, accountId, moveRequest); } @override @@ -94,7 +103,7 @@ class EmailRepositoryImpl extends EmailRepository { List emails, MarkStarAction markStarAction ) { - return emailDataSource.markAsStar(session, accountId, emails, markStarAction); + return emailDataSource[DataSourceType.network]!.markAsStar(session, accountId, emails, markStarAction); } @override @@ -122,17 +131,17 @@ class EmailRepositoryImpl extends EmailRepository { @override Future saveEmailAsDrafts(Session session, AccountId accountId, Email email) { - return emailDataSource.saveEmailAsDrafts(session, accountId, email); + return emailDataSource[DataSourceType.network]!.saveEmailAsDrafts(session, accountId, email); } @override Future removeEmailDrafts(Session session, AccountId accountId, EmailId emailId) { - return emailDataSource.removeEmailDrafts(session, accountId, emailId); + return emailDataSource[DataSourceType.network]!.removeEmailDrafts(session, accountId, emailId); } @override Future updateEmailDrafts(Session session, AccountId accountId, Email newEmail) { - return emailDataSource.updateEmailDrafts(session, accountId, newEmail); + return emailDataSource[DataSourceType.network]!.updateEmailDrafts(session, accountId, newEmail); } @override @@ -144,7 +153,7 @@ class EmailRepositoryImpl extends EmailRepository { AccountRequest accountRequest, StreamController> onReceiveController ) { - return emailDataSource.downloadAttachmentForWeb( + return emailDataSource[DataSourceType.network]!.downloadAttachmentForWeb( taskId, attachment, accountId, @@ -155,12 +164,12 @@ class EmailRepositoryImpl extends EmailRepository { @override Future> deleteMultipleEmailsPermanently(Session session, AccountId accountId, List emailIds) { - return emailDataSource.deleteMultipleEmailsPermanently(session, accountId, emailIds); + return emailDataSource[DataSourceType.network]!.deleteMultipleEmailsPermanently(session, accountId, emailIds); } @override Future deleteEmailPermanently(Session session, AccountId accountId, EmailId emailId) { - return emailDataSource.deleteEmailPermanently(session, accountId, emailId); + return emailDataSource[DataSourceType.network]!.deleteEmailPermanently(session, accountId, emailId); } @override @@ -174,4 +183,9 @@ class EmailRepositoryImpl extends EmailRepository { Future getEmailState(Session session, AccountId accountId) { return _stateDataSource.getState(accountId, session.username, StateType.email); } + + @override + Future storeDetailedEmailToCache(Session session, AccountId accountId, DetailedEmail detailedEmail) { + return emailDataSource[DataSourceType.hiveCache]!.storeDetailedEmailToCache(session, accountId, detailedEmail); + } } \ No newline at end of file diff --git a/lib/features/email/domain/extensions/attachment_extension.dart b/lib/features/email/domain/extensions/attachment_extension.dart new file mode 100644 index 000000000..76a87755e --- /dev/null +++ b/lib/features/email/domain/extensions/attachment_extension.dart @@ -0,0 +1,17 @@ + +import 'package:model/email/attachment.dart'; +import 'package:tmail_ui_user/features/offline_mode/model/attachment_hive_cache.dart'; + +extension AttachmentExtension on Attachment { + AttachmentHiveCache toHiveCache() { + return AttachmentHiveCache( + partId: partId?.value, + blobId: blobId?.value, + size: size?.value.toInt(), + name: name, + type: type?.mimeType, + cid: cid, + disposition: disposition?.name + ); + } +} \ No newline at end of file diff --git a/lib/features/email/domain/extensions/detailed_email_extension.dart b/lib/features/email/domain/extensions/detailed_email_extension.dart new file mode 100644 index 000000000..c0c5d16a8 --- /dev/null +++ b/lib/features/email/domain/extensions/detailed_email_extension.dart @@ -0,0 +1,31 @@ + +import 'package:model/extensions/email_id_extensions.dart'; +import 'package:tmail_ui_user/features/caching/utils/caching_constants.dart'; +import 'package:tmail_ui_user/features/email/domain/extensions/list_attachments_extension.dart'; +import 'package:tmail_ui_user/features/email/domain/extensions/list_email_header_extension.dart'; +import 'package:tmail_ui_user/features/email/domain/model/detailed_email.dart'; +import 'package:tmail_ui_user/features/offline_mode/model/detailed_email_hive_cache.dart'; + +extension DetailedEmailExtension on DetailedEmail { + DetailedEmailHiveCache toHiveCache() { + return DetailedEmailHiveCache( + emailId: emailId.asString, + timeSaved: DateTime.now(), + attachments: attachments?.toHiveCache(), + headers: headers?.toHiveCache(), + emailContentPath: emailContentPath + ); + } + + String get folderPath => '${CachingConstants.appFolderName}/${CachingConstants.emailContentFolderName}'; + + DetailedEmail fromEmailContentPath(String path) { + return DetailedEmail( + emailId: emailId, + attachments: attachments, + headers: headers, + htmlEmailContent: htmlEmailContent, + emailContentPath: path, + ); + } +} \ No newline at end of file diff --git a/lib/features/email/domain/extensions/email_header_extension.dart b/lib/features/email/domain/extensions/email_header_extension.dart new file mode 100644 index 000000000..77e049374 --- /dev/null +++ b/lib/features/email/domain/extensions/email_header_extension.dart @@ -0,0 +1,7 @@ + +import 'package:jmap_dart_client/jmap/mail/email/email_header.dart'; +import 'package:tmail_ui_user/features/offline_mode/model/email_header_hive_cache.dart'; + +extension EmailHeaderExtension on EmailHeader { + EmailHeaderHiveCache toHiveCache() => EmailHeaderHiveCache(name: name, value: value); +} \ No newline at end of file diff --git a/lib/features/email/domain/extensions/list_attachments_extension.dart b/lib/features/email/domain/extensions/list_attachments_extension.dart new file mode 100644 index 000000000..1cbfc601a --- /dev/null +++ b/lib/features/email/domain/extensions/list_attachments_extension.dart @@ -0,0 +1,8 @@ + +import 'package:model/email/attachment.dart'; +import 'package:tmail_ui_user/features/email/domain/extensions/attachment_extension.dart'; +import 'package:tmail_ui_user/features/offline_mode/model/attachment_hive_cache.dart'; + +extension ListAttachmentsExtension on List { + List toHiveCache() => map((attachment) => attachment.toHiveCache()).toList(); +} \ No newline at end of file diff --git a/lib/features/email/domain/extensions/list_email_header_extension.dart b/lib/features/email/domain/extensions/list_email_header_extension.dart new file mode 100644 index 000000000..f1cd02775 --- /dev/null +++ b/lib/features/email/domain/extensions/list_email_header_extension.dart @@ -0,0 +1,8 @@ + +import 'package:jmap_dart_client/jmap/mail/email/email_header.dart'; +import 'package:tmail_ui_user/features/email/domain/extensions/email_header_extension.dart'; +import 'package:tmail_ui_user/features/offline_mode/model/email_header_hive_cache.dart'; + +extension ListEmailHeaderExtension on List { + List toHiveCache() => map((emailHeader) => emailHeader.toHiveCache()).toList(); +} \ No newline at end of file diff --git a/lib/features/email/domain/model/detailed_email.dart b/lib/features/email/domain/model/detailed_email.dart new file mode 100644 index 000000000..a9b822def --- /dev/null +++ b/lib/features/email/domain/model/detailed_email.dart @@ -0,0 +1,29 @@ +import 'package:equatable/equatable.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_header.dart'; +import 'package:model/email/attachment.dart'; + +class DetailedEmail with EquatableMixin { + final EmailId emailId; + final List? attachments; + final List? headers; + final String? htmlEmailContent; + final String? emailContentPath; + + DetailedEmail({ + required this.emailId, + this.attachments, + this.headers, + this.htmlEmailContent, + this.emailContentPath + }); + + @override + List get props => [ + emailId, + attachments, + headers, + htmlEmailContent, + emailContentPath + ]; +} \ No newline at end of file diff --git a/lib/features/email/domain/repository/email_repository.dart b/lib/features/email/domain/repository/email_repository.dart index a056cacb0..2d9bacab9 100644 --- a/lib/features/email/domain/repository/email_repository.dart +++ b/lib/features/email/domain/repository/email_repository.dart @@ -17,6 +17,7 @@ import 'package:model/email/email_content.dart'; import 'package:model/email/mark_star_action.dart'; import 'package:model/email/read_actions.dart'; import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'; +import 'package:tmail_ui_user/features/email/domain/model/detailed_email.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; @@ -86,4 +87,6 @@ abstract class EmailRepository { Future deleteEmailPermanently(Session session, AccountId accountId, EmailId emailId); Future getEmailState(Session session, AccountId accountId); + + Future storeDetailedEmailToCache(Session session, AccountId accountId, DetailedEmail detailedEmail); } \ No newline at end of file diff --git a/lib/features/email/domain/state/store_detailed_email_to_cache_state.dart b/lib/features/email/domain/state/store_detailed_email_to_cache_state.dart new file mode 100644 index 000000000..008abb722 --- /dev/null +++ b/lib/features/email/domain/state/store_detailed_email_to_cache_state.dart @@ -0,0 +1,13 @@ +import 'package:core/core.dart'; + +class StoreDetailedEmailToCacheLoading extends UIState {} + +class StoreDetailedEmailToCacheSuccess extends UIState {} + +class StoreDetailedEmailToCacheFailure extends FeatureFailure { + + StoreDetailedEmailToCacheFailure(dynamic exception) : super(exception: exception); + + @override + List get props => [exception]; +} \ No newline at end of file diff --git a/lib/features/email/domain/usecases/store_detailed_email_to_cache_interator.dart b/lib/features/email/domain/usecases/store_detailed_email_to_cache_interator.dart new file mode 100644 index 000000000..10c570d5b --- /dev/null +++ b/lib/features/email/domain/usecases/store_detailed_email_to_cache_interator.dart @@ -0,0 +1,24 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:tmail_ui_user/features/email/domain/model/detailed_email.dart'; +import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; +import 'package:tmail_ui_user/features/email/domain/state/store_detailed_email_to_cache_state.dart'; + +class StoreDetailedEmailToCacheInteractor { + final EmailRepository _emailRepository; + + StoreDetailedEmailToCacheInteractor(this._emailRepository); + + Stream> execute(Session session, AccountId accountId, DetailedEmail detailedEmail) async* { + try { + yield Right(StoreDetailedEmailToCacheLoading()); + await _emailRepository.storeDetailedEmailToCache(session, accountId, detailedEmail); + yield Right(StoreDetailedEmailToCacheSuccess()); + } catch (e) { + yield Left(StoreDetailedEmailToCacheFailure(e)); + } + } +} \ No newline at end of file diff --git a/lib/features/email/presentation/email_bindings.dart b/lib/features/email/presentation/email_bindings.dart index 81d03912e..7108dd775 100644 --- a/lib/features/email/presentation/email_bindings.dart +++ b/lib/features/email/presentation/email_bindings.dart @@ -1,10 +1,12 @@ import 'package:core/core.dart'; +import 'package:core/utils/file_utils.dart'; import 'package:get/get.dart'; import 'package:tmail_ui_user/features/base/base_bindings.dart'; import 'package:tmail_ui_user/features/caching/clients/state_cache_client.dart'; import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart'; import 'package:tmail_ui_user/features/email/data/datasource/html_datasource.dart'; import 'package:tmail_ui_user/features/email/data/datasource_impl/email_datasource_impl.dart'; +import 'package:tmail_ui_user/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart'; import 'package:tmail_ui_user/features/email/data/datasource_impl/html_datasource_impl.dart'; import 'package:tmail_ui_user/features/email/data/local/html_analyzer.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; @@ -40,6 +42,8 @@ import 'package:tmail_ui_user/features/mailbox/data/repository/mailbox_repositor import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identity_interactors_bindings.dart'; +import 'package:tmail_ui_user/features/offline_mode/manager/detailed_email_cache_manager.dart'; +import 'package:tmail_ui_user/features/offline_mode/manager/detailed_email_cache_worker_queue.dart'; import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart'; import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart'; @@ -90,6 +94,11 @@ class EmailBindings extends BaseBindings { Get.find(), Get.find())); Get.lazyPut(() => StateDataSourceImpl(Get.find(), Get.find())); + Get.lazyPut(() => EmailHiveCacheDataSourceImpl( + Get.find(), + Get.find(), + Get.find(), + Get.find())); } @override @@ -141,9 +150,12 @@ class EmailBindings extends BaseBindings { Get.find(), )); Get.lazyPut(() => EmailRepositoryImpl( - Get.find(), - Get.find(), - Get.find() + { + DataSourceType.network: Get.find(), + DataSourceType.hiveCache: Get.find() + }, + Get.find(), + Get.find() )); Get.lazyPut(() => AccountRepositoryImpl(Get.find())); } diff --git a/lib/features/mailbox_dashboard/data/repository/spam_report_repository_impl.dart b/lib/features/mailbox_dashboard/data/repository/spam_report_repository_impl.dart index 08a2fdebc..d1732e7e4 100644 --- a/lib/features/mailbox_dashboard/data/repository/spam_report_repository_impl.dart +++ b/lib/features/mailbox_dashboard/data/repository/spam_report_repository_impl.dart @@ -63,6 +63,6 @@ class SpamReportRepositoryImpl extends SpamReportRepository { @override Future getSpamMailboxCached(AccountId accountId, UserName userName) { - return mapDataSource[DataSourceType.cache]!.getSpamMailboxCached(accountId, userName); + return mapDataSource[DataSourceType.hiveCache]!.getSpamMailboxCached(accountId, userName); } } \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart b/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart index 722477755..4664f4ec0 100644 --- a/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart +++ b/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart @@ -1,6 +1,7 @@ import 'package:core/data/model/source_type/data_source_type.dart'; import 'package:core/data/network/dio_client.dart'; import 'package:core/utils/config/app_config_loader.dart'; +import 'package:core/utils/file_utils.dart'; import 'package:get/get.dart'; import 'package:tmail_ui_user/features/base/base_bindings.dart'; import 'package:tmail_ui_user/features/caching/clients/recent_search_cache_client.dart'; @@ -11,6 +12,7 @@ import 'package:tmail_ui_user/features/composer/domain/usecases/send_email_inter import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart'; import 'package:tmail_ui_user/features/email/data/datasource/html_datasource.dart'; import 'package:tmail_ui_user/features/email/data/datasource_impl/email_datasource_impl.dart'; +import 'package:tmail_ui_user/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart'; import 'package:tmail_ui_user/features/email/data/datasource_impl/html_datasource_impl.dart'; import 'package:tmail_ui_user/features/email/data/local/html_analyzer.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; @@ -92,6 +94,8 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart'; +import 'package:tmail_ui_user/features/offline_mode/manager/detailed_email_cache_manager.dart'; +import 'package:tmail_ui_user/features/offline_mode/manager/detailed_email_cache_worker_queue.dart'; import 'package:tmail_ui_user/features/quotas/presentation/quotas_controller_bindings.dart'; import 'package:tmail_ui_user/features/search/email/domain/usecases/refresh_changes_search_email_interactor.dart'; import 'package:tmail_ui_user/features/search/email/presentation/search_email_bindings.dart'; @@ -227,6 +231,11 @@ class MailboxDashBoardBindings extends BaseBindings { Get.lazyPut(() => SpamReportCacheDataSourceImpl( Get.find(), Get.find())); + Get.lazyPut(() => EmailHiveCacheDataSourceImpl( + Get.find(), + Get.find(), + Get.find(), + Get.find())); } @override @@ -322,9 +331,12 @@ class MailboxDashBoardBindings extends BaseBindings { @override void bindingsRepositoryImpl() { Get.lazyPut(() => EmailRepositoryImpl( - Get.find(), - Get.find(), - Get.find(), + { + DataSourceType.network: Get.find(), + DataSourceType.hiveCache: Get.find() + }, + Get.find(), + Get.find(), )); Get.lazyPut(() => SearchRepositoryImpl(Get.find())); Get.lazyPut(() => ThreadRepositoryImpl( @@ -348,7 +360,7 @@ class MailboxDashBoardBindings extends BaseBindings { { DataSourceType.network: Get.find(), DataSourceType.local: Get.find(), - DataSourceType.cache: Get.find() + DataSourceType.hiveCache: Get.find() }, )); } diff --git a/lib/features/offline_mode/manager/detailed_email_cache_manager.dart b/lib/features/offline_mode/manager/detailed_email_cache_manager.dart index 2e5ddc7c0..1a4eae5a7 100644 --- a/lib/features/offline_mode/manager/detailed_email_cache_manager.dart +++ b/lib/features/offline_mode/manager/detailed_email_cache_manager.dart @@ -15,7 +15,7 @@ class DetailedEmailCacheManager { DetailedEmailCacheManager(this._cacheClient); - Future handleStoreDetailedEmailToCache( + Future handleStoreDetailedEmailToCache( AccountId accountId, UserName userName, DetailedEmailHiveCache detailedEmailCache @@ -27,8 +27,9 @@ class DetailedEmailCacheManager { log('DetailedEmailCacheManager::handleStoreDetailedEmailToCache():latestEmail: $latestEmail'); await removeDetailedEmail(accountId, userName, latestEmail.emailId); } + await insertDetailedEmail(accountId, userName, detailedEmailCache); - return insertDetailedEmail(accountId, userName, detailedEmailCache); + return detailedEmailCache; } Future insertDetailedEmail( diff --git a/lib/features/offline_mode/manager/detailed_email_cache_worker_queue.dart b/lib/features/offline_mode/manager/detailed_email_cache_worker_queue.dart new file mode 100644 index 000000000..06c7ac4a4 --- /dev/null +++ b/lib/features/offline_mode/manager/detailed_email_cache_worker_queue.dart @@ -0,0 +1,8 @@ + +import 'package:tmail_ui_user/features/offline_mode/worker/hive_worker_queue.dart'; + +class DetailedEmailCacheWorkerQueue extends WorkerQueue { + + @override + String get workerName => 'DetailedEmailCache'; +} \ No newline at end of file diff --git a/lib/features/push_notification/presentation/bindings/fcm_interactor_bindings.dart b/lib/features/push_notification/presentation/bindings/fcm_interactor_bindings.dart index 3b9f95a08..d5a1d0df0 100644 --- a/lib/features/push_notification/presentation/bindings/fcm_interactor_bindings.dart +++ b/lib/features/push_notification/presentation/bindings/fcm_interactor_bindings.dart @@ -1,11 +1,13 @@ import 'package:core/data/model/source_type/data_source_type.dart'; import 'package:core/data/network/dio_client.dart'; +import 'package:core/utils/file_utils.dart'; import 'package:get/get.dart'; import 'package:tmail_ui_user/features/base/interactors_bindings.dart'; import 'package:tmail_ui_user/features/caching/clients/state_cache_client.dart'; import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart'; import 'package:tmail_ui_user/features/email/data/datasource/html_datasource.dart'; import 'package:tmail_ui_user/features/email/data/datasource_impl/email_datasource_impl.dart'; +import 'package:tmail_ui_user/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart'; import 'package:tmail_ui_user/features/email/data/datasource_impl/html_datasource_impl.dart'; import 'package:tmail_ui_user/features/email/data/local/html_analyzer.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; @@ -19,6 +21,8 @@ import 'package:tmail_ui_user/features/mailbox/data/datasource_impl/state_dataso import 'package:tmail_ui_user/features/mailbox/data/local/mailbox_cache_manager.dart'; import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_api.dart'; import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_isolate_worker.dart'; +import 'package:tmail_ui_user/features/offline_mode/manager/detailed_email_cache_manager.dart'; +import 'package:tmail_ui_user/features/offline_mode/manager/detailed_email_cache_worker_queue.dart'; import 'package:tmail_ui_user/features/push_notification/data/datasource/fcm_datasource.dart'; import 'package:tmail_ui_user/features/push_notification/data/datasource_impl/fcm_datasource_impl.dart'; import 'package:tmail_ui_user/features/push_notification/data/datasource_impl/cache_fcm_datasource_impl.dart'; @@ -92,6 +96,11 @@ class FcmInteractorBindings extends InteractorsBindings { Get.lazyPut(() => StateDataSourceImpl( Get.find(), Get.find())); + Get.lazyPut(() => EmailHiveCacheDataSourceImpl( + Get.find(), + Get.find(), + Get.find(), + Get.find())); } @override @@ -135,7 +144,10 @@ class FcmInteractorBindings extends InteractorsBindings { }, )); Get.lazyPut(() => EmailRepositoryImpl( - Get.find(), + { + DataSourceType.network: Get.find(), + DataSourceType.hiveCache: Get.find() + }, Get.find(), Get.find())); } diff --git a/lib/main/bindings/core/core_bindings.dart b/lib/main/bindings/core/core_bindings.dart index 97f7bed52..77a61cdfb 100644 --- a/lib/main/bindings/core/core_bindings.dart +++ b/lib/main/bindings/core/core_bindings.dart @@ -4,6 +4,7 @@ import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/utils/app_toast.dart'; import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:core/utils/config/app_config_loader.dart'; +import 'package:core/utils/file_utils.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -58,5 +59,6 @@ class CoreBindings extends Bindings { Get.put(const Uuid()); Get.put(CompressFileUtils()); Get.put(AppConfigLoader()); + Get.put(FileUtils()); } } \ No newline at end of file diff --git a/lib/main/bindings/local/local_bindings.dart b/lib/main/bindings/local/local_bindings.dart index 81e487514..40a920d8b 100644 --- a/lib/main/bindings/local/local_bindings.dart +++ b/lib/main/bindings/local/local_bindings.dart @@ -4,6 +4,7 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:tmail_ui_user/features/caching/clients/account_cache_client.dart'; import 'package:tmail_ui_user/features/caching/clients/authentication_info_cache_client.dart'; import 'package:tmail_ui_user/features/caching/caching_manager.dart'; +import 'package:tmail_ui_user/features/caching/clients/detailed_email_hive_cache_client.dart'; import 'package:tmail_ui_user/features/caching/clients/email_cache_client.dart'; import 'package:tmail_ui_user/features/caching/clients/encryption_key_cache_client.dart'; import 'package:tmail_ui_user/features/caching/clients/fcm_cache_client.dart'; @@ -26,6 +27,8 @@ import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager import 'package:tmail_ui_user/features/mailbox/data/local/mailbox_cache_manager.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/local/share_preference_spam_report_data_source.dart'; import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart'; +import 'package:tmail_ui_user/features/offline_mode/manager/detailed_email_cache_manager.dart'; +import 'package:tmail_ui_user/features/offline_mode/manager/detailed_email_cache_worker_queue.dart'; import 'package:tmail_ui_user/features/push_notification/data/local/fcm_cache_manager.dart'; import 'package:tmail_ui_user/features/thread/data/local/email_cache_manager.dart'; import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart'; @@ -36,6 +39,7 @@ class LocalBindings extends Bindings { void dependencies() { _bindingException(); _bindingCaching(); + _bindingWorkerQueue(); } void _bindingCaching() { @@ -64,6 +68,8 @@ class LocalBindings extends Bindings { Get.put(FcmCacheClient()); Get.put(FCMCacheManager(Get.find(),Get.find())); Get.put(HiveCacheVersionClient(Get.find(), Get.find())); + Get.put(DetailedEmailHiveCacheClient()); + Get.put(DetailedEmailCacheManager(Get.find())); Get.put(CachingManager( Get.find(), Get.find(), @@ -80,4 +86,8 @@ class LocalBindings extends Bindings { void _bindingException() { Get.put(CacheExceptionThrower()); } + + void _bindingWorkerQueue() { + Get.put(DetailedEmailCacheWorkerQueue()); + } } \ No newline at end of file diff --git a/lib/main/utils/app_constants.dart b/lib/main/utils/app_constants.dart index c4b8fc78a..3be1be58b 100644 --- a/lib/main/utils/app_constants.dart +++ b/lib/main/utils/app_constants.dart @@ -1,4 +1,5 @@ class AppConstants { static const int limitCharToStartSearch = 3; + static const String applicationName = 'TeamMail'; } \ No newline at end of file