From 9760b6f0773e7dd665e94ae6ad8beec8db5e23e8 Mon Sep 17 00:00:00 2001 From: dab246 Date: Mon, 12 Jan 2026 15:58:53 +0700 Subject: [PATCH] TF-4243 Implement delete a label in presentation layer --- .../domain/state/delete_a_label_state.dart | 7 +- .../usecases/delete_a_label_interactor.dart | 5 +- .../handle_label_action_type_extension.dart | 70 +++++++++++++++++++ .../labels/presentation/label_controller.dart | 11 +++ .../label_interactor_bindings.dart | 2 + .../presentation/base_mailbox_view.dart | 1 + .../handle_label_action_type_extension.dart | 19 ++++- .../check_label_available_extension.dart | 0 lib/l10n/intl_messages.arb | 16 +++++ lib/main/localizations/app_localizations.dart | 15 ++++ lib/main/utils/toast_manager.dart | 7 ++ 11 files changed, 144 insertions(+), 9 deletions(-) delete mode 100644 lib/features/mailbox_dashboard/presentation/extensions/labels/check_label_available_extension.dart diff --git a/lib/features/labels/domain/state/delete_a_label_state.dart b/lib/features/labels/domain/state/delete_a_label_state.dart index deb3c2956..70dfac441 100644 --- a/lib/features/labels/domain/state/delete_a_label_state.dart +++ b/lib/features/labels/domain/state/delete_a_label_state.dart @@ -1,15 +1,16 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; +import 'package:labels/model/label.dart'; class DeletingALabel extends LoadingState {} class DeleteALabelSuccess extends UIState { - final String labelDisplayName; + final Label deletedLabel; - DeleteALabelSuccess(this.labelDisplayName); + DeleteALabelSuccess(this.deletedLabel); @override - List get props => [labelDisplayName]; + List get props => [deletedLabel]; } class DeleteALabelFailure extends FeatureFailure { diff --git a/lib/features/labels/domain/usecases/delete_a_label_interactor.dart b/lib/features/labels/domain/usecases/delete_a_label_interactor.dart index f90819107..1224eafb1 100644 --- a/lib/features/labels/domain/usecases/delete_a_label_interactor.dart +++ b/lib/features/labels/domain/usecases/delete_a_label_interactor.dart @@ -2,7 +2,6 @@ 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:labels/extensions/label_extension.dart'; import 'package:labels/model/label.dart'; import 'package:tmail_ui_user/features/labels/domain/repository/label_repository.dart'; import 'package:tmail_ui_user/features/labels/domain/state/delete_a_label_state.dart'; @@ -18,8 +17,8 @@ class DeleteALabelInteractor { ) async* { try { yield Right(DeletingALabel()); - await _labelRepository.createNewLabel(accountId, label); - yield Right(DeleteALabelSuccess(label.safeDisplayName)); + await _labelRepository.deleteLabel(accountId, label); + yield Right(DeleteALabelSuccess(label)); } catch (e) { yield Left(DeleteALabelFailure(e)); } diff --git a/lib/features/labels/presentation/extensions/handle_label_action_type_extension.dart b/lib/features/labels/presentation/extensions/handle_label_action_type_extension.dart index 21f51510c..2ff5634eb 100644 --- a/lib/features/labels/presentation/extensions/handle_label_action_type_extension.dart +++ b/lib/features/labels/presentation/extensions/handle_label_action_type_extension.dart @@ -1,21 +1,28 @@ import 'package:core/utils/app_logger.dart'; import 'package:dartz/dartz.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:labels/extensions/label_extension.dart'; import 'package:labels/extensions/list_label_extension.dart'; import 'package:labels/model/label.dart'; +import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_manager.dart'; import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart'; import 'package:tmail_ui_user/features/labels/domain/exceptions/label_exceptions.dart'; import 'package:tmail_ui_user/features/labels/domain/model/edit_label_request.dart'; +import 'package:tmail_ui_user/features/labels/domain/state/delete_a_label_state.dart'; import 'package:tmail_ui_user/features/labels/domain/state/edit_label_state.dart'; import 'package:tmail_ui_user/features/labels/presentation/label_controller.dart'; import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart'; import 'package:tmail_ui_user/features/labels/presentation/widgets/create_new_label_modal.dart'; import 'package:tmail_ui_user/main/exceptions/logic_exception.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/dialog_router.dart'; +import 'package:tmail_ui_user/main/routes/route_navigation.dart'; extension HandleLabelActionTypeExtension on LabelController { void handleLabelActionType({ + required BuildContext context, required LabelActionType actionType, required AccountId? accountId, Label? label, @@ -29,6 +36,12 @@ extension HandleLabelActionTypeExtension on LabelController { openEditLabelModal(accountId: accountId, label: label); break; case LabelActionType.delete: + if (label == null) return; + _openDeleteLabelModal( + context: context, + accountId: accountId, + label: label, + ); break; } } @@ -109,4 +122,61 @@ extension HandleLabelActionTypeExtension on LabelController { labels.add(newLabel); labels.sortByAlphabetically(); } + + Future _openDeleteLabelModal({ + required BuildContext context, + required AccountId? accountId, + required Label label, + }) async { + final appLocalizations = AppLocalizations.of(context); + + MessageDialogActionManager().showConfirmDialogAction( + context, + appLocalizations.areYouSureYouWantToDeleteTheLabel(label.safeDisplayName), + appLocalizations.delete, + key: const Key('confirm_dialog_delete_label'), + title: appLocalizations.deleteLabel, + cancelTitle: appLocalizations.cancel, + onConfirmAction: () => _deleteLabel(accountId: accountId, label: label), + onCloseButtonAction: popBack, + alignCenter: true, + dialogMargin: MediaQuery.paddingOf(context).add( + const EdgeInsets.only(bottom: 12), + ), + ); + } + + void _deleteLabel({ + required AccountId? accountId, + required Label label, + }) { + log('LabelController::deleteLabel:label: $label'); + if (accountId == null) { + emitFailure( + controller: this, + failure: DeleteALabelFailure(NotFoundAccountIdException()), + ); + } else if (deleteALabelInteractor == null) { + emitFailure( + controller: this, + failure: DeleteALabelFailure(InteractorNotInitialized()), + ); + } else { + consumeState(deleteALabelInteractor!.execute(accountId, label)); + } + } + + void handleDeleteLabelSuccess(DeleteALabelSuccess success) { + toastManager.showMessageSuccess(success); + syncListLabelsAfterDelete(success.deletedLabel); + } + + void handleDeleteLabelFailure(DeleteALabelFailure failure) { + toastManager.showMessageFailure(failure); + } + + void syncListLabelsAfterDelete(Label deletedLabel) { + labels.removeWhere((label) => label.id == deletedLabel.id); + labels.sortByAlphabetically(); + } } diff --git a/lib/features/labels/presentation/label_controller.dart b/lib/features/labels/presentation/label_controller.dart index fe555e40b..d34445400 100644 --- a/lib/features/labels/presentation/label_controller.dart +++ b/lib/features/labels/presentation/label_controller.dart @@ -13,9 +13,11 @@ import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart'; import 'package:tmail_ui_user/features/labels/domain/state/create_new_label_state.dart'; import 'package:tmail_ui_user/features/labels/domain/state/edit_label_state.dart'; +import 'package:tmail_ui_user/features/labels/domain/state/delete_a_label_state.dart'; import 'package:tmail_ui_user/features/labels/domain/state/get_all_label_state.dart'; import 'package:tmail_ui_user/features/labels/domain/usecases/create_new_label_interactor.dart'; import 'package:tmail_ui_user/features/labels/domain/usecases/edit_label_interactor.dart'; +import 'package:tmail_ui_user/features/labels/domain/usecases/delete_a_label_interactor.dart'; import 'package:tmail_ui_user/features/labels/domain/usecases/get_all_label_interactor.dart'; import 'package:tmail_ui_user/features/labels/presentation/extensions/handle_label_action_type_extension.dart'; import 'package:tmail_ui_user/features/labels/presentation/label_interactor_bindings.dart'; @@ -37,6 +39,7 @@ class LabelController extends BaseController with LabelContextMenuMixin { CreateNewLabelInteractor? _createNewLabelInteractor; GetLabelSettingStateInteractor? _getLabelSettingStateInteractor; EditLabelInteractor? _editLabelInteractor; + DeleteALabelInteractor? _deleteALabelInteractor; bool isLabelCapabilitySupported(Session session, AccountId accountId) { return LabelsConstants.labelsCapability.isSupported(session, accountId); @@ -62,10 +65,13 @@ class LabelController extends BaseController with LabelContextMenuMixin { _getAllLabelInteractor = getBinding(); _createNewLabelInteractor = getBinding(); _editLabelInteractor = getBinding(); + _deleteALabelInteractor = getBinding(); } EditLabelInteractor? get editLabelInteractor => _editLabelInteractor; + DeleteALabelInteractor? get deleteALabelInteractor => _deleteALabelInteractor; + void getAllLabels(AccountId accountId) { if (_getAllLabelInteractor == null) return; @@ -139,6 +145,8 @@ class LabelController extends BaseController with LabelContextMenuMixin { _handleGetLabelSettingStateSuccess(success.isEnabled, success.accountId); } else if (success is EditLabelSuccess) { handleEditLabelSuccess(success); + } else if (success is DeleteALabelSuccess) { + handleDeleteLabelSuccess(success); } else { super.handleSuccessViewState(success); } @@ -155,6 +163,8 @@ class LabelController extends BaseController with LabelContextMenuMixin { _clearLabelData(); } else if (failure is EditLabelFailure) { handleEditLabelFailure(failure); + } else if (failure is DeleteALabelFailure) { + handleDeleteLabelFailure(failure); } else { super.handleFailureViewState(failure); } @@ -165,6 +175,7 @@ class LabelController extends BaseController with LabelContextMenuMixin { _getAllLabelInteractor = null; _createNewLabelInteractor = null; _editLabelInteractor = null; + _deleteALabelInteractor = null; _getLabelSettingStateInteractor = null; super.onClose(); } diff --git a/lib/features/labels/presentation/label_interactor_bindings.dart b/lib/features/labels/presentation/label_interactor_bindings.dart index 0c64cf2d1..f58a8e797 100644 --- a/lib/features/labels/presentation/label_interactor_bindings.dart +++ b/lib/features/labels/presentation/label_interactor_bindings.dart @@ -8,6 +8,7 @@ import 'package:tmail_ui_user/features/labels/data/repository/label_repository_i import 'package:tmail_ui_user/features/labels/domain/repository/label_repository.dart'; import 'package:tmail_ui_user/features/labels/domain/usecases/create_new_label_interactor.dart'; import 'package:tmail_ui_user/features/labels/domain/usecases/edit_label_interactor.dart'; +import 'package:tmail_ui_user/features/labels/domain/usecases/delete_a_label_interactor.dart'; import 'package:tmail_ui_user/features/labels/domain/usecases/get_all_label_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart'; import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart'; @@ -35,6 +36,7 @@ class LabelInteractorBindings extends InteractorsBindings { Get.lazyPut(() => GetAllLabelInteractor(Get.find())); Get.lazyPut(() => CreateNewLabelInteractor(Get.find())); Get.lazyPut(() => EditLabelInteractor(Get.find())); + Get.lazyPut(() => DeleteALabelInteractor(Get.find())); Get.lazyPut(() => VerifyNameInteractor()); } diff --git a/lib/features/mailbox/presentation/base_mailbox_view.dart b/lib/features/mailbox/presentation/base_mailbox_view.dart index a4c9f6279..b2a280cae 100644 --- a/lib/features/mailbox/presentation/base_mailbox_view.dart +++ b/lib/features/mailbox/presentation/base_mailbox_view.dart @@ -380,6 +380,7 @@ abstract class BaseMailboxView extends GetWidget onToggleLabelListState: labelController.toggleLabelListState, onAddNewLabel: () => labelController.handleLabelActionType( + context: context, actionType: LabelActionType.create, accountId: accountId, ), diff --git a/lib/features/mailbox/presentation/extensions/handle_label_action_type_extension.dart b/lib/features/mailbox/presentation/extensions/handle_label_action_type_extension.dart index 7160303c8..c5c0b4299 100644 --- a/lib/features/mailbox/presentation/extensions/handle_label_action_type_extension.dart +++ b/lib/features/mailbox/presentation/extensions/handle_label_action_type_extension.dart @@ -22,7 +22,11 @@ extension HandleLabelActionTypeExtension on MailboxDashBoardController { imagePaths, label, position: position, - _onPerformLabelActionType, + (label, actionType) => _onPerformLabelActionType( + context: context, + label: label, + actionType: actionType, + ), ); } finally { if (PlatformInfo.isWeb) { @@ -31,8 +35,13 @@ extension HandleLabelActionTypeExtension on MailboxDashBoardController { } } - void _onPerformLabelActionType(Label label, LabelActionType actionType) { + void _onPerformLabelActionType({ + required BuildContext context, + required Label label, + required LabelActionType actionType, + }) { labelController.handleLabelActionType( + context: context, actionType: actionType, accountId: accountId.value, label: label, @@ -48,7 +57,11 @@ extension HandleLabelActionTypeExtension on MailboxDashBoardController { context, imagePaths, label, - _onPerformLabelActionType, + (label, actionType) => _onPerformLabelActionType( + context: context, + label: label, + actionType: actionType, + ), ); } } diff --git a/lib/features/mailbox_dashboard/presentation/extensions/labels/check_label_available_extension.dart b/lib/features/mailbox_dashboard/presentation/extensions/labels/check_label_available_extension.dart deleted file mode 100644 index e69de29bb..000000000 diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 9dfa3a0f0..78b8017da 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -5437,5 +5437,21 @@ "placeholders": { "labelName": {} } + }, + "deleteLabelSuccessfullyMessage": "You successfully deleted the {labelName} label", + "@deleteLabelSuccessfullyMessage": { + "type": "text", + "placeholders_order": [ + "labelName" + ], + "placeholders": { + "labelName": {} + } + }, + "deleteALabelFailure": "Delete a label failure", + "@deleteALabelFailure": { + "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 7fe3f299e..5a687fb1e 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -5754,4 +5754,19 @@ class AppLocalizations { args: [labelName], ); } + + String deleteLabelSuccessfullyMessage(String labelName) { + return Intl.message( + 'You successfully deleted the $labelName label', + name: 'deleteLabelSuccessfullyMessage', + args: [labelName], + ); + } + + String get deleteALabelFailure { + return Intl.message( + 'Delete a label failure', + name: 'deleteALabelFailure', + ); + } } diff --git a/lib/main/utils/toast_manager.dart b/lib/main/utils/toast_manager.dart index 6bebbe325..7a07ef8d5 100644 --- a/lib/main/utils/toast_manager.dart +++ b/lib/main/utils/toast_manager.dart @@ -34,6 +34,7 @@ import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.d import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart'; import 'package:tmail_ui_user/features/labels/domain/state/create_new_label_state.dart'; import 'package:tmail_ui_user/features/labels/domain/state/edit_label_state.dart'; +import 'package:tmail_ui_user/features/labels/domain/state/delete_a_label_state.dart'; import 'package:tmail_ui_user/features/login/data/network/oidc_error.dart'; import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; import 'package:tmail_ui_user/features/login/domain/exceptions/oauth_authorization_error.dart'; @@ -224,6 +225,8 @@ class ToastManager { appLocalizations.removeLabelFromThreadFailureMessage( failure.labelDisplay, ); + } else if (failure is DeleteALabelFailure) { + message = message ?? appLocalizations.deleteALabelFailure; } log('ToastManager::showMessageFailure: Message: $message'); if (message?.trim().isNotEmpty == true) { @@ -311,6 +314,10 @@ class ToastManager { message = appLocalizations.removeLabelFromThreadSuccessfullyMessage( success.labelDisplay, ); + } else if (success is DeleteALabelSuccess) { + message = appLocalizations.deleteLabelSuccessfullyMessage( + success.deletedLabel.safeDisplayName, + ); } log('ToastManager::showMessageSuccess: Message: $message'); if (message?.trim().isNotEmpty == true) {