diff --git a/lib/features/composer/data/repository/composer_repository_impl.dart b/lib/features/composer/data/repository/composer_repository_impl.dart index 2f502bd40c..295fa32c00 100644 --- a/lib/features/composer/data/repository/composer_repository_impl.dart +++ b/lib/features/composer/data/repository/composer_repository_impl.dart @@ -51,7 +51,8 @@ class ComposerRepositoryImpl extends ComposerRepository { if (createEmailRequest.inlineAttachments?.isNotEmpty == true) { final tupleContentInlineAttachments = await _replaceImageBase64ToImageCID( emailContent: emailContent, - inlineAttachments: createEmailRequest.inlineAttachments! + inlineAttachments: createEmailRequest.inlineAttachments!, + isCaching: createEmailRequest.isCaching ); emailContent = tupleContentInlineAttachments.value1; @@ -77,12 +78,14 @@ class ComposerRepositoryImpl extends ComposerRepository { Future>> _replaceImageBase64ToImageCID({ required String emailContent, - required Map inlineAttachments + required Map inlineAttachments, + bool isCaching = false }) { try { return _htmlDataSource.replaceImageBase64ToImageCID( emailContent: emailContent, - inlineAttachments: inlineAttachments); + inlineAttachments: inlineAttachments, + isCaching: isCaching); } catch (e) { logError('ComposerRepositoryImpl::_replaceImageBase64ToImageCID: Exception: $e'); return Future.value( diff --git a/lib/features/composer/domain/usecases/restore_email_inline_images_interactor.dart b/lib/features/composer/domain/usecases/convert_image_cid_to_base64_interactor.dart similarity index 83% rename from lib/features/composer/domain/usecases/restore_email_inline_images_interactor.dart rename to lib/features/composer/domain/usecases/convert_image_cid_to_base64_interactor.dart index 0d10be8191..75e541625d 100644 --- a/lib/features/composer/domain/usecases/restore_email_inline_images_interactor.dart +++ b/lib/features/composer/domain/usecases/convert_image_cid_to_base64_interactor.dart @@ -5,8 +5,8 @@ import 'package:dartz/dartz.dart'; import 'package:tmail_ui_user/features/composer/domain/state/restore_email_inline_images_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/composer_cache_repository.dart'; -class RestoreEmailInlineImagesInteractor { - RestoreEmailInlineImagesInteractor(this._composerCacheRepository); +class ConvertImageCIDToBase46Interactor { + ConvertImageCIDToBase46Interactor(this._composerCacheRepository); final ComposerCacheRepository _composerCacheRepository; @@ -17,8 +17,7 @@ class RestoreEmailInlineImagesInteractor { }) async* { try { yield Right(RestoringEmailInlineImages()); - - final emailContent = await _composerCacheRepository.restoreEmailInlineImages( + final emailContent = await _composerCacheRepository.convertImageCIDToBase64( htmlContent, transformConfiguration, mapUrlDownloadCID); diff --git a/lib/features/composer/domain/usecases/save_composer_cache_on_web_interactor.dart b/lib/features/composer/domain/usecases/save_composer_cache_on_web_interactor.dart index 086709ab73..7a84ef6c6b 100644 --- a/lib/features/composer/domain/usecases/save_composer_cache_on_web_interactor.dart +++ b/lib/features/composer/domain/usecases/save_composer_cache_on_web_interactor.dart @@ -5,7 +5,7 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:tmail_ui_user/features/composer/domain/repository/composer_repository.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/create_email_request.dart'; -import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/model/composer_cache.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/composer_cache_repository.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/save_composer_cache_state.dart'; @@ -21,19 +21,20 @@ class SaveComposerCacheOnWebInteractor { Future> execute( CreateEmailRequest createEmailRequest, AccountId accountId, - UserName userName, - {required ScreenDisplayMode displayMode} + UserName userName ) async { try { final emailCreated = await _composerRepository.generateEmail(createEmailRequest); - final identity = createEmailRequest.identity; + final composerCache = ComposerCache( + email: emailCreated, + identity: createEmailRequest.identity, + isRequestReadReceipt: createEmailRequest.isRequestReadReceipt, + displayMode: createEmailRequest.displayMode, + ); await _composerCacheRepository.saveComposerCacheOnWeb( - emailCreated, - accountId: accountId, - userName: userName, - displayMode: displayMode, - identity: identity, - readReceipentEnabled: createEmailRequest.isRequestReadReceipt); + composerCache, + accountId, + userName); return Right(SaveComposerCacheSuccess()); } catch (exception) { return Left(SaveComposerCacheFailure(exception)); diff --git a/lib/features/composer/presentation/composer_bindings.dart b/lib/features/composer/presentation/composer_bindings.dart index 83a6556616..2882c7347e 100644 --- a/lib/features/composer/presentation/composer_bindings.dart +++ b/lib/features/composer/presentation/composer_bindings.dart @@ -10,10 +10,10 @@ import 'package:tmail_ui_user/features/composer/data/repository/composer_reposit import 'package:tmail_ui_user/features/composer/data/repository/contact_repository_impl.dart'; import 'package:tmail_ui_user/features/composer/domain/repository/composer_repository.dart'; import 'package:tmail_ui_user/features/composer/domain/repository/contact_repository.dart'; +import 'package:tmail_ui_user/features/composer/domain/usecases/convert_image_cid_to_base64_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/create_new_and_save_email_to_drafts_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/create_new_and_send_email_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/download_image_as_base64_interactor.dart'; -import 'package:tmail_ui_user/features/composer/domain/usecases/restore_email_inline_images_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/save_composer_cache_on_web_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/upload_attachment_interactor.dart'; import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart'; @@ -45,6 +45,7 @@ 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/mailbox_dashboard/domain/repository/composer_cache_repository.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_composer_cache_on_web_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_composer_cache_to_local_storage_browser_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/new_email_cache_manager.dart'; @@ -209,8 +210,12 @@ class ComposerBindings extends BaseBindings { Get.find(), Get.find(), )); - Get.lazyPut(() => RestoreEmailInlineImagesInteractor( + Get.lazyPut(() => ConvertImageCIDToBase46Interactor( Get.find())); + Get.lazyPut(() => SaveComposerCacheToLocalStorageBrowserInteractor( + Get.find(), + Get.find(), + )); IdentityInteractorsBindings().dependencies(); } @@ -236,6 +241,7 @@ class ComposerBindings extends BaseBindings { Get.find(), 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 711c052ee1..25d806e36e 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -15,7 +15,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; -import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; @@ -41,14 +40,15 @@ import 'package:tmail_ui_user/features/composer/domain/state/restore_email_inlin import 'package:tmail_ui_user/features/composer/domain/state/save_email_as_drafts_state.dart'; import 'package:tmail_ui_user/features/composer/domain/state/send_email_state.dart'; import 'package:tmail_ui_user/features/composer/domain/state/update_email_drafts_state.dart'; +import 'package:tmail_ui_user/features/composer/domain/usecases/convert_image_cid_to_base64_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/create_new_and_save_email_to_drafts_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/create_new_and_send_email_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/download_image_as_base64_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/get_all_autocomplete_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/get_device_contact_suggestions_interactor.dart'; -import 'package:tmail_ui_user/features/composer/domain/usecases/restore_email_inline_images_interactor.dart'; import 'package:tmail_ui_user/features/composer/domain/usecases/save_composer_cache_on_web_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/save_composer_cache_to_local_storage_browser_state.dart'; import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_mobile_tablet_controller.dart'; import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_web_controller.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.dart'; @@ -72,6 +72,7 @@ import 'package:tmail_ui_user/features/email/domain/usecases/transform_html_emai import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_composer_cache_on_web_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_composer_cache_to_local_storage_browser_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/draggable_app_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_identities_state.dart'; @@ -93,14 +94,16 @@ import 'package:tmail_ui_user/features/upload/domain/usecases/local_file_picker_ import 'package:tmail_ui_user/features/upload/domain/usecases/local_image_picker_interactor.dart'; import 'package:tmail_ui_user/features/upload/presentation/controller/upload_controller.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; +import 'package:tmail_ui_user/main/routes/app_routes.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; +import 'package:tmail_ui_user/main/routes/route_utils.dart'; +import 'package:tmail_ui_user/main/utils/app_utils.dart'; import 'package:universal_html/html.dart' as html; class ComposerController extends BaseController with DragDropFileMixin implements BeforeUnloadHandler { final mailboxDashBoardController = Get.find(); final networkConnectionController = Get.find(); - final _dynamicUrlInterceptors = Get.find(); final _beforeUnloadManager = Get.find(); final composerArguments = Rxn(); @@ -131,11 +134,12 @@ class ComposerController extends BaseController with DragDropFileMixin implement final GetAlwaysReadReceiptSettingInteractor _getAlwaysReadReceiptSettingInteractor; final CreateNewAndSendEmailInteractor _createNewAndSendEmailInteractor; final CreateNewAndSaveEmailToDraftsInteractor _createNewAndSaveEmailToDraftsInteractor; + final SaveComposerCacheToLocalStorageBrowserInteractor _saveComposerCacheToLocalStorageBrowserInteractor; GetAllAutoCompleteInteractor? _getAllAutoCompleteInteractor; GetAutoCompleteInteractor? _getAutoCompleteInteractor; GetDeviceContactSuggestionsInteractor? _getDeviceContactSuggestionsInteractor; - RestoreEmailInlineImagesInteractor? _restoreEmailInlineImagesInteractor; + ConvertImageCIDToBase46Interactor? _convertImageCIDToBase46Interactor; List listToEmailAddress = []; List listCcEmailAddress = []; @@ -188,6 +192,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement ButtonState _closeComposerButtonState = ButtonState.enabled; ButtonState _saveToDraftButtonState = ButtonState.enabled; ButtonState _sendButtonState = ButtonState.enabled; + ButtonState openNewTabButtonState = ButtonState.enabled; late Worker uploadInlineImageWorker; late Worker dashboardViewStateWorker; @@ -206,6 +211,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement this._getAlwaysReadReceiptSettingInteractor, this._createNewAndSendEmailInteractor, this._createNewAndSaveEmailToDraftsInteractor, + this._saveComposerCacheToLocalStorageBrowserInteractor, ); @override @@ -222,7 +228,6 @@ class ComposerController extends BaseController with DragDropFileMixin implement createFocusNodeInput(); scrollControllerEmailAddress.addListener(_scrollControllerEmailAddressListener); _listenStreamEvent(); - _getAlwaysReadReceiptSetting(); _beforeUnloadManager.addListener(onBeforeUnload); } @@ -317,14 +322,14 @@ class ComposerController extends BaseController with DragDropFileMixin implement hasRequestReadReceipt.value = success.alwaysReadReceiptEnabled; } else if (success is RestoreEmailInlineImagesSuccess) { _updateEditorContent(success); + } else if (success is SaveComposerCacheToLocalStorageBrowserSuccess) { + _handleSaveComposerCacheToLocalStorageBrowserSuccess(); } } void _updateEditorContent(RestoreEmailInlineImagesSuccess success) { - richTextWebController?.editorController.setText(success.emailContent); consumeState(Stream.value(Right(GetEmailContentSuccess( - htmlEmailContent: success.emailContent, - attachments: [])))); + htmlEmailContent: success.emailContent)))); } @override @@ -344,6 +349,8 @@ class ComposerController extends BaseController with DragDropFileMixin implement } } else if (failure is GetAlwaysReadReceiptSettingFailure) { hasRequestReadReceipt.value = false; + } else if (failure is SaveComposerCacheToLocalStorageBrowserFailure) { + openNewTabButtonState = ButtonState.enabled; } } @@ -393,17 +400,16 @@ class ComposerController extends BaseController with DragDropFileMixin implement Future _saveComposerCacheOnWebAction() async { _autoCreateEmailTag(); - final createEmailRequest = await _generateCreateEmailRequest(); + final createEmailRequest = await _generateCreateEmailRequest(isCaching: true); if (createEmailRequest == null) return; await _saveComposerCacheOnWebInteractor.execute( createEmailRequest, mailboxDashBoardController.accountId.value!, - mailboxDashBoardController.sessionCurrent!.username, - displayMode: screenDisplayMode.value); + mailboxDashBoardController.sessionCurrent!.username); } - Future _generateCreateEmailRequest() async { + Future _generateCreateEmailRequest({bool isCaching = false}) async { if (composerArguments.value == null || mailboxDashBoardController.sessionCurrent == null || mailboxDashBoardController.accountId.value == null @@ -411,9 +417,9 @@ class ComposerController extends BaseController with DragDropFileMixin implement log('ComposerController::_generateCreateEmailRequest: SESSION or ACCOUNT_ID or ARGUMENTS is NULL'); return null; } - + final emailContent = await _getContentInEditor(); - + return CreateEmailRequest( session: mailboxDashBoardController.sessionCurrent!, accountId: mailboxDashBoardController.accountId.value!, @@ -436,7 +442,9 @@ class ComposerController extends BaseController with DragDropFileMixin implement unsubscribeEmailId: composerArguments.value!.previousEmailId, messageId: composerArguments.value!.messageId, references: composerArguments.value!.references, - emailSendingQueue: composerArguments.value!.sendingEmail + emailSendingQueue: composerArguments.value!.sendingEmail, + isCaching: isCaching, + displayMode: screenDisplayMode.value ); } @@ -488,7 +496,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement } void onCreatedMobileEditorAction(BuildContext context, HtmlEditorApi editorApi, String? content) { - if (identitySelected.value != null) { + if (listFromIdentities.isNotEmpty) { initTextEditor(content); } richTextMobileTabletController?.htmlEditorApi = editorApi; @@ -502,14 +510,12 @@ class ComposerController extends BaseController with DragDropFileMixin implement ); } - void onLoadCompletedMobileEditorAction(HtmlEditorApi editorApi, WebUri? url) async { + Future onLoadCompletedMobileEditorAction( + HtmlEditorApi editorApi, + WebUri? url + ) async { _isEmailBodyLoaded = true; - if (identitySelected.value == null) { - _getAllIdentities(); - } else { - await _selectIdentity(identitySelected.value); - _autoFocusFieldWhenLauncher(); - } + await _applyIdentityToEditor(); } void _initEmail() { @@ -552,6 +558,13 @@ class ComposerController extends BaseController with DragDropFileMixin implement presentationEmail: arguments.sendingEmail!.presentationEmail, actionType: EmailActionType.editSendingEmail ); + + final allAttachments = arguments.sendingEmail!.email.allAttachments; + _initAttachmentsAndInlineImages( + attachments: allAttachments.getListAttachmentsDisplayedOutside( + arguments.sendingEmail!.email.htmlBodyAttachments), + inlineImages: allAttachments.listAttachmentsDisplayedInContent); + _getEmailContentFromSendingEmail(arguments.sendingEmail!); _emailIdEditing = arguments.sendingEmail!.presentationEmail.id!; break; @@ -591,6 +604,10 @@ class ComposerController extends BaseController with DragDropFileMixin implement presentationEmail: arguments.presentationEmail!, actionType: arguments.emailActionType ); + _initAttachmentsAndInlineImages( + attachments: arguments.attachments, + inlineImages: arguments.inlineImages); + _transformHtmlEmailContent(arguments.emailContents); break; case EmailActionType.forward: @@ -598,38 +615,11 @@ class ComposerController extends BaseController with DragDropFileMixin implement presentationEmail: arguments.presentationEmail!, actionType: arguments.emailActionType ); - _initAttachments(arguments.attachments ?? []); - _transformHtmlEmailContent(arguments.emailContents); - break; - case EmailActionType.reopenComposerBrowser: - if (!PlatformInfo.isWeb) return; - - screenDisplayMode.value = arguments.displayMode; + _initAttachmentsAndInlineImages( + attachments: arguments.attachments, + inlineImages: arguments.inlineImages); - _initEmailAddress( - presentationEmail: arguments.presentationEmail!, - actionType: EmailActionType.reopenComposerBrowser - ); - _initSubjectEmail( - presentationEmail: arguments.presentationEmail!, - actionType: EmailActionType.reopenComposerBrowser - ); - _initAttachments( - arguments.attachments ?? [], - inlineAttachments: arguments.inlineImages); - - final accountId = mailboxDashBoardController.accountId.value; - final downloadUrl = mailboxDashBoardController.sessionCurrent - ?.getDownloadUrl(jmapUrl: dynamicUrlInterceptors.jmapUrl); - if (accountId == null || downloadUrl == null) return; - _getEmailContentFromSessionStorageBrowser( - htmlContent: arguments.emailContents ?? '', - inlineImages: arguments.inlineImages ?? [], - accountId: accountId, - downloadUrl: downloadUrl - ); - - hasRequestReadReceipt.value = arguments.readRecepientEnabled ?? false; + _transformHtmlEmailContent(arguments.emailContents); break; case EmailActionType.composeFromUnsubscribeMailtoLink: if (arguments.subject != null) { @@ -644,10 +634,38 @@ class ComposerController extends BaseController with DragDropFileMixin implement _getEmailContentFromUnsubscribeMailtoLink(arguments.body ?? ''); _updateStatusEmailSendButton(); break; + case EmailActionType.reopenComposerBrowser: + case EmailActionType.composeEmailIntoNewTab: + screenDisplayMode.value = arguments.displayMode; + + _initEmailAddress( + presentationEmail: arguments.presentationEmail!, + actionType: arguments.emailActionType + ); + _initSubjectEmail( + presentationEmail: arguments.presentationEmail!, + actionType: arguments.emailActionType + ); + _initAttachmentsAndInlineImages( + attachments: arguments.attachments, + inlineImages: arguments.inlineImages); + + _getEmailContentFromStorageBrowser( + htmlContent: arguments.emailContents ?? '', + inlineImages: arguments.inlineImages ?? []); + break; default: break; } } + + if ((composerArguments.value?.emailActionType == EmailActionType.composeEmailIntoNewTab + || composerArguments.value?.emailActionType == EmailActionType.reopenComposerBrowser) + && composerArguments.value?.isRequestReadReceipt == true) { + hasRequestReadReceipt.value = arguments.isRequestReadReceipt; + } else { + _getAlwaysReadReceiptSetting(); + } } void _initSubjectEmail({ @@ -660,55 +678,51 @@ class ComposerController extends BaseController with DragDropFileMixin implement subjectEmailInputController.text = newSubject; } - void _initAttachments(List attachments, {List? inlineAttachments}) { - if (attachments.isNotEmpty) { - initialAttachments = attachments; + void _initAttachmentsAndInlineImages({ + List? attachments, + List? inlineImages + }) { + if (attachments?.isNotEmpty == true) { + initialAttachments = attachments!; uploadController.initializeUploadAttachments(attachments); } - if (inlineAttachments != null) { - uploadController.initializeUploadInlineAttachments(inlineAttachments); + if (inlineImages?.isNotEmpty == true) { + uploadController.initializeUploadInlineAttachments(inlineImages!); } } void _initIdentities(ComposerArguments composerArguments) { listFromIdentities.value = composerArguments.identities ?? []; - if (listFromIdentities.isEmpty) { - _getAllIdentities(); - } else if (composerArguments.selectedIdentity != null - && listFromIdentities.contains(composerArguments.selectedIdentity!) - ) { - _selectIdentity(composerArguments.selectedIdentity!); - } else if (composerArguments.identities?.isNotEmpty == true) { - _selectIdentity(composerArguments.identities!.first); - } } void _getAllIdentities() { log('ComposerController::_getAllIdentities: Fetch again identity !'); final accountId = mailboxDashBoardController.accountId.value; final session = mailboxDashBoardController.sessionCurrent; - if (accountId != null && session != null) { - consumeState(_getAllIdentitiesInteractor.execute(session, accountId)); - } + if (accountId == null || session == null) return; + consumeState(_getAllIdentitiesInteractor.execute(session, accountId)); } - void _handleGetAllIdentitiesSuccess(GetAllIdentitiesSuccess success) async { - final listIdentitiesMayDeleted = success.identities?.toListMayDeleted() ?? []; - if (listIdentitiesMayDeleted.isNotEmpty) { - listFromIdentities.value = listIdentitiesMayDeleted; - - if (identitySelected.value == null) { - final selectedIdentityFromArguments = composerArguments.value?.selectedIdentity; - if (selectedIdentityFromArguments != null - && listFromIdentities.contains(selectedIdentityFromArguments) - ) { - await _selectIdentity(selectedIdentityFromArguments); - } else { - await _selectIdentity(listIdentitiesMayDeleted.firstOrNull); - } + Future _applyIdentityToEditor() async { + if (listFromIdentities.isEmpty) { + _getAllIdentities(); + } else { + final selectedIdentityFromArguments = composerArguments.value?.selectedIdentity; + if (selectedIdentityFromArguments != null + && listFromIdentities.contains(selectedIdentityFromArguments)) { + await _selectIdentity(selectedIdentityFromArguments); + } else { + await _selectIdentity(listFromIdentities.first); } + await _autoFocusFieldWhenLauncher(); } - _autoFocusFieldWhenLauncher(); + } + + Future _handleGetAllIdentitiesSuccess(GetAllIdentitiesSuccess success) async { + final listIdentitiesMayDeleted = success.identities?.toListMayDeleted() ?? []; + if (listIdentitiesMayDeleted.isEmpty) return; + listFromIdentities.value = listIdentitiesMayDeleted; + await _applyIdentityToEditor(); } void _initEmailAddress({ @@ -795,9 +809,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement clearFocus(context); - if (toEmailAddressController.text.isNotEmpty - || ccEmailAddressController.text.isNotEmpty - || bccEmailAddressController.text.isNotEmpty) { + if (_isExistRecipientInputText) { _collapseAllRecipient(); _autoCreateEmailTag(); } @@ -1156,7 +1168,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement final session = mailboxDashBoardController.sessionCurrent; final accountId = mailboxDashBoardController.accountId.value; if (session != null && accountId != null) { - final uploadUri = session.getUploadUri(accountId, jmapUrl: _dynamicUrlInterceptors.jmapUrl); + final uploadUri = session.getUploadUri(accountId, jmapUrl: dynamicUrlInterceptors.jmapUrl); uploadController.justUploadAttachmentsAction( uploadFiles: pickedFiles, uploadUri: uploadUri, @@ -1293,55 +1305,50 @@ class ComposerController extends BaseController with DragDropFileMixin implement consumeState(Stream.value( Right(GetEmailContentSuccess( htmlEmailContent: sendingEmail.presentationEmail.emailContentList.asHtmlString, - attachments: sendingEmail.email.allAttachments, emailCurrent: sendingEmail.email )) )); } - void _getEmailContentFromSessionStorageBrowser({ + void _getEmailContentFromStorageBrowser({ required String htmlContent, required List inlineImages, - required AccountId accountId, - required String downloadUrl }) { - _restoreEmailInlineImagesInteractor = getBinding(); - if (_restoreEmailInlineImagesInteractor == null) return; - consumeState(_restoreEmailInlineImagesInteractor!.execute( + consumeState(Stream.value(Right(GetEmailContentLoading()))); + + final accountId = mailboxDashBoardController.accountId.value; + final baseDownloadUrl = mailboxDashBoardController.baseDownloadUrl; + + if (accountId == null || baseDownloadUrl.isEmpty) { + consumeState(Stream.value(Right(GetEmailContentSuccess(htmlEmailContent: htmlContent)))); + return; + } + + _convertImageCIDToBase46Interactor = getBinding(); + if (_convertImageCIDToBase46Interactor == null) return; + + final mapUrlDownloadCID = inlineImages.toMapCidImageDownloadUrl( + accountId: accountId, + downloadUrl: baseDownloadUrl); + + consumeState(_convertImageCIDToBase46Interactor!.execute( htmlContent: htmlContent, transformConfiguration: TransformConfiguration.forRestoreEmail(), - mapUrlDownloadCID: inlineImages.toMapCidImageDownloadUrl( - accountId: accountId, - downloadUrl: downloadUrl))); + mapUrlDownloadCID: mapUrlDownloadCID)); } void _getEmailContentFromContentShared(String content) { - consumeState(Stream.value( - Right(GetEmailContentSuccess( - htmlEmailContent: content, - attachments: [], - )) - )); + consumeState(Stream.value(Right(GetEmailContentSuccess(htmlEmailContent: content)))); } void _getEmailContentFromMailtoUri(String content) { log('ComposerController::_getEmailContentFromMailtoUri:content: $content'); - consumeState(Stream.value( - Right(GetEmailContentSuccess( - htmlEmailContent: content, - attachments: [], - )) - )); + consumeState(Stream.value(Right(GetEmailContentSuccess(htmlEmailContent: content)))); } void _getEmailContentFromUnsubscribeMailtoLink(String content) { log('ComposerController::_getEmailContentFromUnsubscribeMailtoLink:content: $content'); - consumeState(Stream.value( - Right(GetEmailContentSuccess( - htmlEmailContent: content, - attachments: [], - )) - )); + consumeState(Stream.value(Right(GetEmailContentSuccess(htmlEmailContent: content)))); } void _getEmailContentFromEmailId({required EmailId emailId, bool isDraftEmail = false}) { @@ -1359,12 +1366,16 @@ class ComposerController extends BaseController with DragDropFileMixin implement } void _getEmailContentOffLineSuccess(GetEmailContentFromCacheSuccess success) { - _initAttachments(success.attachments); + _initAttachmentsAndInlineImages( + attachments: success.attachments, + inlineImages: success.inlineImages); emailContentsViewState.value = Right(success); } void _getEmailContentSuccess(GetEmailContentSuccess success) { - _initAttachments(success.attachments); + _initAttachmentsAndInlineImages( + attachments: success.attachments, + inlineImages: success.inlineImages); emailContentsViewState.value = Right(success); } @@ -1610,36 +1621,36 @@ class ComposerController extends BaseController with DragDropFileMixin implement } } - Future _selectIdentity(Identity? newIdentity) async { - final formerIdentity = identitySelected.value; + Future _selectIdentity(Identity newIdentity) async { + final oldIdentity = identitySelected.value; identitySelected.value = newIdentity; - if (newIdentity == null) return; - - if (composerArguments.value?.emailActionType == EmailActionType.reopenComposerBrowser) { - composerArguments.value = composerArguments.value?.copyWith( - emailActionType: EmailActionType.editDraft); - } else { - await _applyIdentityForAllFieldComposer(formerIdentity, newIdentity); + if (oldIdentity != null + || composerArguments.value?.emailActionType == EmailActionType.reopenComposerBrowser + || composerArguments.value?.emailActionType == EmailActionType.composeEmailIntoNewTab) { + return; } + + await _applyIdentityForAllFieldComposer( + oldIdentity: oldIdentity, + newIdentity: newIdentity); } - Future _applyIdentityForAllFieldComposer( - Identity? formerIdentity, - Identity newIdentity - ) async { - if (formerIdentity != null) { - if (formerIdentity.bcc?.isNotEmpty == true) { - _removeBccEmailAddressFromFormerIdentity(formerIdentity.bcc!); - } - await _removeSignature(); + Future _applyIdentityForAllFieldComposer({ + required Identity? oldIdentity, + required Identity? newIdentity + }) async { + await _removeSignature(); + + if (oldIdentity?.bcc?.isNotEmpty == true) { + _removeBccEmailAddressFromFormerIdentity(oldIdentity!.bcc!); } - if (newIdentity.bcc?.isNotEmpty == true) { - _applyBccEmailAddressFromIdentity(newIdentity.bcc!); + if (newIdentity?.bcc?.isNotEmpty == true) { + _applyBccEmailAddressFromIdentity(newIdentity!.bcc!); } - if (newIdentity.signatureAsString.isNotEmpty == true) { - await _applySignature(newIdentity.signatureAsString.asSignatureHtml()); + if (newIdentity?.signatureAsString.isNotEmpty == true) { + await _applySignature(newIdentity!.signatureAsString.asSignatureHtml()); } if (PlatformInfo.isMobile) { @@ -1703,7 +1714,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement void _handleUploadInlineSuccess(SuccessAttachmentUploadState uploadState) { uploadController.clearUploadInlineViewState(); - final baseDownloadUrl = mailboxDashBoardController.sessionCurrent?.getDownloadUrl(jmapUrl: _dynamicUrlInterceptors.jmapUrl); + final baseDownloadUrl = mailboxDashBoardController.sessionCurrent?.getDownloadUrl(jmapUrl: dynamicUrlInterceptors.jmapUrl); final accountId = mailboxDashBoardController.accountId.value; if (baseDownloadUrl != null && accountId != null) { @@ -1828,19 +1839,14 @@ class ComposerController extends BaseController with DragDropFileMixin implement } - void handleInitHtmlEditorWeb(String initContent) async { + Future handleInitHtmlEditorWeb(String initContent) async { log('ComposerController::handleInitHtmlEditorWeb:'); _isEmailBodyLoaded = true; richTextWebController?.editorController.setFullScreen(); richTextWebController?.editorController.setOnDragDropEvent(); onChangeTextEditorWeb(initContent); richTextWebController?.setEnableCodeView(); - if (identitySelected.value == null) { - _getAllIdentities(); - } else { - await _selectIdentity(identitySelected.value); - _autoFocusFieldWhenLauncher(); - } + await _applyIdentityToEditor(); } void handleOnFocusHtmlEditorWeb() { @@ -1885,7 +1891,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement HtmlEditorApi? get htmlEditorApi => richTextMobileTabletController?.htmlEditorApi; void onChangeTextEditorWeb(String? text) { - if (identitySelected.value != null) { + if (listFromIdentities.isNotEmpty) { initTextEditor(text); } _textEditorWeb = text; @@ -1941,7 +1947,11 @@ class ComposerController extends BaseController with DragDropFileMixin implement } Future onChangeIdentity(Identity? newIdentity) async { - await _selectIdentity(newIdentity); + final oldIdentity = identitySelected.value; + identitySelected.value = newIdentity; + await _applyIdentityForAllFieldComposer( + oldIdentity: oldIdentity, + newIdentity: newIdentity); } void _searchIdentities(String searchText) { @@ -2263,7 +2273,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement ccRecipientState.value = isEnabled ? PrefixRecipientState.disabled : PrefixRecipientState.enabled; bccRecipientState.value = isEnabled ? PrefixRecipientState.disabled : PrefixRecipientState.enabled; } - + @override Future onBeforeUnload() async { if (mailboxDashBoardController.accountId.value != null && @@ -2272,4 +2282,42 @@ class ComposerController extends BaseController with DragDropFileMixin implement await _saveComposerCacheOnWebAction(); } } + + bool get _isExistRecipientInputText { + return toEmailAddressController.text.isNotEmpty + || ccEmailAddressController.text.isNotEmpty + || bccEmailAddressController.text.isNotEmpty; + } + + Future onOpenNewTabAction() async { + if (openNewTabButtonState == ButtonState.disabled) { + log('ComposerController::onOpenNewTabAction: OPENING NEW TAB COMPOSER'); + return; + } + openNewTabButtonState = ButtonState.disabled; + + FocusManager.instance.primaryFocus?.unfocus(); + + if (_isExistRecipientInputText) { + _collapseAllRecipient(); + _autoCreateEmailTag(); + } + + final createEmailRequest = await _generateCreateEmailRequest(isCaching: true); + if (createEmailRequest == null) return; + + consumeState(_saveComposerCacheToLocalStorageBrowserInteractor.execute( + createEmailRequest, + mailboxDashBoardController.accountId.value!, + mailboxDashBoardController.sessionCurrent!.username, + )); + } + + Future _handleSaveComposerCacheToLocalStorageBrowserSuccess() async { + openNewTabButtonState = ButtonState.enabled; + + await AppUtils.launchLink( + RouteUtils.createUrlWebLocationBar(AppRoutes.dashboard).toString() + ); + } } \ No newline at end of file diff --git a/lib/features/composer/presentation/composer_view_web.dart b/lib/features/composer/presentation/composer_view_web.dart index d992b0caaa..576848bc6e 100644 --- a/lib/features/composer/presentation/composer_view_web.dart +++ b/lib/features/composer/presentation/composer_view_web.dart @@ -287,6 +287,7 @@ class ComposerView extends GetWidget { onCloseViewAction: () => controller.handleClickCloseComposer(context), onChangeDisplayModeAction: controller.displayScreenTypeComposerAction, constraints: constraints, + onOpenNewTabAction: controller.onOpenNewTabAction, )), ConstrainedBox( constraints: BoxConstraints( diff --git a/lib/features/composer/presentation/extensions/email_action_type_extension.dart b/lib/features/composer/presentation/extensions/email_action_type_extension.dart index 5c0a523d15..d2692cda55 100644 --- a/lib/features/composer/presentation/extensions/email_action_type_extension.dart +++ b/lib/features/composer/presentation/extensions/email_action_type_extension.dart @@ -31,6 +31,7 @@ extension EmailActionTypeExtension on EmailActionType { case EmailActionType.editDraft: case EmailActionType.editSendingEmail: case EmailActionType.reopenComposerBrowser: + case EmailActionType.composeEmailIntoNewTab: return subject; default: return ''; diff --git a/lib/features/composer/presentation/model/create_email_request.dart b/lib/features/composer/presentation/model/create_email_request.dart index 1cc956a167..91b1ce73a6 100644 --- a/lib/features/composer/presentation/model/create_email_request.dart +++ b/lib/features/composer/presentation/model/create_email_request.dart @@ -8,6 +8,7 @@ import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/email/attachment.dart'; import 'package:model/email/email_action_type.dart'; +import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart'; import 'package:tmail_ui_user/features/sending_queue/domain/model/sending_email.dart'; class CreateEmailRequest with EquatableMixin { @@ -34,6 +35,8 @@ class CreateEmailRequest with EquatableMixin { final MessageIdsHeaderValue? messageId; final MessageIdsHeaderValue? references; final SendingEmail? emailSendingQueue; + final bool isCaching; + final ScreenDisplayMode displayMode; CreateEmailRequest({ required this.session, @@ -57,7 +60,9 @@ class CreateEmailRequest with EquatableMixin { this.unsubscribeEmailId, this.messageId, this.references, - this.emailSendingQueue + this.emailSendingQueue, + this.isCaching = false, + this.displayMode = ScreenDisplayMode.normal, }); @override @@ -83,6 +88,8 @@ class CreateEmailRequest with EquatableMixin { unsubscribeEmailId, references, references, - emailSendingQueue + emailSendingQueue, + isCaching, + displayMode, ]; } \ No newline at end of file diff --git a/lib/features/composer/presentation/view/web/web_editor_view.dart b/lib/features/composer/presentation/view/web/web_editor_view.dart index 5da136ef90..8a6bda52de 100644 --- a/lib/features/composer/presentation/view/web/web_editor_view.dart +++ b/lib/features/composer/presentation/view/web/web_editor_view.dart @@ -7,6 +7,7 @@ import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; import 'package:html_editor_enhanced/html_editor.dart'; import 'package:model/email/email_action_type.dart'; +import 'package:tmail_ui_user/features/composer/domain/state/restore_email_inline_images_state.dart'; import 'package:tmail_ui_user/features/composer/presentation/view/editor_view_mixin.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/web/web_editor_widget.dart'; import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart'; @@ -80,6 +81,7 @@ class WebEditorView extends StatelessWidget with EditorViewMixin { case EmailActionType.reopenComposerBrowser: case EmailActionType.composeFromUnsubscribeMailtoLink: case EmailActionType.composeFromMailtoUri: + case EmailActionType.composeEmailIntoNewTab: if (contentViewState == null) { return const SizedBox.shrink(); } @@ -100,7 +102,7 @@ class WebEditorView extends StatelessWidget with EditorViewMixin { onDragEnter: onDragEnter, ), (success) { - if (success is GetEmailContentLoading) { + if (success is GetEmailContentLoading || success is RestoringEmailInlineImages) { return const CupertinoLoadingWidget(padding: EdgeInsets.all(16.0)); } else { var newContent = success is GetEmailContentSuccess diff --git a/lib/features/composer/presentation/widgets/web/desktop_app_bar_composer_widget.dart b/lib/features/composer/presentation/widgets/web/desktop_app_bar_composer_widget.dart index ae7cc28f39..82ee5366a6 100644 --- a/lib/features/composer/presentation/widgets/web/desktop_app_bar_composer_widget.dart +++ b/lib/features/composer/presentation/widgets/web/desktop_app_bar_composer_widget.dart @@ -15,6 +15,7 @@ class DesktopAppBarComposerWidget extends StatelessWidget { final ScreenDisplayMode? displayMode; final OnChangeDisplayModeAction? onChangeDisplayModeAction; final BoxConstraints? constraints; + final VoidCallback? onOpenNewTabAction; final _imagePaths = Get.find(); @@ -25,6 +26,7 @@ class DesktopAppBarComposerWidget extends StatelessWidget { this.displayMode, this.onChangeDisplayModeAction, this.constraints, + this.onOpenNewTabAction, }); @override @@ -49,6 +51,16 @@ class DesktopAppBarComposerWidget extends StatelessWidget { child: Row( mainAxisSize: MainAxisSize.min, children: [ + TMailButtonWidget.fromIcon( + icon: _imagePaths.icOpenInNewTab, + backgroundColor: Colors.transparent, + tooltipMessage: AppLocalizations.of(context).openInNewTab, + iconSize: AppBarComposerWidgetStyle.iconSize, + iconColor: AppBarComposerWidgetStyle.iconColor, + padding: AppBarComposerWidgetStyle.iconPadding, + onTapActionCallback: onOpenNewTabAction + ), + const SizedBox(width: AppBarComposerWidgetStyle.space), TMailButtonWidget.fromIcon( icon: _imagePaths.icMinimize, backgroundColor: Colors.transparent, diff --git a/lib/features/email/data/datasource/html_datasource.dart b/lib/features/email/data/datasource/html_datasource.dart index 3545774623..d2a65c92e3 100644 --- a/lib/features/email/data/datasource/html_datasource.dart +++ b/lib/features/email/data/datasource/html_datasource.dart @@ -18,8 +18,15 @@ abstract class HtmlDataSource { Future>> replaceImageBase64ToImageCID({ required String emailContent, - required Map inlineAttachments + required Map inlineAttachments, + bool isCaching = false }); Future removeCollapsedExpandedSignatureEffect({required String emailContent}); + + Future convertImageCIDToBase64( + String htmlContent, + TransformConfiguration transformConfiguration, + Map mapUrlDownloadCID + ); } \ No newline at end of file diff --git a/lib/features/email/data/datasource_impl/html_datasource_impl.dart b/lib/features/email/data/datasource_impl/html_datasource_impl.dart index 5d695d739d..860020a515 100644 --- a/lib/features/email/data/datasource_impl/html_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/html_datasource_impl.dart @@ -42,12 +42,14 @@ class HtmlDataSourceImpl extends HtmlDataSource { @override Future>> replaceImageBase64ToImageCID({ required String emailContent, - required Map inlineAttachments + required Map inlineAttachments, + bool isCaching = false }) { return Future.sync(() async { return await _htmlAnalyzer.replaceImageBase64ToImageCID( emailContent: emailContent, - inlineAttachments: inlineAttachments + inlineAttachments: inlineAttachments, + isCaching: isCaching ); }).catchError(_exceptionThrower.throwException); } @@ -60,4 +62,18 @@ class HtmlDataSourceImpl extends HtmlDataSource { ); }).catchError(_exceptionThrower.throwException); } + + @override + Future convertImageCIDToBase64( + String htmlContent, + TransformConfiguration transformConfiguration, + Map mapUrlDownloadCID + ) { + return Future.sync(() async { + return await _htmlAnalyzer.convertImageCIDToBase64( + htmlContent, + mapUrlDownloadCID, + transformConfiguration); + }).catchError(_exceptionThrower.throwException); + } } \ No newline at end of file diff --git a/lib/features/email/data/local/html_analyzer.dart b/lib/features/email/data/local/html_analyzer.dart index 904c320a05..922355e63f 100644 --- a/lib/features/email/data/local/html_analyzer.dart +++ b/lib/features/email/data/local/html_analyzer.dart @@ -107,7 +107,8 @@ class HtmlAnalyzer { Future>> replaceImageBase64ToImageCID({ required String emailContent, - required Map inlineAttachments + required Map inlineAttachments, + bool isCaching = false }) async { final document = parse(emailContent); final listImgTag = document.querySelectorAll('img[src^="data:image/"][id^="cid:"]'); @@ -116,7 +117,9 @@ class HtmlAnalyzer { final idImg = imgTag.attributes['id']; final cid = idImg!.replaceFirst('cid:', '').trim(); imgTag.attributes['src'] = 'cid:$cid'; - imgTag.attributes.remove('id'); + if (!isCaching) { + imgTag.attributes.remove('id'); + } return cid; })).then((listCid) { final listInlineAttachment = listCid @@ -157,4 +160,15 @@ class HtmlAnalyzer { log('HtmlAnalyzer::removeCollapsedExpandedSignatureEffect: AFTER = $newContent'); return newContent; } + + Future convertImageCIDToBase64( + String htmlContent, + Map mapCidImageDownloadUrl, + TransformConfiguration transformConfiguration + ) async { + return await _htmlTransform.transformToHtml( + htmlContent: htmlContent, + mapCidImageDownloadUrl: mapCidImageDownloadUrl, + transformConfiguration: transformConfiguration); + } } \ 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 index c7b9396db9..80180121b1 100644 --- a/lib/features/email/domain/extensions/detailed_email_extension.dart +++ b/lib/features/email/domain/extensions/detailed_email_extension.dart @@ -18,6 +18,7 @@ extension DetailedEmailExtension on DetailedEmail { emailContentPath: emailContentPath, messageId: messageId?.ids.toList(), references: references?.ids.toList(), + inlineImages: inlineImages?.toHiveCache(), ); } @@ -36,6 +37,7 @@ extension DetailedEmailExtension on DetailedEmail { emailContentPath: path, messageId: messageId, references: references, + inlineImages: inlineImages, ); } } \ No newline at end of file diff --git a/lib/features/email/domain/extensions/detailed_email_hive_cache_extension.dart b/lib/features/email/domain/extensions/detailed_email_hive_cache_extension.dart index 1a32803059..78b1c7dae7 100644 --- a/lib/features/email/domain/extensions/detailed_email_hive_cache_extension.dart +++ b/lib/features/email/domain/extensions/detailed_email_hive_cache_extension.dart @@ -22,7 +22,8 @@ extension DetailedEmailHiveCacheExtension on DetailedEmailHiveCache { : null, references: references != null ? MessageIdsHeaderValue(references!.toSet()) - : null + : null, + inlineImages: inlineImages?.toAttachment(), ); } } \ No newline at end of file diff --git a/lib/features/email/domain/extensions/email_extension.dart b/lib/features/email/domain/extensions/email_extension.dart index 782e169e68..957f3736eb 100644 --- a/lib/features/email/domain/extensions/email_extension.dart +++ b/lib/features/email/domain/extensions/email_extension.dart @@ -14,7 +14,8 @@ extension EmailExtension on Email { keywords: keywords, htmlEmailContent: htmlEmailContent, messageId: messageId, - references: references + references: references, + inlineImages: allAttachments.listAttachmentsDisplayedInContent, ); } } \ 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 index 008e19df7c..cbfa6f26c2 100644 --- a/lib/features/email/domain/model/detailed_email.dart +++ b/lib/features/email/domain/model/detailed_email.dart @@ -14,6 +14,7 @@ class DetailedEmail with EquatableMixin { final DateTime createdTime; final MessageIdsHeaderValue? messageId; final MessageIdsHeaderValue? references; + final List? inlineImages; DetailedEmail({ required this.emailId, @@ -25,6 +26,7 @@ class DetailedEmail with EquatableMixin { this.emailContentPath, this.messageId, this.references, + this.inlineImages, }); @override @@ -38,5 +40,6 @@ class DetailedEmail with EquatableMixin { emailContentPath, messageId, references, + inlineImages, ]; } \ No newline at end of file diff --git a/lib/features/email/domain/state/get_email_content_state.dart b/lib/features/email/domain/state/get_email_content_state.dart index 9742216d9c..57250a68f3 100644 --- a/lib/features/email/domain/state/get_email_content_state.dart +++ b/lib/features/email/domain/state/get_email_content_state.dart @@ -7,12 +7,14 @@ class GetEmailContentLoading extends LoadingState {} class GetEmailContentSuccess extends UIState { final String htmlEmailContent; - final List attachments; + final List? attachments; + final List? inlineImages; final Email? emailCurrent; GetEmailContentSuccess({ required this.htmlEmailContent, - required this.attachments, + this.attachments, + this.inlineImages, this.emailCurrent }); @@ -20,18 +22,21 @@ class GetEmailContentSuccess extends UIState { List get props => [ htmlEmailContent, attachments, + inlineImages, emailCurrent ]; } class GetEmailContentFromCacheSuccess extends UIState { final String htmlEmailContent; - final List attachments; + final List? attachments; + final List? inlineImages; final Email? emailCurrent; GetEmailContentFromCacheSuccess({ required this.htmlEmailContent, - required this.attachments, + this.attachments, + this.inlineImages, this.emailCurrent }); @@ -39,6 +44,7 @@ class GetEmailContentFromCacheSuccess extends UIState { List get props => [ htmlEmailContent, attachments, + inlineImages, emailCurrent, ]; } diff --git a/lib/features/email/domain/usecases/get_email_content_interactor.dart b/lib/features/email/domain/usecases/get_email_content_interactor.dart index c2e1c6f355..8b96abbb94 100644 --- a/lib/features/email/domain/usecases/get_email_content_interactor.dart +++ b/lib/features/email/domain/usecases/get_email_content_interactor.dart @@ -48,8 +48,11 @@ class GetEmailContentInteractor { try { final email = await emailRepository.getEmailContent(session, accountId, emailId); + final listInlineImages = email.allAttachments.listAttachmentsDisplayedInContent; + final listAttachments = email.allAttachments.getListAttachmentsDisplayedOutside(email.htmlBodyAttachments); + if (email.emailContentList.isNotEmpty) { - final mapCidImageDownloadUrl = email.attachmentsWithCid.toMapCidImageDownloadUrl( + final mapCidImageDownloadUrl = listInlineImages.toMapCidImageDownloadUrl( accountId: accountId, downloadUrl: baseDownloadUrl ); @@ -61,13 +64,15 @@ class GetEmailContentInteractor { yield Right(GetEmailContentSuccess( htmlEmailContent: newEmailContents.asHtmlString, - attachments: email.allAttachments.getListAttachmentsDisplayedOutside(email.htmlBodyAttachments), + attachments: listAttachments, + inlineImages: listInlineImages, emailCurrent: email )); } else { yield Right(GetEmailContentSuccess( htmlEmailContent: '', - attachments: email.allAttachments.getListAttachmentsDisplayedOutside(email.htmlBodyAttachments), + attachments: listAttachments, + inlineImages: listInlineImages, emailCurrent: email )); } @@ -90,6 +95,7 @@ class GetEmailContentInteractor { yield Right(GetEmailContentFromCacheSuccess( htmlEmailContent: detailedEmail.htmlEmailContent ?? '', attachments: detailedEmail.attachments ?? [], + inlineImages: detailedEmail.inlineImages ?? [], emailCurrent: Email( id: emailId, headers: detailedEmail.headers, @@ -121,6 +127,7 @@ class GetEmailContentInteractor { yield Right(GetEmailContentFromCacheSuccess( htmlEmailContent: detailedEmail.htmlEmailContent ?? '', attachments: detailedEmail.attachments ?? [], + inlineImages: detailedEmail.inlineImages ?? [], emailCurrent: Email( id: emailId, headers: detailedEmail.headers, diff --git a/lib/features/email/domain/usecases/get_list_detailed_email_by_id_interator.dart b/lib/features/email/domain/usecases/get_list_detailed_email_by_id_interator.dart index 8e0e2448a1..21f5eab568 100644 --- a/lib/features/email/domain/usecases/get_list_detailed_email_by_id_interator.dart +++ b/lib/features/email/domain/usecases/get_list_detailed_email_by_id_interator.dart @@ -57,7 +57,7 @@ class GetListDetailedEmailByIdInteractor { final listEmailContent = email.emailContentList; if (listEmailContent.isNotEmpty) { - final mapCidImageDownloadUrl = email.attachmentsWithCid.toMapCidImageDownloadUrl( + final mapCidImageDownloadUrl = email.allAttachments.listAttachmentsDisplayedInContent.toMapCidImageDownloadUrl( accountId: accountId, downloadUrl: baseDownloadUrl ); diff --git a/lib/features/email/presentation/controller/single_email_controller.dart b/lib/features/email/presentation/controller/single_email_controller.dart index 49f05d3310..ed9d012169 100644 --- a/lib/features/email/presentation/controller/single_email_controller.dart +++ b/lib/features/email/presentation/controller/single_email_controller.dart @@ -468,6 +468,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { GetEmailContentSuccess( htmlEmailContent: emailLoaded.htmlContent, attachments: emailLoaded.attachments, + inlineImages: emailLoaded.inlineImages, emailCurrent: emailLoaded.emailCurrent ) ))); @@ -500,22 +501,23 @@ class SingleEmailController extends BaseController with AppLoaderMixin { currentEmailLoaded.value = EmailLoaded( htmlContent: success.htmlEmailContent, - attachments: List.of(success.attachments), + attachments: List.of(success.attachments ?? []), + inlineImages: List.of(success.inlineImages ?? []), emailCurrent: success.emailCurrent, ); emailSupervisorController.pushEmailQueue(currentEmailLoaded.value!); if (success.emailCurrent?.id == currentEmail?.id) { - attachments.value = success.attachments; + attachments.value = success.attachments ?? []; attachmentsViewState.value = { for (var attachment in attachments.where((item) => item.blobId != null)) attachment.blobId!: Right(IdleDownloadAttachmentForWeb()) }; - if (_canParseCalendarEvent(blobIds: success.attachments.calendarEventBlobIds)) { + if (_canParseCalendarEvent(blobIds: success.attachments?.calendarEventBlobIds ?? {})) { _parseCalendarEventAction( accountId: mailboxDashBoardController.accountId.value!, - blobIds: success.attachments.calendarEventBlobIds, + blobIds: success.attachments?.calendarEventBlobIds ?? {}, emailContents: success.htmlEmailContent ); } else { @@ -544,22 +546,23 @@ class SingleEmailController extends BaseController with AppLoaderMixin { currentEmailLoaded.value = EmailLoaded( htmlContent: success.htmlEmailContent, - attachments: List.of(success.attachments), + attachments: List.of(success.attachments ?? []), + inlineImages: List.of(success.inlineImages ?? []), emailCurrent: success.emailCurrent, ); emailSupervisorController.pushEmailQueue(currentEmailLoaded.value!); if (success.emailCurrent?.id == currentEmail?.id) { - attachments.value = success.attachments; + attachments.value = success.attachments ?? []; attachmentsViewState.value = { for (var attachment in attachments.where((item) => item.blobId != null)) attachment.blobId!: Right(IdleDownloadAttachmentForWeb()) }; - if (_canParseCalendarEvent(blobIds: success.attachments.calendarEventBlobIds)) { + if (_canParseCalendarEvent(blobIds: success.attachments?.calendarEventBlobIds ?? {})) { _parseCalendarEventAction( accountId: mailboxDashBoardController.accountId.value!, - blobIds: success.attachments.calendarEventBlobIds, + blobIds: success.attachments?.calendarEventBlobIds ?? {}, emailContents: success.htmlEmailContent ); } else { @@ -576,6 +579,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { htmlEmailContent: success.htmlEmailContent, messageId: success.emailCurrent?.messageId, references: success.emailCurrent?.references, + inlineImages: success.inlineImages, ); _storeOpenedEmailAction( @@ -1342,6 +1346,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { ComposerArguments.replyEmail( presentationEmail: presentationEmail, content: currentEmailLoaded.value?.htmlContent ?? '', + inlineImages: currentEmailLoaded.value?.inlineImages ?? [], mailboxRole: presentationEmail.mailboxContain?.role, messageId: currentEmailLoaded.value?.emailCurrent?.messageId, references: currentEmailLoaded.value?.emailCurrent?.references, @@ -1353,6 +1358,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { ComposerArguments.replyAllEmail( presentationEmail: presentationEmail, content: currentEmailLoaded.value?.htmlContent ?? '', + inlineImages: currentEmailLoaded.value?.inlineImages ?? [], mailboxRole: presentationEmail.mailboxContain?.role, messageId: currentEmailLoaded.value?.emailCurrent?.messageId, references: currentEmailLoaded.value?.emailCurrent?.references, @@ -1365,6 +1371,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { presentationEmail: presentationEmail, content: currentEmailLoaded.value?.htmlContent ?? '', attachments: attachments, + inlineImages: currentEmailLoaded.value?.inlineImages ?? [], messageId: currentEmailLoaded.value?.emailCurrent?.messageId, references: currentEmailLoaded.value?.emailCurrent?.references, ) diff --git a/lib/features/email/presentation/model/composer_arguments.dart b/lib/features/email/presentation/model/composer_arguments.dart index a7cc563d6a..bb755d54d1 100644 --- a/lib/features/email/presentation/model/composer_arguments.dart +++ b/lib/features/email/presentation/model/composer_arguments.dart @@ -28,7 +28,7 @@ class ComposerArguments extends RouterArguments { final List? identities; final Identity? selectedIdentity; final List? inlineImages; - final bool? readRecepientEnabled; + final bool isRequestReadReceipt; final ScreenDisplayMode displayMode; ComposerArguments({ @@ -48,7 +48,7 @@ class ComposerArguments extends RouterArguments { this.identities, this.selectedIdentity, this.inlineImages, - this.readRecepientEnabled, + this.isRequestReadReceipt = false, this.displayMode = ScreenDisplayMode.normal }); @@ -95,18 +95,29 @@ class ComposerArguments extends RouterArguments { emailActionType: EmailActionType.reopenComposerBrowser, presentationEmail: composerCache.email?.toPresentationEmail(), emailContents: composerCache.email?.emailContentList.asHtmlString, - attachments: composerCache.email?.allAttachments - .where((attachment) => attachment.disposition != ContentDisposition.inline) - .toList(), + attachments: composerCache.email?.allAttachments.getListAttachmentsDisplayedOutside(composerCache.email?.htmlBodyAttachments ?? []), selectedIdentity: composerCache.identity, - inlineImages: composerCache.email?.attachmentsWithCid, - readRecepientEnabled: composerCache.readReceipentEnabled, + inlineImages: composerCache.email?.allAttachments.listAttachmentsDisplayedInContent, + isRequestReadReceipt: composerCache.isRequestReadReceipt, + displayMode: composerCache.displayMode, + ); + + factory ComposerArguments.fromLocalStorageBrowser(ComposerCache composerCache) => + ComposerArguments( + emailActionType: EmailActionType.composeEmailIntoNewTab, + presentationEmail: composerCache.email?.toPresentationEmail(), + emailContents: composerCache.email?.emailContentList.asHtmlString, + attachments: composerCache.email?.allAttachments.getListAttachmentsDisplayedOutside(composerCache.email?.htmlBodyAttachments ?? []), + selectedIdentity: composerCache.identity, + inlineImages: composerCache.email?.allAttachments.listAttachmentsDisplayedInContent, + isRequestReadReceipt: composerCache.isRequestReadReceipt, displayMode: composerCache.displayMode, ); factory ComposerArguments.replyEmail({ required PresentationEmail presentationEmail, required String content, + required List inlineImages, Role? mailboxRole, MessageIdsHeaderValue? messageId, MessageIdsHeaderValue? references, @@ -114,6 +125,7 @@ class ComposerArguments extends RouterArguments { emailActionType: EmailActionType.reply, presentationEmail: presentationEmail, emailContents: content, + inlineImages: inlineImages, mailboxRole: mailboxRole, messageId: messageId, references: references, @@ -122,6 +134,7 @@ class ComposerArguments extends RouterArguments { factory ComposerArguments.replyAllEmail({ required PresentationEmail presentationEmail, required String content, + required List inlineImages, Role? mailboxRole, MessageIdsHeaderValue? messageId, MessageIdsHeaderValue? references, @@ -129,6 +142,7 @@ class ComposerArguments extends RouterArguments { emailActionType: EmailActionType.replyAll, presentationEmail: presentationEmail, emailContents: content, + inlineImages: inlineImages, mailboxRole: mailboxRole, messageId: messageId, references: references, @@ -138,6 +152,7 @@ class ComposerArguments extends RouterArguments { required PresentationEmail presentationEmail, required String content, required List attachments, + required List inlineImages, MessageIdsHeaderValue? messageId, MessageIdsHeaderValue? references, }) => ComposerArguments( @@ -145,6 +160,7 @@ class ComposerArguments extends RouterArguments { presentationEmail: presentationEmail, emailContents: content, attachments: attachments, + inlineImages: inlineImages, mailboxRole: presentationEmail.mailboxContain?.role, messageId: messageId, references: references, @@ -203,7 +219,7 @@ class ComposerArguments extends RouterArguments { List? identities, Identity? selectedIdentity, List? inlineImages, - bool? readRecepientEnabled, + bool? isRequestReadReceipt, ScreenDisplayMode? displayMode, }) { return ComposerArguments( @@ -223,7 +239,7 @@ class ComposerArguments extends RouterArguments { identities: identities ?? this.identities, selectedIdentity: selectedIdentity ?? this.selectedIdentity, inlineImages: inlineImages ?? this.inlineImages, - readRecepientEnabled: readRecepientEnabled ?? this.readRecepientEnabled, + isRequestReadReceipt: isRequestReadReceipt ?? this.isRequestReadReceipt, displayMode: displayMode ?? this.displayMode, ); } diff --git a/lib/features/email/presentation/model/email_loaded.dart b/lib/features/email/presentation/model/email_loaded.dart index 948a684216..99886aef70 100644 --- a/lib/features/email/presentation/model/email_loaded.dart +++ b/lib/features/email/presentation/model/email_loaded.dart @@ -5,11 +5,13 @@ import 'package:model/email/attachment.dart'; class EmailLoaded with EquatableMixin { final String htmlContent; final List attachments; + final List inlineImages; final Email? emailCurrent; EmailLoaded({ required this.htmlContent, required this.attachments, + required this.inlineImages, this.emailCurrent, }); @@ -17,6 +19,7 @@ class EmailLoaded with EquatableMixin { List get props => [ htmlContent, attachments, + inlineImages, emailCurrent ]; } diff --git a/lib/features/mailbox_dashboard/data/datasource/local_storage_browser_datasource.dart b/lib/features/mailbox_dashboard/data/datasource/local_storage_browser_datasource.dart new file mode 100644 index 0000000000..ef5f9bbef9 --- /dev/null +++ b/lib/features/mailbox_dashboard/data/datasource/local_storage_browser_datasource.dart @@ -0,0 +1,3 @@ +import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/storage_browser_datasource.dart'; + +abstract class LocalStorageBrowserDatasource extends StorageBrowserDatasource {} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/data/datasource/session_storage_composer_datasource.dart b/lib/features/mailbox_dashboard/data/datasource/session_storage_composer_datasource.dart index 1c36acf85d..b3b5b9553f 100644 --- a/lib/features/mailbox_dashboard/data/datasource/session_storage_composer_datasource.dart +++ b/lib/features/mailbox_dashboard/data/datasource/session_storage_composer_datasource.dart @@ -1,31 +1,3 @@ -import 'package:core/presentation/utils/html_transformer/transform_configuration.dart'; -import 'package:jmap_dart_client/jmap/account_id.dart'; -import 'package:jmap_dart_client/jmap/core/user_name.dart'; -import 'package:jmap_dart_client/jmap/identities/identity.dart'; -import 'package:jmap_dart_client/jmap/mail/email/email.dart'; -import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/data/model/composer_cache.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/storage_browser_datasource.dart'; -abstract class SessionStorageComposerDatasource { - Future saveComposerCacheOnWeb( - Email email, - { - required AccountId accountId, - required UserName userName, - required ScreenDisplayMode displayMode, - Identity? identity, - bool? readReceipentEnabled, - } - ); - - Future getComposerCacheOnWeb( - AccountId accountId, - UserName userName); - - Future removeComposerCacheOnWeb(); - - Future restoreEmailInlineImages( - String htmlContent, - TransformConfiguration transformConfiguration, - Map mapUrlDownloadCID); -} \ No newline at end of file +abstract class SessionStorageComposerDatasource extends StorageBrowserDatasource {} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/data/datasource/storage_browser_datasource.dart b/lib/features/mailbox_dashboard/data/datasource/storage_browser_datasource.dart new file mode 100644 index 0000000000..da50d6ff40 --- /dev/null +++ b/lib/features/mailbox_dashboard/data/datasource/storage_browser_datasource.dart @@ -0,0 +1,13 @@ +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/user_name.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/model/composer_cache.dart'; + +abstract class StorageBrowserDatasource { + String generateComposerCacheKey(AccountId accountId, UserName userName); + + Future saveComposerCache(ComposerCache composerCache, AccountId accountId, UserName userName); + + Future getComposerCache(AccountId accountId, UserName userName); + + Future deleteComposerCache(); +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/data/datasource_impl/local_storage_browser_datasoure_impl.dart b/lib/features/mailbox_dashboard/data/datasource_impl/local_storage_browser_datasoure_impl.dart new file mode 100644 index 0000000000..35c58f5cca --- /dev/null +++ b/lib/features/mailbox_dashboard/data/datasource_impl/local_storage_browser_datasoure_impl.dart @@ -0,0 +1,64 @@ +import 'dart:convert'; + +import 'package:collection/collection.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/user_name.dart'; +import 'package:model/email/email_action_type.dart'; +import 'package:model/extensions/account_id_extensions.dart'; +import 'package:tmail_ui_user/features/caching/utils/cache_utils.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/local_storage_browser_datasource.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/model/composer_cache.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/exceptions/storage_browser_exception.dart'; +import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart'; +import 'package:universal_html/html.dart' as html; + +class LocalStorageBrowserDatasourceImpl extends LocalStorageBrowserDatasource { + final ExceptionThrower _exceptionThrower; + + LocalStorageBrowserDatasourceImpl(this._exceptionThrower); + + @override + String generateComposerCacheKey(AccountId accountId, UserName userName) { + return TupleKey( + EmailActionType.composeEmailIntoNewTab.name, + accountId.asString, + userName.value).toString(); + } + + @override + Future deleteComposerCache() { + return Future.sync(() async { + return html.window.localStorage.removeWhere((key, value) => + key.startsWith(EmailActionType.composeEmailIntoNewTab.name)); + }).catchError(_exceptionThrower.throwException); + } + + @override + Future getComposerCache(AccountId accountId, UserName userName) { + return Future.sync(() async { + final storageKey = generateComposerCacheKey(accountId, userName); + + final result = html.window.localStorage.entries + .firstWhereOrNull((entry) => entry.key == storageKey); + + if (result != null) { + return ComposerCache.fromJson(jsonDecode(result.value)); + } else { + throw NotFoundDataInStorageBrowserException(); + } + }).catchError(_exceptionThrower.throwException); + } + + @override + Future saveComposerCache(ComposerCache composerCache, AccountId accountId, UserName userName) { + return Future.sync(() async { + final storageKey = generateComposerCacheKey(accountId, userName); + + Map entries = { + storageKey: jsonEncode(composerCache.toJson()) + }; + + return html.window.localStorage.addAll(entries); + }).catchError(_exceptionThrower.throwException); + } +} diff --git a/lib/features/mailbox_dashboard/data/datasource_impl/session_storage_composer_datasoure_impl.dart b/lib/features/mailbox_dashboard/data/datasource_impl/session_storage_composer_datasoure_impl.dart index 9ff3d3c88b..8d2a214b55 100644 --- a/lib/features/mailbox_dashboard/data/datasource_impl/session_storage_composer_datasoure_impl.dart +++ b/lib/features/mailbox_dashboard/data/datasource_impl/session_storage_composer_datasoure_impl.dart @@ -1,94 +1,62 @@ import 'dart:convert'; import 'package:collection/collection.dart'; -import 'package:core/core.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; -import 'package:jmap_dart_client/jmap/identities/identity.dart'; -import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/caching/utils/cache_utils.dart'; -import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/session_storage_composer_datasource.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/model/composer_cache.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/exceptions/storage_browser_exception.dart'; import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart'; import 'package:universal_html/html.dart' as html; -class SessionStorageComposerDatasourceImpl - extends SessionStorageComposerDatasource { - SessionStorageComposerDatasourceImpl(this._htmlTransform, this._exceptionThrower); - - final HtmlTransform _htmlTransform; +class SessionStorageComposerDatasourceImpl extends SessionStorageComposerDatasource { final ExceptionThrower _exceptionThrower; + SessionStorageComposerDatasourceImpl(this._exceptionThrower); + @override - Future getComposerCacheOnWeb( - AccountId accountId, - UserName userName - ) async { - return Future.sync(() async { - final keyWithIdentity = TupleKey( + String generateComposerCacheKey(AccountId accountId, UserName userName) { + return TupleKey( EmailActionType.reopenComposerBrowser.name, accountId.asString, userName.value).toString(); - - final result = html.window.sessionStorage.entries.firstWhereOrNull( - (entry) => entry.key == keyWithIdentity); - if (result != null) { - return ComposerCache.fromJson(jsonDecode(result.value)); - } else { - throw NotFoundInWebSessionException(); - } - }).catchError(_exceptionThrower.throwException); } @override - Future removeComposerCacheOnWeb() async { - return Future.sync(() { - html.window.sessionStorage.removeWhere( - (key, value) => key.startsWith(EmailActionType.reopenComposerBrowser.name)); + Future deleteComposerCache() { + return Future.sync(() async { + return html.window.sessionStorage.removeWhere((key, value) => + key.startsWith(EmailActionType.reopenComposerBrowser.name)); }).catchError(_exceptionThrower.throwException); } @override - Future saveComposerCacheOnWeb( - Email email, - { - required AccountId accountId, - required UserName userName, - required ScreenDisplayMode displayMode, - Identity? identity, - bool? readReceipentEnabled - } - ) async { - return Future.sync(() { - final composerCacheKey = TupleKey( - EmailActionType.reopenComposerBrowser.name, - accountId.asString, - userName.value).toString(); - Map entries = { - composerCacheKey: jsonEncode( - ComposerCache( - displayMode: displayMode, - email: email, - identity: identity, - readReceipentEnabled: readReceipentEnabled, - ).toJson() - ) - }; - html.window.sessionStorage.addAll(entries); + Future getComposerCache(AccountId accountId, UserName userName) { + return Future.sync(() async { + final storageKey = generateComposerCacheKey(accountId, userName); + + final result = html.window.sessionStorage.entries + .firstWhereOrNull((entry) => entry.key == storageKey); + + if (result != null) { + return ComposerCache.fromJson(jsonDecode(result.value)); + } else { + throw NotFoundDataInStorageBrowserException(); + } }).catchError(_exceptionThrower.throwException); } - + @override - Future restoreEmailInlineImages( - String htmlContent, - TransformConfiguration transformConfiguration, - Map mapUrlDownloadCID) { + Future saveComposerCache(ComposerCache composerCache, AccountId accountId, UserName userName) { return Future.sync(() async { - return await _htmlTransform.transformToHtml( - htmlContent: htmlContent, - transformConfiguration: transformConfiguration, - mapCidImageDownloadUrl: mapUrlDownloadCID); + final storageKey = generateComposerCacheKey(accountId, userName); + + Map entries = { + storageKey: jsonEncode(composerCache.toJson()) + }; + + return html.window.sessionStorage.addAll(entries); }).catchError(_exceptionThrower.throwException); } } diff --git a/lib/features/mailbox_dashboard/data/model/composer_cache.dart b/lib/features/mailbox_dashboard/data/model/composer_cache.dart index 5eda2a6ba6..025b7c2e4f 100644 --- a/lib/features/mailbox_dashboard/data/model/composer_cache.dart +++ b/lib/features/mailbox_dashboard/data/model/composer_cache.dart @@ -1,44 +1,35 @@ import 'package:equatable/equatable.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:json_annotation/json_annotation.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart'; +part 'composer_cache.g.dart'; + +@JsonSerializable(explicitToJson: true, includeIfNull: false) class ComposerCache with EquatableMixin { final Email? email; final Identity? identity; - final bool? readReceipentEnabled; + final bool isRequestReadReceipt; final ScreenDisplayMode displayMode; ComposerCache({ - required this.displayMode, this.email, this.identity, - this.readReceipentEnabled, + this.isRequestReadReceipt = false, + this.displayMode = ScreenDisplayMode.normal, }); + factory ComposerCache.fromJson(Map json) => _$ComposerCacheFromJson(json); + + Map toJson() => _$ComposerCacheToJson(this); + @override List get props => [ email, identity, - readReceipentEnabled + isRequestReadReceipt, + displayMode ]; - - Map toJson() { - return { - 'email': email?.toJson(), - 'identity': identity?.toJson(), - 'readReceipentEnabled': readReceipentEnabled, - 'displayMode': displayMode.toJson() - }; - } - - factory ComposerCache.fromJson(Map map) { - return ComposerCache( - displayMode: ScreenDisplayMode.fromJson(map['displayMode'] ?? ''), - email: map['email'] != null ? Email.fromJson(map['email']) : null, - identity: map['identity'] != null ? Identity.fromJson(map['identity']) : null, - readReceipentEnabled: map['readReceipentEnabled'] as bool? - ); - } } diff --git a/lib/features/mailbox_dashboard/data/repository/composer_cache_repository_impl.dart b/lib/features/mailbox_dashboard/data/repository/composer_cache_repository_impl.dart index cb0e6d903a..cf6a2507c6 100644 --- a/lib/features/mailbox_dashboard/data/repository/composer_cache_repository_impl.dart +++ b/lib/features/mailbox_dashboard/data/repository/composer_cache_repository_impl.dart @@ -1,60 +1,83 @@ import 'package:core/presentation/utils/html_transformer/transform_configuration.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; -import 'package:jmap_dart_client/jmap/identities/identity.dart'; -import 'package:jmap_dart_client/jmap/mail/email/email.dart'; -import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart'; +import 'package:tmail_ui_user/features/email/data/datasource/html_datasource.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/local_storage_browser_datasource.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/session_storage_composer_datasource.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/model/composer_cache.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/composer_cache_repository.dart'; class ComposerCacheRepositoryImpl extends ComposerCacheRepository { - final SessionStorageComposerDatasource composerCacheDataSource; + final SessionStorageComposerDatasource _sessionStorageComposerDatasource; + final LocalStorageBrowserDatasource _localStorageBrowserDatasource; + final HtmlDataSource _htmlDataSource; - ComposerCacheRepositoryImpl(this.composerCacheDataSource); + ComposerCacheRepositoryImpl( + this._sessionStorageComposerDatasource, + this._localStorageBrowserDatasource, + this._htmlDataSource, + ); @override Future getComposerCacheOnWeb( AccountId accountId, UserName userName ) { - return composerCacheDataSource.getComposerCacheOnWeb(accountId, userName); + return _sessionStorageComposerDatasource.getComposerCache(accountId, userName); } @override Future removeComposerCacheOnWeb() { - return composerCacheDataSource.removeComposerCacheOnWeb(); + return _sessionStorageComposerDatasource.deleteComposerCache(); } @override Future saveComposerCacheOnWeb( - Email email, - { - required AccountId accountId, - required UserName userName, - required ScreenDisplayMode displayMode, - Identity? identity, - bool? readReceipentEnabled - } + ComposerCache composerCache, + AccountId accountId, + UserName userName ) { - return composerCacheDataSource.saveComposerCacheOnWeb( - email, - accountId: accountId, - userName: userName, - displayMode: displayMode, - identity: identity, - readReceipentEnabled: readReceipentEnabled); + return _sessionStorageComposerDatasource.saveComposerCache( + composerCache, + accountId, + userName); } @override - Future restoreEmailInlineImages( + Future convertImageCIDToBase64( String htmlContent, TransformConfiguration transformConfiguration, - Map mapUrlDownloadCID) { - return composerCacheDataSource.restoreEmailInlineImages( + Map mapUrlDownloadCID + ) { + return _htmlDataSource.convertImageCIDToBase64( htmlContent, transformConfiguration, mapUrlDownloadCID); } + + @override + Future deleteComposerCacheInLocalStorageBrowser() { + return _localStorageBrowserDatasource.deleteComposerCache(); + } + + @override + Future getComposerCacheInLocalStorageBrowser( + AccountId accountId, + UserName userName + ) { + return _localStorageBrowserDatasource.getComposerCache(accountId, userName); + } + + @override + Future saveComposerCacheToLocalStorageBrowser( + ComposerCache composerCache, + AccountId accountId, + UserName userName + ) { + return _localStorageBrowserDatasource.saveComposerCache( + composerCache, + accountId, + userName); + } } \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/exceptions/storage_browser_exception.dart b/lib/features/mailbox_dashboard/domain/exceptions/storage_browser_exception.dart new file mode 100644 index 0000000000..567cd085a3 --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/exceptions/storage_browser_exception.dart @@ -0,0 +1,2 @@ + +class NotFoundDataInStorageBrowserException implements Exception {} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/repository/composer_cache_repository.dart b/lib/features/mailbox_dashboard/domain/repository/composer_cache_repository.dart index 07175440fd..ee1963d4d9 100644 --- a/lib/features/mailbox_dashboard/domain/repository/composer_cache_repository.dart +++ b/lib/features/mailbox_dashboard/domain/repository/composer_cache_repository.dart @@ -1,22 +1,13 @@ import 'package:core/presentation/utils/html_transformer/transform_configuration.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; -import 'package:jmap_dart_client/jmap/identities/identity.dart'; -import 'package:jmap_dart_client/jmap/mail/email/email.dart'; -import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/model/composer_cache.dart'; abstract class ComposerCacheRepository { Future saveComposerCacheOnWeb( - Email email, - { - required AccountId accountId, - required UserName userName, - required ScreenDisplayMode displayMode, - Identity? identity, - bool? readReceipentEnabled - } - ); + ComposerCache composerCache, + AccountId accountId, + UserName userName); Future getComposerCacheOnWeb( AccountId accountId, @@ -24,8 +15,19 @@ abstract class ComposerCacheRepository { Future removeComposerCacheOnWeb(); - Future restoreEmailInlineImages( + Future convertImageCIDToBase64( String htmlContent, TransformConfiguration transformConfiguration, Map mapUrlDownloadCID); + + Future saveComposerCacheToLocalStorageBrowser( + ComposerCache composerCache, + AccountId accountId, + UserName userName); + + Future getComposerCacheInLocalStorageBrowser( + AccountId accountId, + UserName userName); + + Future deleteComposerCacheInLocalStorageBrowser(); } diff --git a/lib/features/mailbox_dashboard/domain/state/delete_composer_cache_in_local_storage_browser_state.dart b/lib/features/mailbox_dashboard/domain/state/delete_composer_cache_in_local_storage_browser_state.dart new file mode 100644 index 0000000000..3bd0bdabbb --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/state/delete_composer_cache_in_local_storage_browser_state.dart @@ -0,0 +1,11 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; + +class DeleteComposeCacheInLocalStorageBrowserLoading extends LoadingState {} + +class DeleteComposeCacheInLocalStorageBrowserSuccess extends UIState {} + +class DeleteComposeCacheInLocalStorageBrowserFailure extends FeatureFailure { + + DeleteComposeCacheInLocalStorageBrowserFailure(dynamic exception) : super(exception: exception); +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/state/get_composer_cache_in_local_storage_browser_state.dart b/lib/features/mailbox_dashboard/domain/state/get_composer_cache_in_local_storage_browser_state.dart new file mode 100644 index 0000000000..0d97e45544 --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/state/get_composer_cache_in_local_storage_browser_state.dart @@ -0,0 +1,20 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/model/composer_cache.dart'; + +class GetComposerCacheInLocalStorageBrowserLoading extends LoadingState {} + +class GetComposerCacheInLocalStorageBrowserSuccess extends UIState { + + final ComposerCache composerCache; + + GetComposerCacheInLocalStorageBrowserSuccess(this.composerCache); + + @override + List get props => [composerCache]; +} + +class GetComposerCacheInLocalStorageBrowserFailure extends FeatureFailure { + + GetComposerCacheInLocalStorageBrowserFailure(dynamic exception) : super(exception: exception); +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/state/save_composer_cache_to_local_storage_browser_state.dart b/lib/features/mailbox_dashboard/domain/state/save_composer_cache_to_local_storage_browser_state.dart new file mode 100644 index 0000000000..d5773dd15d --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/state/save_composer_cache_to_local_storage_browser_state.dart @@ -0,0 +1,11 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; + +class SaveComposerCacheToLocalStorageBrowserLoading extends LoadingState {} + +class SaveComposerCacheToLocalStorageBrowserSuccess extends UIState {} + +class SaveComposerCacheToLocalStorageBrowserFailure extends FeatureFailure { + + SaveComposerCacheToLocalStorageBrowserFailure(dynamic exception) : super(exception: exception); +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/usecases/delete_composer_cache_in_local_storage_browser_interactor.dart b/lib/features/mailbox_dashboard/domain/usecases/delete_composer_cache_in_local_storage_browser_interactor.dart new file mode 100644 index 0000000000..86df892c1e --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/usecases/delete_composer_cache_in_local_storage_browser_interactor.dart @@ -0,0 +1,21 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/composer_cache_repository.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/delete_composer_cache_in_local_storage_browser_state.dart'; + +class DeleteComposerCacheInLocalStorageBrowserInteractor { + final ComposerCacheRepository _composerCacheRepository; + + DeleteComposerCacheInLocalStorageBrowserInteractor(this._composerCacheRepository); + + Stream> execute() async* { + try { + yield Right(DeleteComposeCacheInLocalStorageBrowserLoading()); + await _composerCacheRepository.deleteComposerCacheInLocalStorageBrowser(); + yield Right(DeleteComposeCacheInLocalStorageBrowserSuccess()); + } catch (exception) { + yield Left(DeleteComposeCacheInLocalStorageBrowserFailure(exception)); + } + } +} diff --git a/lib/features/mailbox_dashboard/domain/usecases/get_composer_cache_in_local_storage_browser_interactor.dart b/lib/features/mailbox_dashboard/domain/usecases/get_composer_cache_in_local_storage_browser_interactor.dart new file mode 100644 index 0000000000..59e37afd3d --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/usecases/get_composer_cache_in_local_storage_browser_interactor.dart @@ -0,0 +1,23 @@ +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/user_name.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/composer_cache_repository.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_composer_cache_in_local_storage_browser_state.dart'; + +class GetComposerCacheInLocalStorageBrowserInteractor { + final ComposerCacheRepository _composerCacheRepository; + + GetComposerCacheInLocalStorageBrowserInteractor(this._composerCacheRepository); + + Stream> execute(AccountId accountId, UserName userName) async* { + try { + yield Right(GetComposerCacheInLocalStorageBrowserLoading()); + final composerCache = await _composerCacheRepository.getComposerCacheInLocalStorageBrowser(accountId, userName); + yield Right(GetComposerCacheInLocalStorageBrowserSuccess(composerCache)); + } catch (exception) { + yield Left(GetComposerCacheInLocalStorageBrowserFailure(exception)); + } + } +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/usecases/save_composer_cache_to_local_storage_browser_interactor.dart b/lib/features/mailbox_dashboard/domain/usecases/save_composer_cache_to_local_storage_browser_interactor.dart new file mode 100644 index 0000000000..be727f2268 --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/usecases/save_composer_cache_to_local_storage_browser_interactor.dart @@ -0,0 +1,46 @@ +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/user_name.dart'; +import 'package:tmail_ui_user/features/composer/domain/repository/composer_repository.dart'; +import 'package:tmail_ui_user/features/composer/presentation/model/create_email_request.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/model/composer_cache.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/composer_cache_repository.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/save_composer_cache_to_local_storage_browser_state.dart'; + +class SaveComposerCacheToLocalStorageBrowserInteractor { + final ComposerCacheRepository _composerCacheRepository; + final ComposerRepository _composerRepository; + + SaveComposerCacheToLocalStorageBrowserInteractor( + this._composerCacheRepository, + this._composerRepository, + ); + + Stream> execute( + CreateEmailRequest createEmailRequest, + AccountId accountId, + UserName userName, + ) async* { + try { + yield Right(SaveComposerCacheToLocalStorageBrowserLoading()); + + final emailCreated = await _composerRepository.generateEmail(createEmailRequest); + final composerCache = ComposerCache( + email: emailCreated, + identity: createEmailRequest.identity, + isRequestReadReceipt: createEmailRequest.isRequestReadReceipt, + displayMode: createEmailRequest.displayMode); + + await _composerCacheRepository.saveComposerCacheToLocalStorageBrowser( + composerCache, + accountId, + userName); + + yield Right(SaveComposerCacheToLocalStorageBrowserSuccess()); + } catch (exception) { + yield Left(SaveComposerCacheToLocalStorageBrowserFailure(exception)); + } + } +} 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 ace76dc1ef..90d8f75762 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,5 @@ import 'package:core/data/model/source_type/data_source_type.dart'; import 'package:core/presentation/resources/image_paths.dart'; -import 'package:core/presentation/utils/html_transformer/html_transform.dart'; import 'package:core/utils/config/app_config_loader.dart'; import 'package:core/utils/file_utils.dart'; import 'package:core/utils/print_utils.dart'; @@ -45,11 +44,13 @@ 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/mailbox/domain/usecases/mark_as_mailbox_read_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_bindings.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/local_storage_browser_datasource.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/search_datasource.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/session_storage_composer_datasource.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/spam_report_datasource.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource_impl/hive_spam_report_datasource_impl.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource_impl/local_spam_report_datasource_impl.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource_impl/local_storage_browser_datasoure_impl.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource_impl/search_datasource_impl.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource_impl/session_storage_composer_datasoure_impl.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource_impl/spam_report_datasource_impl.dart'; @@ -61,8 +62,10 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/data/repository/spam_re import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/composer_cache_repository.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/search_repository.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/spam_report_repository.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/delete_composer_cache_in_local_storage_browser_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_all_recent_search_latest_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_app_dashboard_configuration_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_composer_cache_in_local_storage_browser_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_composer_cache_on_web_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_spam_mailbox_cached_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_spam_report_state_interactor.dart'; @@ -178,6 +181,8 @@ class MailboxDashBoardBindings extends BaseBindings { Get.find(), Get.find(), Get.find(), + Get.find(), + Get.find(), )); Get.put(AdvancedFilterController()); } @@ -192,6 +197,7 @@ class MailboxDashBoardBindings extends BaseBindings { Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); + Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); @@ -233,9 +239,8 @@ class MailboxDashBoardBindings extends BaseBindings { Get.lazyPut(() => MailboxCacheDataSourceImpl( Get.find(), Get.find())); - Get.lazyPut(() => SessionStorageComposerDatasourceImpl( - Get.find(), - Get.find())); + Get.lazyPut(() => SessionStorageComposerDatasourceImpl(Get.find())); + Get.lazyPut(() => LocalStorageBrowserDatasourceImpl(Get.find())); Get.lazyPut(() => SpamReportDataSourceImpl( Get.find(), Get.find(), @@ -284,6 +289,8 @@ class MailboxDashBoardBindings extends BaseBindings { Get.find()) ); Get.lazyPut(() => GetComposerCacheOnWebInteractor(Get.find())); + Get.lazyPut(() => GetComposerCacheInLocalStorageBrowserInteractor(Get.find())); + Get.lazyPut(() => DeleteComposerCacheInLocalStorageBrowserInteractor(Get.find())); Get.lazyPut(() => RemoveComposerCacheOnWebInteractor(Get.find())); Get.lazyPut(() => MarkAsEmailReadInteractor( Get.find(), @@ -387,7 +394,11 @@ class MailboxDashBoardBindings extends BaseBindings { }, Get.find(), )); - Get.lazyPut(() => ComposerCacheRepositoryImpl(Get.find())); + Get.lazyPut(() => ComposerCacheRepositoryImpl( + Get.find(), + Get.find(), + Get.find(), + )); Get.lazyPut(() => SpamReportRepositoryImpl( { DataSourceType.network: Get.find(), diff --git a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart index 1c7103e456..932b48c75a 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -79,8 +79,11 @@ import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentat import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/spam_report_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_app_dashboard_configuration_state.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_composer_cache_in_local_storage_browser_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_composer_cache_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/remove_email_drafts_state.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/delete_composer_cache_in_local_storage_browser_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_composer_cache_in_local_storage_browser_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_composer_cache_on_web_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_composer_cache_on_web_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_email_drafts_interactor.dart'; @@ -189,6 +192,8 @@ class MailboxDashBoardController extends ReloadableController { final GetRestoredDeletedMessageInterator _getRestoredDeletedMessageInteractor; final RemoveComposerCacheOnWebInteractor _removeComposerCacheOnWebInteractor; final GetAllIdentitiesInteractor _getAllIdentitiesInteractor; + final GetComposerCacheInLocalStorageBrowserInteractor _getComposerCacheInLocalStorageBrowserInteractor; + final DeleteComposerCacheInLocalStorageBrowserInteractor _deleteComposerCacheInLocalStorageBrowserInteractor; GetAllVacationInteractor? _getAllVacationInteractor; UpdateVacationInteractor? _updateVacationInteractor; @@ -269,6 +274,8 @@ class MailboxDashBoardController extends ReloadableController { this._getRestoredDeletedMessageInteractor, this._removeComposerCacheOnWebInteractor, this._getAllIdentitiesInteractor, + this._getComposerCacheInLocalStorageBrowserInteractor, + this._deleteComposerCacheInLocalStorageBrowserInteractor, ); @override @@ -373,6 +380,9 @@ class MailboxDashBoardController extends ReloadableController { _handleGetAllIdentitiesSuccess(success); } else if (success is GetComposerCacheSuccess) { goToComposer(ComposerArguments.fromSessionStorageBrowser(success.composerCache)); + } else if (success is GetComposerCacheInLocalStorageBrowserSuccess) { + goToComposer(ComposerArguments.fromLocalStorageBrowser(success.composerCache)); + _deleteComposerCacheInLocalStorageBrowser(); } } @@ -397,6 +407,8 @@ class MailboxDashBoardController extends ReloadableController { _handleRestoreDeletedMessageFailed(); } else if (failure is GetRestoredDeletedMessageFailure) { _handleRestoreDeletedMessageFailed(); + } else if (failure is GetComposerCacheInLocalStorageBrowserFailure) { + _deleteComposerCacheInLocalStorageBrowser(); } } @@ -1298,6 +1310,7 @@ class MailboxDashBoardController extends ReloadableController { _setUpComponentsFromSession(session); if (PlatformInfo.isWeb) { _handleComposerCache(); + _restoreComposerCacheInLocalStorageBrowser(session); } } @@ -2549,6 +2562,16 @@ class MailboxDashBoardController extends ReloadableController { List get listIdentities => _identities ?? []; + void _restoreComposerCacheInLocalStorageBrowser(Session session) { + consumeState(_getComposerCacheInLocalStorageBrowserInteractor.execute( + session.personalAccount.accountId, + session.username)); + } + + void _deleteComposerCacheInLocalStorageBrowser() { + consumeState(_deleteComposerCacheInLocalStorageBrowserInteractor.execute()); + } + @override void onClose() { _emailReceiveManager.closeEmailReceiveManagerStream(); diff --git a/lib/features/offline_mode/model/detailed_email_hive_cache.dart b/lib/features/offline_mode/model/detailed_email_hive_cache.dart index f15f266db6..0eefebec65 100644 --- a/lib/features/offline_mode/model/detailed_email_hive_cache.dart +++ b/lib/features/offline_mode/model/detailed_email_hive_cache.dart @@ -35,6 +35,9 @@ class DetailedEmailHiveCache extends HiveObject with EquatableMixin { @HiveField(7) final List? references; + @HiveField(8) + final List? inlineImages; + DetailedEmailHiveCache({ required this.emailId, required this.timeSaved, @@ -44,6 +47,7 @@ class DetailedEmailHiveCache extends HiveObject with EquatableMixin { this.keywords, this.messageId, this.references, + this.inlineImages, }); @override @@ -56,5 +60,6 @@ class DetailedEmailHiveCache extends HiveObject with EquatableMixin { keywords, messageId, references, + inlineImages, ]; } \ No newline at end of file diff --git a/lib/features/upload/presentation/controller/upload_controller.dart b/lib/features/upload/presentation/controller/upload_controller.dart index ee62b3f3b0..057ccacff1 100644 --- a/lib/features/upload/presentation/controller/upload_controller.dart +++ b/lib/features/upload/presentation/controller/upload_controller.dart @@ -427,18 +427,6 @@ class UploadController extends BaseController { uploadInlineViewState.value = Right(UIState.idle); } - List get inlineAttachmentsUploaded { - if (_uploadingStateInlineFiles.uploadingStateFiles.isEmpty) { - return List.empty(); - } - return _uploadingStateInlineFiles.uploadingStateFiles - .whereNotNull() - .map((fileState) => fileState.attachment) - .whereNotNull() - .where((attachment) => attachment.cid != null) - .toList(); - } - List get inlineAttachmentsPicked { if (_uploadingStateInlineFiles.uploadingStateFiles.isEmpty) { return List.empty(); diff --git a/model/lib/email/email_action_type.dart b/model/lib/email/email_action_type.dart index a125b4c3ae..65e83de4d5 100644 --- a/model/lib/email/email_action_type.dart +++ b/model/lib/email/email_action_type.dart @@ -28,5 +28,6 @@ enum EmailActionType { composeFromUnsubscribeMailtoLink, archiveMessage, printAll, - downloadMessageAsEML + downloadMessageAsEML, + composeEmailIntoNewTab } \ No newline at end of file diff --git a/model/lib/extensions/email_extension.dart b/model/lib/extensions/email_extension.dart index 7b7707abde..2a1a9cf519 100644 --- a/model/lib/extensions/email_extension.dart +++ b/model/lib/extensions/email_extension.dart @@ -151,8 +151,6 @@ extension EmailExtension on Email { List get allAttachments => attachments?.map((item) => item.toAttachment()).toList() ?? []; - List get attachmentsWithCid => allAttachments.where((attachment) => attachment.hasCid()).toList(); - PresentationMailbox? findMailboxContain(Map mapMailbox) { final newMailboxIds = mailboxIds; newMailboxIds?.removeWhere((key, value) => !value); diff --git a/model/lib/extensions/list_attachment_extension.dart b/model/lib/extensions/list_attachment_extension.dart index e8f788170a..d67cc6a67f 100644 --- a/model/lib/extensions/list_attachment_extension.dart +++ b/model/lib/extensions/list_attachment_extension.dart @@ -33,7 +33,7 @@ extension ListAttachmentExtension on List { required String downloadUrl }) { final mapUrlDownloadCID = { - for (var attachment in listAttachmentsDisplayedInContent) + for (var attachment in this) attachment.cid! : attachment.getDownloadUrl(downloadUrl, accountId) }; return mapUrlDownloadCID; diff --git a/test/features/composer/presentation/composer_controller_test.dart b/test/features/composer/presentation/composer_controller_test.dart new file mode 100644 index 0000000000..9758c186fc --- /dev/null +++ b/test/features/composer/presentation/composer_controller_test.dart @@ -0,0 +1,211 @@ +import 'package:core/data/network/config/dynamic_url_interceptors.dart'; +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/application_manager.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:get/get.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:tmail_ui_user/features/base/before_unload_manager.dart'; +import 'package:tmail_ui_user/features/base/state/button_state.dart'; +import 'package:tmail_ui_user/features/caching/caching_manager.dart'; +import 'package:tmail_ui_user/features/composer/domain/usecases/create_new_and_save_email_to_drafts_interactor.dart'; +import 'package:tmail_ui_user/features/composer/domain/usecases/create_new_and_send_email_interactor.dart'; +import 'package:tmail_ui_user/features/composer/domain/usecases/download_image_as_base64_interactor.dart'; +import 'package:tmail_ui_user/features/composer/domain/usecases/save_composer_cache_on_web_interactor.dart'; +import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/transform_html_email_content_interactor.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; +import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_composer_cache_on_web_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_composer_cache_to_local_storage_browser_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; +import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; +import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart'; +import 'package:tmail_ui_user/features/network_connection/presentation/network_connection_controller.dart'; +import 'package:tmail_ui_user/features/server_settings/domain/usecases/get_always_read_receipt_setting_interactor.dart'; +import 'package:tmail_ui_user/features/upload/domain/usecases/local_file_picker_interactor.dart'; +import 'package:tmail_ui_user/features/upload/domain/usecases/local_image_picker_interactor.dart'; +import 'package:tmail_ui_user/features/upload/presentation/controller/upload_controller.dart'; +import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart'; +import 'package:uuid/uuid.dart'; + +import '../../../fixtures/account_fixtures.dart'; +import '../../../fixtures/session_fixtures.dart'; +import 'composer_controller_test.mocks.dart'; + +mockControllerCallback() => InternalFinalCallback(callback: () {}); +const fallbackGenerators = { + #onStart: mockControllerCallback, + #onDelete: mockControllerCallback, +}; + +@GenerateNiceMocks([ + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(fallbackGenerators: fallbackGenerators), + MockSpec(fallbackGenerators: fallbackGenerators), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), +]) +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + late ComposerController composerController; + late MockLocalFilePickerInteractor mockLocalFilePickerInteractor; + late MockLocalImagePickerInteractor mockLocalImagePickerInteractor; + late MockGetEmailContentInteractor mockGetEmailContentInteractor; + late MockGetAllIdentitiesInteractor mockGetAllIdentitiesInteractor; + late MockUploadController mockUploadController; + late MockRemoveComposerCacheOnWebInteractor mockRemoveComposerCacheOnWebInteractor; + late MockSaveComposerCacheOnWebInteractor mockSaveComposerCacheOnWebInteractor; + late MockDownloadImageAsBase64Interactor mockDownloadImageAsBase64Interactor; + late MockTransformHtmlEmailContentInteractor mockTransformHtmlEmailContentInteractor; + late MockGetAlwaysReadReceiptSettingInteractor mockGetAlwaysReadReceiptSettingInteractor; + late MockCreateNewAndSendEmailInteractor mockCreateNewAndSendEmailInteractor; + late MockCreateNewAndSaveEmailToDraftsInteractor mockCreateNewAndSaveEmailToDraftsInteractor; + late MockSaveComposerCacheToLocalStorageBrowserInteractor mockSaveComposerCacheToLocalStorageBrowserInteractor; + + late MockNetworkConnectionController mockNetworkConnectionController; + late MockMailboxDashBoardController mockMailboxDashBoardController; + + late MockCachingManager mockCachingManager; + late MockLanguageCacheManager mockLanguageCacheManager; + late MockAuthorizationInterceptors mockAuthorizationInterceptors; + late MockDynamicUrlInterceptors mockDynamicUrlInterceptors; + late MockDeleteCredentialInteractor mockDeleteCredentialInteractor; + late MockLogoutOidcInteractor mockLogoutOidcInteractor; + late MockDeleteAuthorityOidcInteractor mockDeleteAuthorityOidcInteractor; + late MockAppToast mockAppToast; + late MockImagePaths mockImagePaths; + late MockResponsiveUtils mockResponsiveUtils; + late MockUuid mockUuid; + late MockApplicationManager mockApplicationManager; + late MockBeforeUnloadManager mockBeforeUnloadManager; + + setUpAll(() { + mockLocalFilePickerInteractor = MockLocalFilePickerInteractor(); + mockLocalImagePickerInteractor = MockLocalImagePickerInteractor(); + mockGetEmailContentInteractor = MockGetEmailContentInteractor(); + mockGetAllIdentitiesInteractor = MockGetAllIdentitiesInteractor(); + mockUploadController = MockUploadController(); + mockRemoveComposerCacheOnWebInteractor = MockRemoveComposerCacheOnWebInteractor(); + mockSaveComposerCacheOnWebInteractor = MockSaveComposerCacheOnWebInteractor(); + mockDownloadImageAsBase64Interactor = MockDownloadImageAsBase64Interactor(); + mockTransformHtmlEmailContentInteractor = MockTransformHtmlEmailContentInteractor(); + mockGetAlwaysReadReceiptSettingInteractor = MockGetAlwaysReadReceiptSettingInteractor(); + mockCreateNewAndSendEmailInteractor = MockCreateNewAndSendEmailInteractor(); + mockCreateNewAndSaveEmailToDraftsInteractor = MockCreateNewAndSaveEmailToDraftsInteractor(); + mockSaveComposerCacheToLocalStorageBrowserInteractor = MockSaveComposerCacheToLocalStorageBrowserInteractor(); + + mockNetworkConnectionController = MockNetworkConnectionController(); + mockMailboxDashBoardController = MockMailboxDashBoardController(); + + // mock base controller + mockCachingManager = MockCachingManager(); + mockLanguageCacheManager = MockLanguageCacheManager(); + mockAuthorizationInterceptors = MockAuthorizationInterceptors(); + mockDynamicUrlInterceptors = MockDynamicUrlInterceptors(); + mockDeleteCredentialInteractor = MockDeleteCredentialInteractor(); + mockLogoutOidcInteractor = MockLogoutOidcInteractor(); + mockDeleteAuthorityOidcInteractor = MockDeleteAuthorityOidcInteractor(); + mockAppToast = MockAppToast(); + mockImagePaths = MockImagePaths(); + mockResponsiveUtils = MockResponsiveUtils(); + mockUuid = MockUuid(); + mockApplicationManager = MockApplicationManager(); + mockBeforeUnloadManager = MockBeforeUnloadManager(); + + Get.put(mockNetworkConnectionController); + Get.put(mockMailboxDashBoardController); + + Get.put(mockCachingManager); + Get.put(mockLanguageCacheManager); + Get.put(mockAuthorizationInterceptors); + Get.put( + mockAuthorizationInterceptors, + tag: BindingTag.isolateTag, + ); + Get.put(mockDynamicUrlInterceptors); + Get.put(mockDeleteCredentialInteractor); + Get.put(mockLogoutOidcInteractor); + Get.put(mockDeleteAuthorityOidcInteractor); + Get.put(mockAppToast); + Get.put(mockImagePaths); + Get.put(mockResponsiveUtils); + Get.put(mockUuid); + Get.put(mockApplicationManager); + Get.put(mockBeforeUnloadManager); + + Get.testMode = true; + + composerController = ComposerController( + mockLocalFilePickerInteractor, + mockLocalImagePickerInteractor, + mockGetEmailContentInteractor, + mockGetAllIdentitiesInteractor, + mockUploadController, + mockRemoveComposerCacheOnWebInteractor, + mockSaveComposerCacheOnWebInteractor, + mockDownloadImageAsBase64Interactor, + mockTransformHtmlEmailContentInteractor, + mockGetAlwaysReadReceiptSettingInteractor, + mockCreateNewAndSendEmailInteractor, + mockCreateNewAndSaveEmailToDraftsInteractor, + mockSaveComposerCacheToLocalStorageBrowserInteractor, + ); + }); + + group("ComposerController::onOpenNewTabAction test", () { + test( + 'WHEN onOpenNewTabAction invoked\n' + 'THEN _storeComposedEmailToLocalStorageBrowserInteractor should be executed', + () async { + // Arrange + when(mockMailboxDashBoardController.accountId).thenReturn(Rxn(AccountFixtures.aliceAccountId)); + when(mockMailboxDashBoardController.sessionCurrent).thenReturn(SessionFixtures.aliceSession); + + composerController.composerArguments.value = ComposerArguments(); + composerController.openNewTabButtonState = ButtonState.enabled; + + // Act + composerController.onOpenNewTabAction(); + + await untilCalled(mockSaveComposerCacheToLocalStorageBrowserInteractor.execute(any, any, any)); + + // Assert + verify(mockSaveComposerCacheToLocalStorageBrowserInteractor.execute(any, any, any)).called(1); + }); + }); + + tearDown(() { + Get.deleteAll(); + }); +} \ No newline at end of file diff --git a/test/features/features/composer/recipient_composer_widget_test.dart b/test/features/composer/presentation/recipient_composer_widget_test.dart similarity index 100% rename from test/features/features/composer/recipient_composer_widget_test.dart rename to test/features/composer/presentation/recipient_composer_widget_test.dart diff --git a/test/features/email/presentation/extensions/calendar_event_extension_test.dart b/test/features/email/presentation/extensions/calendar_event_extension_test.dart index bd60379076..79325b2daf 100644 --- a/test/features/email/presentation/extensions/calendar_event_extension_test.dart +++ b/test/features/email/presentation/extensions/calendar_event_extension_test.dart @@ -1,10 +1,12 @@ +import 'package:date_format/date_format.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:date_format/date_format.dart' as date_format; +import 'package:jmap_dart_client/jmap/core/utc_date.dart'; import 'package:jmap_dart_client/jmap/mail/calendar/calendar_event.dart'; import 'package:tmail_ui_user/features/email/presentation/extensions/calendar_event_extension.dart'; void main() { - group('calendar event extension test', () { + group('CalendarEventExtension::formatDateTime test', () { final dateTime = DateTime(2021, 10, 10, 10, 30, 00, 00, 00); const locale = date_format.EnglishDateLocale(); @@ -27,4 +29,48 @@ void main() { expect(formattedTime, expectedFormattedTime); }); }); + + group('CalendarEventExtension::getDateTimeEvent test', () { + test('dateTimeEventAsString should return string with format start date - end date (timezone offset) and date formatted as DD, MM dd, YYYY for all-day event for many days', () { + const expectedFormattedDateString = 'Sunday, October 10, 2021 - Sunday, October 24, 2021 (GMT+0)'; + + final startDate = DateTime(2021, 10, 10, 00, 00, 00, 00, 00); + final endDate = DateTime(2021, 10, 25, 00, 00, 00, 00, 00); + + final calendarEvent = CalendarEvent( + startDate: startDate, + endDate: endDate, + startUtcDate: UTCDate(startDate), + endUtcDate: UTCDate(endDate), + ); + + final formattedDateString = calendarEvent.getDateTimeEvent( + timeZone: 'GMT+0', + dateLocale: const EnglishDateLocale() + ); + + expect(formattedDateString, expectedFormattedDateString); + }); + + test('dateTimeEventAsString should return string with format date (timezone offset) and date formatted as DD, MM dd, YYYY for all-day event for one day', () { + const expectedFormattedDateString = 'Sunday, October 10, 2021 (GMT+0)'; + + final startDate = DateTime(2021, 10, 10, 00, 00, 00, 00, 00); + final endDate = DateTime(2021, 10, 11, 00, 00, 00, 00, 00); + + final calendarEvent = CalendarEvent( + startDate: startDate, + endDate: endDate, + startUtcDate: UTCDate(startDate), + endUtcDate: UTCDate(endDate), + ); + + final formattedDateString = calendarEvent.getDateTimeEvent( + timeZone: 'GMT+0', + dateLocale: const EnglishDateLocale() + ); + + expect(formattedDateString, expectedFormattedDateString); + }); + }); } \ No newline at end of file diff --git a/test/features/features/email/presentation/extensions/calendar_event_extension_test.dart b/test/features/features/email/presentation/extensions/calendar_event_extension_test.dart deleted file mode 100644 index 193827bd3e..0000000000 --- a/test/features/features/email/presentation/extensions/calendar_event_extension_test.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:date_format/date_format.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:jmap_dart_client/jmap/core/utc_date.dart'; -import 'package:jmap_dart_client/jmap/mail/calendar/calendar_event.dart'; -import 'package:tmail_ui_user/features/email/presentation/extensions/calendar_event_extension.dart'; - -void main() { - group('calendar event extension test', () { - test('dateTimeEventAsString should return string with format start date - end date (timezone offset) and date formatted as DD, MM dd, YYYY for all-day event for many days', () { - const expectedFormattedDateString = 'Sunday, October 10, 2021 - Sunday, October 24, 2021 (GMT+0)'; - - final startDate = DateTime(2021, 10, 10, 00, 00, 00, 00, 00); - final endDate = DateTime(2021, 10, 25, 00, 00, 00, 00, 00); - - final calendarEvent = CalendarEvent( - startDate: startDate, - endDate: endDate, - startUtcDate: UTCDate(startDate), - endUtcDate: UTCDate(endDate), - ); - - final formattedDateString = calendarEvent.getDateTimeEvent( - timeZone: 'GMT+0', - dateLocale: const EnglishDateLocale() - ); - - expect(formattedDateString, expectedFormattedDateString); - }); - - test('dateTimeEventAsString should return string with format date (timezone offset) and date formatted as DD, MM dd, YYYY for all-day event for one day', () { - const expectedFormattedDateString = 'Sunday, October 10, 2021 (GMT+0)'; - - final startDate = DateTime(2021, 10, 10, 00, 00, 00, 00, 00); - final endDate = DateTime(2021, 10, 11, 00, 00, 00, 00, 00); - - final calendarEvent = CalendarEvent( - startDate: startDate, - endDate: endDate, - startUtcDate: UTCDate(startDate), - endUtcDate: UTCDate(endDate), - ); - - final formattedDateString = calendarEvent.getDateTimeEvent( - timeZone: 'GMT+0', - dateLocale: const EnglishDateLocale() - ); - - expect(formattedDateString, expectedFormattedDateString); - }); - }); -} \ No newline at end of file diff --git a/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart b/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart index dd1cc34f96..a419f26def 100644 --- a/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart +++ b/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart @@ -48,7 +48,9 @@ import 'package:tmail_ui_user/features/mailbox/domain/usecases/subscribe_multipl import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_controller.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree_builder.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/delete_composer_cache_in_local_storage_browser_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_all_recent_search_latest_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_composer_cache_in_local_storage_browser_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_composer_cache_on_web_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/quick_search_email_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_composer_cache_on_web_interactor.dart'; @@ -169,6 +171,8 @@ const fallbackGenerators = { MockSpec(), MockSpec(), MockSpec(), + MockSpec(), + MockSpec(), ]) void main() { // mock mailbox dashboard controller direct dependencies @@ -250,6 +254,8 @@ void main() { final refreshAllMailboxInteractor = MockRefreshAllMailboxInteractor(); final removeComposerCacheOnWebInteractor = MockRemoveComposerCacheOnWebInteractor(); final getAllIdentitiesInteractor = MockGetAllIdentitiesInteractor(); + final getComposedEmailFromLocalStorageBrowserInteractor = MockGetComposerCacheInLocalStorageBrowserInteractor(); + final deleteComposerCacheInLocalStorageBrowserInteractor = MockDeleteComposerCacheInLocalStorageBrowserInteractor(); late MailboxController mailboxController; // mock thread controller direct dependencies @@ -302,6 +308,8 @@ void main() { Get.put(updateAuthenticationAccountInteractor); Get.put(getAllIdentitiesInteractor); Get.put(removeComposerCacheOnWebInteractor); + Get.put(getComposedEmailFromLocalStorageBrowserInteractor); + Get.put(deleteComposerCacheInLocalStorageBrowserInteractor); Get.testMode = true; @@ -340,6 +348,8 @@ void main() { getRestoredDeletedMessageInteractor, removeComposerCacheOnWebInteractor, getAllIdentitiesInteractor, + getComposedEmailFromLocalStorageBrowserInteractor, + deleteComposerCacheInLocalStorageBrowserInteractor, ); Get.put(mailboxDashboardController); mailboxDashboardController.onReady();