From cb3fa99d6828cc50fc0fc1644f5709287d8e11a7 Mon Sep 17 00:00:00 2001 From: dab246 Date: Wed, 29 Oct 2025 15:19:16 +0700 Subject: [PATCH] TF-4053 Implement move folder content on mailbox search view --- .../mixin/mailbox_action_handler_mixin.dart | 125 ++++++++++++++++++ .../handle_move_folder_content_extension.dart | 108 --------------- .../presentation/mailbox_controller.dart | 22 ++- .../mark_mailbox_as_read_loading_banner.dart | 4 +- .../presentation/search_mailbox_bindings.dart | 2 + .../search_mailbox_controller.dart | 29 ++++ 6 files changed, 177 insertions(+), 113 deletions(-) delete mode 100644 lib/features/mailbox/presentation/extensions/handle_move_folder_content_extension.dart diff --git a/lib/features/base/mixin/mailbox_action_handler_mixin.dart b/lib/features/base/mixin/mailbox_action_handler_mixin.dart index ea394c01c..523b2367d 100644 --- a/lib/features/base/mixin/mailbox_action_handler_mixin.dart +++ b/lib/features/base/mixin/mailbox_action_handler_mixin.dart @@ -1,17 +1,26 @@ +import 'package:core/presentation/state/success.dart'; import 'package:core/presentation/utils/app_toast.dart'; import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:core/presentation/views/bottom_popup/confirmation_dialog_action_sheet_builder.dart'; +import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:model/extensions/presentation_mailbox_extension.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; +import 'package:tmail_ui_user/features/base/base_mailbox_controller.dart'; import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_manager.dart'; +import 'package:tmail_ui_user/features/email/domain/model/move_action.dart'; +import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/move_folder_content_request.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/state/move_folder_content_state.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/utils/mailbox_action_reactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; import 'package:tmail_ui_user/main/utils/app_utils.dart'; +import 'package:tmail_ui_user/main/utils/toast_manager.dart'; mixin MailboxActionHandlerMixin { @@ -149,4 +158,120 @@ mixin MailboxActionHandlerMixin { ); } } + + void performMoveFolderContent({ + required BuildContext context, + required PresentationMailbox mailboxSelected, + required MailboxDashBoardController dashboardController, + required BaseMailboxController baseMailboxController, + required MailboxActionReactor mailboxActionReactor, + }) { + final accountId = dashboardController.accountId.value; + final session = dashboardController.sessionCurrent; + + if (accountId == null || session == null) { + baseMailboxController.consumeState( + Stream.value( + Left(MoveFolderContentFailure(NotFoundAccountIdException())), + ), + ); + return; + } + + baseMailboxController.moveFolderContentAction( + context: context, + accountId: accountId, + session: session, + mailboxSelected: mailboxSelected, + onMoveFolderContentAction: (currentMailbox, destinationMailbox) { + baseMailboxController.consumeState( + mailboxActionReactor.moveFolderContent( + session: session, + accountId: accountId, + moveRequest: MoveFolderContentRequest( + moveAction: MoveAction.moving, + mailboxId: currentMailbox.id, + destinationMailboxId: destinationMailbox.id, + destinationMailboxDisplayName: + destinationMailbox.getDisplayName(context), + markAsRead: destinationMailbox.isSpam, + totalEmails: currentMailbox.countTotalEmails, + ), + onProgressController: dashboardController.progressStateController, + ), + ); + }, + ); + } + + void handleMoveFolderContentSuccess({ + required MoveFolderContentSuccess success, + required MailboxDashBoardController dashboardController, + required BaseMailboxController baseMailboxController, + required MailboxActionReactor mailboxActionReactor, + }) { + dashboardController.syncViewStateMailboxActionProgress( + newState: Right(UIState.idle), + ); + final moveFolderRequest = success.request; + + if (moveFolderRequest.moveAction == MoveAction.moving) { + baseMailboxController.toastManager.showMessageSuccessWithAction( + success: success, + onActionCallback: () { + _undoMoveFolderContentAction( + dashboardController: dashboardController, + baseMailboxController: baseMailboxController, + mailboxActionReactor: mailboxActionReactor, + newMoveRequest: MoveFolderContentRequest( + moveAction: MoveAction.undo, + mailboxId: moveFolderRequest.destinationMailboxId, + destinationMailboxId: moveFolderRequest.mailboxId, + destinationMailboxDisplayName: '', + totalEmails: moveFolderRequest.totalEmails, + ), + ); + }, + ); + } + } + + void _undoMoveFolderContentAction({ + required MoveFolderContentRequest newMoveRequest, + required MailboxDashBoardController dashboardController, + required BaseMailboxController baseMailboxController, + required MailboxActionReactor mailboxActionReactor, + }) { + final accountId = dashboardController.accountId.value; + final session = dashboardController.sessionCurrent; + + if (accountId == null || session == null) { + baseMailboxController.consumeState( + Stream.value( + Left(MoveFolderContentFailure(NotFoundAccountIdException())), + ), + ); + return; + } + + baseMailboxController.consumeState( + mailboxActionReactor.moveFolderContent( + session: session, + accountId: accountId, + moveRequest: newMoveRequest, + onProgressController: dashboardController.progressStateController, + ), + ); + } + + void handleMoveFolderContentFailure({ + required MoveFolderContentFailure failure, + required MailboxDashBoardController dashboardController, + required ToastManager toastManager, + }) { + dashboardController.syncViewStateMailboxActionProgress( + newState: Right(UIState.idle), + ); + toastManager.showMessageFailure(failure); + } } \ No newline at end of file diff --git a/lib/features/mailbox/presentation/extensions/handle_move_folder_content_extension.dart b/lib/features/mailbox/presentation/extensions/handle_move_folder_content_extension.dart deleted file mode 100644 index f6bb1fffe..000000000 --- a/lib/features/mailbox/presentation/extensions/handle_move_folder_content_extension.dart +++ /dev/null @@ -1,108 +0,0 @@ -import 'package:core/presentation/state/success.dart'; -import 'package:dartz/dartz.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:model/extensions/presentation_mailbox_extension.dart'; -import 'package:model/mailbox/presentation_mailbox.dart'; -import 'package:tmail_ui_user/features/email/domain/model/move_action.dart'; -import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart'; -import 'package:tmail_ui_user/features/mailbox/domain/model/move_folder_content_request.dart'; -import 'package:tmail_ui_user/features/mailbox/domain/state/move_folder_content_state.dart'; -import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart'; -import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_controller.dart'; - -extension HandleMoveFolderContentExtension on MailboxController { - void performMoveFolderContent({ - required BuildContext context, - required PresentationMailbox mailboxSelected, - }) { - final accountId = mailboxDashBoardController.accountId.value; - final session = mailboxDashBoardController.sessionCurrent; - - if (accountId == null || session == null) { - consumeState( - Stream.value( - Left(MoveFolderContentFailure(NotFoundAccountIdException())), - ), - ); - return; - } - - moveFolderContentAction( - context: context, - accountId: accountId, - session: session, - mailboxSelected: mailboxSelected, - onMoveFolderContentAction: (currentMailbox, destinationMailbox) { - consumeState(mailboxActionReactor.moveFolderContent( - session: session, - accountId: accountId, - moveRequest: MoveFolderContentRequest( - moveAction: MoveAction.moving, - mailboxId: currentMailbox.id, - destinationMailboxId: destinationMailbox.id, - destinationMailboxDisplayName: - destinationMailbox.getDisplayName(context), - markAsRead: destinationMailbox.isSpam, - totalEmails: currentMailbox.countTotalEmails, - ), - onProgressController: - mailboxDashBoardController.progressStateController, - )); - }, - ); - } - - void handleMoveFolderContentSuccess(MoveFolderContentSuccess success) { - mailboxDashBoardController.syncViewStateMailboxActionProgress( - newState: Right(UIState.idle), - ); - final moveFolderRequest = success.request; - - if (moveFolderRequest.moveAction == MoveAction.moving) { - toastManager.showMessageSuccessWithAction( - success: success, - onActionCallback: () { - _undoMoveFolderContentAction( - newMoveRequest: MoveFolderContentRequest( - moveAction: MoveAction.undo, - mailboxId: moveFolderRequest.destinationMailboxId, - destinationMailboxId: moveFolderRequest.mailboxId, - destinationMailboxDisplayName: '', - totalEmails: moveFolderRequest.totalEmails, - ), - ); - }, - ); - } - } - - void _undoMoveFolderContentAction({ - required MoveFolderContentRequest newMoveRequest, - }) { - final accountId = mailboxDashBoardController.accountId.value; - final session = mailboxDashBoardController.sessionCurrent; - - if (accountId == null || session == null) { - consumeState( - Stream.value( - Left(MoveFolderContentFailure(NotFoundAccountIdException())), - ), - ); - return; - } - - consumeState(mailboxActionReactor.moveFolderContent( - session: session, - accountId: accountId, - moveRequest: newMoveRequest, - onProgressController: mailboxDashBoardController.progressStateController, - )); - } - - void handleMoveFolderContentFailure(MoveFolderContentFailure failure) { - mailboxDashBoardController.syncViewStateMailboxActionProgress( - newState: Right(UIState.idle), - ); - toastManager.showMessageFailure(failure); - } -} diff --git a/lib/features/mailbox/presentation/mailbox_controller.dart b/lib/features/mailbox/presentation/mailbox_controller.dart index e32019be0..363499f67 100644 --- a/lib/features/mailbox/presentation/mailbox_controller.dart +++ b/lib/features/mailbox/presentation/mailbox_controller.dart @@ -69,7 +69,6 @@ import 'package:tmail_ui_user/features/mailbox/domain/usecases/subaddressing_int import 'package:tmail_ui_user/features/mailbox/domain/usecases/subscribe_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/subscribe_multiple_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/action/mailbox_ui_action.dart'; -import 'package:tmail_ui_user/features/mailbox/presentation/extensions/handle_move_folder_content_extension.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/mixin/mailbox_widget_mixin.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; @@ -219,7 +218,12 @@ class MailboxController extends BaseMailboxController } else if (success is CreateDefaultMailboxAllSuccess) { _handleCreateDefaultFolderIfMissingSuccess(success); } else if (success is MoveFolderContentSuccess) { - handleMoveFolderContentSuccess(success); + handleMoveFolderContentSuccess( + success: success, + mailboxActionReactor: mailboxActionReactor, + dashboardController: mailboxDashBoardController, + baseMailboxController: this, + ); } else { super.handleSuccessViewState(success); } @@ -236,7 +240,11 @@ class MailboxController extends BaseMailboxController } else if (failure is SubaddressingFailure) { handleSubAddressingFailure(failure); } else if (failure is MoveFolderContentFailure) { - handleMoveFolderContentFailure(failure); + handleMoveFolderContentFailure( + failure: failure, + dashboardController: mailboxDashBoardController, + toastManager: toastManager, + ); } else { super.handleFailureViewState(failure); } @@ -1194,7 +1202,13 @@ class MailboxController extends BaseMailboxController mailboxDashBoardController.gotoEmailRecovery(); break; case MailboxActions.moveFolderContent: - performMoveFolderContent(context: context, mailboxSelected: mailbox); + performMoveFolderContent( + context: context, + mailboxSelected: mailbox, + mailboxActionReactor: mailboxActionReactor, + dashboardController: mailboxDashBoardController, + baseMailboxController: this, + ); mailboxDashBoardController.closeMailboxMenuDrawer(); break; default: diff --git a/lib/features/mailbox_dashboard/presentation/widgets/mark_mailbox_as_read_loading_banner.dart b/lib/features/mailbox_dashboard/presentation/widgets/mark_mailbox_as_read_loading_banner.dart index 90f4a4581..2f4f9f8ca 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/mark_mailbox_as_read_loading_banner.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/mark_mailbox_as_read_loading_banner.dart @@ -22,7 +22,9 @@ class MarkMailboxAsReadLoadingBanner extends StatelessWidget with AppLoaderMixin return viewState.fold( (failure) => const SizedBox.shrink(), (success) { - if (success is MarkAsMailboxReadLoading || success is ClearingMailbox) { + if (success is MarkAsMailboxReadLoading || + success is ClearingMailbox || + success is MovingFolderContent) { return Padding( padding: MarkMailboxAsReadLoadingBannerStyle.bannerMargin, child: horizontalLoadingWidget); diff --git a/lib/features/search/mailbox/presentation/search_mailbox_bindings.dart b/lib/features/search/mailbox/presentation/search_mailbox_bindings.dart index df17880d8..62e617045 100644 --- a/lib/features/search/mailbox/presentation/search_mailbox_bindings.dart +++ b/lib/features/search/mailbox/presentation/search_mailbox_bindings.dart @@ -16,6 +16,7 @@ import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_reposit import 'package:tmail_ui_user/features/mailbox/domain/usecases/create_new_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/delete_multiple_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/get_all_mailbox_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/usecases/move_folder_content_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/move_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/refresh_all_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/rename_mailbox_interactor.dart'; @@ -53,6 +54,7 @@ class SearchMailboxBindings extends BaseBindings { Get.find(), Get.find(), Get.find(), + Get.find(), Get.find(), Get.find(), Get.find(), diff --git a/lib/features/search/mailbox/presentation/search_mailbox_controller.dart b/lib/features/search/mailbox/presentation/search_mailbox_controller.dart index 148f71773..271192063 100644 --- a/lib/features/search/mailbox/presentation/search_mailbox_controller.dart +++ b/lib/features/search/mailbox/presentation/search_mailbox_controller.dart @@ -38,6 +38,7 @@ import 'package:tmail_ui_user/features/mailbox/domain/state/create_new_mailbox_s import 'package:tmail_ui_user/features/mailbox/domain/state/delete_multiple_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/get_all_mailboxes_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read_state.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/state/move_folder_content_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/move_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/refresh_changes_all_mailboxes_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/rename_mailbox_state.dart'; @@ -48,6 +49,7 @@ import 'package:tmail_ui_user/features/mailbox/domain/state/subscribe_multiple_m import 'package:tmail_ui_user/features/mailbox/domain/usecases/create_new_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/delete_multiple_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/get_all_mailbox_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/usecases/move_folder_content_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/move_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/refresh_all_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/rename_mailbox_interactor.dart'; @@ -59,6 +61,7 @@ import 'package:tmail_ui_user/features/mailbox/presentation/action/mailbox_ui_ac 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/presentation/model/mailbox_tree_builder.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/utils/mailbox_action_reactor.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/utils/mailbox_utils.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/mailbox_creator_arguments.dart'; @@ -84,6 +87,7 @@ class SearchMailboxController extends BaseMailboxController with MailboxActionHa final SubscribeMultipleMailboxInteractor _subscribeMultipleMailboxInteractor; final CreateNewMailboxInteractor _createNewMailboxInteractor; final SubaddressingInteractor _subAddressingInteractor; + final MoveFolderContentInteractor _moveFolderContentInteractor; final dashboardController = Get.find(); @@ -92,6 +96,7 @@ class SearchMailboxController extends BaseMailboxController with MailboxActionHa final textInputSearchController = TextEditingController(); late Debouncer _deBouncerTime; FocusNode? searchFocusNode; + late MailboxActionReactor mailboxActionReactor; PresentationMailbox? get selectedMailbox => dashboardController.selectedMailbox.value; @@ -110,6 +115,7 @@ class SearchMailboxController extends BaseMailboxController with MailboxActionHa this._subscribeMultipleMailboxInteractor, this._createNewMailboxInteractor, this._subAddressingInteractor, + this._moveFolderContentInteractor, TreeBuilder treeBuilder, VerifyNameInteractor verifyNameInteractor, GetAllMailboxInteractor getAllMailboxInteractor, @@ -124,6 +130,7 @@ class SearchMailboxController extends BaseMailboxController with MailboxActionHa @override void onInit() { super.onInit(); + mailboxActionReactor = MailboxActionReactor(_moveFolderContentInteractor); _initializeDebounceTimeTextSearchChange(); _registerObxStreamListener(); if (PlatformInfo.isWeb) { @@ -142,6 +149,12 @@ class SearchMailboxController extends BaseMailboxController with MailboxActionHa _renameMailboxFailure(failure); } else if (failure is SubaddressingFailure) { handleSubAddressingFailure(failure); + } else if (failure is MoveFolderContentFailure) { + handleMoveFolderContentFailure( + failure: failure, + dashboardController: dashboardController, + toastManager: toastManager, + ); } else { super.handleFailureViewState(failure); } @@ -182,6 +195,13 @@ class SearchMailboxController extends BaseMailboxController with MailboxActionHa _createNewMailboxSuccess(success); } else if (success is SubaddressingSuccess) { handleSubAddressingSuccess(success); + } else if (success is MoveFolderContentSuccess) { + handleMoveFolderContentSuccess( + success: success, + mailboxActionReactor: mailboxActionReactor, + dashboardController: dashboardController, + baseMailboxController: this, + ); } else { super.handleSuccessViewState(success); } @@ -416,6 +436,15 @@ class SearchMailboxController extends BaseMailboxController with MailboxActionHa case MailboxActions.disallowSubaddressing: _handleSubAddressingAction(mailbox.id, mailbox.rights, actions); break; + case MailboxActions.moveFolderContent: + performMoveFolderContent( + context: context, + mailboxSelected: mailbox, + mailboxActionReactor: mailboxActionReactor, + dashboardController: dashboardController, + baseMailboxController: this, + ); + break; default: break; }