From 102b469a3bf6cd6b62ae31563982f1c9db73594b Mon Sep 17 00:00:00 2001 From: DatDang Date: Mon, 20 Jan 2025 19:40:43 +0700 Subject: [PATCH] TF-3406 Add edit as new email feature Signed-off-by: dab246 --- .../presentation/composer_controller.dart | 13 +++++ .../email_action_type_extension.dart | 5 ++ .../view/mobile/mobile_editor_view.dart | 10 +++- .../view/web/web_editor_view.dart | 1 + .../controller/single_email_controller.dart | 11 ++++ .../email/presentation/email_view.dart | 3 +- .../model/composer_arguments.dart | 6 ++ ...update_current_emails_flags_extension.dart | 4 +- .../email/presentation/search_email_view.dart | 36 ++++++++++++ .../mixin/email_action_controller.dart | 6 ++ .../presentation/thread_controller.dart | 2 + .../thread/presentation/thread_view.dart | 58 ++++++++++++++++++- lib/l10n/intl_messages.arb | 8 ++- lib/main/localizations/app_localizations.dart | 6 ++ model/lib/email/email_action_type.dart | 1 + 15 files changed, 161 insertions(+), 9 deletions(-) diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index 61501fa14d..bad5c7ecf7 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -570,6 +570,19 @@ class ComposerController extends BaseController ); switch(arguments.emailActionType) { + case EmailActionType.composeFromPresentationEmail: + _initEmailAddress( + presentationEmail: arguments.presentationEmail!, + actionType: EmailActionType.composeFromPresentationEmail + ); + _initSubjectEmail( + presentationEmail: arguments.presentationEmail!, + actionType: EmailActionType.composeFromPresentationEmail + ); + _getEmailContentOfEmailDrafts( + emailId: arguments.presentationEmail!.id!, + ); + break; case EmailActionType.editDraft: _initEmailAddress( presentationEmail: arguments.presentationEmail!, 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 2a5b5b510f..cb89aa4d3d 100644 --- a/lib/features/composer/presentation/extensions/email_action_type_extension.dart +++ b/lib/features/composer/presentation/extensions/email_action_type_extension.dart @@ -32,6 +32,7 @@ extension EmailActionTypeExtension on EmailActionType { case EmailActionType.editDraft: case EmailActionType.editSendingEmail: case EmailActionType.reopenComposerBrowser: + case EmailActionType.composeFromPresentationEmail: return subject; default: return ''; @@ -138,6 +139,8 @@ extension EmailActionTypeExtension on EmailActionType { return imagePaths.icMailboxArchived; case EmailActionType.downloadMessageAsEML: return imagePaths.icDownloadAttachment; + case EmailActionType.composeFromPresentationEmail: + return imagePaths.icEdit; default: return ''; } @@ -159,6 +162,8 @@ extension EmailActionTypeExtension on EmailActionType { return AppLocalizations.of(context).archiveMessage; case EmailActionType.downloadMessageAsEML: return AppLocalizations.of(context).downloadMessageAsEML; + case EmailActionType.composeFromPresentationEmail: + return AppLocalizations.of(context).editAsNewEmail; default: return ''; } diff --git a/lib/features/composer/presentation/view/mobile/mobile_editor_view.dart b/lib/features/composer/presentation/view/mobile/mobile_editor_view.dart index 28c6860667..f1f59577f8 100644 --- a/lib/features/composer/presentation/view/mobile/mobile_editor_view.dart +++ b/lib/features/composer/presentation/view/mobile/mobile_editor_view.dart @@ -50,6 +50,7 @@ class MobileEditorView extends StatelessWidget with EditorViewMixin { case EmailActionType.reopenComposerBrowser: case EmailActionType.composeFromMailtoUri: case EmailActionType.composeFromUnsubscribeMailtoLink: + case EmailActionType.composeFromPresentationEmail: if (contentViewState == null) { return const SizedBox.shrink(); } @@ -64,9 +65,12 @@ class MobileEditorView extends StatelessWidget with EditorViewMixin { if (success is GetEmailContentLoading) { return const CupertinoLoadingWidget(padding: EdgeInsets.all(16.0)); } else { - var newContent = success is GetEmailContentSuccess - ? success.htmlEmailContent - : HtmlExtension.editorStartTags; + var newContent = HtmlExtension.editorStartTags; + if (success is GetEmailContentSuccess) { + newContent = success.htmlEmailContent; + } else if (success is GetEmailContentFromCacheSuccess) { + newContent = success.htmlEmailContent; + } if (newContent.isEmpty) { newContent = HtmlExtension.editorStartTags; } 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 731502619e..552dae9844 100644 --- a/lib/features/composer/presentation/view/web/web_editor_view.dart +++ b/lib/features/composer/presentation/view/web/web_editor_view.dart @@ -88,6 +88,7 @@ class WebEditorView extends StatelessWidget with EditorViewMixin { case EmailActionType.reopenComposerBrowser: case EmailActionType.composeFromUnsubscribeMailtoLink: case EmailActionType.composeFromMailtoUri: + case EmailActionType.composeFromPresentationEmail: if (contentViewState == null) { return const SizedBox.shrink(); } diff --git a/lib/features/email/presentation/controller/single_email_controller.dart b/lib/features/email/presentation/controller/single_email_controller.dart index a3cf64d22a..e73a9ca9c3 100644 --- a/lib/features/email/presentation/controller/single_email_controller.dart +++ b/lib/features/email/presentation/controller/single_email_controller.dart @@ -1205,6 +1205,9 @@ class SingleEmailController extends BaseController with AppLoaderMixin { case EmailActionType.downloadMessageAsEML: _downloadMessageAsEML(presentationEmail); break; + case EmailActionType.composeFromPresentationEmail: + _composeFromPresentationEmail(presentationEmail); + break; default: break; } @@ -1906,6 +1909,14 @@ class SingleEmailController extends BaseController with AppLoaderMixin { downloadAttachmentForWeb(emlAttachment); } + void _composeFromPresentationEmail(PresentationEmail presentationEmail) { + if (accountId == null || session == null) return; + + mailboxDashBoardController.goToComposer( + ComposerArguments.fromPresentationEmail(presentationEmail), + ); + } + void handleDownloadAttachmentAction(BuildContext context, Attachment attachment) { if (PlatformInfo.isWeb) { downloadAttachmentForWeb(attachment); diff --git a/lib/features/email/presentation/email_view.dart b/lib/features/email/presentation/email_view.dart index 507ac40c6c..0181e7459d 100644 --- a/lib/features/email/presentation/email_view.dart +++ b/lib/features/email/presentation/email_view.dart @@ -503,7 +503,8 @@ class EmailView extends GetWidget { if (mailboxContain?.isArchive == false) EmailActionType.archiveMessage, if (PlatformInfo.isWeb && PlatformInfo.isCanvasKit) - EmailActionType.downloadMessageAsEML + EmailActionType.downloadMessageAsEML, + EmailActionType.composeFromPresentationEmail, ]; if (position == null) { diff --git a/lib/features/email/presentation/model/composer_arguments.dart b/lib/features/email/presentation/model/composer_arguments.dart index 25ce2fe911..0840b86dfa 100644 --- a/lib/features/email/presentation/model/composer_arguments.dart +++ b/lib/features/email/presentation/model/composer_arguments.dart @@ -102,6 +102,12 @@ class ComposerArguments extends RouterArguments { emailActionType: EmailActionType.editDraft, presentationEmail: presentationEmail, ); + + factory ComposerArguments.fromPresentationEmail(PresentationEmail presentationEmail) => + ComposerArguments( + emailActionType: EmailActionType.composeFromPresentationEmail, + presentationEmail: presentationEmail, + ); factory ComposerArguments.fromSessionStorageBrowser(ComposerCache composerCache) => ComposerArguments( diff --git a/lib/features/mailbox_dashboard/presentation/extensions/update_current_emails_flags_extension.dart b/lib/features/mailbox_dashboard/presentation/extensions/update_current_emails_flags_extension.dart index 1fc76d2718..b52ef900bd 100644 --- a/lib/features/mailbox_dashboard/presentation/extensions/update_current_emails_flags_extension.dart +++ b/lib/features/mailbox_dashboard/presentation/extensions/update_current_emails_flags_extension.dart @@ -83,9 +83,7 @@ extension UpdateCurrentEmailsFlagsExtension on MailboxDashBoardController { setSelectedEmail(newEmail); } - if (emailsInCurrentMailbox.isNotEmpty) { - updateEmailFlagByEmailIds([emailId], markAsAnswered: true); - } + updateEmailFlagByEmailIds([emailId], markAsAnswered: true); } void updateEmailForwarded(EmailId emailId) { diff --git a/lib/features/search/email/presentation/search_email_view.dart b/lib/features/search/email/presentation/search_email_view.dart index 264f741c80..5b6aa66504 100644 --- a/lib/features/search/email/presentation/search_email_view.dart +++ b/lib/features/search/email/presentation/search_email_view.dart @@ -39,6 +39,7 @@ import 'package:tmail_ui_user/features/thread/presentation/styles/item_email_til import 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_builder.dart' if (dart.library.html) 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_web_builder.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; +import 'package:tmail_ui_user/main/routes/route_navigation.dart'; class SearchEmailView extends GetWidget with AppLoaderMixin { @@ -745,6 +746,8 @@ class SearchEmailView extends GetWidget List _contextMenuActionTile(BuildContext context, PresentationEmail email) { return [ _markAsEmailSpamOrUnSpamAction(context, email), + if (email.mailboxContain?.isDrafts == false) + _editAsNewEmailContextMenuItemAction(context, email), ]; } @@ -777,11 +780,44 @@ class SearchEmailView extends GetWidget .build(); } + Widget _editAsNewEmailContextMenuItemAction( + BuildContext context, + PresentationEmail email, + ) { + return ( + EmailActionCupertinoActionSheetActionBuilder( + const Key('edit_as_new_email_action'), + SvgPicture.asset( + controller.imagePaths.icEdit, + width: 24, + height: 24, + fit: BoxFit.fill, + colorFilter: AppColor.colorTextButton.asFilter() + ), + AppLocalizations.of(context).editAsNewEmail, + email, + iconLeftPadding: controller.responsiveUtils.isMobile(context) + ? const EdgeInsetsDirectional.only(start: 12, end: 16) + : const EdgeInsetsDirectional.only(end: 12), + iconRightPadding: controller.responsiveUtils.isMobile(context) + ? const EdgeInsetsDirectional.only(start: 12) + : EdgeInsets.zero) + ..onActionClick((email) { + popBack(); + controller.editAsNewEmail(email); + }) + ).build(); + } + List _popupMenuActionTile(BuildContext context, PresentationEmail email) { return [ PopupMenuItem( padding: const EdgeInsets.symmetric(horizontal: 8), child: _markAsEmailSpamOrUnSpamAction(context, email)), + if (email.mailboxContain?.isDrafts == false) + PopupMenuItem( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: _editAsNewEmailContextMenuItemAction(context, email)), ]; } diff --git a/lib/features/thread/presentation/mixin/email_action_controller.dart b/lib/features/thread/presentation/mixin/email_action_controller.dart index 64e4b1e724..280337c0ca 100644 --- a/lib/features/thread/presentation/mixin/email_action_controller.dart +++ b/lib/features/thread/presentation/mixin/email_action_controller.dart @@ -43,6 +43,12 @@ mixin EmailActionController { mailboxDashBoardController.goToComposer(ComposerArguments.editDraftEmail(presentationEmail)); } + void editAsNewEmail(PresentationEmail presentationEmail) { + mailboxDashBoardController.goToComposer( + ComposerArguments.fromPresentationEmail(presentationEmail), + ); + } + void previewEmail(PresentationEmail presentationEmail) { log('EmailActionController::previewEmail():presentationEmailId: ${presentationEmail.id}'); mailboxDashBoardController.openEmailDetailedView(presentationEmail); diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index 03d8d4da57..543329b30b 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -1152,6 +1152,8 @@ class ThreadController extends BaseController with EmailActionController { case EmailActionType.preview: if (mailboxContain?.isDrafts == true) { editDraftEmail(selectedEmail); + } else if (mailboxContain?.isTemplates == true) { + editAsNewEmail(selectedEmail); } else { previewEmail(selectedEmail); } diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index 5d8924ac2f..95e8ec5e1b 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -697,6 +697,8 @@ class ThreadView extends GetWidget _markAsEmailSpamOrUnSpamContextMenuItemAction(context, email, mailboxContain), if (mailboxContain?.isArchive == false) _archiveMessageContextMenuItemAction(context, email), + if (mailboxContain?.isDrafts == false) + _editAsNewEmailContextMenuItemAction(context, email), ]; } @@ -770,7 +772,7 @@ class ThreadView extends GetWidget email, iconLeftPadding: controller.responsiveUtils.isMobile(context) ? const EdgeInsetsDirectional.only(start: 12, end: 16) - : const EdgeInsetsDirectional.only(start: 12), + : const EdgeInsetsDirectional.only(end: 12), iconRightPadding: controller.responsiveUtils.isMobile(context) ? const EdgeInsetsDirectional.only(start: 12) : EdgeInsets.zero @@ -779,6 +781,35 @@ class ThreadView extends GetWidget ).build(); } + Widget _editAsNewEmailContextMenuItemAction( + BuildContext context, + PresentationEmail email, + ) { + return ( + EmailActionCupertinoActionSheetActionBuilder( + const Key('edit_as_new_email_action'), + SvgPicture.asset( + controller.imagePaths.icEdit, + width: 24, + height: 24, + fit: BoxFit.fill, + colorFilter: AppColor.colorTextButton.asFilter() + ), + AppLocalizations.of(context).editAsNewEmail, + email, + iconLeftPadding: controller.responsiveUtils.isMobile(context) + ? const EdgeInsetsDirectional.only(start: 12, end: 16) + : const EdgeInsetsDirectional.only(end: 12), + iconRightPadding: controller.responsiveUtils.isMobile(context) + ? const EdgeInsetsDirectional.only(start: 12) + : EdgeInsets.zero) + ..onActionClick((email) { + popBack(); + controller.editAsNewEmail(email); + }) + ).build(); + } + List _popupMenuActionTile(BuildContext context, PresentationEmail email) { final mailboxContain = email.mailboxContain; @@ -788,6 +819,8 @@ class ThreadView extends GetWidget _buildMarkAsSpamPopupMenuItem(context, email, mailboxContain), if (mailboxContain?.isArchive == false) _buildArchiveMessagePopupMenuItem(context, email), + if (mailboxContain?.isDrafts == false) + _buildEditAsNewEmailPopupMenuItem(AppLocalizations.of(context), email), ]; } @@ -865,6 +898,29 @@ class ThreadView extends GetWidget ); } + PopupMenuEntry _buildEditAsNewEmailPopupMenuItem( + AppLocalizations appLocalizations, + PresentationEmail email, + ) { + return PopupMenuItem( + padding: EdgeInsets.zero, + child: popupItem( + controller.imagePaths.icEdit, + appLocalizations.editAsNewEmail, + colorIcon: AppColor.colorTextButton, + styleName: const TextStyle( + fontWeight: FontWeight.w500, + fontSize: 16, + color: Colors.black + ), + onCallbackAction: () { + popBack(); + controller.editAsNewEmail(email); + } + ) + ); + } + Widget _buildMailboxActionProgressBanner(BuildContext context) { return Obx(() { return _MailboxActionProgressBanner( diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index e21f385623..d058cda48d 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2025-01-14T12:52:43.828907", + "@@last_modified": "2025-01-20T18:53:16.325121", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -4113,5 +4113,11 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "editAsNewEmail": "Edit as new email", + "@editAsNewEmail": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index 40c84b2f0a..b18771c0d8 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -4321,4 +4321,10 @@ class AppLocalizations { name: 'downloadAttachmentInEMLPreviewWarningMessage', ); } + String get editAsNewEmail { + return Intl.message( + 'Edit as new email', + name: 'editAsNewEmail', + ); + } } diff --git a/model/lib/email/email_action_type.dart b/model/lib/email/email_action_type.dart index c158c2dc93..2edcc80e28 100644 --- a/model/lib/email/email_action_type.dart +++ b/model/lib/email/email_action_type.dart @@ -16,6 +16,7 @@ enum EmailActionType { composeFromFileShared, composeFromEmailAddress, composeFromMailtoUri, + composeFromPresentationEmail, reopenComposerBrowser, moveToTrash, deletePermanently,