From 28c74657e4c519d510bae455e4cb1541e2a98348 Mon Sep 17 00:00:00 2001 From: dab246 Date: Mon, 16 Dec 2024 12:55:24 +0700 Subject: [PATCH] TF-2646 Handle move all selection emails when drag and drop to mailbox --- .../presentation/resources/assets_paths.dart | 1 - .../mixin/email_action_handler_mixin.dart | 37 ++++ .../presentation/mailbox_controller.dart | 6 + .../presentation/mailbox_view_web.dart | 23 ++- .../utils/mailbox_method_action_define.dart | 4 +- .../widgets/mailbox_item_widget.dart | 9 +- .../mailbox_dashboard_controller.dart | 193 +++++++++++++++--- .../mailbox_dashboard_view_web.dart | 56 +++-- .../top_bar_thread_selection.dart | 185 ++++++++--------- ...all_permanently_emails_loading_widget.dart | 8 +- ...d_selection_all_emails_loading_widget.dart | 16 +- ...d_selection_all_emails_loading_widget.dart | 8 +- .../mark_mailbox_as_read_loading_widget.dart | 8 +- ...l_selection_all_emails_loading_widget.dart | 8 +- .../presentation/search_mailbox_view.dart | 21 +- .../mailbox_searched_item_builder.dart | 11 +- .../data/network/thread_isolate_worker.dart | 1 + .../model/draggable_email_data.dart | 22 ++ .../presentation/thread_controller.dart | 163 ++++----------- .../thread/presentation/thread_view.dart | 81 ++++---- .../draggable/draggable_feedback_widget.dart | 52 +++++ ...ge_select_all_email_in_mailbox_widget.dart | 19 +- .../message_select_email_in_page_widget.dart | 19 +- .../select_all_emails_in_mailbox_banner.dart | 2 +- lib/l10n/intl_messages.arb | 34 ++- lib/main/localizations/app_localizations.dart | 22 +- pubspec.lock | 2 +- pubspec.yaml | 2 + 28 files changed, 630 insertions(+), 383 deletions(-) create mode 100644 lib/features/base/mixin/email_action_handler_mixin.dart rename lib/features/mailbox_dashboard/presentation/widgets/{ => app_bar}/top_bar_thread_selection.dart (65%) create mode 100644 lib/features/thread/presentation/model/draggable_email_data.dart create mode 100644 lib/features/thread/presentation/widgets/draggable/draggable_feedback_widget.dart diff --git a/core/lib/presentation/resources/assets_paths.dart b/core/lib/presentation/resources/assets_paths.dart index a8c7401165..40734a5628 100644 --- a/core/lib/presentation/resources/assets_paths.dart +++ b/core/lib/presentation/resources/assets_paths.dart @@ -1,5 +1,4 @@ class AssetsPaths { static const images = 'assets/images/'; - static const icons = 'assets/icons/'; static const configurationImages = 'configurations/icons/'; } \ No newline at end of file diff --git a/lib/features/base/mixin/email_action_handler_mixin.dart b/lib/features/base/mixin/email_action_handler_mixin.dart new file mode 100644 index 0000000000..f914b222fd --- /dev/null +++ b/lib/features/base/mixin/email_action_handler_mixin.dart @@ -0,0 +1,37 @@ + +import 'dart:ui'; + +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_mixin.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; +import 'package:tmail_ui_user/main/routes/route_navigation.dart'; + +mixin EmailActionHandlerMixin implements MessageDialogActionMixin{ + Future showConfirmDialogWhenMakeToActionForSelectionAllEmails({ + required ImagePaths imagePaths, + required int totalEmails, + required String folderName, + required VoidCallback onConfirmAction, + }) async { + if (currentContext == null) return; + + final appLocalizations = AppLocalizations.of(currentContext!); + + await showConfirmDialogAction( + currentContext!, + appLocalizations.messageConfirmationDialogWhenMakeToActionForSelectionAllEmailsInMailbox( + totalEmails, + folderName, + ), + appLocalizations.ok, + title: appLocalizations.confirmBulkAction, + icon: SvgPicture.asset( + imagePaths.icQuotasWarning, + colorFilter: AppColor.colorBackgroundQuotasWarning.asFilter(), + ), + onConfirmAction: onConfirmAction, + ); + } +} \ No newline at end of file diff --git a/lib/features/mailbox/presentation/mailbox_controller.dart b/lib/features/mailbox/presentation/mailbox_controller.dart index ac980dd968..b655502877 100644 --- a/lib/features/mailbox/presentation/mailbox_controller.dart +++ b/lib/features/mailbox/presentation/mailbox_controller.dart @@ -612,6 +612,12 @@ class MailboxController extends BaseMailboxController log('MailboxController::_handleOpenMailbox():MAILBOX_ID = ${presentationMailboxSelected.id.asString} | MAILBOX_NAME: ${presentationMailboxSelected.name?.name}'); KeyboardUtils.hideKeyboard(context); mailboxDashBoardController.clearSelectedEmail(); + if (mailboxDashBoardController.isSelectAllEmailsEnabled.isTrue) { + mailboxDashBoardController.isSelectAllEmailsEnabled.value = false; + } + if (mailboxDashBoardController.isSelectAllPageEnabled.isTrue) { + mailboxDashBoardController.isSelectAllPageEnabled.value = false; + } if (presentationMailboxSelected.id != mailboxDashBoardController.selectedMailbox.value?.id) { mailboxDashBoardController.clearFilterMessageOption(); } diff --git a/lib/features/mailbox/presentation/mailbox_view_web.dart b/lib/features/mailbox/presentation/mailbox_view_web.dart index 0be90fdbf6..6434810aab 100644 --- a/lib/features/mailbox/presentation/mailbox_view_web.dart +++ b/lib/features/mailbox/presentation/mailbox_view_web.dart @@ -14,6 +14,7 @@ import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_load import 'package:tmail_ui_user/features/mailbox/presentation/widgets/user_information_widget.dart'; import 'package:tmail_ui_user/features/quotas/presentation/quotas_view.dart'; import 'package:tmail_ui_user/features/quotas/presentation/styles/quotas_view_styles.dart'; +import 'package:tmail_ui_user/features/thread/presentation/model/draggable_email_data.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/utils/app_config.dart'; @@ -393,15 +394,29 @@ class MailboxView extends BaseMailboxView { }).toList() ?? []; } - void _handleDragItemAccepted(List listEmails, PresentationMailbox presentationMailbox) { + void _handleDragItemAccepted( + DraggableEmailData draggableEmailData, + PresentationMailbox presentationMailbox, + ) { final mailboxPath = controller.findNodePath(presentationMailbox.id) ?? presentationMailbox.name?.name; log('MailboxView::_handleDragItemAccepted(): mailboxPath: $mailboxPath'); if (mailboxPath != null) { - final newMailbox = presentationMailbox.toPresentationMailboxWithMailboxPath(mailboxPath); - controller.mailboxDashBoardController.dragSelectedMultipleEmailToMailboxAction(listEmails, newMailbox); + presentationMailbox = presentationMailbox + .toPresentationMailboxWithMailboxPath(mailboxPath); + } + + if (draggableEmailData.isSelectAllEmailsEnabled) { + controller + .mailboxDashBoardController + .dragAllSelectedEmailToMailboxAction(presentationMailbox); } else { - controller.mailboxDashBoardController.dragSelectedMultipleEmailToMailboxAction(listEmails, presentationMailbox); + controller + .mailboxDashBoardController + .dragSelectedMultipleEmailToMailboxAction( + draggableEmailData.listEmails!, + presentationMailbox, + ); } } } \ No newline at end of file diff --git a/lib/features/mailbox/presentation/utils/mailbox_method_action_define.dart b/lib/features/mailbox/presentation/utils/mailbox_method_action_define.dart index 035c1f0771..cc4fcfd877 100644 --- a/lib/features/mailbox/presentation/utils/mailbox_method_action_define.dart +++ b/lib/features/mailbox/presentation/utils/mailbox_method_action_define.dart @@ -1,13 +1,13 @@ import 'package:flutter/material.dart'; -import 'package:model/email/presentation_email.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart'; +import 'package:tmail_ui_user/features/thread/presentation/model/draggable_email_data.dart'; typedef OnClickOpenMailboxAction = void Function(PresentationMailbox); typedef OnClickOpenMenuMailboxAction = void Function(RelativeRect, PresentationMailbox); typedef OnSelectMailboxAction = void Function(PresentationMailbox); -typedef OnDragEmailToMailboxAccepted = void Function(List, PresentationMailbox); +typedef OnDragEmailToMailboxAccepted = void Function(DraggableEmailData, PresentationMailbox); typedef OnLongPressMailboxAction = void Function(PresentationMailbox); typedef OnClickExpandMailboxNodeAction = void Function(MailboxNode); diff --git a/lib/features/mailbox/presentation/widgets/mailbox_item_widget.dart b/lib/features/mailbox/presentation/widgets/mailbox_item_widget.dart index dba008eb3b..90d278df64 100644 --- a/lib/features/mailbox/presentation/widgets/mailbox_item_widget.dart +++ b/lib/features/mailbox/presentation/widgets/mailbox_item_widget.dart @@ -6,7 +6,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; -import 'package:model/email/presentation_email.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:model/mailbox/select_mode.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; @@ -16,6 +15,7 @@ import 'package:tmail_ui_user/features/mailbox/presentation/styles/mailbox_item_ import 'package:tmail_ui_user/features/mailbox/presentation/utils/mailbox_method_action_define.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/label_mailbox_item_widget.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/leading_mailbox_item_widget.dart'; +import 'package:tmail_ui_user/features/thread/presentation/model/draggable_email_data.dart'; class MailboxItemWidget extends StatefulWidget { @@ -65,8 +65,8 @@ class _MailboxItemWidgetState extends State { @override Widget build(BuildContext context) { if (_responsiveUtils.isWebDesktop(context) && widget.mailboxDisplayed == MailboxDisplayed.mailbox) { - return DragTarget>( - builder: (context, candidateEmails, rejectedEmails) { + return DragTarget( + builder: (context, _, __) { return InkWell( onTap: () => widget.onOpenMailboxFolderClick?.call(widget.mailboxNode), onHover: (value) => setState(() => _isItemHovered = value), @@ -103,7 +103,8 @@ class _MailboxItemWidgetState extends State { ), ); }, - onAcceptWithDetails: (emails) => widget.onDragItemAccepted?.call(emails.data, widget.mailboxNode.item), + onAcceptWithDetails: (details) => + widget.onDragItemAccepted?.call(details.data, widget.mailboxNode.item), ); } else { if (widget.mailboxDisplayed == MailboxDisplayed.mailbox) { 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 047fe16c95..abd3305a9c 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -30,6 +30,7 @@ import 'package:receive_sharing_intent/receive_sharing_intent.dart'; import 'package:rxdart/transformers.dart'; import 'package:tmail_ui_user/features/base/action/ui_action.dart'; import 'package:tmail_ui_user/features/base/mixin/contact_support_mixin.dart'; +import 'package:tmail_ui_user/features/base/mixin/email_action_handler_mixin.dart'; import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart'; import 'package:tmail_ui_user/features/composer/domain/exceptions/set_method_exception.dart'; import 'package:tmail_ui_user/features/composer/domain/extensions/email_request_extension.dart'; @@ -182,7 +183,9 @@ import 'package:tmail_ui_user/main/utils/ios_notification_manager.dart'; import 'package:uuid/uuid.dart'; class MailboxDashBoardController extends ReloadableController - with UserSettingPopupMenuMixin, ContactSupportMixin { + with UserSettingPopupMenuMixin, + ContactSupportMixin, + EmailActionHandlerMixin { final RemoveEmailDraftsInteractor _removeEmailDraftsInteractor = Get.find(); final EmailReceiveManager _emailReceiveManager = Get.find(); @@ -255,6 +258,7 @@ class MailboxDashBoardController extends ReloadableController final isRecoveringDeletedMessage = RxBool(false); final localFileDraggableAppState = Rxn(); final isSelectAllEmailsEnabled = RxBool(false); + final isSelectAllPageEnabled = RxBool(false); final markAllAsUnreadSelectionAllEmailsViewState = Rx>(Right(UIState.idle)); final moveAllSelectionAllEmailsViewState = Rx>(Right(UIState.idle)); final deleteAllPermanentlyEmailsViewState = Rx>(Right(UIState.idle)); @@ -811,8 +815,8 @@ class MailboxDashBoardController extends ReloadableController } MailboxId? get spamMailboxId { - return mapDefaultMailboxIdByRole[PresentationMailbox.roleJunk] - ?? mapDefaultMailboxIdByRole[PresentationMailbox.roleSpam]; + return getMailboxIdByRole(PresentationMailbox.roleJunk) + ?? getMailboxIdByRole(PresentationMailbox.roleSpam); } void setMapDefaultMailboxIdByRole(Map newMapMailboxId) { @@ -1224,6 +1228,122 @@ class MailboxDashBoardController extends ReloadableController } } + void dragAllSelectedEmailToMailboxAction(PresentationMailbox destinationMailbox) { + if (selectedMailbox.value == null) return; + + showConfirmDialogWhenMakeToActionForSelectionAllEmails( + imagePaths: imagePaths, + totalEmails: selectedMailbox.value!.countTotalEmails, + folderName: currentContext != null + ? selectedMailbox.value!.getDisplayName(currentContext!) + : '', + onConfirmAction: () => handleActionsForSelectionAllEmails( + context: currentContext!, + selectedMailbox: selectedMailbox.value!, + actionType: _getTypeMoveAllActionForMailbox(destinationMailbox), + destinationMailbox: destinationMailbox, + ), + ); + } + + EmailActionType _getTypeMoveAllActionForMailbox(PresentationMailbox presentationMailbox) { + if (presentationMailbox.isTrash) { + return EmailActionType.moveAllToTrash; + } else if (presentationMailbox.isSpam) { + return EmailActionType.markAllAsSpam; + } else { + return EmailActionType.moveAll; + } + } + + void handleActionsForSelectionAllEmails({ + required BuildContext context, + required PresentationMailbox selectedMailbox, + required EmailActionType actionType, + PresentationMailbox? destinationMailbox, + }) { + log('MailboxDashBoardController::handleActionsForSelectionAllEmails:actionType = $actionType'); + if (sessionCurrent == null || accountId.value == null) { + logError('MailboxDashBoardController::_handleActionsForSelectionAllEmails: SESSION & ACCOUNT_ID is null'); + return; + } + + switch(actionType) { + case EmailActionType.markAllAsRead: + markAsReadMailbox( + sessionCurrent!, + accountId.value!, + selectedMailbox.mailboxId!, + selectedMailbox.getDisplayName(context), + selectedMailbox.countUnreadEmails, + ); + break; + case EmailActionType.markAllAsUnread: + markAllAsUnreadSelectionAllEmails( + sessionCurrent!, + accountId.value!, + selectedMailbox.mailboxId!, + selectedMailbox.getDisplayName(context), + selectedMailbox.countReadEmails, + ); + break; + case EmailActionType.moveAll: + moveAllSelectionAllEmails( + context, + sessionCurrent!, + accountId.value!, + selectedMailbox, + destinationMailbox: destinationMailbox, + ); + break; + case EmailActionType.moveAllToTrash: + moveAllToTrashSelectionAllEmails( + context, + sessionCurrent!, + accountId.value!, + selectedMailbox, + trashMailbox: destinationMailbox, + ); + break; + case EmailActionType.deleteAllPermanently: + deleteAllPermanentlyEmails( + context, + sessionCurrent!, + accountId.value!, + selectedMailbox, + ); + break; + case EmailActionType.markAllAsStarred: + markAllAsStarredSelectionAllEmails( + sessionCurrent!, + accountId.value!, + selectedMailbox.mailboxId!, + selectedMailbox.getDisplayName(context), + selectedMailbox.countTotalEmails, + ); + break; + case EmailActionType.markAllAsSpam: + maskAllAsSpamSelectionAllEmails( + context, + sessionCurrent!, + accountId.value!, + selectedMailbox, + spamMailbox: destinationMailbox, + ); + break; + case EmailActionType.allUnSpam: + allUnSpamSelectionAllEmails( + context, + sessionCurrent!, + accountId.value!, + selectedMailbox, + ); + break; + default: + break; + } + } + void dragSelectedMultipleEmailToMailboxAction( List listEmails, PresentationMailbox destinationMailbox, @@ -1246,7 +1366,6 @@ class MailboxDashBoardController extends ReloadableController _handleDragSelectedMultipleEmailToMailboxAction({selectedMailbox.value!.id: listEmails.listEmailIds}, destinationMailbox); } } - } void _handleDragSelectedMultipleEmailToMailboxAction( @@ -2631,6 +2750,7 @@ class MailboxDashBoardController extends ReloadableController } void selectAllEmailAction() { + isSelectAllPageEnabled.value = true; dispatchAction(SelectionAllEmailAction()); } @@ -3107,19 +3227,26 @@ class MailboxDashBoardController extends ReloadableController BuildContext context, Session session, AccountId accountId, - PresentationMailbox currentMailbox + PresentationMailbox currentMailbox, + { + PresentationMailbox? destinationMailbox, + } ) async { - final arguments = DestinationPickerArguments( - accountId, - MailboxActions.moveEmail, - session, - mailboxIdSelected: currentMailbox.id); + if (destinationMailbox == null) { + final arguments = DestinationPickerArguments( + accountId, + MailboxActions.moveEmail, + session, + mailboxIdSelected: currentMailbox.id, + ); - final destinationMailbox = PlatformInfo.isWeb - ? await DialogRouter.pushGeneralDialog( - routeName: AppRoutes.destinationPicker, - arguments: arguments) - : await push(AppRoutes.destinationPicker, arguments: arguments); + destinationMailbox = PlatformInfo.isWeb + ? await DialogRouter.pushGeneralDialog( + routeName: AppRoutes.destinationPicker, + arguments: arguments, + ) + : await push(AppRoutes.destinationPicker, arguments: arguments); + } if (destinationMailbox is PresentationMailbox) { consumeState(_moveAllSelectionAllEmailsInteractor.execute( @@ -3127,7 +3254,9 @@ class MailboxDashBoardController extends ReloadableController accountId, currentMailbox.id, destinationMailbox.id, - destinationMailbox.mailboxPath ?? (context.mounted ? destinationMailbox.getDisplayName(context) : ''), + destinationMailbox.mailboxPath ?? (context.mounted + ? destinationMailbox.getDisplayName(context) + : ''), currentMailbox.countTotalEmails, _moveAllSelectionAllEmailsStreamController, isDestinationSpamMailbox: destinationMailbox.isSpam @@ -3163,13 +3292,21 @@ class MailboxDashBoardController extends ReloadableController BuildContext context, Session session, AccountId accountId, - PresentationMailbox currentMailbox + PresentationMailbox currentMailbox, + { + PresentationMailbox? trashMailbox, + } ) async { - final trashMailboxId = getMailboxIdByRole(PresentationMailbox.roleTrash); + MailboxId? trashMailboxId = trashMailbox?.id; + String? trashMailboxPath = trashMailbox?.getDisplayName(context) ?? ''; - if (trashMailboxId == null) return; + if (trashMailbox == null) { + trashMailboxId = getMailboxIdByRole(PresentationMailbox.roleTrash); + if (trashMailboxId == null) return; + trashMailboxPath = mapMailboxById[trashMailboxId]?.getDisplayName(context) ?? ''; + } - final trashMailboxPath = mapMailboxById[trashMailboxId]?.getDisplayName(context) ?? ''; + if (trashMailboxId == null) return; consumeState(_moveAllSelectionAllEmailsInteractor.execute( session, @@ -3266,13 +3403,21 @@ class MailboxDashBoardController extends ReloadableController BuildContext context, Session session, AccountId accountId, - PresentationMailbox currentMailbox + PresentationMailbox currentMailbox, + { + PresentationMailbox? spamMailbox, + } ) async { - final spamMailboxId = getMailboxIdByRole(PresentationMailbox.roleSpam); + MailboxId? spamMailboxId = spamMailbox?.id; + String spamMailboxPath = spamMailbox?.getDisplayName(context) ?? ''; - if (spamMailboxId == null) return; + if (spamMailbox == null) { + spamMailboxId = this.spamMailboxId; + if (spamMailboxId == null) return; + spamMailboxPath = mapMailboxById[spamMailboxId]?.getDisplayName(context) ?? ''; + } - final spamMailboxPath = mapMailboxById[spamMailboxId]?.getDisplayName(context) ?? ''; + if (spamMailboxId == null) return; consumeState(_moveAllSelectionAllEmailsInteractor.execute( session, diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart index 8a9a310653..f80177716d 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart @@ -34,7 +34,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/re import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/search_filters/filter_message_button.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/search_filters/search_filter_button.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/search_input_form_widget.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/top_bar_thread_selection.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/app_bar/top_bar_thread_selection.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/extensions/vacation_response_extension.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/vacation/styles/vacation_notification_message_widget_style.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/vacation/widgets/vacation_notification_message_widget.dart'; @@ -138,11 +138,21 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { } }), _buildListButtonQuickSearchFilter(context), - Obx(() => MarkMailboxAsReadLoadingWidget(viewState: controller.viewStateMarkAsReadMailbox.value)), - Obx(() => MarkAllAsUnreadSelectionAllEmailsLoadingWidget(viewState: controller.markAllAsUnreadSelectionAllEmailsViewState.value)), - Obx(() => MoveAllSelectionAllEmailsLoadingWidget(viewState: controller.moveAllSelectionAllEmailsViewState.value)), - Obx(() => DeleteAllPermanentlyEmailsLoadingWidget(viewState: controller.deleteAllPermanentlyEmailsViewState.value)), - Obx(() => MarkAllAsStarredSelectionAllEmailsLoadingWidget(viewState: controller.markAllAsStarredSelectionAllEmailsViewState.value)), + Obx(() => MarkMailboxAsReadLoadingWidget( + viewState: controller.viewStateMarkAsReadMailbox.value, + )), + Obx(() => MarkAllAsUnreadSelectionAllEmailsLoadingWidget( + viewState: controller.markAllAsUnreadSelectionAllEmailsViewState.value, + )), + Obx(() => MoveAllSelectionAllEmailsLoadingWidget( + viewState: controller.moveAllSelectionAllEmailsViewState.value, + )), + Obx(() => DeleteAllPermanentlyEmailsLoadingWidget( + viewState: controller.deleteAllPermanentlyEmailsViewState.value, + )), + Obx(() => MarkAllAsStarredSelectionAllEmailsLoadingWidget( + viewState: controller.markAllAsStarredSelectionAllEmailsViewState.value, + )), Expanded(child: Obx(() { switch(controller.dashboardRoute.value) { case DashboardRoutes.thread: @@ -245,22 +255,24 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { Obx(() { final listEmailSelected = controller.listEmailSelected; if (controller.isSelectionEnabled() && listEmailSelected.isNotEmpty) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 8.5, horizontal: 16), - child: TopBarThreadSelection( - listEmail: listEmailSelected, - mapMailbox: controller.mapMailboxById, - isSelectAllEmailsEnabled: controller.isSelectAllEmailsEnabled.value, - selectedMailbox: controller.selectedMailbox.value, - onCancelSelection: () => - controller.dispatchAction(CancelSelectionAllEmailAction()), - onEmailActionTypeAction: (listEmails, actionType) => - controller.dispatchAction(HandleEmailActionTypeAction( - listEmails, - actionType - )), - onMoreSelectedEmailAction: (position) => controller.dispatchAction(MoreSelectedEmailAction(context, position)), - ), + return TopBarThreadSelection( + imagePaths: controller.imagePaths, + listEmail: listEmailSelected, + mapMailbox: controller.mapMailboxById, + isSelectAllEmailsEnabled: controller.isSelectAllEmailsEnabled.value, + selectedMailbox: controller.selectedMailbox.value, + onCancelSelection: () => + controller.dispatchAction(CancelSelectionAllEmailAction()), + onEmailActionTypeAction: (listEmails, actionType) => + controller.dispatchAction(HandleEmailActionTypeAction( + listEmails, + actionType, + )), + onMoreSelectedEmailAction: (position) => + controller.dispatchAction(MoreSelectedEmailAction( + context, + position, + )), ); } else { return Padding( diff --git a/lib/features/mailbox_dashboard/presentation/widgets/top_bar_thread_selection.dart b/lib/features/mailbox_dashboard/presentation/widgets/app_bar/top_bar_thread_selection.dart similarity index 65% rename from lib/features/mailbox_dashboard/presentation/widgets/top_bar_thread_selection.dart rename to lib/features/mailbox_dashboard/presentation/widgets/app_bar/top_bar_thread_selection.dart index 739c15f869..fc977262de 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/top_bar_thread_selection.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/app_bar/top_bar_thread_selection.dart @@ -3,7 +3,6 @@ import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/views/button/tmail_button_widget.dart'; import 'package:flutter/material.dart'; -import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/email/email_action_type.dart'; import 'package:model/email/presentation_email.dart'; @@ -12,13 +11,15 @@ import 'package:model/extensions/presentation_mailbox_extension.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -typedef OnEmailActionTypeAction = Function(List listEmail, EmailActionType actionType); +typedef OnEmailActionTypeAction = Function( + List listEmail, + EmailActionType actionType, +); typedef OnMoreSelectedEmailAction = Function(RelativeRect position); class TopBarThreadSelection extends StatelessWidget{ - final imagePaths = Get.find(); - + final ImagePaths imagePaths; final List listEmail; final Map mapMailbox; final OnEmailActionTypeAction? onEmailActionTypeAction; @@ -27,8 +28,9 @@ class TopBarThreadSelection extends StatelessWidget{ final PresentationMailbox? selectedMailbox; final OnMoreSelectedEmailAction? onMoreSelectedEmailAction; - TopBarThreadSelection({ + const TopBarThreadSelection({ super.key, + required this.imagePaths, required this.listEmail, required this.mapMailbox, required this.isSelectAllEmailsEnabled, @@ -40,98 +42,100 @@ class TopBarThreadSelection extends StatelessWidget{ @override Widget build(BuildContext context) { - return Row(children: [ - TMailButtonWidget.fromIcon( - icon: imagePaths.icClose, - iconColor: AppColor.primaryColor, - tooltipMessage: AppLocalizations.of(context).cancel, - backgroundColor: Colors.transparent, - iconSize: 28, - padding: const EdgeInsets.all(3), - onTapActionCallback: onCancelSelection - ), - if (!isSelectAllEmailsEnabled) - Padding( - padding: const EdgeInsetsDirectional.only(end: 30), - child: Text( - AppLocalizations.of(context).count_email_selected(listEmail.length), - style: const TextStyle( - fontSize: 17, - fontWeight: FontWeight.w500, - color: AppColor.colorTextButton - ) - ), + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.5, horizontal: 16), + child: Row(children: [ + TMailButtonWidget.fromIcon( + icon: imagePaths.icClose, + iconColor: AppColor.primaryColor, + tooltipMessage: AppLocalizations.of(context).cancel, + backgroundColor: Colors.transparent, + iconSize: 28, + padding: const EdgeInsets.all(3), + onTapActionCallback: onCancelSelection ), - TMailButtonWidget.fromIcon( - icon: _getIconForMarkAsRead(), - tooltipMessage: _getTooltipMessageForMarkAsRead(context), - backgroundColor: Colors.transparent, - iconSize: 24, - onTapActionCallback: () => onEmailActionTypeAction?.call( - List.from(listEmail), - _getActionTypeForMarkAsRead() - ) - ), - TMailButtonWidget.fromIcon( - icon: _getIconForMarkAsStar(), - tooltipMessage: _getTooltipMessageForMarkAsStar(context), - backgroundColor: Colors.transparent, - iconSize: 24, - onTapActionCallback: () => onEmailActionTypeAction?.call( - List.from(listEmail), - _getActionTypeForMarkAsStar() - ) - ), - if (canSpamAndMove) - ...[ - TMailButtonWidget.fromIcon( - icon: imagePaths.icMove, - iconSize: 22, - tooltipMessage: _getTooltipMessageForMove(context), - backgroundColor: Colors.transparent, - onTapActionCallback: () => onEmailActionTypeAction?.call( - List.from(listEmail), - _getActionTypeForMove() - ) - ), - TMailButtonWidget.fromIcon( - icon: _getIconForMoveToSpam(), - backgroundColor: Colors.transparent, - iconSize: 24, - tooltipMessage: _getTooltipMessageForMoveToSpam(context), - onTapActionCallback: () { - onEmailActionTypeAction?.call( - List.from(listEmail.listEmailCanSpam(mapMailbox)), - _getActionTypeForMoveToSpam() - ); - } - ) - ], - if (isAllBelongToTheSameMailbox) + if (!isSelectAllEmailsEnabled) + Padding( + padding: const EdgeInsetsDirectional.only(end: 30), + child: Text( + AppLocalizations.of(context).count_email_selected(listEmail.length), + style: const TextStyle( + fontSize: 17, + fontWeight: FontWeight.w500, + color: AppColor.colorTextButton + ) + ), + ), TMailButtonWidget.fromIcon( - icon: imagePaths.icDeleteComposer, + icon: _getIconForMarkAsRead(), + tooltipMessage: _getTooltipMessageForMarkAsRead(context), backgroundColor: Colors.transparent, - iconSize: 20, - iconColor: _getIconColorForMoveToTrash(), - tooltipMessage: _getTooltipMessageForMoveToTrash(context), - onTapActionCallback: () { - onEmailActionTypeAction?.call( - List.from(listEmail), - _getActionTypeForMoveToTrash(), - ); - } + iconSize: 24, + onTapActionCallback: () => onEmailActionTypeAction?.call( + List.from(listEmail), + _getActionTypeForMarkAsRead() + ) ), - const Spacer(), - if (isSelectAllEmailsEnabled) TMailButtonWidget.fromIcon( - icon: imagePaths.icMoreVertical, - iconSize: 22, - iconColor: AppColor.primaryColor, - tooltipMessage: AppLocalizations.of(context).more, + icon: _getIconForMarkAsStar(), + tooltipMessage: _getTooltipMessageForMarkAsStar(context), backgroundColor: Colors.transparent, - onTapActionAtPositionCallback: onMoreSelectedEmailAction + iconSize: 24, + onTapActionCallback: () => onEmailActionTypeAction?.call( + List.from(listEmail), + _getActionTypeForMarkAsStar() + ) ), - ]); + if (canSpamAndMove) + ...[ + TMailButtonWidget.fromIcon( + icon: imagePaths.icMove, + iconSize: 22, + tooltipMessage: _getTooltipMessageForMove(context), + backgroundColor: Colors.transparent, + onTapActionCallback: () => onEmailActionTypeAction?.call( + List.from(listEmail), + _getActionTypeForMove() + ) + ), + TMailButtonWidget.fromIcon( + icon: _getIconForMoveToSpam(), + backgroundColor: Colors.transparent, + iconSize: 24, + tooltipMessage: _getTooltipMessageForMoveToSpam(context), + onTapActionCallback: () { + onEmailActionTypeAction?.call( + List.from(listEmail.listEmailCanSpam(mapMailbox)), + _getActionTypeForMoveToSpam() + ); + } + ) + ], + if (isAllBelongToTheSameMailbox) + TMailButtonWidget.fromIcon( + icon: imagePaths.icDeleteComposer, + backgroundColor: Colors.transparent, + iconSize: 20, + iconColor: _getIconColorForMoveToTrash(), + tooltipMessage: _getTooltipMessageForMoveToTrash(context), + onTapActionCallback: () { + onEmailActionTypeAction?.call( + List.from(listEmail), + _getActionTypeForMoveToTrash() + ); + } + ), + if (isSelectAllEmailsEnabled) + TMailButtonWidget.fromIcon( + icon: imagePaths.icMoreVertical, + iconSize: 22, + iconColor: AppColor.primaryColor, + tooltipMessage: AppLocalizations.of(context).more, + backgroundColor: Colors.transparent, + onTapActionAtPositionCallback: onMoreSelectedEmailAction + ), + ]), + ); } bool get canDeletePermanently => listEmail.isAllCanDeletePermanently(mapMailbox); @@ -249,7 +253,6 @@ class TopBarThreadSelection extends StatelessWidget{ } } - EmailActionType _getActionTypeForMarkAsStar() { if (isSelectAllEmailsEnabled) { return EmailActionType.markAllAsStarred; diff --git a/lib/features/mailbox_dashboard/presentation/widgets/loading/delete_all_permanently_emails_loading_widget.dart b/lib/features/mailbox_dashboard/presentation/widgets/loading/delete_all_permanently_emails_loading_widget.dart index d9c0a5ec27..b46e3c24b1 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/loading/delete_all_permanently_emails_loading_widget.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/loading/delete_all_permanently_emails_loading_widget.dart @@ -19,16 +19,16 @@ class DeleteAllPermanentlyEmailsLoadingWidget extends StatelessWidget with AppLo (success) { if (success is DeleteAllPermanentlyEmailsLoading) { return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: horizontalLoadingWidget + padding: const EdgeInsetsDirectional.only(end: 16, bottom: 16), + child: horizontalLoadingWidget, ); } else if (success is DeleteAllPermanentlyEmailsUpdating) { final percent = success.total > 0 ? success.countDeleted / success.total : 0.0; return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: horizontalPercentLoadingWidget(percent) + padding: const EdgeInsetsDirectional.only(end: 16, bottom: 16), + child: horizontalPercentLoadingWidget(percent), ); } return const SizedBox.shrink(); diff --git a/lib/features/mailbox_dashboard/presentation/widgets/loading/mark_all_as_starred_selection_all_emails_loading_widget.dart b/lib/features/mailbox_dashboard/presentation/widgets/loading/mark_all_as_starred_selection_all_emails_loading_widget.dart index c859468221..53ec1be556 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/loading/mark_all_as_starred_selection_all_emails_loading_widget.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/loading/mark_all_as_starred_selection_all_emails_loading_widget.dart @@ -6,11 +6,15 @@ import 'package:flutter/material.dart'; import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; import 'package:tmail_ui_user/features/thread/domain/state/mark_all_as_starred_selection_all_emails_state.dart'; -class MarkAllAsStarredSelectionAllEmailsLoadingWidget extends StatelessWidget with AppLoaderMixin { +class MarkAllAsStarredSelectionAllEmailsLoadingWidget extends StatelessWidget + with AppLoaderMixin { final Either viewState; - const MarkAllAsStarredSelectionAllEmailsLoadingWidget({super.key, required this.viewState}); + const MarkAllAsStarredSelectionAllEmailsLoadingWidget({ + super.key, + required this.viewState, + }); @override Widget build(BuildContext context) { @@ -19,16 +23,16 @@ class MarkAllAsStarredSelectionAllEmailsLoadingWidget extends StatelessWidget wi (success) { if (success is MarkAllAsStarredSelectionAllEmailsLoading) { return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: horizontalLoadingWidget + padding: const EdgeInsetsDirectional.only(end: 16, bottom: 16), + child: horizontalLoadingWidget, ); } else if (success is MarkAllAsStarredSelectionAllEmailsUpdating) { final percent = success.total > 0 ? success.countStarred / success.total : 0.0; return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: horizontalPercentLoadingWidget(percent) + padding: const EdgeInsetsDirectional.only(end: 16, bottom: 16), + child: horizontalPercentLoadingWidget(percent), ); } return const SizedBox.shrink(); diff --git a/lib/features/mailbox_dashboard/presentation/widgets/loading/mark_all_as_unread_selection_all_emails_loading_widget.dart b/lib/features/mailbox_dashboard/presentation/widgets/loading/mark_all_as_unread_selection_all_emails_loading_widget.dart index 2752e9fb82..2f29c6d0e1 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/loading/mark_all_as_unread_selection_all_emails_loading_widget.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/loading/mark_all_as_unread_selection_all_emails_loading_widget.dart @@ -19,16 +19,16 @@ class MarkAllAsUnreadSelectionAllEmailsLoadingWidget extends StatelessWidget wit (success) { if (success is MarkAllAsUnreadSelectionAllEmailsLoading) { return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: horizontalLoadingWidget + padding: const EdgeInsetsDirectional.only(end: 16, bottom: 16), + child: horizontalLoadingWidget, ); } else if (success is MarkAllAsUnreadSelectionAllEmailsUpdating) { final percent = success.totalRead > 0 ? success.countUnread / success.totalRead : 0.0; return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: horizontalPercentLoadingWidget(percent) + padding: const EdgeInsetsDirectional.only(end: 16, bottom: 16), + child: horizontalPercentLoadingWidget(percent), ); } return const SizedBox.shrink(); diff --git a/lib/features/mailbox_dashboard/presentation/widgets/loading/mark_mailbox_as_read_loading_widget.dart b/lib/features/mailbox_dashboard/presentation/widgets/loading/mark_mailbox_as_read_loading_widget.dart index 5926b23db6..73640b7b54 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/loading/mark_mailbox_as_read_loading_widget.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/loading/mark_mailbox_as_read_loading_widget.dart @@ -19,16 +19,16 @@ class MarkMailboxAsReadLoadingWidget extends StatelessWidget with AppLoaderMixin (success) { if (success is MarkAsMailboxReadLoading) { return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: horizontalLoadingWidget + padding: const EdgeInsetsDirectional.only(end: 16, bottom: 16), + child: horizontalLoadingWidget, ); } else if (success is UpdatingMarkAsMailboxReadState) { final percent = success.totalUnread > 0 ? success.countRead / success.totalUnread : 0.0; return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: horizontalPercentLoadingWidget(percent) + padding: const EdgeInsetsDirectional.only(end: 16, bottom: 16), + child: horizontalPercentLoadingWidget(percent), ); } return const SizedBox.shrink(); diff --git a/lib/features/mailbox_dashboard/presentation/widgets/loading/move_all_selection_all_emails_loading_widget.dart b/lib/features/mailbox_dashboard/presentation/widgets/loading/move_all_selection_all_emails_loading_widget.dart index 951b8d7ad4..130f7e94a2 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/loading/move_all_selection_all_emails_loading_widget.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/loading/move_all_selection_all_emails_loading_widget.dart @@ -19,16 +19,16 @@ class MoveAllSelectionAllEmailsLoadingWidget extends StatelessWidget with AppLoa (success) { if (success is MoveAllSelectionAllEmailsLoading) { return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: horizontalLoadingWidget + padding: const EdgeInsetsDirectional.only(end: 16, bottom: 16), + child: horizontalLoadingWidget, ); } else if (success is MoveAllSelectionAllEmailsUpdating) { final percent = success.total > 0 ? success.countMoved / success.total : 0.0; return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: horizontalPercentLoadingWidget(percent) + padding: const EdgeInsetsDirectional.only(end: 16, bottom: 16), + child: horizontalPercentLoadingWidget(percent), ); } return const SizedBox.shrink(); diff --git a/lib/features/search/mailbox/presentation/search_mailbox_view.dart b/lib/features/search/mailbox/presentation/search_mailbox_view.dart index b388f23fc9..3297f66b05 100644 --- a/lib/features/search/mailbox/presentation/search_mailbox_view.dart +++ b/lib/features/search/mailbox/presentation/search_mailbox_view.dart @@ -18,6 +18,7 @@ import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_action import 'package:tmail_ui_user/features/search/mailbox/presentation/search_mailbox_controller.dart'; import 'package:tmail_ui_user/features/search/mailbox/presentation/utils/search_mailbox_utils.dart'; import 'package:tmail_ui_user/features/search/mailbox/presentation/widgets/mailbox_searched_item_builder.dart'; +import 'package:tmail_ui_user/features/thread/presentation/model/draggable_email_data.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; class SearchMailboxView extends GetWidget @@ -183,7 +184,7 @@ class SearchMailboxView extends GetWidget controller.responsiveUtils, mailboxCurrent, maxWidth: constraints.maxWidth, - onDragEmailToMailboxAccepted: controller.dashboardController.dragSelectedMultipleEmailToMailboxAction, + onDragEmailToMailboxAccepted: _handleDragItemAccepted, onClickOpenMailboxAction: (mailbox) => controller.openMailboxAction(context, mailbox), onClickOpenMenuMailboxAction: (position, mailbox) => _openMailboxMenuAction(context, mailbox, position: position), onLongPressMailboxAction: (mailbox) => _openMailboxMenuAction(context, mailbox), @@ -195,6 +196,24 @@ class SearchMailboxView extends GetWidget }); } + void _handleDragItemAccepted( + DraggableEmailData draggableEmailData, + PresentationMailbox presentationMailbox, + ) { + if (draggableEmailData.isSelectAllEmailsEnabled) { + controller + .dashboardController + .dragAllSelectedEmailToMailboxAction(presentationMailbox); + } else { + controller + .dashboardController + .dragSelectedMultipleEmailToMailboxAction( + draggableEmailData.listEmails!, + presentationMailbox, + ); + } + } + List _listPopupMenuItemAction(BuildContext context, PresentationMailbox mailbox) { final bool subaddressingSupported = MailboxWidgetMixin.isSubaddressingSupported( controller.dashboardController.sessionCurrent, diff --git a/lib/features/search/mailbox/presentation/widgets/mailbox_searched_item_builder.dart b/lib/features/search/mailbox/presentation/widgets/mailbox_searched_item_builder.dart index b6bef14622..253f3dd14e 100644 --- a/lib/features/search/mailbox/presentation/widgets/mailbox_searched_item_builder.dart +++ b/lib/features/search/mailbox/presentation/widgets/mailbox_searched_item_builder.dart @@ -8,12 +8,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:focused_menu_custom/focused_menu.dart'; import 'package:focused_menu_custom/modals.dart'; -import 'package:model/email/presentation_email.dart'; import 'package:model/extensions/presentation_mailbox_extension.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/utils/mailbox_method_action_define.dart'; import 'package:tmail_ui_user/features/search/mailbox/presentation/utils/search_mailbox_utils.dart'; +import 'package:tmail_ui_user/features/thread/presentation/model/draggable_email_data.dart'; class MailboxSearchedItemBuilder extends StatefulWidget { @@ -52,10 +52,13 @@ class _MailboxSearchedItemBuilderState extends State @override Widget build(BuildContext context) { if (PlatformInfo.isWeb) { - return DragTarget>( + return DragTarget( builder: (_, __, ___) => _buildMailboxItem(context), - onAcceptWithDetails: (emails) { - widget.onDragEmailToMailboxAccepted?.call(emails.data, widget._presentationMailbox); + onAcceptWithDetails: (details) { + widget.onDragEmailToMailboxAccepted?.call( + details.data, + widget._presentationMailbox, + ); } ); } else { diff --git a/lib/features/thread/data/network/thread_isolate_worker.dart b/lib/features/thread/data/network/thread_isolate_worker.dart index 467afcaff2..1ef9f51b77 100644 --- a/lib/features/thread/data/network/thread_isolate_worker.dart +++ b/lib/features/thread/data/network/thread_isolate_worker.dart @@ -245,6 +245,7 @@ class ThreadIsolateWorker { bool isDestinationSpamMailbox = false } ) async { + log('ThreadIsolateWorker::moveAllSelectionAllEmails: destinationMailboxId = $destinationMailboxId | totalEmails = $totalEmails'); List emailIdListCompleted = List.empty(growable: true); try { bool mailboxHasEmails = true; diff --git a/lib/features/thread/presentation/model/draggable_email_data.dart b/lib/features/thread/presentation/model/draggable_email_data.dart new file mode 100644 index 0000000000..68c1ee0df0 --- /dev/null +++ b/lib/features/thread/presentation/model/draggable_email_data.dart @@ -0,0 +1,22 @@ + +import 'package:equatable/equatable.dart'; +import 'package:model/email/presentation_email.dart'; + +class DraggableEmailData with EquatableMixin { + final List? listEmails; + final bool isSelectAllEmailsEnabled; + + DraggableEmailData({ + this.isSelectAllEmailsEnabled = false, + this.listEmails, + }); + + factory DraggableEmailData.withSelectAllEmails() => + DraggableEmailData(isSelectAllEmailsEnabled: true); + + @override + List get props => [ + listEmails, + isSelectAllEmailsEnabled, + ]; +} \ No newline at end of file diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index bc83a5a4f3..1368176f89 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:core/utils/app_logger.dart'; @@ -8,7 +7,6 @@ import 'package:core/utils/platform_info.dart'; import 'package:dartz/dartz.dart'; 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/core/session/session.dart'; @@ -20,6 +18,7 @@ import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; +import 'package:tmail_ui_user/features/base/mixin/email_action_handler_mixin.dart'; import 'package:tmail_ui_user/features/base/mixin/popup_menu_widget_mixin.dart'; 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'; @@ -95,7 +94,10 @@ import 'package:universal_html/html.dart' as html; typedef StartRangeSelection = int; typedef EndRangeSelection = int; -class ThreadController extends BaseController with EmailActionController, PopupMenuWidgetMixin { +class ThreadController extends BaseController + with EmailActionController, + PopupMenuWidgetMixin, + EmailActionHandlerMixin { final networkConnectionController = Get.find(); @@ -296,7 +298,18 @@ class ThreadController extends BaseController with EmailActionController, PopupM mailboxDashBoardController.clearDashBoardAction(); } else if (action is HandleEmailActionTypeAction) { if (_validateToShowConfirmBulkActionEmailsDialog()) { - _showConfirmDialogWhenMakeToActionForSelectionAllEmails(actionType: action.emailAction); + showConfirmDialogWhenMakeToActionForSelectionAllEmails( + imagePaths: imagePaths, + totalEmails: selectedMailbox?.countTotalEmails ?? 0, + folderName: currentContext != null + ? selectedMailbox?.getDisplayName(currentContext!) ?? '' + : '', + onConfirmAction: () => mailboxDashBoardController.handleActionsForSelectionAllEmails( + context: currentContext!, + selectedMailbox: selectedMailbox!, + actionType: action.emailAction + ), + ); } else { pressEmailSelectionAction( action.emailAction, @@ -766,6 +779,12 @@ class ThreadController extends BaseController with EmailActionController, PopupM } void selectEmail(PresentationEmail presentationEmailSelected) { + if (mailboxDashBoardController.isSelectAllPageEnabled.isTrue) { + mailboxDashBoardController.isSelectAllPageEnabled.value = false; + } + if (mailboxDashBoardController.isSelectAllEmailsEnabled.isTrue) { + mailboxDashBoardController.isSelectAllEmailsEnabled.value = false; + } final emailsInCurrentMailbox = mailboxDashBoardController.emailsInCurrentMailbox; if (_rangeSelectionMode && latestEmailSelectedOrUnselected.value != null && latestEmailSelectedOrUnselected.value?.id != presentationEmailSelected.id) { @@ -817,6 +836,7 @@ class ThreadController extends BaseController with EmailActionController, PopupM .map((email) => email.toSelectedEmail(selectMode: SelectMode.INACTIVE)) .toList(); mailboxDashBoardController.isSelectAllEmailsEnabled.value = false; + mailboxDashBoardController.isSelectAllPageEnabled.value = false; mailboxDashBoardController.updateEmailList(newEmailList); mailboxDashBoardController.currentSelectMode.value = SelectMode.INACTIVE; mailboxDashBoardController.listEmailSelected.clear(); @@ -1389,6 +1409,12 @@ class ThreadController extends BaseController with EmailActionController, PopupM void handleLoadMoreEmailsRequest() { log('ThreadController::handleLoadMoreEmailsRequest:'); + if (mailboxDashBoardController.isSelectAllPageEnabled.isTrue) { + mailboxDashBoardController.isSelectAllPageEnabled.value = false; + } + if (mailboxDashBoardController.isSelectAllEmailsEnabled.isTrue) { + mailboxDashBoardController.isSelectAllEmailsEnabled.value = false; + } if (isSearchActive) { _searchMoreEmails(); } else { @@ -1397,7 +1423,7 @@ class ThreadController extends BaseController with EmailActionController, PopupM } bool validateToShowSelectionEmailsBanner() { - return mailboxDashBoardController.isSelectionEnabled() && + return mailboxDashBoardController.isSelectAllPageEnabled.isTrue && selectedMailbox != null && selectedMailbox!.countTotalEmails > ThreadConstants.maxCountEmails && mailboxDashBoardController.listEmailSelected.length < @@ -1408,7 +1434,8 @@ class ThreadController extends BaseController with EmailActionController, PopupM final listSelectionEmailActions = [ EmailActionType.markAllAsRead, EmailActionType.markAllAsUnread, - EmailActionType.moveAll, + if (selectedMailbox == null || selectedMailbox?.isDrafts == false) + EmailActionType.moveAll, if (selectedMailbox?.isTrash == true || selectedMailbox?.isSpam == true || selectedMailbox?.isDrafts == true) @@ -1434,7 +1461,16 @@ class ThreadController extends BaseController with EmailActionController, PopupM onCallbackAction: () { popBack(); if (!isSearchActive) { - _showConfirmDialogWhenMakeToActionForSelectionAllEmails(actionType: action); + showConfirmDialogWhenMakeToActionForSelectionAllEmails( + imagePaths: imagePaths, + totalEmails: selectedMailbox?.countTotalEmails ?? 0, + folderName: selectedMailbox?.getDisplayName(context) ?? '', + onConfirmAction: () => mailboxDashBoardController.handleActionsForSelectionAllEmails( + context: context, + selectedMailbox: selectedMailbox!, + actionType: action, + ), + ); } } ) @@ -1445,117 +1481,4 @@ class ThreadController extends BaseController with EmailActionController, PopupM bool _validateToShowConfirmBulkActionEmailsDialog() { return mailboxDashBoardController.isSelectAllEmailsEnabled.isTrue; } - - Future _showConfirmDialogWhenMakeToActionForSelectionAllEmails({ - required EmailActionType actionType, - }) async { - final selectedMailbox = mailboxDashBoardController.selectedMailbox.value; - - if (currentContext == null || selectedMailbox == null) return; - - final appLocalizations = AppLocalizations.of(currentContext!); - final totalEmails = selectedMailbox.totalEmails?.value.value.toInt() ?? 0; - final folderName = selectedMailbox.getDisplayName(currentContext!); - - await showConfirmDialogAction( - currentContext!, - appLocalizations.messageConfirmationDialogWhenMakeToActionForSelectionAllEmailsInMailbox(totalEmails, folderName), - appLocalizations.ok, - title: appLocalizations.confirmBulkAction, - icon: SvgPicture.asset( - imagePaths.icQuotasWarning, - colorFilter: AppColor.colorBackgroundQuotasWarning.asFilter(), - ), - onConfirmAction: () { - _handleActionsForSelectionAllEmails( - context: currentContext!, - selectedMailbox: selectedMailbox, - actionType: actionType - ); - } - ); - } - - void _handleActionsForSelectionAllEmails({ - required BuildContext context, - required PresentationMailbox selectedMailbox, - required EmailActionType actionType - }) { - if (_session == null || _accountId == null) { - logError('ThreadController::_handleActionsForSelectionAllEmails: SESSION & ACCOUNT_ID is null'); - return; - } - - switch(actionType) { - case EmailActionType.markAllAsRead: - mailboxDashBoardController.markAsReadMailbox( - _session!, - _accountId!, - selectedMailbox.mailboxId!, - selectedMailbox.getDisplayName(context), - selectedMailbox.countUnreadEmails - ); - break; - case EmailActionType.markAllAsUnread: - mailboxDashBoardController.markAllAsUnreadSelectionAllEmails( - _session!, - _accountId!, - selectedMailbox.mailboxId!, - selectedMailbox.getDisplayName(context), - selectedMailbox.countReadEmails - ); - break; - case EmailActionType.moveAll: - mailboxDashBoardController.moveAllSelectionAllEmails( - context, - _session!, - _accountId!, - selectedMailbox, - ); - break; - case EmailActionType.moveAllToTrash: - mailboxDashBoardController.moveAllToTrashSelectionAllEmails( - context, - _session!, - _accountId!, - selectedMailbox, - ); - break; - case EmailActionType.deleteAllPermanently: - mailboxDashBoardController.deleteAllPermanentlyEmails( - context, - _session!, - _accountId!, - selectedMailbox, - ); - break; - case EmailActionType.markAllAsStarred: - mailboxDashBoardController.markAllAsStarredSelectionAllEmails( - _session!, - _accountId!, - selectedMailbox.mailboxId!, - selectedMailbox.getDisplayName(context), - selectedMailbox.countTotalEmails - ); - break; - case EmailActionType.markAllAsSpam: - mailboxDashBoardController.maskAllAsSpamSelectionAllEmails( - context, - _session!, - _accountId!, - selectedMailbox, - ); - break; - case EmailActionType.allUnSpam: - mailboxDashBoardController.allUnSpamSelectionAllEmails( - context, - _session!, - _accountId!, - selectedMailbox, - ); - break; - default: - break; - } - } } \ No newline at end of file diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index 61b7743896..f6a4fdc168 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -22,6 +22,7 @@ import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option import 'package:tmail_ui_user/features/thread/domain/state/get_all_email_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/search_email_state.dart'; import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_type.dart'; +import 'package:tmail_ui_user/features/thread/presentation/model/draggable_email_data.dart'; import 'package:tmail_ui_user/features/thread/presentation/model/loading_more_status.dart'; import 'package:tmail_ui_user/features/thread/presentation/styles/item_email_tile_styles.dart'; import 'package:tmail_ui_user/features/thread/presentation/styles/scroll_to_top_button_widget_styles.dart'; @@ -31,6 +32,7 @@ import 'package:tmail_ui_user/features/thread/presentation/widgets/app_bar/app_b import 'package:tmail_ui_user/features/thread/presentation/widgets/banner_delete_all_spam_emails_widget.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/banner_empty_trash_widget.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/bottom_bar_thread_selection_widget.dart'; +import 'package:tmail_ui_user/features/thread/presentation/widgets/draggable/draggable_feedback_widget.dart'; 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/features/thread/presentation/widgets/empty_emails_widget.dart'; @@ -168,10 +170,20 @@ class ThreadView extends GetWidget if (controller.responsiveUtils.isWebDesktop(context)) Obx(() { if (controller.validateToShowSelectionEmailsBanner()) { + final selectedMailbox = controller + .mailboxDashBoardController + .selectedMailbox + .value!; + + final listEmailSelectedLength = controller + .mailboxDashBoardController + .listEmailSelected + .length; + return SelectAllEmailInMailboxBanner( - limitEmailsInPage: controller.mailboxDashBoardController.listEmailSelected.length, - totalEmails: controller.mailboxDashBoardController.selectedMailbox.value!.totalEmails!.value.value.toInt(), - folderName: controller.mailboxDashBoardController.selectedMailbox.value!.getDisplayName(context), + limitEmailsInPage: listEmailSelectedLength, + totalEmails: selectedMailbox.countTotalEmails, + folderName: selectedMailbox.getDisplayName(context), onSelectAllEmailAction: controller.enableSelectAllEmails, onClearSelection: controller.cancelSelectEmail ); @@ -487,13 +499,33 @@ class ThreadView extends GetWidget } Widget _buildEmailItemDraggable(BuildContext context, PresentationEmail presentationEmail) { + final isSelectAllEmailsEnabled = controller + .mailboxDashBoardController + .isSelectAllEmailsEnabled + .value; + return GestureDetector( behavior: HitTestBehavior.translucent, onSecondaryTapDown: (_) {}, onTapDown: (_) {}, - child: Draggable>( - data: controller.listEmailDrag, - feedback: _buildFeedBackWidget(context), + child: Draggable( + data: isSelectAllEmailsEnabled + ? DraggableEmailData.withSelectAllEmails() + : DraggableEmailData(listEmails: controller.listEmailDrag), + feedback: Obx(() { + final isSelectAllEmailsEnabled = controller + .mailboxDashBoardController + .isSelectAllEmailsEnabled + .value; + return DraggableFeedbackWidget( + icon: controller.imagePaths.icFilterMessageAll, + title: isSelectAllEmailsEnabled + ? AppLocalizations.of(context).moveAllConversation + : AppLocalizations.of(context).moveConversation( + controller.listEmailDrag.length, + ), + ); + }), childWhenDragging: _buildEmailItemWhenDragging(context, presentationEmail), dragAnchorStrategy: pointerDragAnchorStrategy, onDragStarted: () { @@ -640,43 +672,6 @@ class ThreadView extends GetWidget } } - Widget _buildFeedBackWidget(BuildContext context) { - return SizedBox( - height: 60, - child: Material( - clipBehavior: Clip.hardEdge, - borderRadius: BorderRadius.circular(10), - color: AppColor.colorTextButton, - child: Padding( - padding: const EdgeInsets.all(16), - child: Row( - children: [ - SvgPicture.asset( - controller.imagePaths.icFilterMessageAll, - width: 24, - height: 24, - fit: BoxFit.fill, - colorFilter: Colors.white.asFilter(), - ), - const SizedBox(width: 10), - Obx( - () => Text( - AppLocalizations.of(context).moveConversation(controller.listEmailDrag.length), - overflow: TextOverflow.clip, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), - maxLines: 1, - ), - ), - ], - ), - ), - ), - ); - } - Widget _buildEmptyEmail(BuildContext context) { return Obx(() => controller.viewState.value.fold( (failure) => const SizedBox.shrink(), diff --git a/lib/features/thread/presentation/widgets/draggable/draggable_feedback_widget.dart b/lib/features/thread/presentation/widgets/draggable/draggable_feedback_widget.dart new file mode 100644 index 0000000000..9932d77892 --- /dev/null +++ b/lib/features/thread/presentation/widgets/draggable/draggable_feedback_widget.dart @@ -0,0 +1,52 @@ + +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class DraggableFeedbackWidget extends StatelessWidget { + + final String icon; + final String title; + + const DraggableFeedbackWidget({ + super.key, + required this.icon, + required this.title, + }); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 60, + child: Material( + clipBehavior: Clip.hardEdge, + borderRadius: const BorderRadius.all(Radius.circular(10)), + color: AppColor.colorTextButton, + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + SvgPicture.asset( + icon, + width: 24, + height: 24, + fit: BoxFit.fill, + colorFilter: Colors.white.asFilter(), + ), + const SizedBox(width: 10), + Text( + title, + overflow: TextOverflow.clip, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + maxLines: 1, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/features/thread/presentation/widgets/select_all_banner/message_select_all_email_in_mailbox_widget.dart b/lib/features/thread/presentation/widgets/select_all_banner/message_select_all_email_in_mailbox_widget.dart index 0b0390dd3a..c4d896d296 100644 --- a/lib/features/thread/presentation/widgets/select_all_banner/message_select_all_email_in_mailbox_widget.dart +++ b/lib/features/thread/presentation/widgets/select_all_banner/message_select_all_email_in_mailbox_widget.dart @@ -27,18 +27,7 @@ class MessageSelectAllEmailInMailboxWidget extends StatelessWidget { ), children: [ TextSpan( - text: AppLocalizations.of(context).all, - ), - TextSpan( - text: ' $totalEmails ', - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 14, - ), - ), - TextSpan( - text: AppLocalizations.of(context).mailsInMailboxAreSelected(folderName), + text: '${AppLocalizations.of(context).mailsInMailboxAreSelected(totalEmails, folderName)} ', ), TextSpan( text: AppLocalizations.of(context).clearSelection, @@ -48,9 +37,9 @@ class MessageSelectAllEmailInMailboxWidget extends StatelessWidget { fontSize: 14, ), recognizer: TapGestureRecognizer()..onTap = onClearSelection - ) - ] - ) + ), + ], + ), ); } } \ No newline at end of file diff --git a/lib/features/thread/presentation/widgets/select_all_banner/message_select_email_in_page_widget.dart b/lib/features/thread/presentation/widgets/select_all_banner/message_select_email_in_page_widget.dart index db6f4180cd..9103b13514 100644 --- a/lib/features/thread/presentation/widgets/select_all_banner/message_select_email_in_page_widget.dart +++ b/lib/features/thread/presentation/widgets/select_all_banner/message_select_email_in_page_widget.dart @@ -29,18 +29,7 @@ class MessageSelectEmailInPageWidget extends StatelessWidget { ), children: [ TextSpan( - text: AppLocalizations.of(context).all, - ), - TextSpan( - text: ' $limitEmailsInPage ', - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: 14, - ), - ), - TextSpan( - text: AppLocalizations.of(context).mailsOnThisPageAreSelected, + text: '${AppLocalizations.of(context).mailsOnThisPageAreSelected(limitEmailsInPage)} ', ), TextSpan( text: AppLocalizations.of(context).selectAllMailInMailbox( @@ -53,9 +42,9 @@ class MessageSelectEmailInPageWidget extends StatelessWidget { fontSize: 14, ), recognizer: TapGestureRecognizer()..onTap = onSelectAllEmailAction - ) - ] - ) + ), + ], + ), ); } } \ No newline at end of file diff --git a/lib/features/thread/presentation/widgets/select_all_banner/select_all_emails_in_mailbox_banner.dart b/lib/features/thread/presentation/widgets/select_all_banner/select_all_emails_in_mailbox_banner.dart index 3b6c7cad3e..6ad0a06583 100644 --- a/lib/features/thread/presentation/widgets/select_all_banner/select_all_emails_in_mailbox_banner.dart +++ b/lib/features/thread/presentation/widgets/select_all_banner/select_all_emails_in_mailbox_banner.dart @@ -36,7 +36,7 @@ class _SelectAllEmailInMailboxBannerState extends State