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/integration_test/scenarios/search_email_by_date_time_and_sort_order_relevance_scenario.dart b/integration_test/scenarios/search_email_by_date_time_and_sort_order_relevance_scenario.dart index 76ac5fe773..43e3cf4360 100644 --- a/integration_test/scenarios/search_email_by_date_time_and_sort_order_relevance_scenario.dart +++ b/integration_test/scenarios/search_email_by_date_time_and_sort_order_relevance_scenario.dart @@ -1,6 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart'; import 'package:tmail_ui_user/features/search/email/presentation/search_email_view.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/email_tile_builder.dart'; diff --git a/lib/features/base/mixin/email_action_handler_mixin.dart b/lib/features/base/mixin/email_action_handler_mixin.dart deleted file mode 100644 index a731dc527c..0000000000 --- a/lib/features/base/mixin/email_action_handler_mixin.dart +++ /dev/null @@ -1,37 +0,0 @@ - -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/base/mixin/popup_menu_widget_mixin.dart b/lib/features/base/mixin/popup_menu_widget_mixin.dart deleted file mode 100644 index 507f92c7ed..0000000000 --- a/lib/features/base/mixin/popup_menu_widget_mixin.dart +++ /dev/null @@ -1,43 +0,0 @@ - -import 'package:core/core.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; - -mixin PopupMenuWidgetMixin { - - Widget popupItem( - String iconAction, - String nameAction, { - Color? colorIcon, - double? iconSize, - TextStyle? styleName, - EdgeInsets? padding, - Function()? onCallbackAction - }) { - return InkWell( - onTap: onCallbackAction, - child: Padding( - padding: padding ?? const EdgeInsets.symmetric(horizontal: 20, vertical: 16), - child: SizedBox( - child: Row(children: [ - SvgPicture.asset( - iconAction, - width: iconSize ?? 20, - height: iconSize ?? 20, - fit: BoxFit.fill, - colorFilter: colorIcon.asFilter() - ), - const SizedBox(width: 12), - Expanded(child: Text( - nameAction, - style: styleName ?? const TextStyle( - fontSize: 17, - fontWeight: FontWeight.normal, - color: Colors.black) - )), - ]) - ), - ) - ); - } -} \ No newline at end of file diff --git a/lib/features/email/data/network/email_api.dart b/lib/features/email/data/network/email_api.dart index aa9b2eb52f..e4b0bfe36d 100644 --- a/lib/features/email/data/network/email_api.dart +++ b/lib/features/email/data/network/email_api.dart @@ -53,6 +53,7 @@ import 'package:model/extensions/account_id_extensions.dart'; import 'package:model/extensions/email_extension.dart'; import 'package:model/extensions/email_id_extensions.dart'; import 'package:model/extensions/keyword_identifier_extension.dart'; +import 'package:model/extensions/list_email_extension.dart'; import 'package:model/extensions/list_email_id_extension.dart'; import 'package:model/extensions/list_id_extension.dart'; import 'package:model/extensions/mailbox_id_extension.dart'; @@ -546,15 +547,11 @@ class EmailAPI with HandleSetErrorMixin { final currentEmailIds = emailIds.sublist(start, end); - final moveProperties = isMovingToSpam - ? currentEmailIds.generateMapUpdateObjectMoveToSpam( - currentMailboxId, - destinationMailboxId, - ) - : currentEmailIds.generateMapUpdateObjectMoveToMailbox( - currentMailboxId, - destinationMailboxId, - ); + final moveProperties = currentEmailIds.generateMapUpdateObjectMoveToMailbox( + currentMailboxId, + destinationMailboxId, + isDestinationSpamMailbox: isMovingToSpam, + ); final setEmailMethod = SetEmailMethod(accountId) ..addUpdates(moveProperties); @@ -1016,19 +1013,27 @@ class EmailAPI with HandleSetErrorMixin { Future<(List emailIds, Map mapErrors)> moveSelectionAllEmailsToFolder( Session session, AccountId accountId, - MailboxId currentMailboxId, MailboxId destinationMailboxId, - List listEmailId, { + MailboxId? currentMailboxId, + List? listEmailId, + List? listEmail, bool isDestinationSpamMailbox = false } ) async { - final moveProperties = isDestinationSpamMailbox - ? listEmailId.generateMapUpdateObjectMoveToSpam(currentMailboxId, destinationMailboxId) - : listEmailId.generateMapUpdateObjectMoveToMailbox(currentMailboxId, destinationMailboxId); + final moveProperties = currentMailboxId != null + ? listEmailId?.generateMapUpdateObjectMoveToMailbox( + currentMailboxId, + destinationMailboxId, + isDestinationSpamMailbox: isDestinationSpamMailbox, + ) + : listEmail?.generateMapUpdateObjectMoveToMailbox( + destinationMailboxId, + isDestinationSpamMailbox: isDestinationSpamMailbox, + ); final setEmailMethod = SetEmailMethod(accountId) - ..addUpdates(moveProperties); + ..addUpdates(moveProperties ?? {}); final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation()); final setEmailInvocation = requestBuilder.invocation(setEmailMethod); diff --git a/lib/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart b/lib/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart index 01f7b45cca..250d999b90 100644 --- a/lib/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart +++ b/lib/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart @@ -11,27 +11,31 @@ import 'package:tmail_ui_user/main/routes/route_utils.dart'; extension PresentationMailboxExtension on PresentationMailbox { String getDisplayName(BuildContext context) { + return getDisplayNameByAppLocalizations(AppLocalizations.of(context)); + } + + String getDisplayNameByAppLocalizations(AppLocalizations appLocalizations) { if (isDefault) { switch(role!.value.toLowerCase()) { case PresentationMailbox.inboxRole: - return AppLocalizations.of(context).inboxMailboxDisplayName; + return appLocalizations.inboxMailboxDisplayName; case PresentationMailbox.archiveRole: - return AppLocalizations.of(context).archiveMailboxDisplayName; + return appLocalizations.archiveMailboxDisplayName; case PresentationMailbox.draftsRole: - return AppLocalizations.of(context).draftsMailboxDisplayName; + return appLocalizations.draftsMailboxDisplayName; case PresentationMailbox.sentRole: - return AppLocalizations.of(context).sentMailboxDisplayName; + return appLocalizations.sentMailboxDisplayName; case PresentationMailbox.outboxRole: - return AppLocalizations.of(context).outboxMailboxDisplayName; + return appLocalizations.outboxMailboxDisplayName; case PresentationMailbox.trashRole: - return AppLocalizations.of(context).trashMailboxDisplayName; + return appLocalizations.trashMailboxDisplayName; case PresentationMailbox.spamRole: case PresentationMailbox.junkRole: - return AppLocalizations.of(context).spamMailboxDisplayName; + return appLocalizations.spamMailboxDisplayName; case PresentationMailbox.templatesRole: - return AppLocalizations.of(context).templatesMailboxDisplayName; + return appLocalizations.templatesMailboxDisplayName; case PresentationMailbox.recoveredRole: - return AppLocalizations.of(context).recoveredMailboxDisplayName; + return appLocalizations.recoveredMailboxDisplayName; } } return name?.name ?? ''; diff --git a/lib/features/mailbox/presentation/mailbox_view_web.dart b/lib/features/mailbox/presentation/mailbox_view_web.dart index c408b64ad0..ab34363b60 100644 --- a/lib/features/mailbox/presentation/mailbox_view_web.dart +++ b/lib/features/mailbox/presentation/mailbox_view_web.dart @@ -14,10 +14,11 @@ import 'package:tmail_ui_user/features/mailbox/presentation/widgets/app_grid_vie import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_item_widget.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_loading_bar_widget.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/user_information_widget.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_action_for_select_all_emails_extension.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_email_action_type_when_select_all_active_and_mailbox_opened_extension.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_email_action_type_when_select_all_active_and_search_active_extension.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_email_action_type_when_selection_active_extension.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'; class MailboxView extends BaseMailboxView { @@ -366,7 +367,12 @@ class MailboxView extends BaseMailboxView { onOpenMailboxFolderClick: (mailboxNode) => controller.openMailbox(context, mailboxNode.item), onExpandFolderActionClick: (mailboxNode) => controller.toggleMailboxFolder(mailboxNode, controller.mailboxListScrollController), onSelectMailboxFolderClick: controller.selectMailboxNode, - onDragItemAccepted: _handleDragItemAccepted, + onDragItemAccepted: (draggableEmailData, destinationMailbox) => + _handleDragItemAccepted( + AppLocalizations.of(context), + draggableEmailData, + destinationMailbox, + ), onMenuActionClick: (position, mailboxNode) { openMailboxMenuActionOnWeb( context, @@ -387,7 +393,12 @@ class MailboxView extends BaseMailboxView { mailboxNodeSelected: controller.mailboxDashBoardController.selectedMailbox.value, onOpenMailboxFolderClick: (mailboxNode) => controller.openMailbox(context, mailboxNode.item), onSelectMailboxFolderClick: controller.selectMailboxNode, - onDragItemAccepted: _handleDragItemAccepted, + onDragItemAccepted: (draggableEmailData, destinationMailbox) => + _handleDragItemAccepted( + AppLocalizations.of(context), + draggableEmailData, + destinationMailbox, + ), onMenuActionClick: (position, mailboxNode) { openMailboxMenuActionOnWeb( context, @@ -405,27 +416,39 @@ class MailboxView extends BaseMailboxView { } void _handleDragItemAccepted( - DraggableEmailData draggableEmailData, - PresentationMailbox presentationMailbox, + AppLocalizations appLocalizations, + List listEmails, + PresentationMailbox destinationMailbox, ) { - final mailboxPath = controller.findNodePath(presentationMailbox.id) - ?? presentationMailbox.name?.name; + final mailboxPath = controller.findNodePath(destinationMailbox.id) + ?? destinationMailbox.name?.name; log('MailboxView::_handleDragItemAccepted(): mailboxPath: $mailboxPath'); if (mailboxPath != null) { - presentationMailbox = presentationMailbox + destinationMailbox = destinationMailbox .toPresentationMailboxWithMailboxPath(mailboxPath); } - if (draggableEmailData.isSelectAllEmailsEnabled) { + if (controller.mailboxDashBoardController.isSelectAllActiveAndMailboxOpened) { + controller + .mailboxDashBoardController + .dragEmailsToMailboxWhenSelectAllActiveAndMailboxOpened( + appLocalizations: appLocalizations, + selectedMailbox: controller.mailboxDashBoardController.selectedMailbox.value!, + destinationMailbox: destinationMailbox, + ); + } else if (controller.mailboxDashBoardController.isSelectAllActiveAndMailboxOpened) { controller .mailboxDashBoardController - .dragAllSelectedEmailToMailboxAction(presentationMailbox); + .dragEmailsToMailboxWhenSelectAllActiveAndSearchActive( + appLocalizations: appLocalizations, + destinationMailbox: destinationMailbox, + ); } else { controller .mailboxDashBoardController .dragSelectedMultipleEmailToMailboxAction( - draggableEmailData.listEmails!, - presentationMailbox, + listEmails, + destinationMailbox, ); } } 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 cc4fcfd877..035c1f0771 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(DraggableEmailData, PresentationMailbox); +typedef OnDragEmailToMailboxAccepted = void Function(List, 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 90d278df64..2447a0b170 100644 --- a/lib/features/mailbox/presentation/widgets/mailbox_item_widget.dart +++ b/lib/features/mailbox/presentation/widgets/mailbox_item_widget.dart @@ -6,6 +6,7 @@ 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'; @@ -15,7 +16,6 @@ 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,7 +65,7 @@ class _MailboxItemWidgetState extends State { @override Widget build(BuildContext context) { if (_responsiveUtils.isWebDesktop(context) && widget.mailboxDisplayed == MailboxDisplayed.mailbox) { - return DragTarget( + return DragTarget>( builder: (context, _, __) { return InkWell( onTap: () => widget.onOpenMailboxFolderClick?.call(widget.mailboxNode), diff --git a/lib/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart b/lib/features/mailbox_dashboard/domain/model/email_receive_time_type.dart similarity index 100% rename from lib/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart rename to lib/features/mailbox_dashboard/domain/model/email_receive_time_type.dart diff --git a/lib/features/mailbox_dashboard/presentation/action/dashboard_action.dart b/lib/features/mailbox_dashboard/presentation/action/dashboard_action.dart index 5359452a29..6aafc65b32 100644 --- a/lib/features/mailbox_dashboard/presentation/action/dashboard_action.dart +++ b/lib/features/mailbox_dashboard/presentation/action/dashboard_action.dart @@ -1,12 +1,10 @@ -import 'package:flutter/material.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; -import 'package:model/email/email_action_type.dart'; import 'package:model/email/presentation_email.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:tmail_ui_user/features/base/action/ui_action.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart'; import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; @@ -33,17 +31,6 @@ class FilterMessageAction extends DashBoardAction { List get props => [option]; } -class HandleEmailActionTypeAction extends DashBoardAction { - - final EmailActionType emailAction; - final List listEmailSelected; - - HandleEmailActionTypeAction(this.listEmailSelected, this.emailAction); - - @override - List get props => [listEmailSelected, emailAction]; -} - class OpenEmailDetailedFromSuggestionQuickSearchAction extends DashBoardAction { final PresentationEmail presentationEmail; @@ -148,15 +135,4 @@ class OpenAdvancedSearchViewAction extends DashBoardAction {} class ClearSearchFilterAppliedAction extends DashBoardAction {} -class ClearAdvancedSearchFilterEmailAction extends DashBoardAction {} - -class MoreSelectedEmailAction extends DashBoardAction { - - final BuildContext context; - final RelativeRect position; - - MoreSelectedEmailAction(this.context, this.position); - - @override - List get props => [context, position]; -} \ No newline at end of file +class ClearAdvancedSearchFilterEmailAction extends DashBoardAction {} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart b/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart index c80c7953c9..c5e04310fc 100644 --- a/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart +++ b/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart @@ -128,8 +128,12 @@ import 'package:tmail_ui_user/features/thread/domain/usecases/empty_trash_folder import 'package:tmail_ui_user/features/thread/domain/usecases/get_email_by_id_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_as_starred_selection_all_emails_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_as_unread_selection_all_emails_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_search_as_read_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_search_as_starred_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_search_as_unread_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_multiple_email_read_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_star_multiple_email_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/move_all_email_searched_to_folder_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/move_all_selection_all_emails_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/move_multiple_email_to_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/search_email_interactor.dart'; @@ -201,6 +205,10 @@ class MailboxDashBoardBindings extends BaseBindings { Get.find(), Get.find(), Get.find(), + Get.find(), + Get.find(), + Get.find(), + Get.find(), )); Get.put(AdvancedFilterController()); } @@ -368,6 +376,18 @@ class MailboxDashBoardBindings extends BaseBindings { Get.lazyPut(() => MarkAllAsStarredSelectionAllEmailsInteractor( Get.find(), )); + Get.lazyPut(() => MarkAllSearchAsReadInteractor( + Get.find(), + )); + Get.lazyPut(() => MarkAllSearchAsUnreadInteractor( + Get.find(), + )); + Get.lazyPut(() => MarkAllSearchAsStarredInteractor( + Get.find(), + )); + Get.lazyPut(() => MoveAllEmailSearchedToFolderInteractor( + Get.find(), + )); } @override diff --git a/lib/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart index 7ac8907f51..d58cc43111 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart @@ -19,7 +19,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart' as search; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/advanced_search_filter.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/extensions/datetime_extension.dart'; @@ -239,7 +239,7 @@ class AdvancedFilterController extends BaseController { )) ?? []; } - void initSearchFilterField(BuildContext? context) { + void initSearchFilterField() { subjectFilterInputController.text = StringConvert.writeNullToEmpty( _memorySearchFilter.subject); @@ -275,11 +275,13 @@ class AdvancedFilterController extends BaseController { toAddressExpandMode.value = ExpandMode.COLLAPSE; } - if (context != null) { + if (currentContext != null) { mailBoxFilterInputController.text = _memorySearchFilter.mailbox == null - ? AppLocalizations.of(context).allFolders + ? AppLocalizations.of(currentContext!).allFolders : StringConvert.writeNullToEmpty( - _memorySearchFilter.mailbox?.getDisplayName(context)); + _memorySearchFilter.mailbox?.getDisplayName(currentContext!)); + } else { + mailBoxFilterInputController.text = _memorySearchFilter.mailbox?.name?.name ?? ''; } } @@ -483,7 +485,7 @@ class AdvancedFilterController extends BaseController { } else if (action is QuickSearchEmailByFromAction) { _handleQuickSearchEmailByFromAction(action.emailAddress); } else if (action is OpenAdvancedSearchViewAction) { - initSearchFilterField(currentContext); + initSearchFilterField(); } else if (action is ClearSearchFilterAppliedAction) { clearSearchFilter(); } @@ -518,7 +520,7 @@ class AdvancedFilterController extends BaseController { void _handleStartSearchEmailAction() { _memorySearchFilter = searchController.searchEmailFilter.value; - initSearchFilterField(currentContext); + initSearchFilterField(); } void onHasAttachmentCheckboxChanged(bool? isChecked) { 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 2e80c55302..29ad792ad9 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -30,7 +30,6 @@ 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'; @@ -103,7 +102,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart' as search; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/delete_emails_in_mailbox_extension.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_action_for_select_all_emails_extension.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_selection_email_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/set_error_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/update_current_emails_flags_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mixin/user_setting_popup_menu_mixin.dart'; @@ -112,7 +111,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dash import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/download/download_task_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/draggable_app_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/refresh_action_view_event.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart'; import 'package:tmail_ui_user/features/mailto/presentation/model/mailto_arguments.dart'; @@ -154,8 +153,12 @@ import 'package:tmail_ui_user/features/thread/domain/state/empty_trash_folder_st import 'package:tmail_ui_user/features/thread/domain/state/get_email_by_id_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/mark_all_as_starred_selection_all_emails_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/mark_all_as_unread_selection_all_emails_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/mark_all_search_as_read_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/mark_all_search_as_starred_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/mark_all_search_as_unread_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/mark_as_multiple_email_read_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/mark_as_star_multiple_email_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/move_all_email_searched_to_folder_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/move_all_selection_all_emails_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/move_multiple_email_to_mailbox_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/refresh_all_email_state.dart'; @@ -165,8 +168,12 @@ import 'package:tmail_ui_user/features/thread/domain/usecases/empty_trash_folder import 'package:tmail_ui_user/features/thread/domain/usecases/get_email_by_id_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_as_starred_selection_all_emails_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_as_unread_selection_all_emails_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_search_as_read_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_search_as_starred_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_search_as_unread_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_multiple_email_read_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_star_multiple_email_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/move_all_email_searched_to_folder_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/move_all_selection_all_emails_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/move_multiple_email_to_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_type.dart'; @@ -188,8 +195,7 @@ import 'package:uuid/uuid.dart'; class MailboxDashBoardController extends ReloadableController with UserSettingPopupMenuMixin, - ContactSupportMixin, - EmailActionHandlerMixin { + ContactSupportMixin { final RemoveEmailDraftsInteractor _removeEmailDraftsInteractor = Get.find(); final EmailReceiveManager _emailReceiveManager = Get.find(); @@ -228,6 +234,10 @@ class MailboxDashBoardController extends ReloadableController final MoveAllSelectionAllEmailsInteractor moveAllSelectionAllEmailsInteractor; final DeleteAllPermanentlyEmailsInteractor deleteAllPermanentlyEmailsInteractor; final MarkAllAsStarredSelectionAllEmailsInteractor markAllAsStarredSelectionAllEmailsInteractor; + final MarkAllSearchAsReadInteractor markAllSearchAsReadInteractor; + final MarkAllSearchAsUnreadInteractor markAllSearchAsUnreadInteractor; + final MarkAllSearchAsStarredInteractor markAllSearchAsStarredInteractor; + final MoveAllEmailSearchedToFolderInteractor moveAllEmailSearchedToFolderInteractor; GetAllVacationInteractor? _getAllVacationInteractor; UpdateVacationInteractor? _updateVacationInteractor; @@ -326,6 +336,10 @@ class MailboxDashBoardController extends ReloadableController this.moveAllSelectionAllEmailsInteractor, this.deleteAllPermanentlyEmailsInteractor, this.markAllAsStarredSelectionAllEmailsInteractor, + this.markAllSearchAsReadInteractor, + this.markAllSearchAsUnreadInteractor, + this.markAllSearchAsStarredInteractor, + this.moveAllEmailSearchedToFolderInteractor, ); @override @@ -463,9 +477,19 @@ class MailboxDashBoardController extends ReloadableController || success is MarkAllAsStarredSelectionAllEmailsHasSomeEmailFailure || success is MarkAsMailboxReadAllSuccess || success is MarkAsMailboxReadHasSomeEmailFailure + || success is MarkAllSearchAsReadSuccess + || success is MarkAllSearchAsUnreadSuccess + || success is MarkAllSearchAsStarredSuccess + || success is MoveAllEmailSearchedToFolderSuccess ) { _resetViewStateAndShowToast(success); - cancelSelectAllEmailAction(); + cancelAllSelectionEmailMode(); + } else if (success is MarkAllSearchAsReadLoading + || success is MarkAllSearchAsUnreadLoading + || success is MarkAllSearchAsStarredLoading + || success is MoveAllEmailSearchedToFolderLoading + ) { + viewStateMailboxActionProgress.value = Right(success); } } @@ -494,7 +518,7 @@ class MailboxDashBoardController extends ReloadableController || failure is MarkAsMailboxReadFailure ) { _resetViewStateAndShowToast(failure); - cancelSelectAllEmailAction(); + cancelAllSelectionEmailMode(); } else if (failure is MoveMultipleEmailToMailboxFailure || failure is RestoreDeletedMessageFailure || failure is GetRestoredDeletedMessageFailure diff --git a/lib/features/mailbox_dashboard/presentation/controller/search_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/search_controller.dart index 5a7b2afa69..f26e5d7954 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/search_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/search_controller.dart @@ -27,7 +27,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/quick_sear import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_all_recent_search_latest_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/quick_search_email_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_recent_search_interactor.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; diff --git a/lib/features/mailbox_dashboard/presentation/extensions/handle_action_for_select_all_emails_extension.dart b/lib/features/mailbox_dashboard/presentation/extensions/handle_email_action_type_when_select_all_active_and_mailbox_opened_extension.dart similarity index 73% rename from lib/features/mailbox_dashboard/presentation/extensions/handle_action_for_select_all_emails_extension.dart rename to lib/features/mailbox_dashboard/presentation/extensions/handle_email_action_type_when_select_all_active_and_mailbox_opened_extension.dart index b5eb8db315..4092b4bd90 100644 --- a/lib/features/mailbox_dashboard/presentation/extensions/handle_action_for_select_all_emails_extension.dart +++ b/lib/features/mailbox_dashboard/presentation/extensions/handle_email_action_type_when_select_all_active_and_mailbox_opened_extension.dart @@ -2,7 +2,6 @@ import 'package:core/utils/app_logger.dart'; import 'package:core/utils/platform_info.dart'; import 'package:dartz/dartz.dart'; -import 'package:flutter/material.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; @@ -14,95 +13,95 @@ import 'package:tmail_ui_user/features/mailbox/domain/exceptions/mailbox_excepti import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/exceptions/spam_report_exception.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/thread/domain/state/move_all_selection_all_emails_state.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/app_routes.dart'; import 'package:tmail_ui_user/main/routes/dialog_router.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; -extension HandleActionForSelectAllEmailsExtension on MailboxDashBoardController { +extension HandleEmailActionTypeWhenSelectAllActiveAndMailboxOpenedExtension on MailboxDashBoardController { - void handleActionsForSelectionAllEmails({ - required BuildContext context, + void handleActionTypeWhenSelectAllActiveAndMailboxOpened({ + required AppLocalizations appLocalizations, 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'); + final accountId = this.accountId.value; + final session = sessionCurrent; + if (session == null || accountId == null) { + logError('MailboxDashBoardController::handleActionTypeWhenSelectAllActiveAndMailboxOpened: SESSION & ACCOUNT_ID is null'); return; } switch(actionType) { case EmailActionType.markAllAsRead: markAsReadMailbox( - sessionCurrent!, - accountId.value!, + session, + accountId, selectedMailbox.mailboxId!, - selectedMailbox.getDisplayName(context), + selectedMailbox.getDisplayNameByAppLocalizations(appLocalizations), selectedMailbox.countUnreadEmails, ); break; case EmailActionType.markAllAsUnread: markAllAsUnreadSelectionAllEmails( - sessionCurrent!, - accountId.value!, + session, + accountId, selectedMailbox.mailboxId!, - selectedMailbox.getDisplayName(context), + selectedMailbox.getDisplayNameByAppLocalizations(appLocalizations), selectedMailbox.countReadEmails, ); break; case EmailActionType.moveAll: moveAllSelectionAllEmails( - context, - sessionCurrent!, - accountId.value!, + appLocalizations, + session, + accountId, selectedMailbox, destinationMailbox: destinationMailbox, ); break; case EmailActionType.moveAllToTrash: moveAllToTrashSelectionAllEmails( - context, - sessionCurrent!, - accountId.value!, + appLocalizations, + session, + accountId, selectedMailbox, trashMailbox: destinationMailbox, ); break; case EmailActionType.deleteAllPermanently: deleteAllPermanentlyEmails( - context, - sessionCurrent!, - accountId.value!, + session, + accountId, selectedMailbox, ); break; case EmailActionType.markAllAsStarred: markAllAsStarredSelectionAllEmails( - sessionCurrent!, - accountId.value!, + session, + accountId, selectedMailbox.mailboxId!, - selectedMailbox.getDisplayName(context), + selectedMailbox.getDisplayNameByAppLocalizations(appLocalizations), selectedMailbox.countTotalEmails, ); break; case EmailActionType.markAllAsSpam: maskAllAsSpamSelectionAllEmails( - context, - sessionCurrent!, - accountId.value!, + appLocalizations, + session, + accountId, selectedMailbox, spamMailbox: destinationMailbox, ); break; case EmailActionType.allUnSpam: allUnSpamSelectionAllEmails( - context, - sessionCurrent!, - accountId.value!, + appLocalizations, + session, + accountId, selectedMailbox, ); break; @@ -129,7 +128,7 @@ extension HandleActionForSelectAllEmailsExtension on MailboxDashBoardController } Future moveAllSelectionAllEmails( - BuildContext context, + AppLocalizations appLocalizations, Session session, AccountId accountId, PresentationMailbox currentMailbox, @@ -159,9 +158,7 @@ extension HandleActionForSelectAllEmailsExtension on MailboxDashBoardController accountId, currentMailbox.id, destinationMailbox.id, - destinationMailbox.mailboxPath ?? (context.mounted - ? destinationMailbox.getDisplayName(context) - : ''), + destinationMailbox.mailboxPath ?? destinationMailbox.getDisplayNameByAppLocalizations(appLocalizations), currentMailbox.countTotalEmails, viewStateMailboxActionStreamController, isDestinationSpamMailbox: destinationMailbox.isSpam, @@ -170,7 +167,7 @@ extension HandleActionForSelectAllEmailsExtension on MailboxDashBoardController } Future moveAllToTrashSelectionAllEmails( - BuildContext context, + AppLocalizations appLocalizations, Session session, AccountId accountId, PresentationMailbox currentMailbox, @@ -180,8 +177,8 @@ extension HandleActionForSelectAllEmailsExtension on MailboxDashBoardController ) async { final trashMailboxId = trashMailbox?.id ?? getMailboxIdByRole(PresentationMailbox.roleTrash); - final trashMailboxPath = trashMailbox?.getDisplayName(context) - ?? mapMailboxById[trashMailboxId]?.getDisplayName(context) + final trashMailboxPath = trashMailbox?.getDisplayNameByAppLocalizations(appLocalizations) + ?? mapMailboxById[trashMailboxId]?.getDisplayNameByAppLocalizations(appLocalizations) ?? ''; if (trashMailboxId == null) { @@ -204,7 +201,6 @@ extension HandleActionForSelectAllEmailsExtension on MailboxDashBoardController } Future deleteAllPermanentlyEmails( - BuildContext context, Session session, AccountId accountId, PresentationMailbox currentMailbox, @@ -236,7 +232,7 @@ extension HandleActionForSelectAllEmailsExtension on MailboxDashBoardController } Future maskAllAsSpamSelectionAllEmails( - BuildContext context, + AppLocalizations appLocalizations, Session session, AccountId accountId, PresentationMailbox currentMailbox, @@ -245,8 +241,8 @@ extension HandleActionForSelectAllEmailsExtension on MailboxDashBoardController } ) async { final spamMailboxId = spamMailbox?.id ?? this.spamMailboxId; - final spamMailboxPath = spamMailbox?.getDisplayName(context) - ?? mapMailboxById[spamMailboxId]?.getDisplayName(context) + final spamMailboxPath = spamMailbox?.getDisplayNameByAppLocalizations(appLocalizations) + ?? mapMailboxById[spamMailboxId]?.getDisplayNameByAppLocalizations(appLocalizations) ?? ''; if (spamMailboxId == null) { @@ -269,13 +265,14 @@ extension HandleActionForSelectAllEmailsExtension on MailboxDashBoardController } Future allUnSpamSelectionAllEmails( - BuildContext context, + AppLocalizations appLocalizations, Session session, AccountId accountId, PresentationMailbox currentMailbox, ) async { final inboxMailboxId = getMailboxIdByRole(PresentationMailbox.roleInbox); - final inboxMailboxPath = mapMailboxById[inboxMailboxId]?.getDisplayName(context) ?? ''; + final inboxMailboxPath = mapMailboxById[inboxMailboxId] + ?.getDisplayNameByAppLocalizations(appLocalizations) ?? ''; if (inboxMailboxId == null) { consumeState(Stream.value(Left(MoveAllSelectionAllEmailsFailure( @@ -296,31 +293,20 @@ extension HandleActionForSelectAllEmailsExtension on MailboxDashBoardController )); } - void cancelSelectAllEmailAction() { - isSelectAllPageEnabled.value = false; - isSelectAllEmailsEnabled.value = false; - dispatchAction(CancelSelectionAllEmailAction()); - } - - 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, - ), + void dragEmailsToMailboxWhenSelectAllActiveAndMailboxOpened({ + required AppLocalizations appLocalizations, + required PresentationMailbox selectedMailbox, + required PresentationMailbox destinationMailbox, + }) { + handleActionTypeWhenSelectAllActiveAndMailboxOpened( + appLocalizations: appLocalizations, + selectedMailbox: selectedMailbox, + actionType: getTypeMoveAllActionForMailbox(destinationMailbox), + destinationMailbox: destinationMailbox, ); } - EmailActionType _getTypeMoveAllActionForMailbox(PresentationMailbox presentationMailbox) { + EmailActionType getTypeMoveAllActionForMailbox(PresentationMailbox presentationMailbox) { if (presentationMailbox.isTrash) { return EmailActionType.moveAllToTrash; } else if (presentationMailbox.isSpam) { diff --git a/lib/features/mailbox_dashboard/presentation/extensions/handle_email_action_type_when_select_all_active_and_search_active_extension.dart b/lib/features/mailbox_dashboard/presentation/extensions/handle_email_action_type_when_select_all_active_and_search_active_extension.dart new file mode 100644 index 0000000000..6768b3d199 --- /dev/null +++ b/lib/features/mailbox_dashboard/presentation/extensions/handle_email_action_type_when_select_all_active_and_search_active_extension.dart @@ -0,0 +1,261 @@ + +import 'package:core/utils/app_logger.dart'; +import 'package:core/utils/platform_info.dart'; +import 'package:dartz/dartz.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart'; +import 'package:model/email/email_action_type.dart'; +import 'package:model/extensions/presentation_mailbox_extension.dart'; +import 'package:model/mailbox/presentation_mailbox.dart'; +import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/exceptions/mailbox_exception.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/exceptions/spam_report_exception.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_email_action_type_when_select_all_active_and_mailbox_opened_extension.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; +import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/move_all_email_searched_to_folder_state.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; +import 'package:tmail_ui_user/main/routes/app_routes.dart'; +import 'package:tmail_ui_user/main/routes/dialog_router.dart'; +import 'package:tmail_ui_user/main/routes/route_navigation.dart'; + +extension HandleEmailActionTypeWhenSelectAllActiveAndSearchActiveExtension on MailboxDashBoardController { + + void handleActionTypeWhenSelectAllActiveAndSearchActive({ + required AppLocalizations appLocalizations, + required EmailActionType actionType, + PresentationMailbox? destinationMailbox, + }) { + final accountId = this.accountId.value; + final session = sessionCurrent; + if (session == null || accountId == null) { + logError('MailboxDashBoardController::handleActionTypeWhenSelectAllActiveAndSearchActive: SESSION & ACCOUNT_ID is null'); + return; + } + + final searchEmailFilter = searchController.searchEmailFilter.value; + final filterOption = filterMessageOption.value; + + switch(actionType) { + case EmailActionType.markAllAsRead: + markAllSearchAsRead( + session, + accountId, + searchEmailFilter, + moreFilterCondition: filterOption.getFilterCondition(), + ); + break; + case EmailActionType.markAllAsUnread: + markAllSearchAsUnread( + session, + accountId, + searchEmailFilter, + moreFilterCondition: filterOption.getFilterCondition(), + ); + break; + case EmailActionType.markAllAsStarred: + markAllSearchAsStarred( + session, + accountId, + searchEmailFilter, + moreFilterCondition: filterOption.getFilterCondition(), + ); + break; + case EmailActionType.moveAll: + moveAllEmailSearchedToFolder( + appLocalizations, + session, + accountId, + searchEmailFilter, + moreFilterCondition: filterOption.getFilterCondition(), + + ); + break; + case EmailActionType.moveAllToTrash: + moveAllEmailSearchedToTrash( + appLocalizations, + session, + accountId, + searchEmailFilter, + moreFilterCondition: filterOption.getFilterCondition(), + destinationMailbox: destinationMailbox, + ); + break; + case EmailActionType.markAllAsSpam: + markAllEmailSearchedAsSpam( + appLocalizations, + session, + accountId, + searchEmailFilter, + moreFilterCondition: filterOption.getFilterCondition(), + destinationMailbox: destinationMailbox, + ); + break; + default: + break; + } + } + + void markAllSearchAsRead( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ) { + consumeState(markAllSearchAsReadInteractor.execute( + session, + accountId, + searchEmailFilter, + moreFilterCondition: moreFilterCondition, + )); + } + + void markAllSearchAsUnread( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ) { + consumeState(markAllSearchAsUnreadInteractor.execute( + session, + accountId, + searchEmailFilter, + moreFilterCondition: moreFilterCondition, + )); + } + + void markAllSearchAsStarred( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ) { + consumeState(markAllSearchAsStarredInteractor.execute( + session, + accountId, + searchEmailFilter, + moreFilterCondition: moreFilterCondition, + )); + } + + Future moveAllEmailSearchedToFolder( + AppLocalizations appLocalizations, + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + { + EmailFilterCondition? moreFilterCondition, + PresentationMailbox? destinationMailbox, + } + ) async { + if (destinationMailbox == null) { + final arguments = DestinationPickerArguments( + accountId, + MailboxActions.moveEmail, + session, + ); + + destinationMailbox = PlatformInfo.isWeb + ? await DialogRouter.pushGeneralDialog( + routeName: AppRoutes.destinationPicker, + arguments: arguments, + ) + : await push(AppRoutes.destinationPicker, arguments: arguments); + } + + if (destinationMailbox is PresentationMailbox) { + consumeState(moveAllEmailSearchedToFolderInteractor.execute( + session, + accountId, + destinationMailbox.id, + destinationMailbox.mailboxPath ?? destinationMailbox.getDisplayNameByAppLocalizations(appLocalizations), + searchEmailFilter, + isDestinationSpamMailbox: destinationMailbox.isSpam, + moreFilterCondition: moreFilterCondition, + )); + } + } + + Future moveAllEmailSearchedToTrash( + AppLocalizations appLocalizations, + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + { + EmailFilterCondition? moreFilterCondition, + PresentationMailbox? destinationMailbox, + } + ) async { + final trashMailboxId = destinationMailbox?.id + ?? getMailboxIdByRole(PresentationMailbox.roleTrash); + final trashMailboxPath = destinationMailbox?.getDisplayNameByAppLocalizations(appLocalizations) + ?? mapMailboxById[trashMailboxId]?.getDisplayNameByAppLocalizations(appLocalizations) + ?? ''; + + if (trashMailboxId == null) { + consumeState(Stream.value(Left(MoveAllEmailSearchedToFolderFailure( + trashMailboxPath, + NotFoundTrashMailboxException(), + )))); + return; + } + + consumeState(moveAllEmailSearchedToFolderInteractor.execute( + session, + accountId, + trashMailboxId, + trashMailboxPath, + searchEmailFilter, + moreFilterCondition: moreFilterCondition, + )); + } + + Future markAllEmailSearchedAsSpam( + AppLocalizations appLocalizations, + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + { + EmailFilterCondition? moreFilterCondition, + PresentationMailbox? destinationMailbox, + } + ) async { + final spamMailboxId = destinationMailbox?.id ?? this.spamMailboxId; + final spamMailboxPath = destinationMailbox?.getDisplayNameByAppLocalizations(appLocalizations) + ?? mapMailboxById[spamMailboxId]?.getDisplayNameByAppLocalizations(appLocalizations) + ?? ''; + + if (spamMailboxId == null) { + consumeState(Stream.value(Left(MoveAllEmailSearchedToFolderFailure( + spamMailboxPath, + NotFoundSpamMailboxException(), + )))); + return; + } + + consumeState(moveAllEmailSearchedToFolderInteractor.execute( + session, + accountId, + spamMailboxId, + spamMailboxPath, + searchEmailFilter, + isDestinationSpamMailbox: true, + moreFilterCondition: moreFilterCondition, + )); + } + + void dragEmailsToMailboxWhenSelectAllActiveAndSearchActive({ + required AppLocalizations appLocalizations, + required PresentationMailbox destinationMailbox, + }) { + handleActionTypeWhenSelectAllActiveAndSearchActive( + appLocalizations: appLocalizations, + actionType: getTypeMoveAllActionForMailbox(destinationMailbox), + destinationMailbox: destinationMailbox, + ); + } +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/extensions/handle_email_action_type_when_selection_active_extension.dart b/lib/features/mailbox_dashboard/presentation/extensions/handle_email_action_type_when_selection_active_extension.dart new file mode 100644 index 0000000000..6d6cc7a102 --- /dev/null +++ b/lib/features/mailbox_dashboard/presentation/extensions/handle_email_action_type_when_selection_active_extension.dart @@ -0,0 +1,223 @@ + +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get/get.dart'; +import 'package:model/email/email_action_type.dart'; +import 'package:model/email/mark_star_action.dart'; +import 'package:model/email/presentation_email.dart'; +import 'package:model/email/read_actions.dart'; +import 'package:model/extensions/list_presentation_email_extension.dart'; +import 'package:model/extensions/presentation_mailbox_extension.dart'; +import 'package:tmail_ui_user/features/base/widget/popup_item_widget.dart'; +import 'package:tmail_ui_user/features/composer/presentation/extensions/email_action_type_extension.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_email_action_type_when_select_all_active_and_mailbox_opened_extension.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_email_action_type_when_select_all_active_and_search_active_extension.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_selection_email_extension.dart'; +import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_type.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; +import 'package:tmail_ui_user/main/routes/route_navigation.dart'; + +extension HandleEmailActionTypeWhenSelectionActiveExtension on MailboxDashBoardController { + + void handleEmailActionTypeWhenSelectionActive({ + required EmailActionType actionType, + List? listSelectedEmail, + }) { + if (isSelectAllActiveAndMailboxOpened) { + if (currentContext == null) return; + + final appLocalizations = AppLocalizations.of(currentContext!); + final selectedMailbox = this.selectedMailbox.value!; + + final message = appLocalizations + .messageConfirmationDialogWhenMakeToActionForSelectionAllEmailsInMailbox( + selectedMailbox.countTotalEmails, + selectedMailbox.getDisplayNameByAppLocalizations(appLocalizations), + ); + + showConfirmBulkActionDialog( + message: message, + onConfirmAction: () => handleActionTypeWhenSelectAllActiveAndMailboxOpened( + appLocalizations: appLocalizations, + selectedMailbox: selectedMailbox, + actionType: actionType, + ), + ); + } else if (isSelectAllActiveAndSearchActive) { + if (currentContext == null) return; + + final appLocalizations = AppLocalizations.of(currentContext!); + + final message = appLocalizations.messageConfirmationDialogWhenMakeToActionForSelectionAllEmailsInSearch; + + showConfirmBulkActionDialog( + message: message, + onConfirmAction: () => handleActionTypeWhenSelectAllActiveAndSearchActive( + appLocalizations: appLocalizations, + actionType: actionType, + ), + ); + } else if (listSelectedEmail?.isNotEmpty == true) { + handleEmailActionTypeForListSelectedEmail( + actionType, + listSelectedEmail!, + ); + } + } + + bool get isSelectAllActiveAndMailboxOpened => + isSelectAllEmailsEnabled.isTrue && selectedMailbox.value != null; + + bool get isSelectAllActiveAndSearchActive => + isSelectAllEmailsEnabled.isTrue && searchController.isSearchEmailRunning; + + Future showConfirmBulkActionDialog({ + required String message, + required VoidCallback onConfirmAction, + }) async { + if (currentContext == null) return; + + final appLocalizations = AppLocalizations.of(currentContext!); + + await showConfirmDialogAction( + currentContext!, + message, + appLocalizations.ok, + title: appLocalizations.confirmBulkAction, + icon: SvgPicture.asset( + imagePaths.icQuotasWarning, + colorFilter: AppColor.colorBackgroundQuotasWarning.asFilter(), + ), + onConfirmAction: onConfirmAction, + ); + } + + void handleEmailActionTypeForListSelectedEmail( + EmailActionType actionType, + List listEmail, + ) { + switch(actionType) { + case EmailActionType.markAsRead: + cancelAllSelectionEmailMode(); + markAsReadSelectedMultipleEmail(listEmail, ReadActions.markAsRead); + break; + case EmailActionType.markAsUnread: + cancelAllSelectionEmailMode(); + markAsReadSelectedMultipleEmail(listEmail, ReadActions.markAsUnread); + break; + case EmailActionType.markAsStarred: + cancelAllSelectionEmailMode(); + markAsStarSelectedMultipleEmail(listEmail, MarkStarAction.markStar); + break; + case EmailActionType.unMarkAsStarred: + cancelAllSelectionEmailMode(); + markAsStarSelectedMultipleEmail(listEmail, MarkStarAction.unMarkStar); + break; + case EmailActionType.moveToMailbox: + cancelAllSelectionEmailMode(); + + final currentMailbox = searchController.isSearchEmailRunning + ? listEmail.getCurrentMailboxContain(mapMailboxById) + : selectedMailbox.value; + + if (currentMailbox != null) { + moveSelectedMultipleEmailToMailbox(listEmail, currentMailbox); + } + break; + case EmailActionType.moveToTrash: + cancelAllSelectionEmailMode(); + + final currentMailbox = searchController.isSearchEmailRunning + ? listEmail.getCurrentMailboxContain(mapMailboxById) + : selectedMailbox.value; + + if (currentMailbox != null) { + moveSelectedMultipleEmailToTrash(listEmail, currentMailbox); + } + break; + case EmailActionType.deletePermanently: + final currentMailbox = searchController.isSearchEmailRunning + ? listEmail.getCurrentMailboxContain(mapMailboxById) + : selectedMailbox.value; + + if (currentMailbox != null && currentContext != null) { + deleteSelectionEmailsPermanently( + currentContext!, + DeleteActionType.multiple, + listEmails: listEmail, + mailboxCurrent: currentMailbox, + onCancelSelectionEmail: cancelAllSelectionEmailMode, + ); + } + break; + case EmailActionType.moveToSpam: + cancelAllSelectionEmailMode(); + + final currentMailbox = searchController.isSearchEmailRunning + ? listEmail.getCurrentMailboxContain(mapMailboxById) + : selectedMailbox.value; + + if (currentMailbox != null) { + moveSelectedMultipleEmailToSpam(listEmail, currentMailbox); + } + break; + case EmailActionType.unSpam: + cancelAllSelectionEmailMode(); + unSpamSelectedMultipleEmail(listEmail); + break; + default: + break; + } + } + + void openEmailActionTypePopupMenu({ + required BuildContext context, + required RelativeRect position, + }) { + final selectedMailbox = this.selectedMailbox.value; + + final listSelectionEmailActions = [ + EmailActionType.markAllAsRead, + EmailActionType.markAllAsUnread, + if (selectedMailbox == null || !selectedMailbox.isDrafts) + EmailActionType.moveAll, + if (selectedMailbox?.isTrash == true + || selectedMailbox?.isSpam == true + || selectedMailbox?.isDrafts == true + ) + EmailActionType.deleteAllPermanently + else + EmailActionType.moveAllToTrash + ]; + + final listPopupItem = listSelectionEmailActions + .map((actionType) => PopupMenuItem( + padding: EdgeInsets.zero, + child: PopupItemWidget( + actionType.getIcon(imagePaths), + actionType.getTitle(context), + colorIcon: actionType.getIconColor(), + styleName: const TextStyle( + fontWeight: FontWeight.w500, + fontSize: 14, + color: Colors.black, + ), + padding: const EdgeInsets.symmetric(horizontal: 20), + onCallbackAction: () { + popBack(); + handleEmailActionTypeWhenSelectionActive(actionType: actionType); + }, + ), + )) + .toList(); + + openPopupMenuAction( + context, + position, + listPopupItem, + ); + } +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/extensions/handle_selection_email_extension.dart b/lib/features/mailbox_dashboard/presentation/extensions/handle_selection_email_extension.dart new file mode 100644 index 0000000000..74afd672dd --- /dev/null +++ b/lib/features/mailbox_dashboard/presentation/extensions/handle_selection_email_extension.dart @@ -0,0 +1,17 @@ + +import 'package:model/mailbox/select_mode.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; +import 'package:tmail_ui_user/features/thread/presentation/extensions/list_presentation_email_extensions.dart'; + +extension HandleSelectionEmailExtension on MailboxDashBoardController { + + void cancelAllSelectionEmailMode() { + isSelectAllEmailsEnabled.value = false; + isSelectAllPageEnabled.value = false; + currentSelectMode.value = SelectMode.INACTIVE; + listEmailSelected.clear(); + + final newEmailList = emailsInCurrentMailbox.cancelSelectionMode(); + updateEmailList(newEmailList); + } +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/extensions/update_emails_with_new_mailbox_id_extension.dart b/lib/features/mailbox_dashboard/presentation/extensions/update_emails_with_new_mailbox_id_extension.dart index 729a8dab68..676fb32ed8 100644 --- a/lib/features/mailbox_dashboard/presentation/extensions/update_emails_with_new_mailbox_id_extension.dart +++ b/lib/features/mailbox_dashboard/presentation/extensions/update_emails_with_new_mailbox_id_extension.dart @@ -4,7 +4,7 @@ import 'package:model/email/presentation_email.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; extension UpdateEmailsWithNewMailboxIdExtension on MailboxDashBoardController { - handleUpdateEmailsWithNewMailboxId({ + void handleUpdateEmailsWithNewMailboxId({ required Map> originalMailboxIdsWithEmailIds, required MailboxId destinationMailboxId, }) { 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 9e6d1fe9fe..099743fbd9 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart @@ -14,11 +14,12 @@ import 'package:tmail_ui_user/features/email/presentation/email_view.dart'; import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_view_web.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/base_mailbox_dashboard_view.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_email_action_type_when_selection_active_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/composer_overlay_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/filter_message_button_style.dart'; @@ -105,6 +106,7 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { )), Obx(() => ViewStateMailboxActionProgressLoadingBanner( viewState: controller.viewStateMailboxActionProgress.value, + responsiveUtils: controller.responsiveUtils, )), const SpamReportBannerWebWidget(), QuotasBannerWidget(), @@ -241,15 +243,15 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { onCancelSelection: () => controller.dispatchAction(CancelSelectionAllEmailAction()), onEmailActionTypeAction: (listEmails, actionType) => - controller.dispatchAction(HandleEmailActionTypeAction( - listEmails, - actionType, - )), - onMoreSelectedEmailAction: (position) => - controller.dispatchAction(MoreSelectedEmailAction( - context, - position, - )), + controller.handleEmailActionTypeWhenSelectionActive( + actionType: actionType, + listSelectedEmail: listEmails, + ), + onMoreSelectionAllEmailAction: (position) => + controller.openEmailActionTypePopupMenu( + context: context, + position: position, + ), ); } else { return Padding( diff --git a/lib/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart b/lib/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart index dd5b1bec2f..0d9d52c595 100644 --- a/lib/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart +++ b/lib/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart @@ -7,7 +7,7 @@ import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.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_dashboard/presentation/model/search/email_receive_time_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; diff --git a/lib/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart b/lib/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart index 121c0abdaf..1961b9a8de 100644 --- a/lib/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart +++ b/lib/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart @@ -12,7 +12,7 @@ import 'package:model/email/prefix_email_address.dart'; import 'package:model/extensions/email_filter_condition_extension.dart'; import 'package:model/extensions/presentation_mailbox_extension.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart'; import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; diff --git a/lib/features/mailbox_dashboard/presentation/styles/mark_mailbox_as_read_loading_banner_style.dart b/lib/features/mailbox_dashboard/presentation/styles/mark_mailbox_as_read_loading_banner_style.dart index 50f62da7c7..17f78c19a5 100644 --- a/lib/features/mailbox_dashboard/presentation/styles/mark_mailbox_as_read_loading_banner_style.dart +++ b/lib/features/mailbox_dashboard/presentation/styles/mark_mailbox_as_read_loading_banner_style.dart @@ -1,5 +1,14 @@ +import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:flutter/material.dart'; class MarkMailboxAsReadLoadingBannerStyle { - static const EdgeInsetsGeometry bannerMargin = EdgeInsetsDirectional.only(end: 16, bottom: 8); + const MarkMailboxAsReadLoadingBannerStyle._(); + + static EdgeInsetsGeometry getBannerMargin(BuildContext context, ResponsiveUtils responsiveUtils) { + if (responsiveUtils.isDesktop(context)) { + return const EdgeInsetsDirectional.only(end: 16, bottom: 8); + } else { + return const EdgeInsetsDirectional.only(start: 16, end: 16, bottom: 8); + } + } } \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_input_form.dart b/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_input_form.dart index 572b1407f3..4f0d4803f0 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_input_form.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_input_form.dart @@ -7,7 +7,7 @@ import 'package:tmail_ui_user/features/base/mixin/popup_context_menu_action_mixi import 'package:tmail_ui_user/features/base/widget/popup_item_no_icon_widget.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/advanced_search_filter.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/advanced_search_input_form_style.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_filter_form_bottom_view.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/date_drop_down_button.dart'; diff --git a/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/date_drop_down_button.dart b/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/date_drop_down_button.dart index e786dde2be..112792a5cf 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/date_drop_down_button.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/date_drop_down_button.dart @@ -5,7 +5,7 @@ import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/email_receive_time_type.dart'; class DateDropDownButton extends StatelessWidget { diff --git a/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/icon_open_advanced_search_widget.dart b/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/icon_open_advanced_search_widget.dart index 40318b6d18..93c1c48d73 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/icon_open_advanced_search_widget.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/icon_open_advanced_search_widget.dart @@ -39,7 +39,7 @@ class IconOpenAdvancedSearchWidget extends StatelessWidget { height: 16), onTap: () { log('IconOpenAdvancedSearchWidget::build(): clicked'); - advancedFilterController.initSearchFilterField(context); + advancedFilterController.initSearchFilterField(); searchController.openAdvanceSearch(); }), ), diff --git a/lib/features/mailbox_dashboard/presentation/widgets/app_bar/top_bar_thread_selection.dart b/lib/features/mailbox_dashboard/presentation/widgets/app_bar/top_bar_thread_selection.dart index fc977262de..d89d9e2366 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/app_bar/top_bar_thread_selection.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/app_bar/top_bar_thread_selection.dart @@ -15,7 +15,7 @@ typedef OnEmailActionTypeAction = Function( List listEmail, EmailActionType actionType, ); -typedef OnMoreSelectedEmailAction = Function(RelativeRect position); +typedef OnMoreSelectionAllEmailAction = Function(RelativeRect position); class TopBarThreadSelection extends StatelessWidget{ @@ -26,7 +26,7 @@ class TopBarThreadSelection extends StatelessWidget{ final VoidCallback? onCancelSelection; final bool isSelectAllEmailsEnabled; final PresentationMailbox? selectedMailbox; - final OnMoreSelectedEmailAction? onMoreSelectedEmailAction; + final OnMoreSelectionAllEmailAction? onMoreSelectionAllEmailAction; const TopBarThreadSelection({ super.key, @@ -37,7 +37,7 @@ class TopBarThreadSelection extends StatelessWidget{ this.selectedMailbox, this.onEmailActionTypeAction, this.onCancelSelection, - this.onMoreSelectedEmailAction, + this.onMoreSelectionAllEmailAction, }); @override @@ -132,7 +132,7 @@ class TopBarThreadSelection extends StatelessWidget{ iconColor: AppColor.primaryColor, tooltipMessage: AppLocalizations.of(context).more, backgroundColor: Colors.transparent, - onTapActionAtPositionCallback: onMoreSelectedEmailAction + onTapActionAtPositionCallback: onMoreSelectionAllEmailAction ), ]), ); @@ -145,7 +145,7 @@ class TopBarThreadSelection extends StatelessWidget{ || selectedMailbox?.isSpam == true || selectedMailbox?.isDrafts == true); - bool get canSpamAndMove => listEmail.isAllCanSpamAndMove(mapMailbox); + bool get canSpamAndMove => isSelectAllEmailsEnabled ? true : listEmail.isAllCanSpamAndMove(mapMailbox); bool get isAllSpam => listEmail.isAllSpam(mapMailbox); diff --git a/lib/features/mailbox_dashboard/presentation/widgets/loading/perform_action_search_selection_email_loading_widget.dart b/lib/features/mailbox_dashboard/presentation/widgets/loading/perform_action_search_selection_email_loading_widget.dart new file mode 100644 index 0000000000..7b478ba997 --- /dev/null +++ b/lib/features/mailbox_dashboard/presentation/widgets/loading/perform_action_search_selection_email_loading_widget.dart @@ -0,0 +1,36 @@ + +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +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_search_as_read_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/mark_all_search_as_starred_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/mark_all_search_as_unread_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/move_all_email_searched_to_folder_state.dart'; + +class PerformActionSearchSelectionEmailLoadingWidget extends StatelessWidget with AppLoaderMixin { + + final Either viewState; + + const PerformActionSearchSelectionEmailLoadingWidget({super.key, required this.viewState}); + + @override + Widget build(BuildContext context) { + return viewState.fold( + (failure) => const SizedBox.shrink(), + (success) { + if (success is MarkAllSearchAsReadLoading + || success is MarkAllSearchAsUnreadLoading + || success is MarkAllSearchAsStarredLoading + || success is MoveAllEmailSearchedToFolderLoading) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: horizontalLoadingWidget + ); + } + return const SizedBox.shrink(); + } + ); + } +} diff --git a/lib/features/mailbox_dashboard/presentation/widgets/search_filters/search_filter_button.dart b/lib/features/mailbox_dashboard/presentation/widgets/search_filters/search_filter_button.dart index cdb693224e..0bf71893d7 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/search_filters/search_filter_button.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/search_filters/search_filter_button.dart @@ -5,7 +5,7 @@ import 'package:core/presentation/views/button/tmail_button_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/search_filter_button_style.dart'; diff --git a/lib/features/mailbox_dashboard/presentation/widgets/view_state_mailbox_action_progress_loading_banner.dart b/lib/features/mailbox_dashboard/presentation/widgets/view_state_mailbox_action_progress_loading_banner.dart index 06111b57e3..42261eefe5 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/view_state_mailbox_action_progress_loading_banner.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/view_state_mailbox_action_progress_loading_banner.dart @@ -1,5 +1,6 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; +import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; @@ -7,16 +8,23 @@ import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/mark_mailbox_as_read_loading_banner_style.dart'; import 'package:tmail_ui_user/features/thread/domain/state/delete_all_permanently_emails_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/empty_spam_folder_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/empty_trash_folder_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/mark_all_as_starred_selection_all_emails_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/mark_all_as_unread_selection_all_emails_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/mark_all_search_as_read_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/mark_all_search_as_starred_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/mark_all_search_as_unread_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/move_all_email_searched_to_folder_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/move_all_selection_all_emails_state.dart'; class ViewStateMailboxActionProgressLoadingBanner extends StatelessWidget with AppLoaderMixin { final Either viewState; + final ResponsiveUtils responsiveUtils; const ViewStateMailboxActionProgressLoadingBanner({ super.key, required this.viewState, + required this.responsiveUtils, }); @override @@ -25,26 +33,32 @@ class ViewStateMailboxActionProgressLoadingBanner extends StatelessWidget with A (failure) => const SizedBox.shrink(), (success) { if (success is MarkAsMailboxReadLoading + || success is EmptySpamFolderLoading + || success is EmptyTrashFolderLoading || success is MarkAllAsUnreadSelectionAllEmailsLoading || success is MarkAllAsStarredSelectionAllEmailsLoading || success is MoveAllSelectionAllEmailsLoading || success is DeleteAllPermanentlyEmailsLoading + || success is MarkAllSearchAsReadLoading + || success is MarkAllSearchAsUnreadLoading + || success is MarkAllSearchAsStarredLoading + || success is MoveAllEmailSearchedToFolderLoading ) { return Padding( - padding: MarkMailboxAsReadLoadingBannerStyle.bannerMargin, + padding: MarkMailboxAsReadLoadingBannerStyle.getBannerMargin(context, responsiveUtils), child: horizontalLoadingWidget); } else if (success is UpdatingMarkAsMailboxReadState) { - return _buildProgressBanner(success.countRead, success.totalUnread); + return _buildProgressBanner(context, success.countRead, success.totalUnread); } else if (success is MarkAllAsUnreadSelectionAllEmailsUpdating) { - return _buildProgressBanner(success.countUnread, success.totalRead); + return _buildProgressBanner(context, success.countUnread, success.totalRead); } else if (success is MarkAllAsStarredSelectionAllEmailsUpdating) { - return _buildProgressBanner(success.countStarred, success.total); + return _buildProgressBanner(context, success.countStarred, success.total); } else if (success is MoveAllSelectionAllEmailsUpdating) { - return _buildProgressBanner(success.countMoved, success.total); + return _buildProgressBanner(context, success.countMoved, success.total); } else if (success is DeleteAllPermanentlyEmailsUpdating) { - return _buildProgressBanner(success.countDeleted, success.total); + return _buildProgressBanner(context, success.countDeleted, success.total); } else if (success is EmptyingFolderState) { - return _buildProgressBanner(success.countEmailsDeleted, success.totalEmails); + return _buildProgressBanner(context, success.countEmailsDeleted, success.totalEmails); } else { return const SizedBox.shrink(); } @@ -52,10 +66,10 @@ class ViewStateMailboxActionProgressLoadingBanner extends StatelessWidget with A ); } - Padding _buildProgressBanner(int progress, int total) { + Padding _buildProgressBanner(BuildContext context, int progress, int total) { final percent = total > 0 ? progress / total : 0.68; return Padding( - padding: MarkMailboxAsReadLoadingBannerStyle.bannerMargin, + padding: MarkMailboxAsReadLoadingBannerStyle.getBannerMargin(context, responsiveUtils), child: horizontalPercentLoadingWidget(percent) ); } diff --git a/lib/features/manage_account/presentation/profiles/identities/identities_view.dart b/lib/features/manage_account/presentation/profiles/identities/identities_view.dart index 476ce7f539..89d4d0b55b 100644 --- a/lib/features/manage_account/presentation/profiles/identities/identities_view.dart +++ b/lib/features/manage_account/presentation/profiles/identities/identities_view.dart @@ -1,12 +1,11 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; -import 'package:tmail_ui_user/features/base/mixin/popup_menu_widget_mixin.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/identities_controller.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/widgets/identities_header_widget.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/profiles/identities/widgets/identities_radio_list_builder.dart'; -class IdentitiesView extends GetWidget with PopupMenuWidgetMixin, AppLoaderMixin { +class IdentitiesView extends GetWidget with AppLoaderMixin { IdentitiesView({Key? key}) : super(key: key); diff --git a/lib/features/search/email/presentation/search_email_controller.dart b/lib/features/search/email/presentation/search_email_controller.dart index d85380e289..dec1bf5b8b 100644 --- a/lib/features/search/email/presentation/search_email_controller.dart +++ b/lib/features/search/email/presentation/search_email_controller.dart @@ -47,7 +47,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_re import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/update_emails_with_new_mailbox_id_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; diff --git a/lib/features/search/email/presentation/search_email_view.dart b/lib/features/search/email/presentation/search_email_view.dart index d422074e98..d7e046d26c 100644 --- a/lib/features/search/email/presentation/search_email_view.dart +++ b/lib/features/search/email/presentation/search_email_view.dart @@ -15,7 +15,7 @@ import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; import 'package:tmail_ui_user/features/base/widget/scrollbar_list_view.dart'; import 'package:tmail_ui_user/features/email/presentation/widgets/email_action_cupertino_action_sheet_action_builder.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/recent_search.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/quick_search/contact_quick_search_item.dart'; diff --git a/lib/features/search/email/presentation/widgets/email_receive_time_action_tile_widget.dart b/lib/features/search/email/presentation/widgets/email_receive_time_action_tile_widget.dart index 7e126dcf7d..07d15574f5 100644 --- a/lib/features/search/email/presentation/widgets/email_receive_time_action_tile_widget.dart +++ b/lib/features/search/email/presentation/widgets/email_receive_time_action_tile_widget.dart @@ -3,7 +3,7 @@ import 'package:core/presentation/resources/image_paths.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/email_receive_time_type.dart'; class EmailReceiveTimeActionTileWidget extends StatelessWidget { diff --git a/lib/features/search/email/presentation/widgets/email_receive_time_cupertino_action_sheet_action_builder.dart b/lib/features/search/email/presentation/widgets/email_receive_time_cupertino_action_sheet_action_builder.dart index b6fbbfaa02..354ac6f6cc 100644 --- a/lib/features/search/email/presentation/widgets/email_receive_time_cupertino_action_sheet_action_builder.dart +++ b/lib/features/search/email/presentation/widgets/email_receive_time_cupertino_action_sheet_action_builder.dart @@ -4,7 +4,7 @@ import 'package:core/utils/platform_info.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/email_receive_time_type.dart'; class EmailReceiveTimeCupertinoActionSheetActionBuilder extends CupertinoActionSheetNoIconBuilder { diff --git a/lib/features/search/mailbox/presentation/search_mailbox_view.dart b/lib/features/search/mailbox/presentation/search_mailbox_view.dart index 448a0253df..7c31ddd4ed 100644 --- a/lib/features/search/mailbox/presentation/search_mailbox_view.dart +++ b/lib/features/search/mailbox/presentation/search_mailbox_view.dart @@ -8,6 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:focused_menu_custom/modals.dart'; import 'package:get/get.dart'; +import 'package:model/email/presentation_email.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; @@ -16,11 +17,12 @@ import 'package:tmail_ui_user/features/mailbox/presentation/mixin/mailbox_widget import 'package:tmail_ui_user/features/mailbox/presentation/model/context_item_mailbox_action.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/utils/mailbox_utils.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_action_for_select_all_emails_extension.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_email_action_type_when_select_all_active_and_mailbox_opened_extension.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_email_action_type_when_select_all_active_and_search_active_extension.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_email_action_type_when_selection_active_extension.dart'; 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 @@ -186,7 +188,12 @@ class SearchMailboxView extends GetWidget controller.responsiveUtils, mailboxCurrent, maxWidth: constraints.maxWidth, - onDragEmailToMailboxAccepted: _handleDragItemAccepted, + onDragEmailToMailboxAccepted: (draggableEmailData, destinationMailbox) => + _handleDragItemAccepted( + AppLocalizations.of(context), + draggableEmailData, + destinationMailbox, + ), onClickOpenMailboxAction: (mailbox) => controller.openMailboxAction(context, mailbox), onClickOpenMenuMailboxAction: (position, mailbox) => _openMailboxMenuAction(context, mailbox, position: position), onLongPressMailboxAction: (mailbox) => _openMailboxMenuAction(context, mailbox), @@ -199,20 +206,29 @@ class SearchMailboxView extends GetWidget } void _handleDragItemAccepted( - DraggableEmailData draggableEmailData, - PresentationMailbox presentationMailbox, + AppLocalizations appLocalizations, + List listEmails, + PresentationMailbox destinationMailbox, ) { - if (draggableEmailData.isSelectAllEmailsEnabled) { + if (controller.dashboardController.isSelectAllActiveAndMailboxOpened) { controller .dashboardController - .dragAllSelectedEmailToMailboxAction(presentationMailbox); - } else { + .dragEmailsToMailboxWhenSelectAllActiveAndMailboxOpened( + appLocalizations: appLocalizations, + selectedMailbox: controller.dashboardController.selectedMailbox.value!, + destinationMailbox: destinationMailbox, + ); + } else if (controller.dashboardController.isSelectAllActiveAndSearchActive) { controller .dashboardController - .dragSelectedMultipleEmailToMailboxAction( - draggableEmailData.listEmails!, - presentationMailbox, + .dragEmailsToMailboxWhenSelectAllActiveAndSearchActive( + appLocalizations: appLocalizations, + destinationMailbox: destinationMailbox, ); + } else { + controller + .dashboardController + .dragSelectedMultipleEmailToMailboxAction(listEmails, destinationMailbox); } } 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 253f3dd14e..94bca947fa 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,7 +52,7 @@ class _MailboxSearchedItemBuilderState extends State @override Widget build(BuildContext context) { if (PlatformInfo.isWeb) { - return DragTarget( + return DragTarget>( builder: (_, __, ___) => _buildMailboxItem(context), onAcceptWithDetails: (details) { widget.onDragEmailToMailboxAccepted?.call( diff --git a/lib/features/thread/data/datasource/thread_datasource.dart b/lib/features/thread/data/datasource/thread_datasource.dart index b9fc635862..ff98156b0a 100644 --- a/lib/features/thread/data/datasource/thread_datasource.dart +++ b/lib/features/thread/data/datasource/thread_datasource.dart @@ -12,8 +12,10 @@ import 'package:jmap_dart_client/jmap/core/state.dart'; import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/email/presentation_email.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart'; import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart'; import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart'; @@ -104,4 +106,37 @@ abstract class ThreadDataSource { int totalEmails, StreamController> onProgressController ); + + Future> markAllSearchAsRead( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ); + + Future> markAllSearchAsUnread( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ); + + Future> markAllSearchAsStarred( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ); + + Future> moveAllEmailSearchedToFolder( + Session session, + AccountId accountId, + MailboxId destinationMailboxId, + String destinationPath, + SearchEmailFilter searchEmailFilter, + { + bool isDestinationSpamMailbox = false, + EmailFilterCondition? moreFilterCondition, + } + ); } \ No newline at end of file diff --git a/lib/features/thread/data/datasource_impl/local_thread_datasource_impl.dart b/lib/features/thread/data/datasource_impl/local_thread_datasource_impl.dart index 5fa8ed7446..9aa5631144 100644 --- a/lib/features/thread/data/datasource_impl/local_thread_datasource_impl.dart +++ b/lib/features/thread/data/datasource_impl/local_thread_datasource_impl.dart @@ -12,8 +12,10 @@ import 'package:jmap_dart_client/jmap/core/state.dart'; import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/email/presentation_email.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; import 'package:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart'; import 'package:tmail_ui_user/features/thread/data/local/email_cache_manager.dart'; import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart'; @@ -162,4 +164,49 @@ class LocalThreadDataSourceImpl extends ThreadDataSource { ) { throw UnimplementedError(); } + + @override + Future> markAllSearchAsRead( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ) { + throw UnimplementedError(); + } + + @override + Future> markAllSearchAsUnread( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ) { + throw UnimplementedError(); + } + + @override + Future> markAllSearchAsStarred( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ) { + throw UnimplementedError(); + } + + @override + Future> moveAllEmailSearchedToFolder( + Session session, + AccountId accountId, + MailboxId destinationMailboxId, + String destinationPath, + SearchEmailFilter searchEmailFilter, + { + bool isDestinationSpamMailbox = false, + EmailFilterCondition? moreFilterCondition + } + ) { + throw UnimplementedError(); + } } \ No newline at end of file diff --git a/lib/features/thread/data/datasource_impl/thread_datasource_impl.dart b/lib/features/thread/data/datasource_impl/thread_datasource_impl.dart index 94ed891f16..0c13c2754d 100644 --- a/lib/features/thread/data/datasource_impl/thread_datasource_impl.dart +++ b/lib/features/thread/data/datasource_impl/thread_datasource_impl.dart @@ -12,9 +12,13 @@ import 'package:jmap_dart_client/jmap/core/state.dart'; import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:model/email/mark_star_action.dart'; import 'package:model/email/presentation_email.dart'; +import 'package:model/email/read_actions.dart'; import 'package:model/extensions/email_extension.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; import 'package:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart'; import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart'; import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart'; @@ -203,4 +207,83 @@ class ThreadDataSourceImpl extends ThreadDataSource { ); }).catchError(_exceptionThrower.throwException); } + + @override + Future> markAllSearchAsRead( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ) { + return Future.sync(() async { + return await _threadIsolateWorker.markAllSearchAsReadOrUnread( + session, + accountId, + ReadActions.markAsRead, + searchEmailFilter, + moreFilterCondition: moreFilterCondition, + ); + }).catchError(_exceptionThrower.throwException); + } + + @override + Future> markAllSearchAsUnread( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ) { + return Future.sync(() async { + return await _threadIsolateWorker.markAllSearchAsReadOrUnread( + session, + accountId, + ReadActions.markAsUnread, + searchEmailFilter, + moreFilterCondition: moreFilterCondition, + ); + }).catchError(_exceptionThrower.throwException); + } + + @override + Future> markAllSearchAsStarred( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ) { + return Future.sync(() async { + return await _threadIsolateWorker.markAllSearchAsStarredOrUnStarred( + session, + accountId, + MarkStarAction.markStar, + searchEmailFilter, + moreFilterCondition: moreFilterCondition, + ); + }).catchError(_exceptionThrower.throwException); + } + + @override + Future> moveAllEmailSearchedToFolder( + Session session, + AccountId accountId, + MailboxId destinationMailboxId, + String destinationPath, + SearchEmailFilter searchEmailFilter, + { + bool isDestinationSpamMailbox = false, + EmailFilterCondition? moreFilterCondition, + } + ) { + return Future.sync(() async { + return await _threadIsolateWorker.moveAllEmailSearchedToFolder( + session, + accountId, + destinationMailboxId, + destinationPath, + searchEmailFilter, + isDestinationSpamMailbox: isDestinationSpamMailbox, + moreFilterCondition: moreFilterCondition, + ); + }).catchError(_exceptionThrower.throwException); + } } \ No newline at end of file diff --git a/lib/features/thread/data/network/thread_isolate_worker.dart b/lib/features/thread/data/network/thread_isolate_worker.dart index 2fcd333a64..ec04345a3a 100644 --- a/lib/features/thread/data/network/thread_isolate_worker.dart +++ b/lib/features/thread/data/network/thread_isolate_worker.dart @@ -25,6 +25,7 @@ import 'package:model/extensions/list_email_extension.dart'; import 'package:tmail_ui_user/features/base/isolate/background_isolate_binary_messenger/background_isolate_binary_messenger.dart'; import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; import 'package:tmail_ui_user/features/thread/data/model/empty_mailbox_folder_arguments.dart'; import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart'; import 'package:tmail_ui_user/features/thread/domain/exceptions/thread_exceptions.dart'; @@ -317,9 +318,9 @@ class ThreadIsolateWorker { final movedEmailIds = await _emailAPI.moveSelectionAllEmailsToFolder( session, accountId, - currentMailboxId, destinationMailboxId, - listEmails.listEmailIds, + currentMailboxId: currentMailboxId, + listEmailId: listEmails.listEmailIds, isDestinationSpamMailbox: isDestinationSpamMailbox, ); @@ -405,4 +406,196 @@ class ThreadIsolateWorker { return emailIdListCompleted; } + + Future> markAllSearchAsReadOrUnread( + Session session, + AccountId accountId, + ReadActions readActions, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ) async { + List emailIdListCompleted = []; + try { + bool hasMoreEmails = true; + UTCDate? lastReceivedDate; + EmailId? lastEmailId; + + while (hasMoreEmails) { + final emailResponse = await _threadAPI.getAllEmail( + session, + accountId, + limit: UnsignedInt(30), + filter: searchEmailFilter.mappingToEmailFilterCondition( + moreFilterCondition: moreFilterCondition, + ), + sort: {}..add( + EmailComparator(EmailComparatorProperty.receivedAt)..setIsAscending(false) + ), + properties: Properties({ + EmailProperty.id, + EmailProperty.receivedAt, + }) + ); + + List listEmails = emailResponse.emailList ?? []; + + if (lastEmailId != null) { + listEmails.removeWhere((email) => email.id == lastEmailId); + } + + if (listEmails.isEmpty) { + hasMoreEmails = false; + break; + } + + lastEmailId = listEmails.last.id; + lastReceivedDate = listEmails.last.receivedAt; + searchEmailFilter = searchEmailFilter.copyWith( + beforeOption: optionOf(lastReceivedDate), + ); + + final readEmails = await _emailAPI.markAsRead( + session, + accountId, + listEmails.listEmailIds, + readActions + ); + + emailIdListCompleted.addAll(readEmails.emailIdsSuccess); + } + } catch (e) { + logError('ThreadIsolateWorker::markAllSearchAsReadOrUnread(): ERROR: $e'); + } + return emailIdListCompleted; + } + + Future> markAllSearchAsStarredOrUnStarred( + Session session, + AccountId accountId, + MarkStarAction starAction, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ) async { + List emailIdListCompleted = []; + try { + bool hasMoreEmails = true; + UTCDate? lastReceivedDate; + EmailId? lastEmailId; + + while (hasMoreEmails) { + final emailResponse = await _threadAPI.getAllEmail( + session, + accountId, + limit: UnsignedInt(30), + filter: searchEmailFilter.mappingToEmailFilterCondition( + moreFilterCondition: moreFilterCondition, + ), + sort: {}..add( + EmailComparator(EmailComparatorProperty.receivedAt)..setIsAscending(false) + ), + properties: Properties({ + EmailProperty.id, + EmailProperty.receivedAt, + }) + ); + + List listEmails = emailResponse.emailList ?? []; + + if (lastEmailId != null) { + listEmails.removeWhere((email) => email.id == lastEmailId); + } + + if (listEmails.isEmpty) { + hasMoreEmails = false; + break; + } + + lastEmailId = listEmails.last.id; + lastReceivedDate = listEmails.last.receivedAt; + searchEmailFilter = searchEmailFilter.copyWith( + beforeOption: optionOf(lastReceivedDate), + ); + + final starredEmails = await _emailAPI.markAsStar( + session, + accountId, + listEmails.listEmailIds, + starAction, + ); + + emailIdListCompleted.addAll(starredEmails.emailIdsSuccess); + } + } catch (e) { + logError('ThreadIsolateWorker::markAllSearchAsStarredOrUnStarred(): ERROR: $e'); + } + return emailIdListCompleted; + } + + Future> moveAllEmailSearchedToFolder( + Session session, + AccountId accountId, + MailboxId destinationMailboxId, + String destinationPath, + SearchEmailFilter searchEmailFilter, + { + bool isDestinationSpamMailbox = false, + EmailFilterCondition? moreFilterCondition, + } + ) async { + List emailIdListCompleted = []; + try { + bool hasMoreEmails = true; + UTCDate? lastReceivedDate; + EmailId? lastEmailId; + + while (hasMoreEmails) { + final emailResponse = await _threadAPI.getAllEmail( + session, + accountId, + limit: UnsignedInt(30), + filter: searchEmailFilter.mappingToEmailFilterCondition( + moreFilterCondition: moreFilterCondition, + ), + sort: {}..add( + EmailComparator(EmailComparatorProperty.receivedAt)..setIsAscending(false) + ), + properties: Properties({ + EmailProperty.id, + EmailProperty.receivedAt, + EmailProperty.mailboxIds, + }) + ); + + List listEmails = emailResponse.emailList ?? []; + + if (lastEmailId != null) { + listEmails.removeWhere((email) => email.id == lastEmailId); + } + + if (listEmails.isEmpty) { + hasMoreEmails = false; + break; + } + + lastEmailId = listEmails.last.id; + lastReceivedDate = listEmails.last.receivedAt; + searchEmailFilter = searchEmailFilter.copyWith( + beforeOption: optionOf(lastReceivedDate), + ); + + final listResult = await _emailAPI.moveSelectionAllEmailsToFolder( + session, + accountId, + destinationMailboxId, + listEmail: listEmails, + isDestinationSpamMailbox: isDestinationSpamMailbox, + ); + + emailIdListCompleted.addAll(listResult.$1); + } + } catch (e) { + logError('ThreadIsolateWorker::markAllSearchToFolder(): ERROR: $e'); + } + return emailIdListCompleted; + } } diff --git a/lib/features/thread/data/repository/thread_repository_impl.dart b/lib/features/thread/data/repository/thread_repository_impl.dart index b4aa5da8a0..daa5f05880 100644 --- a/lib/features/thread/data/repository/thread_repository_impl.dart +++ b/lib/features/thread/data/repository/thread_repository_impl.dart @@ -20,6 +20,7 @@ import 'package:model/model.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart'; import 'package:tmail_ui_user/features/mailbox/data/extensions/state_extension.dart'; import 'package:tmail_ui_user/features/mailbox/data/model/state_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; import 'package:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart'; import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart'; import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart'; @@ -465,4 +466,72 @@ class ThreadRepositoryImpl extends ThreadRepository { onProgressController ); } + + @override + Future> markAllSearchAsRead( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ) { + return mapDataSource[DataSourceType.network]!.markAllSearchAsRead( + session, + accountId, + searchEmailFilter, + moreFilterCondition: moreFilterCondition, + ); + } + + @override + Future> markAllSearchAsUnread( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ) { + return mapDataSource[DataSourceType.network]!.markAllSearchAsUnread( + session, + accountId, + searchEmailFilter, + moreFilterCondition: moreFilterCondition, + ); + } + + @override + Future> markAllSearchAsStarred( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ) { + return mapDataSource[DataSourceType.network]!.markAllSearchAsStarred( + session, + accountId, + searchEmailFilter, + moreFilterCondition: moreFilterCondition, + ); + } + + @override + Future> moveAllEmailSearchedToFolder( + Session session, + AccountId accountId, + MailboxId destinationMailboxId, + String destinationPath, + SearchEmailFilter searchEmailFilter, + { + bool isDestinationSpamMailbox = false, + EmailFilterCondition? moreFilterCondition, + } + ) { + return mapDataSource[DataSourceType.network]!.moveAllEmailSearchedToFolder( + session, + accountId, + destinationMailboxId, + destinationPath, + searchEmailFilter, + isDestinationSpamMailbox: isDestinationSpamMailbox, + moreFilterCondition: moreFilterCondition, + ); + } } \ No newline at end of file diff --git a/lib/features/thread/domain/model/filter_message_option.dart b/lib/features/thread/domain/model/filter_message_option.dart index 6aab14c95f..dfb8c4cd63 100644 --- a/lib/features/thread/domain/model/filter_message_option.dart +++ b/lib/features/thread/domain/model/filter_message_option.dart @@ -2,6 +2,9 @@ import 'package:core/core.dart'; import 'package:flutter/material.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart'; +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/main/localizations/app_localizations.dart'; @@ -151,4 +154,35 @@ extension FilterMessageOptionExtension on FilterMessageOption { color: AppColor.primaryColor); } } + + EmailFilterCondition getFilterCondition({ + PresentationEmail? oldestEmail, + MailboxId? mailboxIdSelected, + }) { + switch(this) { + case FilterMessageOption.all: + return EmailFilterCondition( + inMailbox: mailboxIdSelected, + before: oldestEmail?.receivedAt, + ); + case FilterMessageOption.unread: + return EmailFilterCondition( + inMailbox: mailboxIdSelected, + notKeyword: KeyWordIdentifier.emailSeen.value, + before: oldestEmail?.receivedAt, + ); + case FilterMessageOption.attachments: + return EmailFilterCondition( + inMailbox: mailboxIdSelected, + hasAttachment: true, + before: oldestEmail?.receivedAt, + ); + case FilterMessageOption.starred: + return EmailFilterCondition( + inMailbox: mailboxIdSelected, + hasKeyword: KeyWordIdentifier.emailFlagged.value, + before: oldestEmail?.receivedAt, + ); + } + } } \ No newline at end of file diff --git a/lib/features/thread/domain/repository/thread_repository.dart b/lib/features/thread/domain/repository/thread_repository.dart index 775cc7f5d3..ae64105f35 100644 --- a/lib/features/thread/domain/repository/thread_repository.dart +++ b/lib/features/thread/domain/repository/thread_repository.dart @@ -11,8 +11,10 @@ import 'package:jmap_dart_client/jmap/core/sort/comparator.dart'; import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/email/presentation_email.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; import 'package:tmail_ui_user/features/thread/domain/model/email_filter.dart'; import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart'; import 'package:tmail_ui_user/features/thread/domain/model/get_email_request.dart'; @@ -100,4 +102,37 @@ abstract class ThreadRepository { int totalEmails, StreamController> onProgressController ); + + Future> markAllSearchAsRead( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ); + + Future> markAllSearchAsUnread( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ); + + Future> markAllSearchAsStarred( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ); + + Future> moveAllEmailSearchedToFolder( + Session session, + AccountId accountId, + MailboxId destinationMailboxId, + String destinationPath, + SearchEmailFilter searchEmailFilter, + { + bool isDestinationSpamMailbox = false, + EmailFilterCondition? moreFilterCondition, + } + ); } \ No newline at end of file diff --git a/lib/features/thread/domain/state/mark_all_search_as_read_state.dart b/lib/features/thread/domain/state/mark_all_search_as_read_state.dart new file mode 100644 index 0000000000..c572454d3c --- /dev/null +++ b/lib/features/thread/domain/state/mark_all_search_as_read_state.dart @@ -0,0 +1,19 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; + +class MarkAllSearchAsReadLoading extends LoadingState {} + +class MarkAllSearchAsReadSuccess extends UIState { + final List listEmailId; + + MarkAllSearchAsReadSuccess(this.listEmailId); + + @override + List get props => [listEmailId]; +} + +class MarkAllSearchAsReadFailure extends FeatureFailure { + + MarkAllSearchAsReadFailure(dynamic exception) : super(exception: exception); +} \ No newline at end of file diff --git a/lib/features/thread/domain/state/mark_all_search_as_starred_state.dart b/lib/features/thread/domain/state/mark_all_search_as_starred_state.dart new file mode 100644 index 0000000000..34d7ad99cc --- /dev/null +++ b/lib/features/thread/domain/state/mark_all_search_as_starred_state.dart @@ -0,0 +1,19 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; + +class MarkAllSearchAsStarredLoading extends LoadingState {} + +class MarkAllSearchAsStarredSuccess extends UIState { + final List listEmailId; + + MarkAllSearchAsStarredSuccess(this.listEmailId); + + @override + List get props => [listEmailId]; +} + +class MarkAllSearchAsStarredFailure extends FeatureFailure { + + MarkAllSearchAsStarredFailure(dynamic exception) : super(exception: exception); +} \ No newline at end of file diff --git a/lib/features/thread/domain/state/mark_all_search_as_unread_state.dart b/lib/features/thread/domain/state/mark_all_search_as_unread_state.dart new file mode 100644 index 0000000000..dcbebeaa69 --- /dev/null +++ b/lib/features/thread/domain/state/mark_all_search_as_unread_state.dart @@ -0,0 +1,19 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; + +class MarkAllSearchAsUnreadLoading extends LoadingState {} + +class MarkAllSearchAsUnreadSuccess extends UIState { + final List listEmailId; + + MarkAllSearchAsUnreadSuccess(this.listEmailId); + + @override + List get props => [listEmailId]; +} + +class MarkAllSearchAsUnreadFailure extends FeatureFailure { + + MarkAllSearchAsUnreadFailure(dynamic exception) : super(exception: exception); +} \ No newline at end of file diff --git a/lib/features/thread/domain/state/move_all_email_searched_to_folder_state.dart b/lib/features/thread/domain/state/move_all_email_searched_to_folder_state.dart new file mode 100644 index 0000000000..113d353d16 --- /dev/null +++ b/lib/features/thread/domain/state/move_all_email_searched_to_folder_state.dart @@ -0,0 +1,27 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; + +class MoveAllEmailSearchedToFolderLoading extends LoadingState {} + +class MoveAllEmailSearchedToFolderSuccess extends UIState { + final List listEmailId; + final String destinationPath; + + MoveAllEmailSearchedToFolderSuccess(this.listEmailId, this.destinationPath); + + @override + List get props => [listEmailId, destinationPath]; +} + +class MoveAllEmailSearchedToFolderFailure extends FeatureFailure { + final String destinationPath; + + MoveAllEmailSearchedToFolderFailure( + this.destinationPath, + dynamic exception, + ) : super(exception: exception); + + @override + List get props => [destinationPath, ...super.props]; +} \ No newline at end of file diff --git a/lib/features/thread/domain/usecases/mark_all_search_as_read_interactor.dart b/lib/features/thread/domain/usecases/mark_all_search_as_read_interactor.dart new file mode 100644 index 0000000000..329fbe350b --- /dev/null +++ b/lib/features/thread/domain/usecases/mark_all_search_as_read_interactor.dart @@ -0,0 +1,37 @@ + +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; +import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/mark_all_search_as_read_state.dart'; + +class MarkAllSearchAsReadInteractor { + + final ThreadRepository threadRepository; + + MarkAllSearchAsReadInteractor(this.threadRepository); + + Stream> execute( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ) async* { + try { + yield Right(MarkAllSearchAsReadLoading()); + final listEmailId = await threadRepository.markAllSearchAsRead( + session, + accountId, + searchEmailFilter, + moreFilterCondition: moreFilterCondition, + ); + yield Right(MarkAllSearchAsReadSuccess(listEmailId)); + } catch (e) { + yield Left(MarkAllSearchAsReadFailure(e)); + } + } +} \ No newline at end of file diff --git a/lib/features/thread/domain/usecases/mark_all_search_as_starred_interactor.dart b/lib/features/thread/domain/usecases/mark_all_search_as_starred_interactor.dart new file mode 100644 index 0000000000..4e6e0d9cfe --- /dev/null +++ b/lib/features/thread/domain/usecases/mark_all_search_as_starred_interactor.dart @@ -0,0 +1,37 @@ + +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; +import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/mark_all_search_as_starred_state.dart'; + +class MarkAllSearchAsStarredInteractor { + + final ThreadRepository threadRepository; + + MarkAllSearchAsStarredInteractor(this.threadRepository); + + Stream> execute( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ) async* { + try { + yield Right(MarkAllSearchAsStarredLoading()); + final listEmailId = await threadRepository.markAllSearchAsStarred( + session, + accountId, + searchEmailFilter, + moreFilterCondition: moreFilterCondition, + ); + yield Right(MarkAllSearchAsStarredSuccess(listEmailId)); + } catch (e) { + yield Left(MarkAllSearchAsStarredFailure(e)); + } + } +} \ No newline at end of file diff --git a/lib/features/thread/domain/usecases/mark_all_search_as_unread_interactor.dart b/lib/features/thread/domain/usecases/mark_all_search_as_unread_interactor.dart new file mode 100644 index 0000000000..bfb9923254 --- /dev/null +++ b/lib/features/thread/domain/usecases/mark_all_search_as_unread_interactor.dart @@ -0,0 +1,37 @@ + +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; +import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/mark_all_search_as_unread_state.dart'; + +class MarkAllSearchAsUnreadInteractor { + + final ThreadRepository threadRepository; + + MarkAllSearchAsUnreadInteractor(this.threadRepository); + + Stream> execute( + Session session, + AccountId accountId, + SearchEmailFilter searchEmailFilter, + {EmailFilterCondition? moreFilterCondition} + ) async* { + try { + yield Right(MarkAllSearchAsUnreadLoading()); + final listEmailId = await threadRepository.markAllSearchAsUnread( + session, + accountId, + searchEmailFilter, + moreFilterCondition: moreFilterCondition, + ); + yield Right(MarkAllSearchAsUnreadSuccess(listEmailId)); + } catch (e) { + yield Left(MarkAllSearchAsUnreadFailure(e)); + } + } +} \ No newline at end of file diff --git a/lib/features/thread/domain/usecases/move_all_email_searched_to_folder_interactor.dart b/lib/features/thread/domain/usecases/move_all_email_searched_to_folder_interactor.dart new file mode 100644 index 0000000000..bde10da8c6 --- /dev/null +++ b/lib/features/thread/domain/usecases/move_all_email_searched_to_folder_interactor.dart @@ -0,0 +1,46 @@ + +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; +import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/move_all_email_searched_to_folder_state.dart'; + +class MoveAllEmailSearchedToFolderInteractor { + + final ThreadRepository threadRepository; + + MoveAllEmailSearchedToFolderInteractor(this.threadRepository); + + Stream> execute( + Session session, + AccountId accountId, + MailboxId destinationMailboxId, + String destinationPath, + SearchEmailFilter searchEmailFilter, + { + bool isDestinationSpamMailbox = false, + EmailFilterCondition? moreFilterCondition + } + ) async* { + try { + yield Right(MoveAllEmailSearchedToFolderLoading()); + final listEmailId = await threadRepository.moveAllEmailSearchedToFolder( + session, + accountId, + destinationMailboxId, + destinationPath, + searchEmailFilter, + isDestinationSpamMailbox: isDestinationSpamMailbox, + moreFilterCondition: moreFilterCondition, + ); + yield Right(MoveAllEmailSearchedToFolderSuccess(listEmailId, destinationPath)); + } catch (e) { + yield Left(MoveAllEmailSearchedToFolderFailure(destinationPath, e)); + } + } +} \ No newline at end of file diff --git a/lib/features/thread/presentation/extensions/list_presentation_email_extensions.dart b/lib/features/thread/presentation/extensions/list_presentation_email_extensions.dart index 600cecd742..00add4a1c9 100644 --- a/lib/features/thread/presentation/extensions/list_presentation_email_extensions.dart +++ b/lib/features/thread/presentation/extensions/list_presentation_email_extensions.dart @@ -4,6 +4,7 @@ import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/email/presentation_email.dart'; import 'package:model/extensions/presentation_email_extension.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; +import 'package:model/mailbox/select_mode.dart'; import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; import 'package:tmail_ui_user/main/routes/app_routes.dart'; import 'package:tmail_ui_user/main/routes/navigation_router.dart'; @@ -56,4 +57,7 @@ extension ListPresentationEmailExtensions on List { return null; } } + + List cancelSelectionMode() => + map((email) => email.toSelectedEmail(selectMode: SelectMode.INACTIVE)).toList(); } \ No newline at end of file diff --git a/lib/features/thread/presentation/model/draggable_email_data.dart b/lib/features/thread/presentation/model/draggable_email_data.dart deleted file mode 100644 index 68c1ee0df0..0000000000 --- a/lib/features/thread/presentation/model/draggable_email_data.dart +++ /dev/null @@ -1,22 +0,0 @@ - -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 bbaffccae5..489f423237 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -14,14 +14,10 @@ import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; -import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart'; 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/presentation/extensions/email_action_type_extension.dart'; import 'package:tmail_ui_user/features/email/domain/model/mark_read_action.dart'; import 'package:tmail_ui_user/features/email/domain/state/delete_email_permanently_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/delete_multiple_emails_permanently_state.dart'; @@ -30,10 +26,9 @@ import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart'; import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read_state.dart'; -import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart' as search; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_action_for_select_all_emails_extension.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_selection_email_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/move_emails_to_mailbox_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/update_current_emails_flags_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart'; @@ -84,10 +79,7 @@ import 'package:universal_html/html.dart' as html; typedef StartRangeSelection = int; typedef EndRangeSelection = int; -class ThreadController extends BaseController - with EmailActionController, - PopupMenuWidgetMixin, - EmailActionHandlerMixin { +class ThreadController extends BaseController with EmailActionController { final networkConnectionController = Get.find(); @@ -295,27 +287,6 @@ class ThreadController extends BaseController } else if (action is FilterMessageAction) { filterMessagesAction(action.option); mailboxDashBoardController.clearDashBoardAction(); - } else if (action is HandleEmailActionTypeAction) { - if (_validateToShowConfirmBulkActionEmailsDialog()) { - 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, - action.listEmailSelected, - ); - } - mailboxDashBoardController.clearDashBoardAction(); } else if (action is OpenEmailDetailedFromSuggestionQuickSearchAction) { final mailboxContain = action.presentationEmail.findMailboxContain(mailboxDashBoardController.mapMailboxById); final newEmail = generateEmailByPlatform(action.presentationEmail); @@ -356,9 +327,6 @@ class ThreadController extends BaseController } canSearchMore = true; mailboxDashBoardController.emailsInCurrentMailbox.clear(); - } else if (action is MoreSelectedEmailAction) { - showPopupMenuSelectionEmailAction(action.context, action.position); - mailboxDashBoardController.clearDashBoardAction(); } }); @@ -576,14 +544,17 @@ class ThreadController extends BaseController }) { log('ThreadController::_getAllEmailAction:getLatestChanges = $getLatestChanges'); if (_session != null &&_accountId != null) { + final filterMessageOption = mailboxDashBoardController.filterMessageOption.value; consumeState(_getEmailsInMailboxInteractor.execute( _session!, _accountId!, limit: ThreadConstants.defaultLimit, sort: EmailSortOrderType.mostRecent.getSortOrder().toNullable(), emailFilter: EmailFilter( - filter: _getFilterCondition(mailboxIdSelected: selectedMailboxId), - filterOption: mailboxDashBoardController.filterMessageOption.value, + filter: filterMessageOption.getFilterCondition( + mailboxIdSelected: selectedMailboxId, + ), + filterOption: filterMessageOption, mailboxId: selectedMailboxId ), propertiesCreated: EmailUtils.getPropertiesForEmailGetMethod(_session!, _accountId!), @@ -595,43 +566,6 @@ class ThreadController extends BaseController } } - @visibleForTesting - EmailFilterCondition getFilterCondition({ - PresentationEmail? oldestEmail, - MailboxId? mailboxIdSelected, - }) => _getFilterCondition( - oldestEmail: oldestEmail, - mailboxIdSelected: mailboxIdSelected, - ); - - EmailFilterCondition _getFilterCondition({PresentationEmail? oldestEmail, MailboxId? mailboxIdSelected}) { - switch(mailboxDashBoardController.filterMessageOption.value) { - case FilterMessageOption.all: - return EmailFilterCondition( - inMailbox: mailboxIdSelected, - before: oldestEmail?.receivedAt - ); - case FilterMessageOption.unread: - return EmailFilterCondition( - inMailbox: mailboxIdSelected, - notKeyword: KeyWordIdentifier.emailSeen.value, - before: oldestEmail?.receivedAt - ); - case FilterMessageOption.attachments: - return EmailFilterCondition( - inMailbox: mailboxIdSelected, - hasAttachment: true, - before: oldestEmail?.receivedAt - ); - case FilterMessageOption.starred: - return EmailFilterCondition( - inMailbox: mailboxIdSelected, - hasKeyword: KeyWordIdentifier.emailFlagged.value, - before: oldestEmail?.receivedAt - ); - } - } - void refreshAllEmail() { if (searchController.isSearchEmailRunning) { consumeState(Stream.value(Right(SearchingState()))); @@ -718,7 +652,7 @@ class ThreadController extends BaseController position: _searchEmailFilter.position, sort: _searchEmailFilter.sortOrderType.getSortOrder().toNullable(), filter: _searchEmailFilter.mappingToEmailFilterCondition( - moreFilterCondition: _getFilterCondition(), + moreFilterCondition: mailboxDashBoardController.filterMessageOption.value.getFilterCondition(), ), properties: EmailUtils.getPropertiesForEmailGetMethod( _session!, @@ -742,6 +676,7 @@ class ThreadController extends BaseController } Future> _refreshChangeListEmailCache() async { + final filterMessageOption = mailboxDashBoardController.filterMessageOption.value; return _refreshChangesEmailsInMailboxInteractor.execute( _session!, _accountId!, @@ -753,8 +688,8 @@ class ThreadController extends BaseController ), propertiesUpdated: ThreadConstants.propertiesUpdatedDefault, emailFilter: EmailFilter( - filter: _getFilterCondition(mailboxIdSelected: selectedMailboxId), - filterOption: mailboxDashBoardController.filterMessageOption.value, + filter: filterMessageOption.getFilterCondition(mailboxIdSelected: selectedMailboxId), + filterOption: filterMessageOption, mailboxId: selectedMailboxId, ), ).last; @@ -781,14 +716,15 @@ class ThreadController extends BaseController ? mailboxDashBoardController.emailsInCurrentMailbox.last : null; log('ThreadController::_loadMoreEmails: OldestEmailID = ${oldestEmail?.id?.asString}'); + final filterMessageOption = mailboxDashBoardController.filterMessageOption.value; consumeState(_loadMoreEmailsInMailboxInteractor.execute( GetEmailRequest( _session!, _accountId!, limit: ThreadConstants.defaultLimit, sort: EmailSortOrderType.mostRecent.getSortOrder().toNullable(), - filterOption: mailboxDashBoardController.filterMessageOption.value, - filter: _getFilterCondition(oldestEmail: oldestEmail, mailboxIdSelected: selectedMailboxId), + filterOption: filterMessageOption, + filter: filterMessageOption.getFilterCondition(oldestEmail: oldestEmail, mailboxIdSelected: selectedMailboxId), properties: EmailUtils.getPropertiesForEmailGetMethod(_session!, _accountId!), lastEmailId: oldestEmail?.id ) @@ -940,19 +876,7 @@ class ThreadController extends BaseController .every((email) => email.selectMode == SelectMode.INACTIVE); } - void cancelSelectEmail() { - if (mailboxDashBoardController.currentSelectMode.value == SelectMode.INACTIVE) { - return; - } - final newEmailList = mailboxDashBoardController.emailsInCurrentMailbox - .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(); - } + void cancelSelectEmail() => mailboxDashBoardController.cancelAllSelectionEmailMode(); void enableSelectAllEmails() { mailboxDashBoardController.isSelectAllEmailsEnabled.value = true; @@ -1027,7 +951,7 @@ class ThreadController extends BaseController position: _searchEmailFilter.position, sort: _searchEmailFilter.sortOrderType.getSortOrder().toNullable(), filter: _searchEmailFilter.mappingToEmailFilterCondition( - moreFilterCondition: _getFilterCondition() + moreFilterCondition: mailboxDashBoardController.filterMessageOption.value.getFilterCondition() ), properties: EmailUtils.getPropertiesForEmailGetMethod(_session!, _accountId!), needRefreshSearchState: needRefreshSearchState @@ -1107,7 +1031,7 @@ class ThreadController extends BaseController sort: _searchEmailFilter.sortOrderType.getSortOrder().toNullable(), position: _searchEmailFilter.position, filter: _searchEmailFilter.mappingToEmailFilterCondition( - moreFilterCondition: _getFilterCondition() + moreFilterCondition: mailboxDashBoardController.filterMessageOption.value.getFilterCondition() ), properties: EmailUtils.getPropertiesForEmailGetMethod(_session!, _accountId!), lastEmailId: lastEmail?.id @@ -1546,7 +1470,7 @@ class ThreadController extends BaseController } } - bool validateToShowSelectionEmailsBanner() { + bool get isMailboxSelectionEmailBannerDisplayed { return mailboxDashBoardController.isSelectAllPageEnabled.isTrue && selectedMailbox != null && selectedMailbox!.countTotalEmails > ThreadConstants.maxCountEmails && @@ -1554,55 +1478,8 @@ class ThreadController extends BaseController selectedMailbox!.countTotalEmails; } - void showPopupMenuSelectionEmailAction(BuildContext context, RelativeRect position) { - final listSelectionEmailActions = [ - EmailActionType.markAllAsRead, - EmailActionType.markAllAsUnread, - if (selectedMailbox == null || selectedMailbox?.isDrafts == false) - EmailActionType.moveAll, - if (selectedMailbox?.isTrash == true || - selectedMailbox?.isSpam == true || - selectedMailbox?.isDrafts == true) - EmailActionType.deleteAllPermanently - else - EmailActionType.moveAllToTrash - ]; - - openPopupMenuAction( - context, - position, - listSelectionEmailActions.map((action) => PopupMenuItem( - padding: EdgeInsets.zero, - child: popupItem( - action.getIcon(imagePaths), - action.getTitle(context), - colorIcon: action.getIconColor(), - styleName: const TextStyle( - fontWeight: FontWeight.w500, - fontSize: 14, - color: Colors.black - ), - onCallbackAction: () { - popBack(); - if (!isSearchActive) { - showConfirmDialogWhenMakeToActionForSelectionAllEmails( - imagePaths: imagePaths, - totalEmails: selectedMailbox?.countTotalEmails ?? 0, - folderName: selectedMailbox?.getDisplayName(context) ?? '', - onConfirmAction: () => mailboxDashBoardController.handleActionsForSelectionAllEmails( - context: context, - selectedMailbox: selectedMailbox!, - actionType: action, - ), - ); - } - } - ) - )).toList() - ); - } - - bool _validateToShowConfirmBulkActionEmailsDialog() { - return mailboxDashBoardController.isSelectAllEmailsEnabled.isTrue; + bool get isSearchSelectionEmailBannerDisplayed { + return mailboxDashBoardController.isSelectAllPageEnabled.isTrue + && searchController.isSearchEmailRunning; } } \ 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 b41f06ade2..96c7c63bbf 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -1,5 +1,4 @@ import 'package:core/core.dart'; -import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -7,25 +6,22 @@ import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; -import 'package:tmail_ui_user/features/base/mixin/popup_menu_widget_mixin.dart'; import 'package:tmail_ui_user/features/base/widget/compose_floating_button.dart'; +import 'package:tmail_ui_user/features/base/widget/popup_item_widget.dart'; import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; import 'package:tmail_ui_user/features/email/presentation/widgets/email_action_cupertino_action_sheet_action_builder.dart'; -import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read_state.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mixin/filter_email_popup_menu_mixin.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/recover_deleted_message_loading_banner_widget.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/view_state_mailbox_action_progress_loading_banner.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/widgets/vacation_notification_message_widget.dart'; import 'package:tmail_ui_user/features/network_connection/presentation/network_connection_banner_widget.dart'; import 'package:tmail_ui_user/features/quotas/presentation/widget/quotas_banner_widget.dart'; import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart'; -import 'package:tmail_ui_user/features/thread/domain/state/empty_spam_folder_state.dart'; -import 'package:tmail_ui_user/features/thread/domain/state/empty_trash_folder_state.dart'; 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'; @@ -42,15 +38,14 @@ import 'package:tmail_ui_user/features/thread/presentation/widgets/empty_emails_ import 'package:tmail_ui_user/features/thread/presentation/widgets/filter_message_cupertino_action_sheet_action_builder.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/scroll_to_top_button_widget.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/select_all_banner/select_all_emails_in_mailbox_banner.dart'; +import 'package:tmail_ui_user/features/thread/presentation/widgets/select_all_banner/select_all_emails_in_search_banner.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/spam_banner/spam_report_banner_widget.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/thread_view_loading_bar_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; class ThreadView extends GetWidget - with AppLoaderMixin, - FilterEmailPopupMenuMixin, - PopupMenuWidgetMixin { + with AppLoaderMixin, FilterEmailPopupMenuMixin { ThreadView({Key? key}) : super(key: key); @@ -118,6 +113,15 @@ class ThreadView extends GetWidget hintTextSearch: AppLocalizations.of(context).search_emails, onOpenSearchViewAction: controller.goToSearchView ), + Obx(() => RecoverDeletedMessageLoadingBannerWidget( + isLoading: controller.mailboxDashBoardController.isRecoveringDeletedMessage.value, + horizontalLoadingWidget: horizontalLoadingWidget, + responsiveUtils: controller.responsiveUtils, + )), + Obx(() => ViewStateMailboxActionProgressLoadingBanner( + viewState: controller.mailboxDashBoardController.viewStateMailboxActionProgress.value, + responsiveUtils: controller.responsiveUtils, + )), SpamReportBannerWidget( spamReportController: controller.mailboxDashBoardController.spamReportController, margin: ThreadViewStyle.getBannerMargin( @@ -140,11 +144,6 @@ class ThreadView extends GetWidget return const SizedBox.shrink(); } }), - Obx(() => RecoverDeletedMessageLoadingBannerWidget( - isLoading: controller.mailboxDashBoardController.isRecoveringDeletedMessage.value, - horizontalLoadingWidget: horizontalLoadingWidget, - responsiveUtils: controller.responsiveUtils, - )), ], Obx(() { final presentationMailbox = controller.mailboxDashBoardController.selectedMailbox.value; @@ -168,20 +167,11 @@ class ThreadView extends GetWidget return const SizedBox.shrink(); } }), - if (!controller.responsiveUtils.isDesktop(context)) - _buildMailboxActionProgressBanner(context), if (controller.responsiveUtils.isWebDesktop(context)) Obx(() { - if (controller.validateToShowSelectionEmailsBanner()) { - final selectedMailbox = controller - .mailboxDashBoardController - .selectedMailbox - .value!; - - final listEmailSelectedLength = controller - .mailboxDashBoardController - .listEmailSelected - .length; + if (controller.isMailboxSelectionEmailBannerDisplayed) { + final selectedMailbox = controller.selectedMailbox!; + final listEmailSelectedLength = controller.listEmailSelected.length; return SelectAllEmailInMailboxBanner( limitEmailsInPage: listEmailSelectedLength, @@ -190,6 +180,14 @@ class ThreadView extends GetWidget onSelectAllEmailAction: controller.enableSelectAllEmails, onClearSelection: controller.cancelSelectEmail ); + } else if (controller.isSearchSelectionEmailBannerDisplayed) { + final listEmailSelectedLength = controller.listEmailSelected.length; + + return SelectAllEmailInSearchBanner( + limitEmailsInPage: listEmailSelectedLength, + onSelectAllEmailAction: controller.enableSelectAllEmails, + onClearSelection: controller.cancelSelectEmail + ); } else { return const SizedBox.shrink(); } @@ -502,19 +500,12 @@ 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: isSelectAllEmailsEnabled - ? DraggableEmailData.withSelectAllEmails() - : DraggableEmailData(listEmails: controller.listEmailDrag), + child: Draggable>( + data: controller.listEmailDrag, feedback: Obx(() { final isSelectAllEmailsEnabled = controller .mailboxDashBoardController @@ -842,12 +833,13 @@ class ThreadView extends GetWidget ) { return PopupMenuItem( padding: EdgeInsets.zero, - child: popupItem( + child: PopupItemWidget( mailboxContain?.isSpam == true ? controller.imagePaths.icNotSpam : controller.imagePaths.icSpam, mailboxContain?.isSpam == true ? AppLocalizations.of(context).remove_from_spam : AppLocalizations.of(context).mark_as_spam, colorIcon: AppColor.colorTextButton, + padding: const EdgeInsets.symmetric(horizontal: 20), styleName: const TextStyle( fontWeight: FontWeight.w500, fontSize: 16, @@ -869,10 +861,11 @@ class ThreadView extends GetWidget ) { return PopupMenuItem( padding: EdgeInsets.zero, - child: popupItem( + child: PopupItemWidget( controller.imagePaths.icOpenInNewTab, AppLocalizations.of(context).openInNewTab, colorIcon: AppColor.colorTextButton, + padding: const EdgeInsets.symmetric(horizontal: 20), styleName: const TextStyle( fontWeight: FontWeight.w500, fontSize: 16, @@ -892,10 +885,11 @@ class ThreadView extends GetWidget ) { return PopupMenuItem( padding: EdgeInsets.zero, - child: popupItem( + child: PopupItemWidget( controller.imagePaths.icMailboxArchived, AppLocalizations.of(context).archiveMessage, colorIcon: AppColor.colorTextButton, + padding: const EdgeInsets.symmetric(horizontal: 20), styleName: const TextStyle( fontWeight: FontWeight.w500, fontSize: 16, @@ -915,10 +909,11 @@ class ThreadView extends GetWidget ) { return PopupMenuItem( padding: EdgeInsets.zero, - child: popupItem( + child: PopupItemWidget( controller.imagePaths.icEdit, appLocalizations.editAsNewEmail, colorIcon: AppColor.colorTextButton, + padding: const EdgeInsets.symmetric(horizontal: 20), styleName: const TextStyle( fontWeight: FontWeight.w500, fontSize: 16, @@ -931,69 +926,4 @@ class ThreadView extends GetWidget ) ); } - - Widget _buildMailboxActionProgressBanner(BuildContext context) { - return Obx(() { - return _MailboxActionProgressBanner( - viewState: controller.mailboxDashBoardController.viewStateMailboxActionProgress.value, - responsiveUtils: controller.responsiveUtils, - ); - }); - } } - -class _MailboxActionProgressBanner extends StatelessWidget with AppLoaderMixin { - final Either viewState; - final ResponsiveUtils responsiveUtils; - - const _MailboxActionProgressBanner({ - required this.viewState, - required this.responsiveUtils, - }); - - @override - Widget build(BuildContext context) { - return viewState.fold( - (failure) => const SizedBox.shrink(), - (success) { - if (success is MarkAsMailboxReadLoading || - success is EmptySpamFolderLoading || - success is EmptyTrashFolderLoading) { - return Padding( - padding: EdgeInsets.only( - top: responsiveUtils.isDesktop(context) ? 16 : 0, - left: 16, - right: 16, - bottom: responsiveUtils.isDesktop(context) ? 0 : 16, - ), - child: horizontalLoadingWidget, - ); - } else if (success is UpdatingMarkAsMailboxReadState) { - return _buildProgressBanner( - context, - success.countRead, - success.totalUnread, - ); - } else if (success is EmptyingFolderState) { - return _buildProgressBanner( - context, - success.countEmailsDeleted, - success.totalEmails, - ); - } - return const SizedBox.shrink(); - }, - ); - } - - Padding _buildProgressBanner(BuildContext context, int progress, int total) { - final percent = total > 0 ? progress / total : 0.68; - return Padding( - padding: EdgeInsets.only( - top: responsiveUtils.isDesktop(context) ? 16 : 0, - left: 16, - right: 16, - bottom: responsiveUtils.isDesktop(context) ? 0 : 16), - child: horizontalPercentLoadingWidget(percent)); - } -} \ No newline at end of file diff --git a/lib/features/thread/presentation/widgets/select_all_banner/message_select_all_email_in_search_widget.dart b/lib/features/thread/presentation/widgets/select_all_banner/message_select_all_email_in_search_widget.dart new file mode 100644 index 0000000000..4b94439c25 --- /dev/null +++ b/lib/features/thread/presentation/widgets/select_all_banner/message_select_all_email_in_search_widget.dart @@ -0,0 +1,41 @@ + +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +class MessageSelectAllEmailInSearchWidget extends StatelessWidget { + + final VoidCallback onClearSelection; + + const MessageSelectAllEmailInSearchWidget({ + super.key, + required this.onClearSelection + }); + + @override + Widget build(BuildContext context) { + return Text.rich( + TextSpan( + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Colors.black, + fontSize: 14, + ), + children: [ + TextSpan( + text: AppLocalizations.of(context).allMailsInThisSearchAreSelected, + ), + TextSpan( + text: AppLocalizations.of(context).clearSelection, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: AppColor.primaryColor, + fontWeight: FontWeight.w500, + 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_on_page_in_mailbox_widget.dart similarity index 91% rename from lib/features/thread/presentation/widgets/select_all_banner/message_select_email_in_page_widget.dart rename to lib/features/thread/presentation/widgets/select_all_banner/message_select_email_on_page_in_mailbox_widget.dart index 9103b13514..3b77bcd27c 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_on_page_in_mailbox_widget.dart @@ -4,14 +4,14 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -class MessageSelectEmailInPageWidget extends StatelessWidget { +class MessageSelectEmailOnPageInMailboxWidget extends StatelessWidget { final int limitEmailsInPage; final int totalEmails; final String folderName; final VoidCallback onSelectAllEmailAction; - const MessageSelectEmailInPageWidget({ + const MessageSelectEmailOnPageInMailboxWidget({ super.key, required this.limitEmailsInPage, required this.totalEmails, diff --git a/lib/features/thread/presentation/widgets/select_all_banner/message_select_email_on_page_in_search_widget.dart b/lib/features/thread/presentation/widgets/select_all_banner/message_select_email_on_page_in_search_widget.dart new file mode 100644 index 0000000000..822b8b0f4b --- /dev/null +++ b/lib/features/thread/presentation/widgets/select_all_banner/message_select_email_on_page_in_search_widget.dart @@ -0,0 +1,54 @@ + +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +class MessageSelectEmailOnPageInSearchWidget extends StatelessWidget { + + final int limitEmailsInPage; + final VoidCallback onSelectAllEmailAction; + + const MessageSelectEmailOnPageInSearchWidget({ + super.key, + required this.limitEmailsInPage, + required this.onSelectAllEmailAction + }); + + @override + Widget build(BuildContext context) { + return Text.rich( + TextSpan( + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Colors.black, + fontSize: 14, + ), + 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).mailsOnThisPageAreSelectedForSearch} ', + ), + TextSpan( + text: AppLocalizations.of(context).selectAllMailsThatMatchThisSearch, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: AppColor.primaryColor, + fontWeight: FontWeight.w500, + 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 6ad0a06583..25a50593be 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 @@ -1,7 +1,7 @@ import 'package:core/presentation/extensions/color_extension.dart'; import 'package:flutter/material.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/select_all_banner/message_select_all_email_in_mailbox_widget.dart'; -import 'package:tmail_ui_user/features/thread/presentation/widgets/select_all_banner/message_select_email_in_page_widget.dart'; +import 'package:tmail_ui_user/features/thread/presentation/widgets/select_all_banner/message_select_email_on_page_in_mailbox_widget.dart'; class SelectAllEmailInMailboxBanner extends StatefulWidget { final int limitEmailsInPage; @@ -43,7 +43,7 @@ class _SelectAllEmailInMailboxBannerState extends State createState() => _SelectAllEmailInSearchBannerState(); +} + +class _SelectAllEmailInSearchBannerState extends State { + + bool _isSelectAllEmailsEnabled = false; + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + color: AppColor.colorBgDesktop, + padding: const EdgeInsetsDirectional.symmetric( + horizontal: 16, + vertical: 12, + ), + child: _isSelectAllEmailsEnabled + ? MessageSelectAllEmailInSearchWidget(onClearSelection: widget.onClearSelection) + : MessageSelectEmailOnPageInSearchWidget( + limitEmailsInPage: widget.limitEmailsInPage, + onSelectAllEmailAction: () { + widget.onSelectAllEmailAction(); + setState(() => _isSelectAllEmailsEnabled = true); + }) + ); + } +} diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 71569f0593..00dbaa3ea4 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2025-02-26T15:15:51.116032", + "@@last_modified": "2025-02-27T13:58:41.032123", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -4527,5 +4527,99 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "selectAllMailsThatMatchThisSearch": "Select all mails that match this search", + "@selectAllMailsThatMatchThisSearch": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "allMailsInThisSearchAreSelected": "All mails in this search are selected. ", + "@allMailsInThisSearchAreSelected": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "messageConfirmationDialogWhenMakeToActionForSelectionAllEmailsInSearch": "This action will affect all mails in search. Are you sure you want to continue?", + "@messageConfirmationDialogWhenMakeToActionForSelectionAllEmailsInSearch": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "toastMessageMarkAllSearchAsReadSuccess": "You’ve marked all messages in search as read", + "@toastMessageMarkAllSearchAsReadSuccess": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "toastMessageMarkAllSearchAsReadFailureWithReason": "All message in search could not be marked as read. Due \"{reason}\"", + "@toastMessageMarkAllSearchAsReadFailureWithReason": { + "type": "text", + "placeholders_order": [ + "reason" + ], + "placeholders": { + "reason": {} + } + }, + "toastMessageMarkAllSearchAsUnreadSuccess": "You’ve marked all messages in search as unread", + "@toastMessageMarkAllSearchAsUnreadSuccess": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "toastMessageMarkAllSearchAsUnreadFailureWithReason": "All message in search could not be marked as unread. Due \"{reason}\"", + "@toastMessageMarkAllSearchAsUnreadFailureWithReason": { + "type": "text", + "placeholders_order": [ + "reason" + ], + "placeholders": { + "reason": {} + } + }, + "toastMessageMarkAllSearchAsStarredSuccess": "You’ve marked all messages in search as starred", + "@toastMessageMarkAllSearchAsStarredSuccess": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "toastMessageMarkAllSearchAsStarredFailureWithReason": "All message in search could not be marked as starred. Due \"{reason}\"", + "@toastMessageMarkAllSearchAsStarredFailureWithReason": { + "type": "text", + "placeholders_order": [ + "reason" + ], + "placeholders": { + "reason": {} + } + }, + "toastMessageMoveAllEmailSearchedToFolderSuccess": "You’ve moved all mails in search to {destinationPath}", + "@toastMessageMoveAllEmailSearchedToFolderSuccess": { + "type": "text", + "placeholders_order": [ + "destinationPath" + ], + "placeholders": { + "destinationPath": {} + } + }, + "toastMessageMoveAllEmailSearchedToFolderFailureWithReason": "All mails in search could not be moved to {destinationPath}. Due \"{reason}\"", + "@toastMessageMoveAllEmailSearchedToFolderFailureWithReason": { + "type": "text", + "placeholders_order": [ + "destinationPath", + "reason" + ], + "placeholders": { + "destinationPath": {}, + "reason": {} + } + }, + "mailsOnThisPageAreSelectedForSearch": "All mails on this page are selected.", + "@mailsOnThisPageAreSelectedForSearch": { + "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 068aea484f..649a89aeb2 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -4698,4 +4698,90 @@ class AppLocalizations { name: 'moveAllConversation', ); } + + String get selectAllMailsThatMatchThisSearch { + return Intl.message( + 'Select all mails that match this search', + name: 'selectAllMailsThatMatchThisSearch' + ); + } + + String get allMailsInThisSearchAreSelected { + return Intl.message( + 'All mails in this search are selected. ', + name: 'allMailsInThisSearchAreSelected' + ); + } + + String get messageConfirmationDialogWhenMakeToActionForSelectionAllEmailsInSearch { + return Intl.message( + 'This action will affect all mails in search. Are you sure you want to continue?', + name: 'messageConfirmationDialogWhenMakeToActionForSelectionAllEmailsInSearch' + ); + } + + String get toastMessageMarkAllSearchAsReadSuccess { + return Intl.message( + 'You’ve marked all messages in search as read', + name: 'toastMessageMarkAllSearchAsReadSuccess'); + } + + String toastMessageMarkAllSearchAsReadFailureWithReason(String reason) { + return Intl.message( + 'All message in search could not be marked as read. Due "$reason"', + name: 'toastMessageMarkAllSearchAsReadFailureWithReason', + args: [reason] + ); + } + + String get toastMessageMarkAllSearchAsUnreadSuccess { + return Intl.message( + 'You’ve marked all messages in search as unread', + name: 'toastMessageMarkAllSearchAsUnreadSuccess'); + } + + String toastMessageMarkAllSearchAsUnreadFailureWithReason(String reason) { + return Intl.message( + 'All message in search could not be marked as unread. Due "$reason"', + name: 'toastMessageMarkAllSearchAsUnreadFailureWithReason', + args: [reason] + ); + } + + String get toastMessageMarkAllSearchAsStarredSuccess { + return Intl.message( + 'You’ve marked all messages in search as starred', + name: 'toastMessageMarkAllSearchAsStarredSuccess'); + } + + String toastMessageMarkAllSearchAsStarredFailureWithReason(String reason) { + return Intl.message( + 'All message in search could not be marked as starred. Due "$reason"', + name: 'toastMessageMarkAllSearchAsStarredFailureWithReason', + args: [reason] + ); + } + + String toastMessageMoveAllEmailSearchedToFolderSuccess(String destinationPath) { + return Intl.message( + 'You’ve moved all mails in search to $destinationPath', + name: 'toastMessageMoveAllEmailSearchedToFolderSuccess', + args: [destinationPath] + ); + } + + String toastMessageMoveAllEmailSearchedToFolderFailureWithReason(String destinationPath, String reason) { + return Intl.message( + 'All mails in search could not be moved to $destinationPath. Due "$reason"', + name: 'toastMessageMoveAllEmailSearchedToFolderFailureWithReason', + args: [destinationPath, reason] + ); + } + + String get mailsOnThisPageAreSelectedForSearch { + return Intl.message( + 'All mails on this page are selected.', + name: 'mailsOnThisPageAreSelectedForSearch', + ); + } } \ No newline at end of file diff --git a/lib/main/utils/toast_manager.dart b/lib/main/utils/toast_manager.dart index d465fe3a40..5cd03afe17 100644 --- a/lib/main/utils/toast_manager.dart +++ b/lib/main/utils/toast_manager.dart @@ -25,6 +25,10 @@ import 'package:tmail_ui_user/features/thread/domain/state/empty_spam_folder_sta import 'package:tmail_ui_user/features/thread/domain/state/empty_trash_folder_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/mark_all_as_starred_selection_all_emails_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/mark_all_as_unread_selection_all_emails_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/mark_all_search_as_read_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/mark_all_search_as_starred_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/mark_all_search_as_unread_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/move_all_email_searched_to_folder_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/move_all_selection_all_emails_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/move_multiple_email_to_mailbox_state.dart'; import 'package:tmail_ui_user/main/exceptions/remote_exception.dart'; @@ -86,6 +90,10 @@ class ToastManager { MarkAllAsStarredSelectionAllEmailsHasSomeEmailFailure() => appLocalizations.toastMessageMarkAllAsStarredSelectionAllEmailsHasSomeEmailFailure(success.countStarred), SendEmailLoading() => appLocalizations.your_email_being_sent, DeleteEmailPermanentlySuccess() => appLocalizations.toast_message_delete_a_email_permanently_success, + MarkAllSearchAsReadSuccess() => appLocalizations.toastMessageMarkAllSearchAsReadSuccess, + MarkAllSearchAsUnreadSuccess() => appLocalizations.toastMessageMarkAllSearchAsUnreadSuccess, + MarkAllSearchAsStarredSuccess() => appLocalizations.toastMessageMarkAllSearchAsStarredSuccess, + MoveAllEmailSearchedToFolderSuccess() => appLocalizations.toastMessageMoveAllEmailSearchedToFolderSuccess(success.destinationPath), _ => null }; } @@ -111,6 +119,10 @@ class ToastManager { ParseEmailByBlobIdFailure() => appLocalizations.parseEmailByBlobIdFailed, PreviewEmailFromEmlFileFailure() => _getMessageForPreviewEmailFromEmlFileFailure(appLocalizations, failure.exception), RestoreDeletedMessageFailure() => appLocalizations.restoreDeletedMessageFailed, + MarkAllSearchAsReadFailure() => appLocalizations.toastMessageMarkAllSearchAsReadFailureWithReason(failure.exception.toString()), + MarkAllSearchAsUnreadFailure() => appLocalizations.toastMessageMarkAllSearchAsUnreadFailureWithReason(failure.exception.toString()), + MarkAllSearchAsStarredFailure() => appLocalizations.toastMessageMarkAllSearchAsStarredFailureWithReason(failure.exception.toString()), + MoveAllEmailSearchedToFolderFailure() => appLocalizations.toastMessageMoveAllEmailSearchedToFolderFailureWithReason(failure.destinationPath, failure.exception.toString()), _ => null }; } @@ -120,10 +132,10 @@ class ToastManager { required Success success, }) { return switch (success) { - MarkAsMailboxReadAllSuccess() || MarkAsMailboxReadHasSomeEmailFailure() => appToast.imagePaths.icReadToast, - MarkAllAsUnreadSelectionAllEmailsAllSuccess() || MarkAllAsUnreadSelectionAllEmailsHasSomeEmailFailure() => appToast.imagePaths.icUnreadToast, - MoveAllSelectionAllEmailsAllSuccess() || MoveAllSelectionAllEmailsHasSomeEmailFailure() => appToast.imagePaths.icFolderMailbox, - MarkAllAsStarredSelectionAllEmailsAllSuccess() || MarkAllAsStarredSelectionAllEmailsHasSomeEmailFailure() => appToast.imagePaths.icStar, + MarkAsMailboxReadAllSuccess() || MarkAsMailboxReadHasSomeEmailFailure() || MarkAllSearchAsReadSuccess() => appToast.imagePaths.icReadToast, + MarkAllAsUnreadSelectionAllEmailsAllSuccess() || MarkAllAsUnreadSelectionAllEmailsHasSomeEmailFailure() || MarkAllSearchAsUnreadSuccess() => appToast.imagePaths.icUnreadToast, + MoveAllSelectionAllEmailsAllSuccess() || MoveAllSelectionAllEmailsHasSomeEmailFailure() || MoveAllEmailSearchedToFolderSuccess() => appToast.imagePaths.icFolderMailbox, + MarkAllAsStarredSelectionAllEmailsAllSuccess() || MarkAllAsStarredSelectionAllEmailsHasSomeEmailFailure() || MarkAllSearchAsStarredSuccess() => appToast.imagePaths.icStar, SendEmailLoading() => appToast.imagePaths.icSendToast, DeleteEmailPermanentlySuccess() => appToast.imagePaths.icDeleteToast, _ => null diff --git a/model/lib/extensions/email_extension.dart b/model/lib/extensions/email_extension.dart index fefdee347c..06089dc1cc 100644 --- a/model/lib/extensions/email_extension.dart +++ b/model/lib/extensions/email_extension.dart @@ -1,8 +1,9 @@ import 'dart:convert'; -import 'package:core/domain/extensions/datetime_extension.dart'; +import 'package:core/core.dart'; import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/core/patch_object.dart'; import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; @@ -46,7 +47,7 @@ extension EmailExtension on Email { } bool hasReadReceipt(Map mapMailbox) { - final mailboxCurrent = findMailboxContain(mapMailbox); + final mailboxCurrent = findMailboxContainingEmail(mapMailbox); return !hasMdnSent && headers.readReceiptHasBeenRequested && mailboxCurrent?.isSent == false; @@ -174,7 +175,36 @@ extension EmailExtension on Email { List get allAttachments => attachments?.map((item) => item.toAttachment()).toList() ?? []; - PresentationMailbox? findMailboxContain(Map mapMailbox) { + List get attachmentsWithCid => allAttachments.where((attachment) => attachment.hasCid()).toList(); + + MailboxId? findMailboxIdContainingEmail() { + final mailboxIdsIsTrue = mailboxIds?.where((key, value) => value) ?? {}; + + if (mailboxIdsIsTrue.isNotEmpty == true) { + return mailboxIdsIsTrue.keys.first; + } + + return null; + } + + PatchObject generateMoveToMailboxActionPath( + MailboxId destinationMailboxId, + { + bool isDestinationSpamMailbox = false + } + ) { + final mailboxIdContainingEmail = findMailboxIdContainingEmail(); + + return PatchObject({ + if (mailboxIdContainingEmail != null) + mailboxIdContainingEmail.generatePath(): null, + destinationMailboxId.generatePath(): true, + if (isDestinationSpamMailbox) + KeyWordIdentifier.emailSeen.generatePath(): true + }); + } + + PresentationMailbox? findMailboxContainingEmail(Map mapMailbox) { final newMailboxIds = mailboxIds; newMailboxIds?.removeWhere((key, value) => !value); diff --git a/model/lib/extensions/list_email_extension.dart b/model/lib/extensions/list_email_extension.dart index a4a609e48c..1d368ea6d8 100644 --- a/model/lib/extensions/list_email_extension.dart +++ b/model/lib/extensions/list_email_extension.dart @@ -1,11 +1,15 @@ import 'package:collection/collection.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/core/patch_object.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/core/sort/comparator.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_comparator_property.dart'; import 'package:jmap_dart_client/jmap/core/extensions/utc_date_extension.dart'; import 'package:jmap_dart_client/jmap/core/extensions/string_extension.dart'; import 'package:jmap_dart_client/jmap/core/extensions/unsigned_int_extension.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:model/extensions/email_extension.dart'; extension ListEmailExtension on List { @@ -34,4 +38,21 @@ extension ListEmailExtension on List { } }); } + + Map generateMapUpdateObjectMoveToMailbox( + MailboxId destinationMailboxId, + { + bool isDestinationSpamMailbox = false + } + ) { + final listEmailWithIdNotNull = where((email) => email.id != null).toList(); + + return { + for (var email in listEmailWithIdNotNull) + email.id!.id: email.generateMoveToMailboxActionPath( + destinationMailboxId, + isDestinationSpamMailbox: isDestinationSpamMailbox + ) + }; + } } \ No newline at end of file diff --git a/model/lib/extensions/list_email_id_extension.dart b/model/lib/extensions/list_email_id_extension.dart index 26d72e4fe6..79cf23ba0f 100644 --- a/model/lib/extensions/list_email_id_extension.dart +++ b/model/lib/extensions/list_email_id_extension.dart @@ -16,10 +16,19 @@ extension ListEmailIdExtension on List { }; } - Map generateMapUpdateObjectMoveToMailbox(MailboxId currentMailboxId, MailboxId destinationMailboxId) { + Map generateMapUpdateObjectMoveToMailbox( + MailboxId currentMailboxId, + MailboxId destinationMailboxId, + { + bool isDestinationSpamMailbox = false + } + ) { return { for (var emailId in this) - emailId.id: currentMailboxId.generateMoveToMailboxActionPath(destinationMailboxId) + emailId.id: currentMailboxId.generateMoveToMailboxActionPath( + destinationMailboxId, + isDestinationSpamMailbox: isDestinationSpamMailbox + ) }; } @@ -30,13 +39,6 @@ extension ListEmailIdExtension on List { }; } - Map generateMapUpdateObjectMoveToSpam(MailboxId currentMailboxId, MailboxId spamMailboxId) { - return { - for (var emailId in this) - emailId.id: currentMailboxId.generateMoveToSpamActionPath(currentMailboxId, spamMailboxId) - }; - } - Map generateMapUpdateObjectMarkAsAnswered() { return { for (var emailId in this) diff --git a/model/lib/extensions/mailbox_id_extension.dart b/model/lib/extensions/mailbox_id_extension.dart index 5cfd5e7a31..1c058d8d40 100644 --- a/model/lib/extensions/mailbox_id_extension.dart +++ b/model/lib/extensions/mailbox_id_extension.dart @@ -13,24 +13,17 @@ extension MailboxIdExtension on MailboxId { } } - PatchObject generateMoveToMailboxActionPath(MailboxId destinationMailboxId) { - return PatchObject({ - generatePath(): null, - destinationMailboxId.generatePath(): true - }); - } - - PatchObject generateActionPath() { - return PatchObject({ - generatePath(): true, - }); - } - - PatchObject generateMoveToSpamActionPath(MailboxId currentMailboxId, MailboxId spamMailboxId) { + PatchObject generateMoveToMailboxActionPath( + MailboxId destinationMailboxId, + { + bool isDestinationSpamMailbox = false + } + ) { return PatchObject({ - currentMailboxId.generatePath(): null, - spamMailboxId.generatePath(): true, - KeyWordIdentifier.emailSeen.generatePath(): true + generatePath(): null, + destinationMailboxId.generatePath(): true, + if (isDestinationSpamMailbox) + KeyWordIdentifier.emailSeen.generatePath(): true }); } diff --git a/test/features/mailbox_dashboard/presentation/controller/advanced_filter_controller_test.dart b/test/features/mailbox_dashboard/presentation/controller/advanced_filter_controller_test.dart index efc8237948..2c78fd8766 100644 --- a/test/features/mailbox_dashboard/presentation/controller/advanced_filter_controller_test.dart +++ b/test/features/mailbox_dashboard/presentation/controller/advanced_filter_controller_test.dart @@ -3,7 +3,6 @@ import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/utils/app_toast.dart'; import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:core/utils/application_manager.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/core/id.dart'; @@ -17,6 +16,7 @@ import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_all_recent_search_latest_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/quick_search_email_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_recent_search_interactor.dart'; @@ -24,7 +24,6 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/advanced_search_filter.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart'; @@ -62,7 +61,6 @@ const fallbackGenerators = { // Advanced filter controller mock specs MockSpec(fallbackGenerators: fallbackGenerators), MockSpec(), - MockSpec(), // Search controller mock specs MockSpec(), MockSpec(), @@ -73,7 +71,6 @@ void main() { late AdvancedFilterController advancedFilterController; late MockMailboxDashBoardController mockMailboxDashBoardController; late MockGetAutoCompleteInteractor mockGetAutoCompleteInteractor; - late MockBuildContext mockBuildContext; // Declaration search controller late SearchController searchController; @@ -148,7 +145,6 @@ void main() { // Mock advanced filter controller mockMailboxDashBoardController = MockMailboxDashBoardController(); mockGetAutoCompleteInteractor = MockGetAutoCompleteInteractor(); - mockBuildContext = MockBuildContext(); Get.put(mockMailboxDashBoardController); Get.put(mockGetAutoCompleteInteractor); @@ -207,7 +203,7 @@ void main() { advancedFilterController.setMemorySearchFilter(memorySearchFilter); // Act - advancedFilterController.initSearchFilterField(mockBuildContext); + advancedFilterController.initSearchFilterField(); // Assert expect(advancedFilterController.subjectFilterInputController.text, equals('subject')); diff --git a/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart b/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart index 0940da8c5f..50fca58ad6 100644 --- a/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart +++ b/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart @@ -65,7 +65,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/advanced_search_filter.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart'; @@ -87,8 +87,12 @@ import 'package:tmail_ui_user/features/thread/domain/usecases/get_emails_in_mail import 'package:tmail_ui_user/features/thread/domain/usecases/load_more_emails_in_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_as_starred_selection_all_emails_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_as_unread_selection_all_emails_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_search_as_read_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_search_as_starred_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_search_as_unread_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_multiple_email_read_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_star_multiple_email_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/move_all_email_searched_to_folder_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/move_all_selection_all_emails_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/move_multiple_email_to_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor.dart'; @@ -186,6 +190,10 @@ const fallbackGenerators = { MockSpec(), MockSpec(), MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), ]) void main() { // mock mailbox dashboard controller direct dependencies @@ -275,6 +283,10 @@ void main() { final moveAllSelectionAllEmailsInteractor = MockMoveAllSelectionAllEmailsInteractor(); final deleteAllPermanentlyEmailsInteractor = MockDeleteAllPermanentlyEmailsInteractor(); final markAllAsStarredSelectionAllEmailsInteractor = MockMarkAllAsStarredSelectionAllEmailsInteractor(); + final markAllSearchAsReadInteractor = MockMarkAllSearchAsReadInteractor(); + final markAllSearchAsUnreadInteractor = MockMarkAllSearchAsUnreadInteractor(); + final markAllSearchAsStarredInteractor = MockMarkAllSearchAsStarredInteractor(); + final moveAllEmailSearchedToFolderInteractor = MockMoveAllEmailSearchedToFolderInteractor(); late MailboxController mailboxController; // mock thread controller direct dependencies @@ -330,6 +342,10 @@ void main() { Get.put(moveAllSelectionAllEmailsInteractor); Get.put(deleteAllPermanentlyEmailsInteractor); Get.put(markAllAsStarredSelectionAllEmailsInteractor); + Get.put(markAllSearchAsReadInteractor); + Get.put(markAllSearchAsUnreadInteractor); + Get.put(markAllSearchAsStarredInteractor); + Get.put(moveAllEmailSearchedToFolderInteractor); searchController = SearchController( quickSearchEmailInteractor, @@ -369,6 +385,10 @@ void main() { moveAllSelectionAllEmailsInteractor, deleteAllPermanentlyEmailsInteractor, markAllAsStarredSelectionAllEmailsInteractor, + markAllSearchAsReadInteractor, + markAllSearchAsUnreadInteractor, + markAllSearchAsStarredInteractor, + moveAllEmailSearchedToFolderInteractor, ); }); diff --git a/test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart b/test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart index a8444b8d73..20fd9df5b2 100644 --- a/test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart +++ b/test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart @@ -85,8 +85,12 @@ import 'package:tmail_ui_user/features/thread/domain/usecases/get_emails_in_mail import 'package:tmail_ui_user/features/thread/domain/usecases/load_more_emails_in_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_as_starred_selection_all_emails_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_as_unread_selection_all_emails_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_search_as_read_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_search_as_starred_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_search_as_unread_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_multiple_email_read_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_star_multiple_email_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/move_all_email_searched_to_folder_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/move_all_selection_all_emails_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/move_multiple_email_to_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor.dart'; @@ -188,6 +192,10 @@ const fallbackGenerators = { MockSpec(), MockSpec(), MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), ]) void main() { final moveToMailboxInteractor = MockMoveToMailboxInteractor(); @@ -262,6 +270,10 @@ void main() { final moveAllSelectionAllEmailsInteractor = MockMoveAllSelectionAllEmailsInteractor(); final deleteAllPermanentlyEmailsInteractor = MockDeleteAllPermanentlyEmailsInteractor(); final markAllAsStarredSelectionAllEmailsInteractor = MockMarkAllAsStarredSelectionAllEmailsInteractor(); + final markAllSearchAsReadInteractor = MockMarkAllSearchAsReadInteractor(); + final markAllSearchAsUnreadInteractor = MockMarkAllSearchAsUnreadInteractor(); + final markAllSearchAsStarredInteractor = MockMarkAllSearchAsStarredInteractor(); + final moveAllEmailSearchedToFolderInteractor = MockMoveAllEmailSearchedToFolderInteractor(); final getEmailsInMailboxInteractor = MockGetEmailsInMailboxInteractor(); final refreshChangesEmailsInMailboxInteractor = MockRefreshChangesEmailsInMailboxInteractor(); @@ -328,6 +340,10 @@ void main() { Get.put(moveAllSelectionAllEmailsInteractor); Get.put(deleteAllPermanentlyEmailsInteractor); Get.put(markAllAsStarredSelectionAllEmailsInteractor); + Get.put(markAllSearchAsReadInteractor); + Get.put(markAllSearchAsUnreadInteractor); + Get.put(markAllSearchAsStarredInteractor); + Get.put(moveAllEmailSearchedToFolderInteractor); when(emailReceiveManager.pendingSharedFileInfo).thenAnswer((_) => BehaviorSubject.seeded([])); @@ -368,6 +384,10 @@ void main() { moveAllSelectionAllEmailsInteractor, deleteAllPermanentlyEmailsInteractor, markAllAsStarredSelectionAllEmailsInteractor, + markAllSearchAsReadInteractor, + markAllSearchAsUnreadInteractor, + markAllSearchAsStarredInteractor, + moveAllEmailSearchedToFolderInteractor, ); Get.put(mailboxDashboardController); mailboxDashboardController.onReady(); diff --git a/test/features/search/search_email_filter_test.dart b/test/features/search/search_email_filter_test.dart index 06bfd10655..4caf0effd5 100644 --- a/test/features/search/search_email_filter_test.dart +++ b/test/features/search/search_email_filter_test.dart @@ -6,7 +6,7 @@ import 'package:jmap_dart_client/jmap/core/utc_date.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; diff --git a/test/features/search/verify_before_time_in_search_email_filter_test.dart b/test/features/search/verify_before_time_in_search_email_filter_test.dart index 475c7f95b1..49d9fe1cee 100644 --- a/test/features/search/verify_before_time_in_search_email_filter_test.dart +++ b/test/features/search/verify_before_time_in_search_email_filter_test.dart @@ -72,8 +72,12 @@ import 'package:tmail_ui_user/features/thread/domain/usecases/get_emails_in_mail import 'package:tmail_ui_user/features/thread/domain/usecases/load_more_emails_in_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_as_starred_selection_all_emails_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_as_unread_selection_all_emails_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_search_as_read_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_search_as_starred_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/mark_all_search_as_unread_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_multiple_email_read_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_star_multiple_email_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/move_all_email_searched_to_folder_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/move_all_selection_all_emails_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/move_multiple_email_to_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor.dart'; @@ -161,6 +165,10 @@ const fallbackGenerators = { MockSpec(), MockSpec(), MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), ]) void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -220,6 +228,10 @@ void main() { late MockMoveAllSelectionAllEmailsInteractor moveAllSelectionAllEmailsInteractor; late MockDeleteAllPermanentlyEmailsInteractor deleteAllPermanentlyEmailsInteractor; late MockMarkAllAsStarredSelectionAllEmailsInteractor markAllAsStarredSelectionAllEmailsInteractor; + late MockMarkAllSearchAsReadInteractor markAllSearchAsReadInteractor; + late MockMarkAllSearchAsUnreadInteractor markAllSearchAsUnreadInteractor; + late MockMarkAllSearchAsStarredInteractor markAllSearchAsStarredInteractor; + late MockMoveAllEmailSearchedToFolderInteractor moveAllEmailSearchedToFolderInteractor; // Declaration base controller late MockCachingManager mockCachingManager; @@ -316,6 +328,10 @@ void main() { moveAllSelectionAllEmailsInteractor = MockMoveAllSelectionAllEmailsInteractor(); deleteAllPermanentlyEmailsInteractor = MockDeleteAllPermanentlyEmailsInteractor(); markAllAsStarredSelectionAllEmailsInteractor = MockMarkAllAsStarredSelectionAllEmailsInteractor(); + markAllSearchAsReadInteractor = MockMarkAllSearchAsReadInteractor(); + markAllSearchAsUnreadInteractor = MockMarkAllSearchAsUnreadInteractor(); + markAllSearchAsStarredInteractor = MockMarkAllSearchAsStarredInteractor(); + moveAllEmailSearchedToFolderInteractor = MockMoveAllEmailSearchedToFolderInteractor(); searchController = SearchController( mockQuickSearchEmailInteractor, @@ -364,6 +380,10 @@ void main() { moveAllSelectionAllEmailsInteractor, deleteAllPermanentlyEmailsInteractor, markAllAsStarredSelectionAllEmailsInteractor, + markAllSearchAsReadInteractor, + markAllSearchAsUnreadInteractor, + markAllSearchAsStarredInteractor, + moveAllEmailSearchedToFolderInteractor, ); when(emailReceiveManager.pendingSharedFileInfo).thenAnswer((_) => BehaviorSubject.seeded([])); @@ -829,7 +849,7 @@ void main() { ), propertiesUpdated: ThreadConstants.propertiesUpdatedDefault, emailFilter: EmailFilter( - filter: threadController.getFilterCondition(mailboxIdSelected: threadController.selectedMailboxId), + filter: mailboxDashboardController.filterMessageOption.value.getFilterCondition(mailboxIdSelected: threadController.selectedMailboxId), filterOption: mailboxDashboardController.filterMessageOption.value, mailboxId: threadController.selectedMailboxId, ), @@ -997,7 +1017,7 @@ void main() { ), propertiesUpdated: ThreadConstants.propertiesUpdatedDefault, emailFilter: EmailFilter( - filter: threadController.getFilterCondition(mailboxIdSelected: threadController.selectedMailboxId), + filter: mailboxDashboardController.filterMessageOption.value.getFilterCondition(mailboxIdSelected: threadController.selectedMailboxId), filterOption: mailboxDashboardController.filterMessageOption.value, mailboxId: threadController.selectedMailboxId, ), @@ -1110,7 +1130,7 @@ void main() { ), propertiesUpdated: ThreadConstants.propertiesUpdatedDefault, emailFilter: EmailFilter( - filter: threadController.getFilterCondition(mailboxIdSelected: threadController.selectedMailboxId), + filter: mailboxDashboardController.filterMessageOption.value.getFilterCondition(mailboxIdSelected: threadController.selectedMailboxId), filterOption: mailboxDashboardController.filterMessageOption.value, mailboxId: threadController.selectedMailboxId, ),