TF-4301 Add "Create a new label" button in Label As menus and wire up tagging in email view

This commit is contained in:
dab246
2026-02-04 13:53:01 +07:00
committed by Dat H. Pham
parent 061793846e
commit 190ccc7bc3
29 changed files with 600 additions and 247 deletions
@@ -1,7 +1,5 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:core/utils/app_logger.dart';
import 'package:dartz/dartz.dart' hide State;
import 'package:flutter/material.dart' hide State;
import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
@@ -14,7 +12,6 @@ import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.d
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
import 'package:tmail_ui_user/features/labels/domain/state/create_new_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/edit_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/delete_a_label_interactor.dart';
@@ -25,17 +22,21 @@ import 'package:tmail_ui_user/features/labels/presentation/extensions/handle_lab
import 'package:tmail_ui_user/features/labels/presentation/extensions/handle_label_websocket_extension.dart';
import 'package:tmail_ui_user/features/labels/presentation/label_interactor_bindings.dart';
import 'package:tmail_ui_user/features/labels/presentation/mixin/label_context_menu_mixin.dart';
import 'package:tmail_ui_user/features/labels/presentation/mixin/label_modal_mixin.dart';
import 'package:tmail_ui_user/features/labels/presentation/widgets/create_new_label_modal.dart';
import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart';
import 'package:tmail_ui_user/features/manage_account/domain/state/get_label_setting_state.dart';
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_label_setting_state_interactor.dart';
import 'package:tmail_ui_user/features/push_notification/presentation/websocket/web_socket_queue_handler.dart';
import 'package:tmail_ui_user/main/error/capability_validator.dart';
import 'package:tmail_ui_user/main/exceptions/logic_exception.dart';
import 'package:tmail_ui_user/main/routes/dialog_router.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
class LabelController extends BaseController with LabelContextMenuMixin {
final labels = <Label>[].obs;
class LabelController extends BaseController
with LabelContextMenuMixin,
LabelModalMixin {
@override
final RxList<Label> labels = <Label>[].obs;
final labelListExpandMode = Rx(ExpandMode.EXPAND);
final isLabelSettingEnabled = RxBool(false);
final isLabelsLoaded = RxBool(false);
@@ -80,8 +81,6 @@ class LabelController extends BaseController with LabelContextMenuMixin {
if (_getLabelSettingStateInteractor != null) {
consumeState(_getLabelSettingStateInteractor!.execute(accountId));
} else {
isLabelSettingEnabled.value = false;
isLabelSettingEnabled.refresh();
_clearLabelData();
setLabelLoaded();
}
@@ -92,6 +91,8 @@ class LabelController extends BaseController with LabelContextMenuMixin {
}
void _clearLabelData() {
isLabelSettingEnabled.value = false;
isLabelSettingEnabled.refresh();
labels.clear();
isLabelsLoaded.value = false;
}
@@ -105,6 +106,10 @@ class LabelController extends BaseController with LabelContextMenuMixin {
_getLabelChangesInteractor = getBinding<GetLabelChangesInteractor>();
}
@override
CreateNewLabelInteractor? get createNewLabelInteractor => _createNewLabelInteractor;
@override
EditLabelInteractor? get editLabelInteractor => _editLabelInteractor;
DeleteALabelInteractor? get deleteALabelInteractor => _deleteALabelInteractor;
@@ -146,42 +151,79 @@ class LabelController extends BaseController with LabelContextMenuMixin {
: ExpandMode.COLLAPSE;
}
Future<void> openCreateNewLabelModal(AccountId? accountId) async {
if (accountId == null) {
consumeState(
Stream.value(Left(CreateNewLabelFailure(NotFoundAccountIdException()))),
Future<void> onCreateALabelAction({
required AccountId? accountId,
OnLabelActionCallback? onLabelActionCallback,
bool shouldPop = false,
}) async {
if (_createNewLabelInteractor == null || _editLabelInteractor == null) {
_handleCreateNewLabelFailure(
failure: CreateNewLabelFailure(const InteractorNotInitialized()),
shouldPop: shouldPop,
);
return;
}
final verifyNameInteractor = getBinding<VerifyNameInteractor>();
if (verifyNameInteractor == null) {
_handleCreateNewLabelFailure(
failure: CreateNewLabelFailure(const InteractorNotInitialized()),
shouldPop: shouldPop,
);
return;
}
await DialogRouter().openDialogModal(
child: CreateNewLabelModal(
key: const Key('create_new_label_modal'),
labels: labels,
onLabelActionCallback: (label) => _createNewLabel(accountId, label),
),
dialogLabel: 'create-new-label-modal',
);
}
void _createNewLabel(AccountId accountId, Label label) {
log('LabelController::_createNewLabel:Label: $label');
if (_createNewLabelInteractor == null) {
consumeState(
Stream.value(Left(CreateNewLabelFailure(const InteractorNotInitialized()))),
if (accountId == null) {
_handleCreateNewLabelFailure(
failure: CreateNewLabelFailure(NotFoundAccountIdException()),
shouldPop: shouldPop,
);
return;
}
final resultState = await openCreateNewLabelModal(
accountId: accountId,
verifyNameInteractor: verifyNameInteractor,
);
if (resultState is CreateNewLabelSuccess) {
_handleCreateNewLabelSuccess(
success: resultState,
onLabelActionCallback: onLabelActionCallback,
shouldPop: shouldPop,
);
} else if (resultState is CreateNewLabelFailure) {
_handleCreateNewLabelFailure(
failure: resultState,
shouldPop: shouldPop,
);
} else {
consumeState(_createNewLabelInteractor!.execute(accountId, label));
}
}
void _handleCreateNewLabelSuccess(CreateNewLabelSuccess success) {
toastManager.showMessageSuccess(success);
void _handleCreateNewLabelSuccess({
required CreateNewLabelSuccess success,
OnLabelActionCallback? onLabelActionCallback,
bool shouldPop = false,
}) {
if (onLabelActionCallback == null && !shouldPop) {
toastManager.showMessageSuccess(success);
}
_addLabelToList(success.newLabel);
if (onLabelActionCallback != null) {
onLabelActionCallback(success.newLabel);
}
if (shouldPop) {
popBack(result: success.newLabel);
}
}
void _handleCreateNewLabelFailure(CreateNewLabelFailure failure) {
void _handleCreateNewLabelFailure({
required CreateNewLabelFailure failure,
bool shouldPop = false,
}) {
toastManager.showMessageFailure(failure);
if (shouldPop) {
popBack();
}
}
void _addLabelToList(Label newLabel) {
@@ -190,6 +232,10 @@ class LabelController extends BaseController with LabelContextMenuMixin {
}
void _handleGetLabelSettingStateSuccess(bool isEnabled, AccountId accountId) {
updateLabelSettingEnabled(isEnabled, accountId);
}
void updateLabelSettingEnabled(bool isEnabled, AccountId accountId) {
isLabelSettingEnabled.value = isEnabled;
isLabelSettingEnabled.refresh();
if (isEnabled) {
@@ -207,12 +253,8 @@ class LabelController extends BaseController with LabelContextMenuMixin {
labels.value = success.labels..sortByAlphabetically();
setCurrentLabelState(success.newState);
setLabelLoaded();
} else if (success is CreateNewLabelSuccess) {
_handleCreateNewLabelSuccess(success);
} else if (success is GetLabelSettingStateSuccess) {
_handleGetLabelSettingStateSuccess(success.isEnabled, success.accountId);
} else if (success is EditLabelSuccess) {
handleEditLabelSuccess(success);
} else if (success is DeleteALabelSuccess) {
handleDeleteLabelSuccess(success);
} else {
@@ -225,15 +267,9 @@ class LabelController extends BaseController with LabelContextMenuMixin {
if (failure is GetAllLabelFailure) {
labels.value = [];
setLabelLoaded();
} else if (failure is CreateNewLabelFailure) {
_handleCreateNewLabelFailure(failure);
} else if (failure is GetLabelSettingStateFailure) {
isLabelSettingEnabled.value = false;
isLabelSettingEnabled.refresh();
_clearLabelData();
setLabelLoaded();
} else if (failure is EditLabelFailure) {
handleEditLabelFailure(failure);
} else if (failure is DeleteALabelFailure) {
handleDeleteLabelFailure(failure);
} else {