From b0ee2f24badebb6e20670dc8856f8b20579fdecd Mon Sep 17 00:00:00 2001 From: dab246 Date: Wed, 17 Jul 2024 13:25:58 +0700 Subject: [PATCH] TF-2930 Fix inline attachment lost in draft emails --- .../transform_configuration.dart | 2 +- .../presentation/composer_controller.dart | 63 +++++++++---------- .../extensions/detailed_email_extension.dart | 2 + .../detailed_email_hive_cache_extension.dart | 3 +- .../domain/extensions/email_extension.dart | 3 +- .../email/domain/model/detailed_email.dart | 3 + .../domain/state/get_email_content_state.dart | 14 +++-- .../get_email_content_interactor.dart | 12 +++- ...t_list_detailed_email_by_id_interator.dart | 2 +- .../controller/single_email_controller.dart | 22 ++++--- .../model/composer_arguments.dart | 12 ++-- .../presentation/model/email_loaded.dart | 3 + .../model/detailed_email_hive_cache.dart | 5 ++ model/lib/extensions/email_extension.dart | 2 - 14 files changed, 90 insertions(+), 58 deletions(-) diff --git a/core/lib/presentation/utils/html_transformer/transform_configuration.dart b/core/lib/presentation/utils/html_transformer/transform_configuration.dart index 17fdea07e..45a009175 100644 --- a/core/lib/presentation/utils/html_transformer/transform_configuration.dart +++ b/core/lib/presentation/utils/html_transformer/transform_configuration.dart @@ -46,7 +46,7 @@ class TransformConfiguration { const RemoveCollapsedSignatureButtonTransformer(), ]); - factory TransformConfiguration.forDraftsEmail() => TransformConfiguration.empty(); + factory TransformConfiguration.forDraftsEmail() => TransformConfiguration.fromDomTransformers([const ImageTransformer()]); factory TransformConfiguration.forPreviewEmailOnWeb() => TransformConfiguration.create( customDomTransformers: [ diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index 11f087024..8f8f50312 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -326,9 +326,7 @@ class ComposerController extends BaseController with DragDropFileMixin implement void _updateEditorContent(RestoreEmailInlineImagesSuccess success) { richTextWebController?.editorController.setText(success.emailContent); - consumeState(Stream.value(Right(GetEmailContentSuccess( - htmlEmailContent: success.emailContent, - attachments: [])))); + consumeState(Stream.value(Right(GetEmailContentSuccess(htmlEmailContent: success.emailContent)))); } @override @@ -582,6 +580,12 @@ 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; @@ -628,7 +632,9 @@ class ComposerController extends BaseController with DragDropFileMixin implement presentationEmail: arguments.presentationEmail!, actionType: arguments.emailActionType ); - _initAttachments(arguments.attachments ?? []); + _initAttachmentsAndInlineImages( + attachments: arguments.attachments, + inlineImages: arguments.inlineImages); _transformHtmlEmailContent(arguments.emailContents); break; case EmailActionType.reopenComposerBrowser: @@ -644,9 +650,9 @@ class ComposerController extends BaseController with DragDropFileMixin implement presentationEmail: arguments.presentationEmail!, actionType: EmailActionType.reopenComposerBrowser ); - _initAttachments( - arguments.attachments ?? [], - inlineAttachments: arguments.inlineImages); + _initAttachmentsAndInlineImages( + attachments: arguments.attachments, + inlineImages: arguments.inlineImages); final accountId = mailboxDashBoardController.accountId.value; final downloadUrl = mailboxDashBoardController.sessionCurrent @@ -690,13 +696,16 @@ 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!); } } @@ -1323,7 +1332,6 @@ class ComposerController extends BaseController with DragDropFileMixin implement consumeState(Stream.value( Right(GetEmailContentSuccess( htmlEmailContent: sendingEmail.presentationEmail.emailContentList.asHtmlString, - attachments: sendingEmail.email.allAttachments, emailCurrent: sendingEmail.email )) )); @@ -1346,32 +1354,17 @@ class ComposerController extends BaseController with DragDropFileMixin implement } 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}) { @@ -1389,12 +1382,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); } diff --git a/lib/features/email/domain/extensions/detailed_email_extension.dart b/lib/features/email/domain/extensions/detailed_email_extension.dart index c7b9396db..80180121b 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 1a3280305..78b1c7dae 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 782e169e6..957f3736e 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 008e19df7..cbfa6f26c 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 9742216d9..57250a68f 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 c2e1c6f35..ce49e5149 100644 --- a/lib/features/email/domain/usecases/get_email_content_interactor.dart +++ b/lib/features/email/domain/usecases/get_email_content_interactor.dart @@ -47,9 +47,11 @@ class GetEmailContentInteractor { ) async* { try { final email = await emailRepository.getEmailContent(session, accountId, emailId); + final listAttachments = email.allAttachments.getListAttachmentsDisplayedOutside(email.htmlBodyAttachments); + final listInlineImages = email.allAttachments.listAttachmentsDisplayedInContent; if (email.emailContentList.isNotEmpty) { - final mapCidImageDownloadUrl = email.attachmentsWithCid.toMapCidImageDownloadUrl( + final mapCidImageDownloadUrl = listInlineImages.toMapCidImageDownloadUrl( accountId: accountId, downloadUrl: baseDownloadUrl ); @@ -61,13 +63,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 +94,7 @@ class GetEmailContentInteractor { yield Right(GetEmailContentFromCacheSuccess( htmlEmailContent: detailedEmail.htmlEmailContent ?? '', attachments: detailedEmail.attachments ?? [], + inlineImages: detailedEmail.inlineImages ?? [], emailCurrent: Email( id: emailId, headers: detailedEmail.headers, @@ -121,6 +126,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 8e0e2448a..21f5eab56 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 72f922a86..70d6df7cb 100644 --- a/lib/features/email/presentation/controller/single_email_controller.dart +++ b/lib/features/email/presentation/controller/single_email_controller.dart @@ -469,6 +469,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { GetEmailContentSuccess( htmlEmailContent: emailLoaded.htmlContent, attachments: emailLoaded.attachments, + inlineImages: emailLoaded.inlineImages, emailCurrent: emailLoaded.emailCurrent ) ))); @@ -501,22 +502,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 { @@ -545,22 +547,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 { @@ -1343,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, @@ -1354,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, @@ -1366,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 a7cc563d6..bf113bab0 100644 --- a/lib/features/email/presentation/model/composer_arguments.dart +++ b/lib/features/email/presentation/model/composer_arguments.dart @@ -95,11 +95,9 @@ 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, + inlineImages: composerCache.email?.allAttachments.listAttachmentsDisplayedInContent, readRecepientEnabled: composerCache.readReceipentEnabled, displayMode: composerCache.displayMode, ); @@ -107,6 +105,7 @@ class ComposerArguments extends RouterArguments { factory ComposerArguments.replyEmail({ required PresentationEmail presentationEmail, required String content, + required List inlineImages, Role? mailboxRole, MessageIdsHeaderValue? messageId, MessageIdsHeaderValue? references, @@ -114,6 +113,7 @@ class ComposerArguments extends RouterArguments { emailActionType: EmailActionType.reply, presentationEmail: presentationEmail, emailContents: content, + inlineImages: inlineImages, mailboxRole: mailboxRole, messageId: messageId, references: references, @@ -122,6 +122,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 +130,7 @@ class ComposerArguments extends RouterArguments { emailActionType: EmailActionType.replyAll, presentationEmail: presentationEmail, emailContents: content, + inlineImages: inlineImages, mailboxRole: mailboxRole, messageId: messageId, references: references, @@ -138,6 +140,7 @@ class ComposerArguments extends RouterArguments { required PresentationEmail presentationEmail, required String content, required List attachments, + required List inlineImages, MessageIdsHeaderValue? messageId, MessageIdsHeaderValue? references, }) => ComposerArguments( @@ -145,6 +148,7 @@ class ComposerArguments extends RouterArguments { presentationEmail: presentationEmail, emailContents: content, attachments: attachments, + inlineImages: inlineImages, mailboxRole: presentationEmail.mailboxContain?.role, messageId: messageId, references: references, diff --git a/lib/features/email/presentation/model/email_loaded.dart b/lib/features/email/presentation/model/email_loaded.dart index 948a68421..99886aef7 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/offline_mode/model/detailed_email_hive_cache.dart b/lib/features/offline_mode/model/detailed_email_hive_cache.dart index f15f266db..0eefebec6 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/model/lib/extensions/email_extension.dart b/model/lib/extensions/email_extension.dart index 7b7707abd..2a1a9cf51 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);