From caa01bf92612eb9875330bcc395872f7438eadcb Mon Sep 17 00:00:00 2001 From: DatDang Date: Wed, 8 Jan 2025 15:25:38 +0700 Subject: [PATCH 1/4] TF-3406 Add edit as new email feature --- .../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 ++ .../thread/presentation/thread_view.dart | 64 ++++++++++++++++++- lib/main/localizations/app_localizations.dart | 6 ++ model/lib/email/email_action_type.dart | 1 + 10 files changed, 113 insertions(+), 7 deletions(-) diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index 65ab024418..26381cae62 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -594,6 +594,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 b58370c9de..09c773a99a 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.composeFromPresentationEmail: return subject; default: return ''; @@ -143,6 +144,8 @@ extension EmailActionTypeExtension on EmailActionType { return imagePaths.icMailboxArchived; case EmailActionType.downloadMessageAsEML: return imagePaths.icDownloadAttachment; + case EmailActionType.composeFromPresentationEmail: + return imagePaths.icEdit; default: return ''; } @@ -164,6 +167,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 1859f9026f..af0a8d92b3 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 e06e3e84c2..40a21c16c9 100644 --- a/lib/features/composer/presentation/view/web/web_editor_view.dart +++ b/lib/features/composer/presentation/view/web/web_editor_view.dart @@ -94,6 +94,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 92b83c34d2..7c7b738247 100644 --- a/lib/features/email/presentation/controller/single_email_controller.dart +++ b/lib/features/email/presentation/controller/single_email_controller.dart @@ -1186,6 +1186,9 @@ class SingleEmailController extends BaseController with AppLoaderMixin { case EmailActionType.downloadMessageAsEML: _downloadMessageAsEML(presentationEmail); break; + case EmailActionType.composeFromPresentationEmail: + _composeFromPresentationEmail(presentationEmail); + break; default: break; } @@ -1871,6 +1874,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 3f111a6209..7ca9e793f2 100644 --- a/lib/features/email/presentation/email_view.dart +++ b/lib/features/email/presentation/email_view.dart @@ -504,7 +504,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 479d0e617a..dbc63963e5 100644 --- a/lib/features/email/presentation/model/composer_arguments.dart +++ b/lib/features/email/presentation/model/composer_arguments.dart @@ -100,6 +100,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/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index 5d8924ac2f..0d7f5c6df9 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -697,6 +697,7 @@ class ThreadView extends GetWidget _markAsEmailSpamOrUnSpamContextMenuItemAction(context, email, mailboxContain), if (mailboxContain?.isArchive == false) _archiveMessageContextMenuItemAction(context, email), + _editAsNewEmailContextMenuItemAction(context, email), ]; } @@ -769,16 +770,47 @@ class ThreadView extends GetWidget AppLocalizations.of(context).archiveMessage, email, iconLeftPadding: controller.responsiveUtils.isMobile(context) - ? const EdgeInsetsDirectional.only(start: 12, end: 16) - : const EdgeInsetsDirectional.only(start: 12), + ? const EdgeInsets.only(left: 12, right: 16) + : const EdgeInsets.only(right: 12), iconRightPadding: controller.responsiveUtils.isMobile(context) - ? const EdgeInsetsDirectional.only(start: 12) + ? const EdgeInsets.only(right: 12) : EdgeInsets.zero ) ..onActionClick((email) => controller.archiveMessage(context, email)) ).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 EdgeInsets.only(left: 12, right: 16) + : const EdgeInsets.only(right: 12), + iconRightPadding: controller.responsiveUtils.isMobile(context) + ? const EdgeInsets.only(right: 12) + : EdgeInsets.zero) + ..onActionClick((email) { + popBack(); + controller.mailboxDashBoardController.goToComposer( + ComposerArguments.fromPresentationEmail(email) + ); + }) + ).build(); + } + List _popupMenuActionTile(BuildContext context, PresentationEmail email) { final mailboxContain = email.mailboxContain; @@ -788,6 +820,7 @@ class ThreadView extends GetWidget _buildMarkAsSpamPopupMenuItem(context, email, mailboxContain), if (mailboxContain?.isArchive == false) _buildArchiveMessagePopupMenuItem(context, email), + _buildEditAsNewEmailPopupMenuItem(AppLocalizations.of(context), email), ]; } @@ -865,6 +898,31 @@ 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.mailboxDashBoardController.goToComposer( + ComposerArguments.fromPresentationEmail(email) + ); + } + ) + ); + } + Widget _buildMailboxActionProgressBanner(BuildContext context) { return Obx(() { return _MailboxActionProgressBanner( diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index 6d1666dfbc..6d42751ebd 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -4422,4 +4422,10 @@ class AppLocalizations { ); } + 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 a125b4c3ae..84bed58967 100644 --- a/model/lib/email/email_action_type.dart +++ b/model/lib/email/email_action_type.dart @@ -15,6 +15,7 @@ enum EmailActionType { composeFromFileShared, composeFromEmailAddress, composeFromMailtoUri, + composeFromPresentationEmail, reopenComposerBrowser, moveToTrash, deletePermanently, From 0624157272f232bbc2750f547fdaf558f38247e9 Mon Sep 17 00:00:00 2001 From: DatDang Date: Wed, 8 Jan 2025 19:54:23 +0700 Subject: [PATCH 2/4] fixup! TF-3406 Add edit as new email feature --- .../email/presentation/search_email_view.dart | 34 +++++++++++++++++++ .../mixin/email_action_controller.dart | 6 ++++ .../presentation/thread_controller.dart | 3 ++ .../thread/presentation/thread_view.dart | 8 ++--- 4 files changed, 45 insertions(+), 6 deletions(-) diff --git a/lib/features/search/email/presentation/search_email_view.dart b/lib/features/search/email/presentation/search_email_view.dart index 262e6deb75..332ebc84d9 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 { @@ -746,6 +747,7 @@ class SearchEmailView extends GetWidget List _contextMenuActionTile(BuildContext context, PresentationEmail email) { return [ _markAsEmailSpamOrUnSpamAction(context, email), + _editAsNewEmailContextMenuItemAction(context, email), ]; } @@ -778,11 +780,43 @@ 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 EdgeInsets.only(left: 12, right: 16) + : const EdgeInsets.only(right: 12), + iconRightPadding: controller.responsiveUtils.isMobile(context) + ? const EdgeInsets.only(right: 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)), + 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 6ab3605c1d..acb1556ca5 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -1127,6 +1127,9 @@ class ThreadController extends BaseController with EmailActionController { case EmailActionType.preview: if (mailboxContain?.isDrafts == true) { editDraftEmail(selectedEmail); + } else if (mailboxContain?.isTemplates == true) { + popBack(); + editAsNewEmail(selectedEmail); } else { previewEmail(selectedEmail); } diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index 0d7f5c6df9..d26a6750a1 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -804,9 +804,7 @@ class ThreadView extends GetWidget : EdgeInsets.zero) ..onActionClick((email) { popBack(); - controller.mailboxDashBoardController.goToComposer( - ComposerArguments.fromPresentationEmail(email) - ); + controller.editAsNewEmail(email); }) ).build(); } @@ -915,9 +913,7 @@ class ThreadView extends GetWidget ), onCallbackAction: () { popBack(); - controller.mailboxDashBoardController.goToComposer( - ComposerArguments.fromPresentationEmail(email) - ); + controller.editAsNewEmail(email); } ) ); From 61e84b1a481871f2fea99b4e61aec24ff456d355 Mon Sep 17 00:00:00 2001 From: DatDang Date: Thu, 9 Jan 2025 09:40:34 +0700 Subject: [PATCH 3/4] fixup! TF-3406 Add edit as new email feature --- .../search/email/presentation/search_email_view.dart | 10 ++++++---- lib/features/thread/presentation/thread_view.dart | 6 ++++-- lib/l10n/intl_messages.arb | 6 ++++++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/features/search/email/presentation/search_email_view.dart b/lib/features/search/email/presentation/search_email_view.dart index 332ebc84d9..67842f46a0 100644 --- a/lib/features/search/email/presentation/search_email_view.dart +++ b/lib/features/search/email/presentation/search_email_view.dart @@ -747,7 +747,8 @@ class SearchEmailView extends GetWidget List _contextMenuActionTile(BuildContext context, PresentationEmail email) { return [ _markAsEmailSpamOrUnSpamAction(context, email), - _editAsNewEmailContextMenuItemAction(context, email), + if (email.mailboxContain?.isDrafts == false) + _editAsNewEmailContextMenuItemAction(context, email), ]; } @@ -814,9 +815,10 @@ class SearchEmailView extends GetWidget PopupMenuItem( padding: const EdgeInsets.symmetric(horizontal: 8), child: _markAsEmailSpamOrUnSpamAction(context, email)), - PopupMenuItem( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: _editAsNewEmailContextMenuItemAction(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/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index d26a6750a1..009a72d27f 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -697,7 +697,8 @@ class ThreadView extends GetWidget _markAsEmailSpamOrUnSpamContextMenuItemAction(context, email, mailboxContain), if (mailboxContain?.isArchive == false) _archiveMessageContextMenuItemAction(context, email), - _editAsNewEmailContextMenuItemAction(context, email), + if (mailboxContain?.isDrafts == false) + _editAsNewEmailContextMenuItemAction(context, email), ]; } @@ -818,7 +819,8 @@ class ThreadView extends GetWidget _buildMarkAsSpamPopupMenuItem(context, email, mailboxContain), if (mailboxContain?.isArchive == false) _buildArchiveMessagePopupMenuItem(context, email), - _buildEditAsNewEmailPopupMenuItem(AppLocalizations.of(context), email), + if (mailboxContain?.isDrafts == false) + _buildEditAsNewEmailPopupMenuItem(AppLocalizations.of(context), email), ]; } diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 872b3c5611..aa823f9164 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -4211,5 +4211,11 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "editAsNewEmail": "Edit as new email", + "@editAsNewEmail": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file From 73e771873dc5f1113dbee0aa2844c5c500550e98 Mon Sep 17 00:00:00 2001 From: DatDang Date: Thu, 9 Jan 2025 17:32:38 +0700 Subject: [PATCH 4/4] fixup! TF-3406 Add edit as new email feature --- .../search/email/presentation/search_email_view.dart | 6 +++--- lib/features/thread/presentation/thread_view.dart | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/features/search/email/presentation/search_email_view.dart b/lib/features/search/email/presentation/search_email_view.dart index 67842f46a0..2e95dbe37e 100644 --- a/lib/features/search/email/presentation/search_email_view.dart +++ b/lib/features/search/email/presentation/search_email_view.dart @@ -798,10 +798,10 @@ class SearchEmailView extends GetWidget AppLocalizations.of(context).editAsNewEmail, email, iconLeftPadding: controller.responsiveUtils.isMobile(context) - ? const EdgeInsets.only(left: 12, right: 16) - : const EdgeInsets.only(right: 12), + ? const EdgeInsetsDirectional.only(start: 12, end: 16) + : const EdgeInsetsDirectional.only(end: 12), iconRightPadding: controller.responsiveUtils.isMobile(context) - ? const EdgeInsets.only(right: 12) + ? const EdgeInsetsDirectional.only(start: 12) : EdgeInsets.zero) ..onActionClick((email) { popBack(); diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index 009a72d27f..95e8ec5e1b 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -771,10 +771,10 @@ class ThreadView extends GetWidget AppLocalizations.of(context).archiveMessage, email, iconLeftPadding: controller.responsiveUtils.isMobile(context) - ? const EdgeInsets.only(left: 12, right: 16) - : const EdgeInsets.only(right: 12), + ? const EdgeInsetsDirectional.only(start: 12, end: 16) + : const EdgeInsetsDirectional.only(end: 12), iconRightPadding: controller.responsiveUtils.isMobile(context) - ? const EdgeInsets.only(right: 12) + ? const EdgeInsetsDirectional.only(start: 12) : EdgeInsets.zero ) ..onActionClick((email) => controller.archiveMessage(context, email)) @@ -798,10 +798,10 @@ class ThreadView extends GetWidget AppLocalizations.of(context).editAsNewEmail, email, iconLeftPadding: controller.responsiveUtils.isMobile(context) - ? const EdgeInsets.only(left: 12, right: 16) - : const EdgeInsets.only(right: 12), + ? const EdgeInsetsDirectional.only(start: 12, end: 16) + : const EdgeInsetsDirectional.only(end: 12), iconRightPadding: controller.responsiveUtils.isMobile(context) - ? const EdgeInsets.only(right: 12) + ? const EdgeInsetsDirectional.only(start: 12) : EdgeInsets.zero) ..onActionClick((email) { popBack();