TF-4233 Implement edit label action
This commit is contained in:
@@ -17,4 +17,11 @@ extension ListLabelExtension on List<Label> {
|
|||||||
|
|
||||||
List<KeyWordIdentifier> get keywords =>
|
List<KeyWordIdentifier> get keywords =>
|
||||||
map((label) => label.keyword).nonNulls.toList();
|
map((label) => label.keyword).nonNulls.toList();
|
||||||
|
|
||||||
|
List<String> getDisplayNameListWithoutSelectedLabel(Label selectedLabel) {
|
||||||
|
return map((label) => label.safeDisplayName)
|
||||||
|
.where((name) =>
|
||||||
|
name.trim().isNotEmpty && name != selectedLabel.safeDisplayName)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
import 'package:labels/model/label.dart';
|
import 'package:labels/model/label.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/domain/model/edit_label_request.dart';
|
||||||
|
|
||||||
abstract class LabelDatasource {
|
abstract class LabelDatasource {
|
||||||
Future<List<Label>> getAllLabels(AccountId accountId);
|
Future<List<Label>> getAllLabels(AccountId accountId);
|
||||||
|
|
||||||
Future<Label> createNewLabel(AccountId accountId, Label labelData);
|
Future<Label> createNewLabel(AccountId accountId, Label labelData);
|
||||||
|
|
||||||
|
Future<Label> editLabel(AccountId accountId, EditLabelRequest labelRequest);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:jmap_dart_client/jmap/account_id.dart';
|
|||||||
import 'package:labels/model/label.dart';
|
import 'package:labels/model/label.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/data/datasource/label_datasource.dart';
|
import 'package:tmail_ui_user/features/labels/data/datasource/label_datasource.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/data/network/label_api.dart';
|
import 'package:tmail_ui_user/features/labels/data/network/label_api.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/domain/model/edit_label_request.dart';
|
||||||
import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart';
|
import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart';
|
||||||
|
|
||||||
class LabelDatasourceImpl extends LabelDatasource {
|
class LabelDatasourceImpl extends LabelDatasource {
|
||||||
@@ -23,4 +24,11 @@ class LabelDatasourceImpl extends LabelDatasource {
|
|||||||
return await _labelApi.createNewLabel(accountId, labelData);
|
return await _labelApi.createNewLabel(accountId, labelData);
|
||||||
}).catchError(_exceptionThrower.throwException);
|
}).catchError(_exceptionThrower.throwException);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Label> editLabel(AccountId accountId, EditLabelRequest labelRequest) {
|
||||||
|
return Future.sync(() async {
|
||||||
|
return await _labelApi.editLabel(accountId, labelRequest);
|
||||||
|
}).catchError(_exceptionThrower.throwException);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import 'package:jmap_dart_client/http/http_client.dart';
|
import 'package:jmap_dart_client/http/http_client.dart';
|
||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/id.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/jmap_request.dart';
|
import 'package:jmap_dart_client/jmap/jmap_request.dart';
|
||||||
import 'package:labels/labels.dart';
|
import 'package:labels/labels.dart';
|
||||||
import 'package:tmail_ui_user/features/base/mixin/handle_error_mixin.dart';
|
import 'package:tmail_ui_user/features/base/mixin/handle_error_mixin.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/domain/model/edit_label_request.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class LabelApi with HandleSetErrorMixin {
|
class LabelApi with HandleSetErrorMixin {
|
||||||
@@ -57,4 +59,40 @@ class LabelApi with HandleSetErrorMixin {
|
|||||||
throw parseErrorForSetResponse(response, generateCreateId);
|
throw parseErrorForSetResponse(response, generateCreateId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Label> editLabel(
|
||||||
|
AccountId accountId,
|
||||||
|
EditLabelRequest labelRequest,
|
||||||
|
) async {
|
||||||
|
final labelId = labelRequest.labelId;
|
||||||
|
final newLabel = labelRequest.newLabel;
|
||||||
|
|
||||||
|
final method = SetLabelMethod(accountId)
|
||||||
|
..addUpdates({
|
||||||
|
labelId: PatchObject(newLabel.toJson())
|
||||||
|
});
|
||||||
|
|
||||||
|
final builder = JmapRequestBuilder(_httpClient, ProcessingInvocation());
|
||||||
|
final invocation = builder.invocation(method);
|
||||||
|
|
||||||
|
final result =
|
||||||
|
await (builder..usings(method.requiredCapabilities)).build().execute();
|
||||||
|
|
||||||
|
final response = result.parse<SetLabelResponse>(
|
||||||
|
invocation.methodCallId,
|
||||||
|
SetLabelResponse.deserialize,
|
||||||
|
);
|
||||||
|
|
||||||
|
final labelUpdated = response?.updated?[labelId];
|
||||||
|
|
||||||
|
if (labelUpdated != null) {
|
||||||
|
final newLabelUpdated = newLabel.copyWith(
|
||||||
|
id: labelId,
|
||||||
|
keyword: labelRequest.labelKeyword,
|
||||||
|
);
|
||||||
|
return newLabelUpdated;
|
||||||
|
} else {
|
||||||
|
throw parseErrorForSetResponse(response, labelId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
import 'package:labels/model/label.dart';
|
import 'package:labels/model/label.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/data/datasource/label_datasource.dart';
|
import 'package:tmail_ui_user/features/labels/data/datasource/label_datasource.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/domain/model/edit_label_request.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/domain/repository/label_repository.dart';
|
import 'package:tmail_ui_user/features/labels/domain/repository/label_repository.dart';
|
||||||
|
|
||||||
class LabelRepositoryImpl extends LabelRepository {
|
class LabelRepositoryImpl extends LabelRepository {
|
||||||
@@ -17,4 +18,9 @@ class LabelRepositoryImpl extends LabelRepository {
|
|||||||
Future<Label> createNewLabel(AccountId accountId, Label labelData) {
|
Future<Label> createNewLabel(AccountId accountId, Label labelData) {
|
||||||
return _labelDatasource.createNewLabel(accountId, labelData);
|
return _labelDatasource.createNewLabel(accountId, labelData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Label> editLabel(AccountId accountId, EditLabelRequest labelRequest) {
|
||||||
|
return _labelDatasource.editLabel(accountId, labelRequest);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
|
||||||
|
import 'package:labels/model/label.dart';
|
||||||
|
|
||||||
|
class EditLabelRequest with EquatableMixin {
|
||||||
|
final Id labelId;
|
||||||
|
final KeyWordIdentifier? labelKeyword;
|
||||||
|
final Label newLabel;
|
||||||
|
|
||||||
|
EditLabelRequest({
|
||||||
|
required this.labelId,
|
||||||
|
required this.labelKeyword,
|
||||||
|
required this.newLabel,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [labelId, labelKeyword, newLabel];
|
||||||
|
}
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
import 'package:labels/model/label.dart';
|
import 'package:labels/model/label.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/domain/model/edit_label_request.dart';
|
||||||
|
|
||||||
abstract class LabelRepository {
|
abstract class LabelRepository {
|
||||||
Future<List<Label>> getAllLabels(AccountId accountId);
|
Future<List<Label>> getAllLabels(AccountId accountId);
|
||||||
|
|
||||||
Future<Label> createNewLabel(AccountId accountId, Label labelData);
|
Future<Label> createNewLabel(AccountId accountId, Label labelData);
|
||||||
|
|
||||||
|
Future<Label> editLabel(AccountId accountId, EditLabelRequest labelRequest);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import 'package:core/presentation/state/failure.dart';
|
||||||
|
import 'package:core/presentation/state/success.dart';
|
||||||
|
import 'package:labels/model/label.dart';
|
||||||
|
|
||||||
|
class EditingLabel extends LoadingState {}
|
||||||
|
|
||||||
|
class EditLabelSuccess extends UIState {
|
||||||
|
final Label newLabel;
|
||||||
|
|
||||||
|
EditLabelSuccess(this.newLabel);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [newLabel];
|
||||||
|
}
|
||||||
|
|
||||||
|
class EditLabelFailure extends FeatureFailure {
|
||||||
|
EditLabelFailure(dynamic exception) : super(exception: exception);
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
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:tmail_ui_user/features/labels/domain/model/edit_label_request.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/domain/repository/label_repository.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/domain/state/edit_label_state.dart';
|
||||||
|
|
||||||
|
class EditLabelInteractor {
|
||||||
|
final LabelRepository _labelRepository;
|
||||||
|
|
||||||
|
EditLabelInteractor(this._labelRepository);
|
||||||
|
|
||||||
|
Stream<Either<Failure, Success>> execute(
|
||||||
|
AccountId accountId,
|
||||||
|
EditLabelRequest labelRequest,
|
||||||
|
) async* {
|
||||||
|
try {
|
||||||
|
yield Right(EditingLabel());
|
||||||
|
final newLabel = await _labelRepository.editLabel(
|
||||||
|
accountId,
|
||||||
|
labelRequest,
|
||||||
|
);
|
||||||
|
yield Right(EditLabelSuccess(newLabel));
|
||||||
|
} catch (e) {
|
||||||
|
yield Left(EditLabelFailure(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,16 @@
|
|||||||
|
import 'package:core/utils/app_logger.dart';
|
||||||
|
import 'package:dartz/dartz.dart';
|
||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
|
import 'package:labels/extensions/list_label_extension.dart';
|
||||||
import 'package:labels/model/label.dart';
|
import 'package:labels/model/label.dart';
|
||||||
|
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/domain/model/edit_label_request.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/label_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.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/routes/dialog_router.dart';
|
||||||
|
|
||||||
extension HandleLabelActionTypeExtension on LabelController {
|
extension HandleLabelActionTypeExtension on LabelController {
|
||||||
void handleLabelActionType({
|
void handleLabelActionType({
|
||||||
@@ -14,7 +23,69 @@ extension HandleLabelActionTypeExtension on LabelController {
|
|||||||
openCreateNewLabelModal(accountId);
|
openCreateNewLabelModal(accountId);
|
||||||
break;
|
break;
|
||||||
case LabelActionType.edit:
|
case LabelActionType.edit:
|
||||||
|
if (label == null) return;
|
||||||
|
openEditLabelModal(accountId: accountId, label: label);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> openEditLabelModal({
|
||||||
|
required AccountId? accountId,
|
||||||
|
required Label label,
|
||||||
|
}) async {
|
||||||
|
await DialogRouter().openDialogModal(
|
||||||
|
child: CreateNewLabelModal(
|
||||||
|
labels: labels,
|
||||||
|
selectedLabel: label,
|
||||||
|
actionType: LabelActionType.edit,
|
||||||
|
onCreateNewLabelCallback: (newLabel) =>
|
||||||
|
editLabel(
|
||||||
|
accountId: accountId,
|
||||||
|
selectedLabel: label,
|
||||||
|
newLabel: newLabel,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
dialogLabel: 'edit-label-modal',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void editLabel({
|
||||||
|
required AccountId? accountId,
|
||||||
|
required Label selectedLabel,
|
||||||
|
required Label newLabel,
|
||||||
|
}) {
|
||||||
|
log('LabelController::editLabel:selectedLabel: $selectedLabel, newLabel: $newLabel');
|
||||||
|
if (accountId == null) {
|
||||||
|
consumeState(
|
||||||
|
Stream.value(Left(EditLabelFailure(NotFoundAccountIdException()))),
|
||||||
|
);
|
||||||
|
} else if (editLabelInteractor == null) {
|
||||||
|
consumeState(
|
||||||
|
Stream.value(Left(EditLabelFailure(InteractorNotInitialized()))),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
final labelRequest = EditLabelRequest(
|
||||||
|
labelId: selectedLabel.id!,
|
||||||
|
labelKeyword: selectedLabel.keyword,
|
||||||
|
newLabel: newLabel,
|
||||||
|
);
|
||||||
|
|
||||||
|
consumeState(editLabelInteractor!.execute(accountId, labelRequest));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleEditLabelSuccess(EditLabelSuccess success) {
|
||||||
|
toastManager.showMessageSuccess(success);
|
||||||
|
syncListLabels(success.newLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleEditLabelFailure(EditLabelFailure failure) {
|
||||||
|
toastManager.showMessageFailure(failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
void syncListLabels(Label newLabel) {
|
||||||
|
labels.removeWhere((label) => label.id == newLabel.id);
|
||||||
|
labels.add(newLabel);
|
||||||
|
labels.sortByAlphabetically();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,12 @@ import 'package:model/mailbox/expand_mode.dart';
|
|||||||
import 'package:tmail_ui_user/features/base/base_controller.dart';
|
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/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/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/get_all_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/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/get_all_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';
|
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_context_menu_mixin.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/widgets/create_new_label_modal.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/widgets/create_new_label_modal.dart';
|
||||||
@@ -33,6 +36,7 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
|||||||
GetAllLabelInteractor? _getAllLabelInteractor;
|
GetAllLabelInteractor? _getAllLabelInteractor;
|
||||||
CreateNewLabelInteractor? _createNewLabelInteractor;
|
CreateNewLabelInteractor? _createNewLabelInteractor;
|
||||||
GetLabelSettingStateInteractor? _getLabelSettingStateInteractor;
|
GetLabelSettingStateInteractor? _getLabelSettingStateInteractor;
|
||||||
|
EditLabelInteractor? _editLabelInteractor;
|
||||||
|
|
||||||
bool isLabelCapabilitySupported(Session session, AccountId accountId) {
|
bool isLabelCapabilitySupported(Session session, AccountId accountId) {
|
||||||
return LabelsConstants.labelsCapability.isSupported(session, accountId);
|
return LabelsConstants.labelsCapability.isSupported(session, accountId);
|
||||||
@@ -57,8 +61,11 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
|||||||
LabelInteractorBindings().dependencies();
|
LabelInteractorBindings().dependencies();
|
||||||
_getAllLabelInteractor = getBinding<GetAllLabelInteractor>();
|
_getAllLabelInteractor = getBinding<GetAllLabelInteractor>();
|
||||||
_createNewLabelInteractor = getBinding<CreateNewLabelInteractor>();
|
_createNewLabelInteractor = getBinding<CreateNewLabelInteractor>();
|
||||||
|
_editLabelInteractor = getBinding<EditLabelInteractor>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EditLabelInteractor? get editLabelInteractor => _editLabelInteractor;
|
||||||
|
|
||||||
void getAllLabels(AccountId accountId) {
|
void getAllLabels(AccountId accountId) {
|
||||||
if (_getAllLabelInteractor == null) return;
|
if (_getAllLabelInteractor == null) return;
|
||||||
|
|
||||||
@@ -130,6 +137,8 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
|||||||
_handleCreateNewLabelSuccess(success);
|
_handleCreateNewLabelSuccess(success);
|
||||||
} else if (success is GetLabelSettingStateSuccess) {
|
} else if (success is GetLabelSettingStateSuccess) {
|
||||||
_handleGetLabelSettingStateSuccess(success.isEnabled, success.accountId);
|
_handleGetLabelSettingStateSuccess(success.isEnabled, success.accountId);
|
||||||
|
} else if (success is EditLabelSuccess) {
|
||||||
|
handleEditLabelSuccess(success);
|
||||||
} else {
|
} else {
|
||||||
super.handleSuccessViewState(success);
|
super.handleSuccessViewState(success);
|
||||||
}
|
}
|
||||||
@@ -144,6 +153,8 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
|||||||
} else if (failure is GetLabelSettingStateFailure) {
|
} else if (failure is GetLabelSettingStateFailure) {
|
||||||
isLabelSettingEnabled.value = false;
|
isLabelSettingEnabled.value = false;
|
||||||
_clearLabelData();
|
_clearLabelData();
|
||||||
|
} else if (failure is EditLabelFailure) {
|
||||||
|
handleEditLabelFailure(failure);
|
||||||
} else {
|
} else {
|
||||||
super.handleFailureViewState(failure);
|
super.handleFailureViewState(failure);
|
||||||
}
|
}
|
||||||
@@ -153,6 +164,7 @@ class LabelController extends BaseController with LabelContextMenuMixin {
|
|||||||
void onClose() {
|
void onClose() {
|
||||||
_getAllLabelInteractor = null;
|
_getAllLabelInteractor = null;
|
||||||
_createNewLabelInteractor = null;
|
_createNewLabelInteractor = null;
|
||||||
|
_editLabelInteractor = null;
|
||||||
_getLabelSettingStateInteractor = null;
|
_getLabelSettingStateInteractor = null;
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:tmail_ui_user/features/labels/data/network/label_api.dart';
|
|||||||
import 'package:tmail_ui_user/features/labels/data/repository/label_repository_impl.dart';
|
import 'package:tmail_ui_user/features/labels/data/repository/label_repository_impl.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/domain/repository/label_repository.dart';
|
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/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/get_all_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/features/mailbox_creator/domain/usecases/verify_name_interactor.dart';
|
||||||
import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart';
|
import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart';
|
||||||
@@ -33,6 +34,7 @@ class LabelInteractorBindings extends InteractorsBindings {
|
|||||||
void bindingsInteractor() {
|
void bindingsInteractor() {
|
||||||
Get.lazyPut(() => GetAllLabelInteractor(Get.find<LabelRepository>()));
|
Get.lazyPut(() => GetAllLabelInteractor(Get.find<LabelRepository>()));
|
||||||
Get.lazyPut(() => CreateNewLabelInteractor(Get.find<LabelRepository>()));
|
Get.lazyPut(() => CreateNewLabelInteractor(Get.find<LabelRepository>()));
|
||||||
|
Get.lazyPut(() => EditLabelInteractor(Get.find<LabelRepository>()));
|
||||||
Get.lazyPut(() => VerifyNameInteractor());
|
Get.lazyPut(() => VerifyNameInteractor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,11 +20,29 @@ enum LabelActionType {
|
|||||||
switch (this) {
|
switch (this) {
|
||||||
case LabelActionType.create:
|
case LabelActionType.create:
|
||||||
return appLocalizations.createANewLabel;
|
return appLocalizations.createANewLabel;
|
||||||
|
case LabelActionType.edit:
|
||||||
|
return appLocalizations.editLabel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String getModalSubtitle(AppLocalizations appLocalizations) {
|
||||||
|
switch (this) {
|
||||||
|
case LabelActionType.create:
|
||||||
|
return appLocalizations.organizeYourInboxWithACustomCategory;
|
||||||
case LabelActionType.edit:
|
case LabelActionType.edit:
|
||||||
return appLocalizations.updateTheLabelName;
|
return appLocalizations.updateTheLabelName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getModalPositiveAction(AppLocalizations appLocalizations) {
|
||||||
|
switch (this) {
|
||||||
|
case LabelActionType.create:
|
||||||
|
return appLocalizations.createLabel;
|
||||||
|
case LabelActionType.edit:
|
||||||
|
return appLocalizations.save;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String getContextMenuIcon(ImagePaths imagePaths) {
|
String getContextMenuIcon(ImagePaths imagePaths) {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case LabelActionType.create:
|
case LabelActionType.create:
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:core/presentation/extensions/color_extension.dart';
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
|
import 'package:core/presentation/extensions/hex_color_extension.dart';
|
||||||
import 'package:core/presentation/resources/image_paths.dart';
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
import 'package:core/presentation/utils/responsive_utils.dart';
|
import 'package:core/presentation/utils/responsive_utils.dart';
|
||||||
import 'package:core/presentation/utils/theme_utils.dart';
|
import 'package:core/presentation/utils/theme_utils.dart';
|
||||||
@@ -13,6 +14,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:labels/labels.dart';
|
import 'package:labels/labels.dart';
|
||||||
import 'package:tmail_ui_user/features/base/widget/label_input_field_builder.dart';
|
import 'package:tmail_ui_user/features/base/widget/label_input_field_builder.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/duplicate_name_validator.dart';
|
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/duplicate_name_validator.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/empty_name_validator.dart';
|
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/empty_name_validator.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/name_with_space_only_validator.dart';
|
import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/name_with_space_only_validator.dart';
|
||||||
@@ -27,12 +29,16 @@ typedef OnCreateNewLabelCallback = Function(Label label);
|
|||||||
|
|
||||||
class CreateNewLabelModal extends StatefulWidget {
|
class CreateNewLabelModal extends StatefulWidget {
|
||||||
final List<Label> labels;
|
final List<Label> labels;
|
||||||
|
final LabelActionType actionType;
|
||||||
final OnCreateNewLabelCallback onCreateNewLabelCallback;
|
final OnCreateNewLabelCallback onCreateNewLabelCallback;
|
||||||
|
final Label? selectedLabel;
|
||||||
|
|
||||||
const CreateNewLabelModal({
|
const CreateNewLabelModal({
|
||||||
super.key,
|
super.key,
|
||||||
required this.labels,
|
required this.labels,
|
||||||
required this.onCreateNewLabelCallback,
|
required this.onCreateNewLabelCallback,
|
||||||
|
this.actionType = LabelActionType.create,
|
||||||
|
this.selectedLabel,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -56,7 +62,23 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_labelDisplayNameList = widget.labels.displayNameNotNullList;
|
final selectedLabel = widget.selectedLabel;
|
||||||
|
final labels = widget.labels;
|
||||||
|
if (selectedLabel != null) {
|
||||||
|
_selectedColor = selectedLabel.color?.value.toColor();
|
||||||
|
_labelDisplayNameList = labels
|
||||||
|
.getDisplayNameListWithoutSelectedLabel(selectedLabel);
|
||||||
|
} else {
|
||||||
|
_labelDisplayNameList = labels.displayNameNotNullList;
|
||||||
|
}
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
if (selectedLabel != null) {
|
||||||
|
_nameInputController.text = selectedLabel.safeDisplayName;
|
||||||
|
_nameInputFocusNode.requestFocus();
|
||||||
|
_labelSelectedColorNotifier.value = _selectedColor;
|
||||||
|
_createLabelStateNotifier.value = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -97,42 +119,8 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
_buildTitle(appLocalizations, isMobile, theme),
|
||||||
height: 64,
|
_buildSubTitle(appLocalizations),
|
||||||
alignment: Alignment.center,
|
|
||||||
padding: EdgeInsetsDirectional.only(
|
|
||||||
start: 32,
|
|
||||||
end: 32,
|
|
||||||
top: 16,
|
|
||||||
bottom: isMobile ? 0 : 16,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
appLocalizations.createANewLabel,
|
|
||||||
style: theme.textTheme.headlineSmall?.copyWith(
|
|
||||||
color: AppColor.m3SurfaceBackground,
|
|
||||||
),
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Center(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsetsDirectional.only(
|
|
||||||
start: 32,
|
|
||||||
end: 32,
|
|
||||||
bottom: 24,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
appLocalizations.organizeYourInboxWithACustomCategory,
|
|
||||||
style: ThemeUtils.textStyleInter400.copyWith(
|
|
||||||
color: AppColor.steelGrayA540,
|
|
||||||
fontSize: 13,
|
|
||||||
height: 20 / 13,
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Flexible(
|
Flexible(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
physics: const ClampingScrollPhysics(),
|
physics: const ClampingScrollPhysics(),
|
||||||
@@ -146,34 +134,7 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
ValueListenableBuilder(
|
_buildLabelNameInputField(appLocalizations),
|
||||||
valueListenable: _labelNameErrorTextNotifier,
|
|
||||||
builder: (_, errorText, __) {
|
|
||||||
return LabelInputFieldBuilder(
|
|
||||||
label: appLocalizations.labelName,
|
|
||||||
hintText: appLocalizations
|
|
||||||
.pleaseEnterNameYourNewLabel,
|
|
||||||
textEditingController: _nameInputController,
|
|
||||||
focusNode: _nameInputFocusNode,
|
|
||||||
errorText: errorText,
|
|
||||||
arrangeHorizontally: false,
|
|
||||||
isLabelHasColon: false,
|
|
||||||
labelStyle:
|
|
||||||
ThemeUtils.textStyleInter600().copyWith(
|
|
||||||
fontSize: 14,
|
|
||||||
height: 18 / 14,
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
runSpacing: 16,
|
|
||||||
inputFieldMaxWidth: double.infinity,
|
|
||||||
onTextChange: (value) =>
|
|
||||||
_onLabelNameInputChanged(
|
|
||||||
appLocalizations,
|
|
||||||
value,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 26, bottom: 16),
|
padding: const EdgeInsets.only(top: 26, bottom: 16),
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -213,7 +174,7 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
valueListenable: _createLabelStateNotifier,
|
valueListenable: _createLabelStateNotifier,
|
||||||
builder: (_, value, __) {
|
builder: (_, value, __) {
|
||||||
return ModalListActionButtonWidget(
|
return ModalListActionButtonWidget(
|
||||||
positiveLabel: appLocalizations.createLabel,
|
positiveLabel: widget.actionType.getModalPositiveAction(appLocalizations),
|
||||||
negativeLabel: appLocalizations.cancel,
|
negativeLabel: appLocalizations.cancel,
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: 25,
|
vertical: 25,
|
||||||
@@ -258,6 +219,83 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildTitle(
|
||||||
|
AppLocalizations appLocalizations,
|
||||||
|
bool isMobile,
|
||||||
|
ThemeData theme,
|
||||||
|
) {
|
||||||
|
return Container(
|
||||||
|
height: 64,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
padding: EdgeInsetsDirectional.only(
|
||||||
|
start: 32,
|
||||||
|
end: 32,
|
||||||
|
top: 16,
|
||||||
|
bottom: isMobile ? 0 : 16,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
widget.actionType.getModalTitle(appLocalizations),
|
||||||
|
style: theme.textTheme.headlineSmall?.copyWith(
|
||||||
|
color: AppColor.m3SurfaceBackground,
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSubTitle(AppLocalizations appLocalizations) {
|
||||||
|
return Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.only(
|
||||||
|
start: 32,
|
||||||
|
end: 32,
|
||||||
|
bottom: 24,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
widget.actionType.getModalSubtitle(appLocalizations),
|
||||||
|
style: ThemeUtils.textStyleInter400.copyWith(
|
||||||
|
color: AppColor.steelGrayA540,
|
||||||
|
fontSize: 13,
|
||||||
|
height: 20 / 13,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildLabelNameInputField(AppLocalizations appLocalizations) {
|
||||||
|
return ValueListenableBuilder(
|
||||||
|
valueListenable: _labelNameErrorTextNotifier,
|
||||||
|
builder: (_, errorText, __) {
|
||||||
|
return LabelInputFieldBuilder(
|
||||||
|
label: appLocalizations.labelName,
|
||||||
|
hintText: appLocalizations
|
||||||
|
.pleaseEnterNameYourNewLabel,
|
||||||
|
textEditingController: _nameInputController,
|
||||||
|
focusNode: _nameInputFocusNode,
|
||||||
|
errorText: errorText,
|
||||||
|
arrangeHorizontally: false,
|
||||||
|
isLabelHasColon: false,
|
||||||
|
labelStyle:
|
||||||
|
ThemeUtils.textStyleInter600().copyWith(
|
||||||
|
fontSize: 14,
|
||||||
|
height: 18 / 14,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
runSpacing: 16,
|
||||||
|
inputFieldMaxWidth: double.infinity,
|
||||||
|
onTextChange: (value) =>
|
||||||
|
_onLabelNameInputChanged(
|
||||||
|
appLocalizations,
|
||||||
|
value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _onLabelNameInputChanged(
|
void _onLabelNameInputChanged(
|
||||||
AppLocalizations appLocalizations,
|
AppLocalizations appLocalizations,
|
||||||
String value,
|
String value,
|
||||||
|
|||||||
@@ -5365,5 +5365,21 @@
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders_order": [],
|
"placeholders_order": [],
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"editLabelSuccessfullyMessage": "You successfully edited the {labelName} label",
|
||||||
|
"@editLabelSuccessfullyMessage": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [
|
||||||
|
"labelName"
|
||||||
|
],
|
||||||
|
"placeholders": {
|
||||||
|
"labelName": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"editLabelFailure": "Edit label failure",
|
||||||
|
"@editLabelFailure": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [],
|
||||||
|
"placeholders": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5694,4 +5694,19 @@ class AppLocalizations {
|
|||||||
name: 'updateTheLabelName',
|
name: 'updateTheLabelName',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String editLabelSuccessfullyMessage(String labelName) {
|
||||||
|
return Intl.message(
|
||||||
|
'You successfully edited the $labelName label',
|
||||||
|
name: 'editLabelSuccessfullyMessage',
|
||||||
|
args: [labelName],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String get editLabelFailure {
|
||||||
|
return Intl.message(
|
||||||
|
'Edit label failure',
|
||||||
|
name: 'editLabelFailure',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import 'package:tmail_ui_user/features/download/domain/state/preview_email_from_
|
|||||||
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
|
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
|
||||||
import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart';
|
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/create_new_label_state.dart';
|
||||||
|
import 'package:tmail_ui_user/features/labels/domain/state/edit_label_state.dart';
|
||||||
import 'package:tmail_ui_user/features/login/data/network/oidc_error.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/authentication_exception.dart';
|
||||||
import 'package:tmail_ui_user/features/login/domain/exceptions/oauth_authorization_error.dart';
|
import 'package:tmail_ui_user/features/login/domain/exceptions/oauth_authorization_error.dart';
|
||||||
@@ -209,6 +210,8 @@ class ToastManager {
|
|||||||
} else if (failure is AddALabelToAThreadFailure) {
|
} else if (failure is AddALabelToAThreadFailure) {
|
||||||
message = message ??
|
message = message ??
|
||||||
appLocalizations.addLabelToThreadFailureMessage(failure.labelDisplay);
|
appLocalizations.addLabelToThreadFailureMessage(failure.labelDisplay);
|
||||||
|
} else if (failure is EditLabelFailure) {
|
||||||
|
message = message ?? appLocalizations.editLabelFailure;
|
||||||
}
|
}
|
||||||
log('ToastManager::showMessageFailure: Message: $message');
|
log('ToastManager::showMessageFailure: Message: $message');
|
||||||
if (message?.trim().isNotEmpty == true) {
|
if (message?.trim().isNotEmpty == true) {
|
||||||
@@ -276,6 +279,10 @@ class ToastManager {
|
|||||||
message = appLocalizations.createLabelSuccessfullyMessage(
|
message = appLocalizations.createLabelSuccessfullyMessage(
|
||||||
success.newLabel.safeDisplayName,
|
success.newLabel.safeDisplayName,
|
||||||
);
|
);
|
||||||
|
} else if (success is EditLabelSuccess) {
|
||||||
|
message = appLocalizations.editLabelSuccessfullyMessage(
|
||||||
|
success.newLabel.safeDisplayName,
|
||||||
|
);
|
||||||
} else if (success is AddALabelToAnEmailSuccess) {
|
} else if (success is AddALabelToAnEmailSuccess) {
|
||||||
message = appLocalizations.addLabelToEmailSuccessfullyMessage(
|
message = appLocalizations.addLabelToEmailSuccessfullyMessage(
|
||||||
success.labelDisplay,
|
success.labelDisplay,
|
||||||
|
|||||||
Reference in New Issue
Block a user