From 322c449d490d744aee95a6fb09f14dcd79eb84b1 Mon Sep 17 00:00:00 2001 From: dab246 Date: Tue, 6 May 2025 16:44:56 +0700 Subject: [PATCH] TF-3707 Handle empty trash with clear mailbox method Signed-off-by: dab246 --- .../domain/state/clear_mailbox_state.dart | 6 ++-- .../usecases/clear_mailbox_interactor.dart | 2 +- .../presentation/mailbox_controller.dart | 6 ++++ .../bindings/mailbox_dashboard_bindings.dart | 3 ++ .../mailbox_dashboard_controller.dart | 31 ++++++++++++---- .../handle_clear_mailbox_extension.dart | 36 +++++++++++++++++++ .../thread/presentation/thread_view.dart | 4 ++- .../mailbox_dashboard_controller_test.dart | 5 +++ .../mailbox_dashboard_view_widget_test.dart | 5 +++ ...fore_time_in_search_email_filter_test.dart | 5 +++ 10 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 lib/features/mailbox_dashboard/presentation/extensions/handle_clear_mailbox_extension.dart diff --git a/lib/features/mailbox/domain/state/clear_mailbox_state.dart b/lib/features/mailbox/domain/state/clear_mailbox_state.dart index 91954c3c4..b68f6a93a 100644 --- a/lib/features/mailbox/domain/state/clear_mailbox_state.dart +++ b/lib/features/mailbox/domain/state/clear_mailbox_state.dart @@ -1,16 +1,18 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; class ClearingMailbox extends LoadingState {} class ClearMailboxSuccess extends UIState { final UnsignedInt totalDeletedMessages; + final MailboxId mailboxId; - ClearMailboxSuccess(this.totalDeletedMessages); + ClearMailboxSuccess(this.mailboxId, this.totalDeletedMessages); @override - List get props => [totalDeletedMessages]; + List get props => [mailboxId, totalDeletedMessages]; } class ClearMailboxFailure extends FeatureFailure { diff --git a/lib/features/mailbox/domain/usecases/clear_mailbox_interactor.dart b/lib/features/mailbox/domain/usecases/clear_mailbox_interactor.dart index 3a14e7042..7384baab0 100644 --- a/lib/features/mailbox/domain/usecases/clear_mailbox_interactor.dart +++ b/lib/features/mailbox/domain/usecases/clear_mailbox_interactor.dart @@ -24,7 +24,7 @@ class ClearMailboxInteractor { accountId, mailboxId, ); - yield Right(ClearMailboxSuccess(totalDeletedMessages)); + yield Right(ClearMailboxSuccess(mailboxId, totalDeletedMessages)); } catch (e) { yield Left(ClearMailboxFailure(e)); } diff --git a/lib/features/mailbox/presentation/mailbox_controller.dart b/lib/features/mailbox/presentation/mailbox_controller.dart index b0fb7ff0c..520c6af42 100644 --- a/lib/features/mailbox/presentation/mailbox_controller.dart +++ b/lib/features/mailbox/presentation/mailbox_controller.dart @@ -46,6 +46,7 @@ import 'package:tmail_ui_user/features/mailbox/domain/model/mailbox_right_reques import 'package:tmail_ui_user/features/mailbox/domain/model/subscribe_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/subscribe_multiple_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/subscribe_request.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/state/clear_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/create_default_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/create_new_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/delete_multiple_mailbox_state.dart'; @@ -397,6 +398,11 @@ class MailboxController extends BaseMailboxController destinationMailboxId: reactionState.destinationMailboxId, emailIdsWithReadStatus: reactionState.moveSucceededEmailIdsWithReadStatus, ); + } else if (reactionState is ClearMailboxSuccess) { + _handleDeleteEmailsFromMailbox( + affectedMailboxId: reactionState.mailboxId, + totalEmailsChanged: -reactionState.totalDeletedMessages.value.toInt(), + ); } }); } 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 1d93aedeb..8401484e9 100644 --- a/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart +++ b/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart @@ -55,6 +55,7 @@ import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_api.dart'; import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_isolate_worker.dart'; import 'package:tmail_ui_user/features/mailbox/data/repository/mailbox_repository_impl.dart'; import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/usecases/clear_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/mark_as_mailbox_read_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_bindings.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/app_grid_datasource.dart'; @@ -200,6 +201,7 @@ class MailboxDashBoardBindings extends BaseBindings { Get.find(), Get.find(), Get.find(), + Get.find(), )); Get.put(AdvancedFilterController()); } @@ -352,6 +354,7 @@ class MailboxDashBoardBindings extends BaseBindings { Get.find(), Get.find() )); + Get.lazyPut(() => ClearMailboxInteractor(Get.find())); IdentityInteractorsBindings().dependencies(); Get.lazyPut(() => GetAllIdentitiesInteractor( 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 d12ed60dc..c3cf184a0 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -81,8 +81,10 @@ import 'package:tmail_ui_user/features/login/domain/exceptions/logout_exception. import 'package:tmail_ui_user/features/login/presentation/model/login_navigate_arguments.dart'; import 'package:tmail_ui_user/features/mailbox/domain/exceptions/mailbox_exception.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/state/clear_mailbox_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/refresh_all_mailboxes_state.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/usecases/clear_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/mark_as_mailbox_read_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/action/mailbox_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart'; @@ -101,6 +103,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_clear_mailbox_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_preferences_setting_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/reopen_composer_cache_extension.dart'; @@ -166,6 +169,7 @@ import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_t import 'package:tmail_ui_user/main/deep_links/deep_link_data.dart'; import 'package:tmail_ui_user/main/deep_links/deep_links_manager.dart'; import 'package:tmail_ui_user/main/deep_links/open_app_deep_link_data.dart'; +import 'package:tmail_ui_user/main/error/capability_validator.dart'; import 'package:tmail_ui_user/main/exceptions/remote_exception.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/app_routes.dart'; @@ -218,6 +222,7 @@ class MailboxDashBoardController extends ReloadableController final RemoveComposerCacheByIdOnWebInteractor _removeComposerCacheByIdOnWebInteractor; final RemoveAllComposerCacheOnWebInteractor _removeAllComposerCacheOnWebInteractor; final GetAllIdentitiesInteractor _getAllIdentitiesInteractor; + final ClearMailboxInteractor clearMailboxInteractor; GetAllVacationInteractor? _getAllVacationInteractor; UpdateVacationInteractor? _updateVacationInteractor; @@ -310,6 +315,7 @@ class MailboxDashBoardController extends ReloadableController this._removeAllComposerCacheOnWebInteractor, this._removeComposerCacheByIdOnWebInteractor, this._getAllIdentitiesInteractor, + this.clearMailboxInteractor, ); @override @@ -448,6 +454,8 @@ class MailboxDashBoardController extends ReloadableController ); } else if (success is GetServerSettingSuccess) { isSenderImportantFlagEnabled.value = success.settingOption.isDisplaySenderPriority; + } else if (success is ClearMailboxSuccess) { + clearMailboxSuccess(success); } } @@ -1608,15 +1616,26 @@ class MailboxDashBoardController extends ReloadableController onCancelSelectionEmail?.call(); final trashMailboxId = trashFolderId ?? mapDefaultMailboxIdByRole[PresentationMailbox.roleTrash]; - final trashMailbox = mapMailboxById[trashMailboxId]; - final totalEmailsInTrash = totalEmails == 0 ? trashMailbox?.countTotalEmails : totalEmails; - if (sessionCurrent != null && accountId.value != null && trashMailboxId != null) { + final accountId = this.accountId.value; + if (sessionCurrent == null || + accountId == null || + trashMailboxId == null) { + return; + } + + if (CapabilityIdentifier.jmapMailboxClear.isSupported(sessionCurrent!, accountId)) { + clearMailbox(sessionCurrent!, accountId, trashMailboxId); + } else { + final totalEmailsInTrash = totalEmails == 0 + ? mapMailboxById[trashMailboxId]?.countTotalEmails ?? 0 + : totalEmails; + consumeState(_emptyTrashFolderInteractor.execute( sessionCurrent!, - accountId.value!, + accountId, trashMailboxId, - totalEmailsInTrash ?? 0, - _progressStateController + totalEmailsInTrash, + _progressStateController, )); } } diff --git a/lib/features/mailbox_dashboard/presentation/extensions/handle_clear_mailbox_extension.dart b/lib/features/mailbox_dashboard/presentation/extensions/handle_clear_mailbox_extension.dart new file mode 100644 index 000000000..a288e0f67 --- /dev/null +++ b/lib/features/mailbox_dashboard/presentation/extensions/handle_clear_mailbox_extension.dart @@ -0,0 +1,36 @@ + +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/mailbox/mailbox.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/state/clear_mailbox_state.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'; + +extension HandleClearMailboxExtension on MailboxDashBoardController { + + void clearMailbox(Session session, AccountId accountId, MailboxId mailboxId) { + consumeState(clearMailboxInteractor.execute( + session, + accountId, + mailboxId, + )); + } + + void clearMailboxSuccess(ClearMailboxSuccess success) { + viewStateMailboxActionProgress.value = Right(UIState.idle); + + if (currentOverlayContext != null && currentContext != null) { + appToast.showToastSuccessMessage( + currentOverlayContext!, + AppLocalizations.of(currentContext!).toast_message_empty_trash_folder_success, + ); + } + + if (selectedMailbox.value?.id == success.mailboxId) { + emailsInCurrentMailbox.clear(); + } + } +} \ 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 9ba424bef..7a9797019 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -11,6 +11,7 @@ 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/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/clear_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mixin/filter_email_popup_menu_mixin.dart'; @@ -969,7 +970,8 @@ class _MailboxActionProgressBanner extends StatelessWidget with AppLoaderMixin { (success) { if (success is MarkAsMailboxReadLoading || success is EmptySpamFolderLoading || - success is EmptyTrashFolderLoading) { + success is EmptyTrashFolderLoading || + success is ClearingMailbox) { return Padding( padding: EdgeInsets.only( top: responsiveUtils.isDesktop(context) ? 16 : 0, 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 4a5d5d9f9..01f4d21ba 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 @@ -39,6 +39,7 @@ import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_a import 'package:tmail_ui_user/features/login/domain/usecases/update_account_cache_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/exceptions/empty_folder_name_exception.dart'; import 'package:tmail_ui_user/features/mailbox/domain/exceptions/invalid_mail_format_exception.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/usecases/clear_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/create_new_default_mailbox_interactor.dart'; 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'; @@ -184,6 +185,7 @@ const fallbackGenerators = { MockSpec(), MockSpec(fallbackGenerators: fallbackGenerators), MockSpec(), + MockSpec(), ]) void main() { // mock mailbox dashboard controller direct dependencies @@ -271,6 +273,7 @@ void main() { final removeAllComposerCacheOnWebInteractor = MockRemoveAllComposerCacheOnWebInteractor(); final removeComposerCacheByIdOnWebInteractor = MockRemoveComposerCacheByIdOnWebInteractor(); final getAllIdentitiesInteractor = MockGetAllIdentitiesInteractor(); + final clearMailboxInteractor = MockClearMailboxInteractor(); final composerManager = MockComposerManager(); late MailboxController mailboxController; @@ -322,6 +325,7 @@ void main() { Get.put(getAuthenticatedAccountInteractor); Get.put(updateAccountCacheInteractor); Get.put(getAllIdentitiesInteractor); + Get.put(clearMailboxInteractor); Get.put(removeAllComposerCacheOnWebInteractor); Get.put(removeComposerCacheByIdOnWebInteractor); Get.put(composerManager); @@ -361,6 +365,7 @@ void main() { removeAllComposerCacheOnWebInteractor, removeComposerCacheByIdOnWebInteractor, getAllIdentitiesInteractor, + clearMailboxInteractor, ); }); 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 24ae61201..79a0d48e8 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 @@ -37,6 +37,7 @@ import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oi import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/update_account_cache_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/usecases/clear_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/create_new_default_mailbox_interactor.dart'; 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'; @@ -186,6 +187,7 @@ const fallbackGenerators = { MockSpec(), MockSpec(fallbackGenerators: fallbackGenerators), MockSpec(), + MockSpec(), ]) void main() { final moveToMailboxInteractor = MockMoveToMailboxInteractor(); @@ -258,6 +260,7 @@ void main() { final removeAllComposerCacheOnWebInteractor = MockRemoveAllComposerCacheOnWebInteractor(); final removeComposerCacheByIdOnWebInteractor = MockRemoveComposerCacheByIdOnWebInteractor(); final getAllIdentitiesInteractor = MockGetAllIdentitiesInteractor(); + final clearMailboxInteractor = MockClearMailboxInteractor(); final composerManager = MockComposerManager(); final getEmailsInMailboxInteractor = MockGetEmailsInMailboxInteractor(); @@ -320,6 +323,7 @@ void main() { Get.put(getAuthenticatedAccountInteractor); Get.put(updateAccountCacheInteractor); Get.put(getAllIdentitiesInteractor); + Get.put(clearMailboxInteractor); Get.put(removeAllComposerCacheOnWebInteractor); Get.put(removeComposerCacheByIdOnWebInteractor); Get.put(composerManager); @@ -360,6 +364,7 @@ void main() { removeAllComposerCacheOnWebInteractor, removeComposerCacheByIdOnWebInteractor, getAllIdentitiesInteractor, + clearMailboxInteractor, ); Get.put(mailboxDashboardController); mailboxDashboardController.onReady(); 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 b95324c77..d9d786437 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 @@ -38,6 +38,7 @@ import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oi import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/get_authenticated_account_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/update_account_cache_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/usecases/clear_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/mark_as_mailbox_read_interactor.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/get_composer_cache_on_web_interactor.dart'; @@ -159,6 +160,7 @@ const fallbackGenerators = { MockSpec(), MockSpec(fallbackGenerators: fallbackGenerators), MockSpec(), + MockSpec(), ]) void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -217,6 +219,7 @@ void main() { late MockRemoveAllComposerCacheOnWebInteractor removeAllComposerCacheOnWebInteractor; late MockRemoveComposerCacheByIdOnWebInteractor removeComposerCacheByIdOnWebInteractor; late MockGetAllIdentitiesInteractor getAllIdentitiesInteractor; + late MockClearMailboxInteractor clearMailboxInteractor; // Declaration base controller late MockCachingManager mockCachingManager; @@ -311,6 +314,7 @@ void main() { removeAllComposerCacheOnWebInteractor = MockRemoveAllComposerCacheOnWebInteractor(); removeComposerCacheByIdOnWebInteractor = MockRemoveComposerCacheByIdOnWebInteractor(); getAllIdentitiesInteractor = MockGetAllIdentitiesInteractor(); + clearMailboxInteractor = MockClearMailboxInteractor(); searchController = SearchController( mockQuickSearchEmailInteractor, @@ -357,6 +361,7 @@ void main() { removeAllComposerCacheOnWebInteractor, removeComposerCacheByIdOnWebInteractor, getAllIdentitiesInteractor, + clearMailboxInteractor, ); when(emailReceiveManager.pendingSharedFileInfo).thenAnswer((_) => BehaviorSubject.seeded([]));