[Part 2] TF-4308 Add Create new label button in ChooseLabelModal (#4457)
This commit is contained in:
@@ -1,31 +1,19 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
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/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/mailbox_creator/domain/usecases/verify_name_interactor.dart';
|
|
||||||
|
|
||||||
class OpenEditLabelModalParams with EquatableMixin {
|
class OpenEditLabelModalParams with EquatableMixin {
|
||||||
final Label selectedLabel;
|
final Label selectedLabel;
|
||||||
final AccountId accountId;
|
final AccountId? accountId;
|
||||||
final VerifyNameInteractor verifyNameInteractor;
|
|
||||||
final CreateNewLabelInteractor createNewLabelInteractor;
|
|
||||||
final EditLabelInteractor editLabelInteractor;
|
|
||||||
|
|
||||||
const OpenEditLabelModalParams({
|
const OpenEditLabelModalParams({
|
||||||
required this.selectedLabel,
|
required this.selectedLabel,
|
||||||
required this.accountId,
|
required this.accountId,
|
||||||
required this.verifyNameInteractor,
|
|
||||||
required this.createNewLabelInteractor,
|
|
||||||
required this.editLabelInteractor,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
selectedLabel,
|
selectedLabel,
|
||||||
accountId,
|
accountId,
|
||||||
verifyNameInteractor,
|
|
||||||
createNewLabelInteractor,
|
|
||||||
editLabelInteractor,
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ class AddListLabelToListEmailsDelegate extends BaseController {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
imagePaths: imagePaths,
|
imagePaths: imagePaths,
|
||||||
|
onCreateALabelAction: () => _labelController.onCreateALabelAction(
|
||||||
|
accountId: _labelController.accountId, hasResult: true),
|
||||||
),
|
),
|
||||||
dialogLabel: 'choose-label-modal',
|
dialogLabel: 'choose-label-modal',
|
||||||
);
|
);
|
||||||
|
|||||||
+2
-24
@@ -9,12 +9,9 @@ import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.d
|
|||||||
import 'package:tmail_ui_user/features/labels/domain/model/open_edit_label_modal_params.dart';
|
import 'package:tmail_ui_user/features/labels/domain/model/open_edit_label_modal_params.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/domain/state/delete_a_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/edit_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/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/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/main/exceptions/logic_exception.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/localizations/app_localizations.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
@@ -48,31 +45,10 @@ extension HandleLabelActionTypeExtension on LabelController {
|
|||||||
required AccountId? accountId,
|
required AccountId? accountId,
|
||||||
required Label label,
|
required Label label,
|
||||||
}) async {
|
}) async {
|
||||||
final createNewLabelInteractor = getBinding<CreateNewLabelInteractor>();
|
|
||||||
final editLabelInteractor = getBinding<EditLabelInteractor>();
|
|
||||||
final verifyNameInteractor = getBinding<VerifyNameInteractor>();
|
|
||||||
|
|
||||||
final isInteractorsInitialized = createNewLabelInteractor != null &&
|
|
||||||
editLabelInteractor != null &&
|
|
||||||
verifyNameInteractor != null;
|
|
||||||
|
|
||||||
if (!isInteractorsInitialized) {
|
|
||||||
_handleEditLabelFailure(EditLabelFailure(const InteractorNotInitialized()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (accountId == null) {
|
|
||||||
_handleEditLabelFailure(EditLabelFailure(NotFoundAccountIdException()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final resultState = await openEditLabelModal(
|
final resultState = await openEditLabelModal(
|
||||||
params: OpenEditLabelModalParams(
|
params: OpenEditLabelModalParams(
|
||||||
selectedLabel: label,
|
selectedLabel: label,
|
||||||
accountId: accountId,
|
accountId: accountId,
|
||||||
verifyNameInteractor: verifyNameInteractor,
|
|
||||||
createNewLabelInteractor: createNewLabelInteractor,
|
|
||||||
editLabelInteractor: editLabelInteractor,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -86,10 +62,12 @@ extension HandleLabelActionTypeExtension on LabelController {
|
|||||||
void _handleEditLabelSuccess(EditLabelSuccess success) {
|
void _handleEditLabelSuccess(EditLabelSuccess success) {
|
||||||
toastManager.showMessageSuccess(success);
|
toastManager.showMessageSuccess(success);
|
||||||
syncListLabels(success.newLabel);
|
syncListLabels(success.newLabel);
|
||||||
|
popBack();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleEditLabelFailure(EditLabelFailure failure) {
|
void _handleEditLabelFailure(EditLabelFailure failure) {
|
||||||
toastManager.showMessageFailure(failure);
|
toastManager.showMessageFailure(failure);
|
||||||
|
popBack();
|
||||||
}
|
}
|
||||||
|
|
||||||
void syncListLabels(Label newLabel) {
|
void syncListLabels(Label newLabel) {
|
||||||
|
|||||||
@@ -8,15 +8,12 @@ import 'package:jmap_dart_client/jmap/core/state.dart';
|
|||||||
import 'package:labels/labels.dart';
|
import 'package:labels/labels.dart';
|
||||||
import 'package:model/mailbox/expand_mode.dart';
|
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/domain/extensions/session_extensions.dart';
|
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/domain/model/open_edit_label_modal_params.dart';
|
import 'package:tmail_ui_user/features/labels/domain/model/open_edit_label_modal_params.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/delete_a_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/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';
|
import 'package:tmail_ui_user/features/labels/domain/usecases/delete_a_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/domain/usecases/get_label_changes_interactor.dart';
|
import 'package:tmail_ui_user/features/labels/domain/usecases/get_label_changes_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/extensions/handle_label_action_type_extension.dart';
|
||||||
@@ -25,13 +22,11 @@ import 'package:tmail_ui_user/features/labels/presentation/extensions/handle_lab
|
|||||||
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';
|
||||||
import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart';
|
|
||||||
import 'package:tmail_ui_user/main/routes/dialog_router.dart';
|
import 'package:tmail_ui_user/main/routes/dialog_router.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/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/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/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/error/capability_validator.dart';
|
||||||
import 'package:tmail_ui_user/main/exceptions/logic_exception.dart';
|
|
||||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
|
|
||||||
class LabelController extends BaseController
|
class LabelController extends BaseController
|
||||||
@@ -142,10 +137,7 @@ class LabelController extends BaseController
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<dynamic> openCreateNewLabelModal({
|
Future<dynamic> openCreateNewLabelModal({
|
||||||
required AccountId accountId,
|
required AccountId? accountId,
|
||||||
required VerifyNameInteractor verifyNameInteractor,
|
|
||||||
required CreateNewLabelInteractor createNewLabelInteractor,
|
|
||||||
required EditLabelInteractor editLabelInteractor,
|
|
||||||
}) async {
|
}) async {
|
||||||
return DialogRouter().openDialogModal(
|
return DialogRouter().openDialogModal(
|
||||||
child: CreateNewLabelModal(
|
child: CreateNewLabelModal(
|
||||||
@@ -153,9 +145,6 @@ class LabelController extends BaseController
|
|||||||
labels: labels,
|
labels: labels,
|
||||||
accountId: accountId,
|
accountId: accountId,
|
||||||
imagePaths: imagePaths,
|
imagePaths: imagePaths,
|
||||||
verifyNameInteractor: verifyNameInteractor,
|
|
||||||
createNewLabelInteractor: createNewLabelInteractor,
|
|
||||||
editLabelInteractor: editLabelInteractor,
|
|
||||||
),
|
),
|
||||||
dialogLabel: 'create-new-label-modal',
|
dialogLabel: 'create-new-label-modal',
|
||||||
);
|
);
|
||||||
@@ -172,50 +161,28 @@ class LabelController extends BaseController
|
|||||||
imagePaths: imagePaths,
|
imagePaths: imagePaths,
|
||||||
selectedLabel: params.selectedLabel,
|
selectedLabel: params.selectedLabel,
|
||||||
actionType: LabelActionType.edit,
|
actionType: LabelActionType.edit,
|
||||||
verifyNameInteractor: params.verifyNameInteractor,
|
|
||||||
createNewLabelInteractor: params.createNewLabelInteractor,
|
|
||||||
editLabelInteractor: params.editLabelInteractor,
|
|
||||||
),
|
),
|
||||||
dialogLabel: 'edit-label-modal',
|
dialogLabel: 'edit-label-modal',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> onCreateALabelAction({
|
Future<Label?> onCreateALabelAction({
|
||||||
required AccountId? accountId,
|
required AccountId? accountId,
|
||||||
OnLabelActionCallback? onLabelActionCallback,
|
OnLabelActionCallback? onLabelActionCallback,
|
||||||
bool shouldPop = false,
|
bool shouldPop = false,
|
||||||
|
bool hasResult = false,
|
||||||
}) async {
|
}) async {
|
||||||
final createNewLabelInteractor = getBinding<CreateNewLabelInteractor>();
|
|
||||||
final editLabelInteractor = getBinding<EditLabelInteractor>();
|
|
||||||
final verifyNameInteractor = getBinding<VerifyNameInteractor>();
|
|
||||||
|
|
||||||
final isInteractorsInitialized = createNewLabelInteractor != null &&
|
|
||||||
editLabelInteractor != null &&
|
|
||||||
verifyNameInteractor != null;
|
|
||||||
|
|
||||||
if (!isInteractorsInitialized) {
|
|
||||||
_handleCreateNewLabelFailure(
|
|
||||||
failure: CreateNewLabelFailure(const InteractorNotInitialized()),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (accountId == null) {
|
|
||||||
_handleCreateNewLabelFailure(
|
|
||||||
failure: CreateNewLabelFailure(NotFoundAccountIdException()),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final resultState = await openCreateNewLabelModal(
|
final resultState = await openCreateNewLabelModal(
|
||||||
accountId: accountId,
|
accountId: accountId,
|
||||||
verifyNameInteractor: verifyNameInteractor,
|
|
||||||
createNewLabelInteractor: createNewLabelInteractor,
|
|
||||||
editLabelInteractor: editLabelInteractor,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (resultState is CreateNewLabelSuccess) {
|
if (resultState is CreateNewLabelSuccess) {
|
||||||
_handleCreateNewLabelSuccess(
|
_addLabelToList(resultState.newLabel);
|
||||||
|
if (hasResult) {
|
||||||
|
toastManager.showMessageSuccess(resultState);
|
||||||
|
return resultState.newLabel;
|
||||||
|
}
|
||||||
|
_handleCreateNewLabelSuccessWithoutListUpdate(
|
||||||
success: resultState,
|
success: resultState,
|
||||||
onLabelActionCallback: onLabelActionCallback,
|
onLabelActionCallback: onLabelActionCallback,
|
||||||
shouldPop: shouldPop,
|
shouldPop: shouldPop,
|
||||||
@@ -223,9 +190,11 @@ class LabelController extends BaseController
|
|||||||
} else if (resultState is CreateNewLabelFailure) {
|
} else if (resultState is CreateNewLabelFailure) {
|
||||||
_handleCreateNewLabelFailure(failure: resultState);
|
_handleCreateNewLabelFailure(failure: resultState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleCreateNewLabelSuccess({
|
void _handleCreateNewLabelSuccessWithoutListUpdate({
|
||||||
required CreateNewLabelSuccess success,
|
required CreateNewLabelSuccess success,
|
||||||
OnLabelActionCallback? onLabelActionCallback,
|
OnLabelActionCallback? onLabelActionCallback,
|
||||||
bool shouldPop = false,
|
bool shouldPop = false,
|
||||||
@@ -233,7 +202,6 @@ class LabelController extends BaseController
|
|||||||
if (onLabelActionCallback == null && !shouldPop) {
|
if (onLabelActionCallback == null && !shouldPop) {
|
||||||
toastManager.showMessageSuccess(success);
|
toastManager.showMessageSuccess(success);
|
||||||
}
|
}
|
||||||
_addLabelToList(success.newLabel);
|
|
||||||
if (onLabelActionCallback != null) {
|
if (onLabelActionCallback != null) {
|
||||||
onLabelActionCallback(success.newLabel);
|
onLabelActionCallback(success.newLabel);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import 'package:tmail_ui_user/features/labels/domain/usecases/edit_label_interac
|
|||||||
import 'package:tmail_ui_user/features/labels/domain/usecases/delete_a_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/domain/usecases/get_all_label_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/domain/usecases/get_label_changes_interactor.dart';
|
import 'package:tmail_ui_user/features/labels/domain/usecases/get_label_changes_interactor.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart';
|
|
||||||
import 'package:tmail_ui_user/main/exceptions/thrower/remote_exception_thrower.dart';
|
import 'package:tmail_ui_user/main/exceptions/thrower/remote_exception_thrower.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
@@ -35,11 +34,11 @@ class LabelInteractorBindings extends InteractorsBindings {
|
|||||||
@override
|
@override
|
||||||
void bindingsInteractor() {
|
void bindingsInteractor() {
|
||||||
Get.lazyPut(() => GetAllLabelInteractor(Get.find<LabelRepository>()));
|
Get.lazyPut(() => GetAllLabelInteractor(Get.find<LabelRepository>()));
|
||||||
Get.lazyPut(() => CreateNewLabelInteractor(Get.find<LabelRepository>()));
|
// Use `fenix: true` to avoid GetX dispose or SmartManagement cleanup when using it in dialogs.
|
||||||
Get.lazyPut(() => EditLabelInteractor(Get.find<LabelRepository>()));
|
Get.lazyPut(() => CreateNewLabelInteractor(Get.find<LabelRepository>()), fenix: true);
|
||||||
|
Get.lazyPut(() => EditLabelInteractor(Get.find<LabelRepository>()), fenix: true);
|
||||||
Get.lazyPut(() => DeleteALabelInteractor(Get.find<LabelRepository>()));
|
Get.lazyPut(() => DeleteALabelInteractor(Get.find<LabelRepository>()));
|
||||||
Get.lazyPut(() => GetLabelChangesInteractor(Get.find<LabelRepository>()));
|
Get.lazyPut(() => GetLabelChangesInteractor(Get.find<LabelRepository>()));
|
||||||
Get.lazyPut(() => VerifyNameInteractor());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -12,17 +12,20 @@ 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/routes/route_navigation.dart';
|
||||||
|
|
||||||
typedef OnLabelAsToEmailsAction = Function(List<Label> labels);
|
typedef OnLabelAsToEmailsAction = Function(List<Label> labels);
|
||||||
|
typedef OnCreateALabelAction = Future<Label?> Function();
|
||||||
|
|
||||||
class ChooseLabelModal extends StatefulWidget {
|
class ChooseLabelModal extends StatefulWidget {
|
||||||
final List<Label> labels;
|
final List<Label> labels;
|
||||||
final ImagePaths imagePaths;
|
final ImagePaths imagePaths;
|
||||||
final OnLabelAsToEmailsAction onLabelAsToEmailsAction;
|
final OnLabelAsToEmailsAction onLabelAsToEmailsAction;
|
||||||
|
final OnCreateALabelAction onCreateALabelAction;
|
||||||
|
|
||||||
const ChooseLabelModal({
|
const ChooseLabelModal({
|
||||||
super.key,
|
super.key,
|
||||||
required this.labels,
|
required this.labels,
|
||||||
required this.imagePaths,
|
required this.imagePaths,
|
||||||
required this.onLabelAsToEmailsAction,
|
required this.onLabelAsToEmailsAction,
|
||||||
|
required this.onCreateALabelAction,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -30,84 +33,80 @@ class ChooseLabelModal extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ChooseLabelModalState extends State<ChooseLabelModal> {
|
class _ChooseLabelModalState extends State<ChooseLabelModal> {
|
||||||
|
late final ValueNotifier<List<Label>> _labelListNotifier;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_labelListNotifier = ValueNotifier([...widget.labels]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_labelListNotifier.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final appLocalizations = AppLocalizations.of(context);
|
|
||||||
|
|
||||||
return LayoutBuilder(builder: (_, constraints) {
|
return LayoutBuilder(builder: (_, constraints) {
|
||||||
final currentScreenWidth = constraints.maxWidth;
|
final currentScreenWidth = constraints.maxWidth;
|
||||||
final currentScreenHeight = constraints.maxHeight;
|
final currentScreenHeight = constraints.maxHeight;
|
||||||
final height = math.max(0.0, math.min(currentScreenHeight - 100, 645.0));
|
final height = math.max(0.0, math.min(currentScreenHeight - 100, 645.0));
|
||||||
final width = math.max(0.0, math.min(currentScreenWidth - 32, 536.0));
|
final width = math.max(0.0, math.min(currentScreenWidth - 32, 536.0));
|
||||||
|
|
||||||
Widget bodyWidget = Container(
|
return Center(
|
||||||
decoration: BoxDecoration(
|
child: Container(
|
||||||
color: Colors.white,
|
width: width,
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
height: height,
|
||||||
boxShadow: [
|
decoration: _buildModalDecoration(),
|
||||||
BoxShadow(
|
clipBehavior: Clip.antiAlias,
|
||||||
color: Colors.black.withValues(alpha: 0.08),
|
child: Stack(children: [_buildBodyContent(), _buildCloseButton()]),
|
||||||
blurRadius: 24,
|
|
||||||
offset: const Offset(0, 2),
|
|
||||||
),
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withValues(alpha: 0.08),
|
|
||||||
blurRadius: 2,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
clipBehavior: Clip.antiAlias,
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
_buildTitle(appLocalizations),
|
|
||||||
const Divider(height: 1, color: Colors.black12),
|
|
||||||
_buildSubtitle(appLocalizations),
|
|
||||||
if (widget.labels.isNotEmpty)
|
|
||||||
Expanded(
|
|
||||||
child: ListLabelWithActionModal(
|
|
||||||
labels: widget.labels,
|
|
||||||
imagePaths: widget.imagePaths,
|
|
||||||
onLabelAsToEmailsAction: widget.onLabelAsToEmailsAction,
|
|
||||||
onCloseModal: _onCloseModal,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
else
|
|
||||||
Expanded(
|
|
||||||
child: Center(
|
|
||||||
child: NoLabelYetWidget(imagePaths: widget.imagePaths),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
DefaultCloseButtonWidget(
|
|
||||||
iconClose: widget.imagePaths.icCloseDialog,
|
|
||||||
isAlignTopEnd: false,
|
|
||||||
onTapActionCallback: _onCloseModal,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return Center(child: bodyWidget);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTitle(AppLocalizations appLocalizations) {
|
BoxDecoration _buildModalDecoration() {
|
||||||
|
return BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withValues(alpha: 0.08),
|
||||||
|
blurRadius: 24,
|
||||||
|
offset: const Offset(0, 2),
|
||||||
|
),
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withValues(alpha: 0.08),
|
||||||
|
blurRadius: 2,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBodyContent() {
|
||||||
|
final appLocalizations = AppLocalizations.of(context);
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
_buildHeader(appLocalizations),
|
||||||
|
const Divider(height: 1, color: Colors.black12),
|
||||||
|
_buildSubtitle(appLocalizations),
|
||||||
|
Expanded(child: _buildListOrEmptyState()),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildHeader(AppLocalizations appLocalizations) {
|
||||||
return Container(
|
return Container(
|
||||||
height: 52,
|
height: 52,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
padding: const EdgeInsetsDirectional.symmetric(horizontal: 32),
|
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
child: Text(
|
child: Text(
|
||||||
appLocalizations.chooseLabel,
|
appLocalizations.chooseLabel,
|
||||||
style: ThemeUtils.textStyleHeadingH6().copyWith(
|
style: ThemeUtils.textStyleHeadingH6().copyWith(color: Colors.black),
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@@ -130,6 +129,45 @@ class _ChooseLabelModalState extends State<ChooseLabelModal> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildListOrEmptyState() {
|
||||||
|
return ValueListenableBuilder<List<Label>>(
|
||||||
|
valueListenable: _labelListNotifier,
|
||||||
|
builder: (context, labels, _) {
|
||||||
|
if (labels.isEmpty) {
|
||||||
|
return Center(
|
||||||
|
child: NoLabelYetWidget(
|
||||||
|
imagePaths: widget.imagePaths,
|
||||||
|
onCreateLabel: _onCreateNewLabel,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListLabelWithActionModal(
|
||||||
|
labels: labels,
|
||||||
|
imagePaths: widget.imagePaths,
|
||||||
|
onLabelAsToEmailsAction: widget.onLabelAsToEmailsAction,
|
||||||
|
onCloseModal: _onCloseModal,
|
||||||
|
onCreateALabelAction: _onCreateNewLabel,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildCloseButton() {
|
||||||
|
return DefaultCloseButtonWidget(
|
||||||
|
iconClose: widget.imagePaths.icCloseDialog,
|
||||||
|
isAlignTopEnd: false,
|
||||||
|
onTapActionCallback: _onCloseModal,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onCreateNewLabel() async {
|
||||||
|
final newLabel = await widget.onCreateALabelAction();
|
||||||
|
if (newLabel != null) {
|
||||||
|
_labelListNotifier.value = [..._labelListNotifier.value, newLabel];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _onCloseModal() {
|
void _onCloseModal() {
|
||||||
popBack();
|
popBack();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.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/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/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/model/edit_label_request.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';
|
||||||
@@ -33,6 +34,7 @@ import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification
|
|||||||
import 'package:tmail_ui_user/features/mailbox_creator/domain/state/verify_name_view_state.dart';
|
import 'package:tmail_ui_user/features/mailbox_creator/domain/state/verify_name_view_state.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/features/mailbox_creator/presentation/extensions/validator_failure_extension.dart';
|
import 'package:tmail_ui_user/features/mailbox_creator/presentation/extensions/validator_failure_extension.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/localizations/app_localizations.dart';
|
||||||
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
|
||||||
|
|
||||||
@@ -46,12 +48,9 @@ enum LabelPositiveButtonState {
|
|||||||
|
|
||||||
class CreateNewLabelModal extends StatefulWidget {
|
class CreateNewLabelModal extends StatefulWidget {
|
||||||
final List<Label> labels;
|
final List<Label> labels;
|
||||||
final AccountId accountId;
|
final AccountId? accountId;
|
||||||
final ImagePaths imagePaths;
|
final ImagePaths imagePaths;
|
||||||
final LabelActionType actionType;
|
final LabelActionType actionType;
|
||||||
final CreateNewLabelInteractor createNewLabelInteractor;
|
|
||||||
final EditLabelInteractor editLabelInteractor;
|
|
||||||
final VerifyNameInteractor verifyNameInteractor;
|
|
||||||
final Label? selectedLabel;
|
final Label? selectedLabel;
|
||||||
|
|
||||||
const CreateNewLabelModal({
|
const CreateNewLabelModal({
|
||||||
@@ -59,9 +58,6 @@ class CreateNewLabelModal extends StatefulWidget {
|
|||||||
required this.labels,
|
required this.labels,
|
||||||
required this.accountId,
|
required this.accountId,
|
||||||
required this.imagePaths,
|
required this.imagePaths,
|
||||||
required this.createNewLabelInteractor,
|
|
||||||
required this.editLabelInteractor,
|
|
||||||
required this.verifyNameInteractor,
|
|
||||||
this.actionType = LabelActionType.create,
|
this.actionType = LabelActionType.create,
|
||||||
this.selectedLabel,
|
this.selectedLabel,
|
||||||
});
|
});
|
||||||
@@ -85,10 +81,16 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
List<String> _labelDisplayNameList = <String>[];
|
List<String> _labelDisplayNameList = <String>[];
|
||||||
Color? _selectedColor;
|
Color? _selectedColor;
|
||||||
StreamSubscription? _streamSubscription;
|
StreamSubscription? _streamSubscription;
|
||||||
|
CreateNewLabelInteractor? _createNewLabelInteractor;
|
||||||
|
EditLabelInteractor? _editLabelInteractor;
|
||||||
|
VerifyNameInteractor? _verifyNameInteractor;
|
||||||
|
AccountId? _accountId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_accountId = widget.accountId;
|
||||||
|
_initInteractors();
|
||||||
final selectedLabel = widget.selectedLabel;
|
final selectedLabel = widget.selectedLabel;
|
||||||
final labels = widget.labels;
|
final labels = widget.labels;
|
||||||
if (selectedLabel != null) {
|
if (selectedLabel != null) {
|
||||||
@@ -109,6 +111,18 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _initInteractors() {
|
||||||
|
_createNewLabelInteractor = getBinding<CreateNewLabelInteractor>();
|
||||||
|
_editLabelInteractor = getBinding<EditLabelInteractor>();
|
||||||
|
_verifyNameInteractor = getBinding<VerifyNameInteractor>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _disposeInteractors() {
|
||||||
|
_createNewLabelInteractor = null;
|
||||||
|
_editLabelInteractor = null;
|
||||||
|
_verifyNameInteractor = null;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final appLocalizations = AppLocalizations.of(context);
|
final appLocalizations = AppLocalizations.of(context);
|
||||||
@@ -376,7 +390,10 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String? _verifyLabelName(AppLocalizations appLocalizations, String value) {
|
String? _verifyLabelName(AppLocalizations appLocalizations, String value) {
|
||||||
final result = widget.verifyNameInteractor.execute(
|
if (_verifyNameInteractor == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final result = _verifyNameInteractor!.execute(
|
||||||
value,
|
value,
|
||||||
_validators,
|
_validators,
|
||||||
);
|
);
|
||||||
@@ -459,8 +476,18 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _performCreateLabel(Label newLabel) {
|
void _performCreateLabel(Label newLabel) {
|
||||||
_streamSubscription = widget.createNewLabelInteractor
|
if (_createNewLabelInteractor == null) {
|
||||||
.execute(widget.accountId, newLabel)
|
popBack(result: CreateNewLabelFailure(const InteractorNotInitialized()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_accountId == null) {
|
||||||
|
popBack(result: CreateNewLabelFailure(NotFoundAccountIdException()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_streamSubscription = _createNewLabelInteractor!
|
||||||
|
.execute(_accountId!, newLabel)
|
||||||
.listen(_handleDataStream, onError: _handleErrorStream);
|
.listen(_handleDataStream, onError: _handleErrorStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -511,8 +538,16 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
newLabel: newLabel,
|
newLabel: newLabel,
|
||||||
);
|
);
|
||||||
|
|
||||||
_streamSubscription = widget.editLabelInteractor
|
if (_editLabelInteractor == null) {
|
||||||
.execute(widget.accountId, labelRequest)
|
popBack(result: EditLabelFailure(const InteractorNotInitialized()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_accountId == null) {
|
||||||
|
popBack(result: EditLabelFailure(NotFoundAccountIdException()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_streamSubscription = _editLabelInteractor!
|
||||||
|
.execute(_accountId!, labelRequest)
|
||||||
.listen(_handleDataStream, onError: _handleErrorStream);
|
.listen(_handleDataStream, onError: _handleErrorStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -528,6 +563,8 @@ class _CreateNewLabelModalState extends State<CreateNewLabelModal> {
|
|||||||
_labelDisplayNameList = [];
|
_labelDisplayNameList = [];
|
||||||
_streamSubscription?.cancel();
|
_streamSubscription?.cancel();
|
||||||
_streamSubscription = null;
|
_streamSubscription = null;
|
||||||
|
_accountId = null;
|
||||||
|
_disposeInteractors();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import 'package:core/presentation/resources/image_paths.dart';
|
import 'package:core/utils/app_logger.dart';
|
||||||
import 'package:core/presentation/views/dialog/modal_list_action_button_widget.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
|
import 'package:core/presentation/views/dialog/confirm_dialog_button.dart';
|
||||||
|
import 'package:core/presentation/views/dialog/modal_list_action_button_widget.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||||
import 'package:labels/model/label.dart';
|
import 'package:labels/model/label.dart';
|
||||||
import 'package:tmail_ui_user/features/labels/presentation/widgets/choose_label_modal.dart';
|
import 'package:tmail_ui_user/features/labels/presentation/widgets/choose_label_modal.dart';
|
||||||
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/labels/label_list_item.dart';
|
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/labels/label_list_item.dart';
|
||||||
@@ -11,6 +16,7 @@ class ListLabelWithActionModal extends StatefulWidget {
|
|||||||
final ImagePaths imagePaths;
|
final ImagePaths imagePaths;
|
||||||
final OnLabelAsToEmailsAction onLabelAsToEmailsAction;
|
final OnLabelAsToEmailsAction onLabelAsToEmailsAction;
|
||||||
final VoidCallback onCloseModal;
|
final VoidCallback onCloseModal;
|
||||||
|
final AsyncCallback onCreateALabelAction;
|
||||||
|
|
||||||
const ListLabelWithActionModal({
|
const ListLabelWithActionModal({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -18,6 +24,7 @@ class ListLabelWithActionModal extends StatefulWidget {
|
|||||||
required this.imagePaths,
|
required this.imagePaths,
|
||||||
required this.onLabelAsToEmailsAction,
|
required this.onLabelAsToEmailsAction,
|
||||||
required this.onCloseModal,
|
required this.onCloseModal,
|
||||||
|
required this.onCreateALabelAction,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -26,87 +33,112 @@ class ListLabelWithActionModal extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ListLabelWithActionModalState extends State<ListLabelWithActionModal> {
|
class _ListLabelWithActionModalState extends State<ListLabelWithActionModal> {
|
||||||
final ValueNotifier<bool> _addLabelStateNotifier = ValueNotifier(false);
|
final ValueNotifier<Set<Id>> _selectedIdsNotifier = ValueNotifier({});
|
||||||
final ValueNotifier<List<Label>> _selectedLabelStateNotifier =
|
|
||||||
ValueNotifier([]);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final labelList = widget.labels;
|
|
||||||
final appLocalizations = AppLocalizations.of(context);
|
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(child: _buildLabelListView()),
|
||||||
child: ListView.builder(
|
_buildBottomActionButtons(),
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
itemCount: labelList.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final label = labelList[index];
|
|
||||||
return ValueListenableBuilder(
|
|
||||||
valueListenable: _selectedLabelStateNotifier,
|
|
||||||
builder: (_, selectedLabels, __) {
|
|
||||||
final isSelected = selectedLabels.contains(label);
|
|
||||||
return LabelListItem(
|
|
||||||
label: label,
|
|
||||||
imagePaths: widget.imagePaths,
|
|
||||||
padding: const EdgeInsetsDirectional.only(
|
|
||||||
start: 4,
|
|
||||||
end: 16,
|
|
||||||
),
|
|
||||||
enableSelectedIcon: true,
|
|
||||||
isSelected: isSelected,
|
|
||||||
onOpenLabelCallback: _onToggleLabel,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ValueListenableBuilder(
|
|
||||||
valueListenable: _addLabelStateNotifier,
|
|
||||||
builder: (_, value, __) {
|
|
||||||
return ModalListActionButtonWidget(
|
|
||||||
positiveLabel: appLocalizations.addLabel,
|
|
||||||
negativeLabel: appLocalizations.cancel,
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 25,
|
|
||||||
horizontal: 25,
|
|
||||||
),
|
|
||||||
isPositiveActionEnabled: value,
|
|
||||||
onPositiveAction: _onAddLabel,
|
|
||||||
onNegativeAction: widget.onCloseModal,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onToggleLabel(Label selectedLabel) {
|
Widget _buildLabelListView() {
|
||||||
final currentLabels = _selectedLabelStateNotifier.value;
|
final labels = widget.labels;
|
||||||
|
return ListView.builder(
|
||||||
if (currentLabels.contains(selectedLabel)) {
|
itemCount: labels.length + 1,
|
||||||
_selectedLabelStateNotifier.value =
|
padding: EdgeInsets.zero,
|
||||||
currentLabels.where((label) => label.id != selectedLabel.id).toList();
|
itemBuilder: (_, index) {
|
||||||
} else {
|
if (index == labels.length) {
|
||||||
_selectedLabelStateNotifier.value = [...currentLabels, selectedLabel];
|
return _buildCreateLabelButton();
|
||||||
}
|
}
|
||||||
|
return _buildLabelItem(labels[index]);
|
||||||
_addLabelStateNotifier.value = _selectedLabelStateNotifier.value.isNotEmpty;
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onAddLabel() {
|
Widget _buildLabelItem(Label label) {
|
||||||
widget.onLabelAsToEmailsAction(_selectedLabelStateNotifier.value);
|
return ValueListenableBuilder<Set<Id>>(
|
||||||
|
valueListenable: _selectedIdsNotifier,
|
||||||
|
builder: (_, selectedIds, __) {
|
||||||
|
final isSelected = selectedIds.contains(label.id);
|
||||||
|
return LabelListItem(
|
||||||
|
label: label,
|
||||||
|
imagePaths: widget.imagePaths,
|
||||||
|
padding: const EdgeInsetsDirectional.only(start: 4, end: 16),
|
||||||
|
enableSelectedIcon: true,
|
||||||
|
isSelected: isSelected,
|
||||||
|
onOpenLabelCallback: _onToggleLabel,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildCreateLabelButton() {
|
||||||
|
return Container(
|
||||||
|
height: 36,
|
||||||
|
alignment: AlignmentDirectional.centerStart,
|
||||||
|
margin:
|
||||||
|
const EdgeInsetsDirectional.only(start: 4, end: 8, top: 4, bottom: 8),
|
||||||
|
child: ConfirmDialogButton(
|
||||||
|
label: AppLocalizations.of(context).createALabel,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
textColor: AppColor.primaryMain,
|
||||||
|
padding: const EdgeInsetsDirectional.only(start: 10, end: 16),
|
||||||
|
icon: widget.imagePaths.icAddIdentity,
|
||||||
|
onTapAction: widget.onCreateALabelAction,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBottomActionButtons() {
|
||||||
|
final localizations = AppLocalizations.of(context);
|
||||||
|
return ValueListenableBuilder<Set<Id>>(
|
||||||
|
valueListenable: _selectedIdsNotifier,
|
||||||
|
builder: (_, selectedIds, __) {
|
||||||
|
return ModalListActionButtonWidget(
|
||||||
|
positiveLabel: localizations.addLabel,
|
||||||
|
negativeLabel: localizations.cancel,
|
||||||
|
padding: const EdgeInsets.all(25),
|
||||||
|
isPositiveActionEnabled: selectedIds.isNotEmpty,
|
||||||
|
onPositiveAction: _onConfirmAction,
|
||||||
|
onNegativeAction: widget.onCloseModal,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onToggleLabel(Label label) {
|
||||||
|
final currentIds = Set<Id>.from(_selectedIdsNotifier.value);
|
||||||
|
final labelId = label.id;
|
||||||
|
if (labelId == null) {
|
||||||
|
logWarning('ListLabelWithActionModal::_onToggleLabel: labelId is null for label: $label');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currentIds.contains(labelId)) {
|
||||||
|
currentIds.remove(labelId);
|
||||||
|
} else {
|
||||||
|
currentIds.add(labelId);
|
||||||
|
}
|
||||||
|
_selectedIdsNotifier.value = currentIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onConfirmAction() {
|
||||||
|
final selectedLabels = widget.labels
|
||||||
|
.where((label) => _selectedIdsNotifier.value.contains(label.id))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
widget.onLabelAsToEmailsAction(selectedLabels);
|
||||||
widget.onCloseModal();
|
widget.onCloseModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_addLabelStateNotifier.dispose();
|
_selectedIdsNotifier.dispose();
|
||||||
_selectedLabelStateNotifier.dispose();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:core/presentation/extensions/color_extension.dart';
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
import 'package:core/presentation/resources/image_paths.dart';
|
import 'package:core/presentation/resources/image_paths.dart';
|
||||||
import 'package:core/presentation/utils/theme_utils.dart';
|
import 'package:core/presentation/utils/theme_utils.dart';
|
||||||
import 'package:core/presentation/views/dialog/confirm_dialog_button.dart';
|
import 'package:core/presentation/views/dialog/confirm_dialog_button.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
|
||||||
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
|
||||||
|
|
||||||
class NoLabelYetWidget extends StatelessWidget {
|
class NoLabelYetWidget extends StatelessWidget {
|
||||||
final ImagePaths imagePaths;
|
final ImagePaths imagePaths;
|
||||||
final VoidCallback? onCreateLabel;
|
final AsyncCallback? onCreateLabel;
|
||||||
|
|
||||||
const NoLabelYetWidget({
|
const NoLabelYetWidget({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -20,22 +21,22 @@ class NoLabelYetWidget extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final appLocalizations = AppLocalizations.of(context);
|
final appLocalizations = AppLocalizations.of(context);
|
||||||
|
|
||||||
return Container(
|
return Center(
|
||||||
margin: const EdgeInsets.all(16),
|
|
||||||
constraints: const BoxConstraints(maxWidth: 352),
|
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
physics: const ClampingScrollPhysics(),
|
physics: const ClampingScrollPhysics(),
|
||||||
child: Column(
|
padding: const EdgeInsets.all(16),
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: ConstrainedBox(
|
||||||
children: [
|
constraints: const BoxConstraints(maxWidth: 352),
|
||||||
_IconNoTag(icon: imagePaths.icNoTag),
|
child: Column(
|
||||||
_LabelNoTag(
|
mainAxisSize: MainAxisSize.min,
|
||||||
text: appLocalizations.noLabelsYet,
|
children: [
|
||||||
textStyle: ThemeUtils.textStyleInter600(),
|
_IconNoTag(icon: imagePaths.icNoTag),
|
||||||
padding: const EdgeInsetsDirectional.only(top: 16),
|
_LabelNoTag(
|
||||||
),
|
text: appLocalizations.noLabelsYet,
|
||||||
Flexible(
|
textStyle: ThemeUtils.textStyleInter600(),
|
||||||
child: _LabelNoTag(
|
padding: const EdgeInsetsDirectional.only(top: 16),
|
||||||
|
),
|
||||||
|
_LabelNoTag(
|
||||||
text: appLocalizations.noLabelsYetMessageDescriptions,
|
text: appLocalizations.noLabelsYetMessageDescriptions,
|
||||||
textStyle: ThemeUtils.textStyleInter400.copyWith(
|
textStyle: ThemeUtils.textStyleInter400.copyWith(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
@@ -45,22 +46,22 @@ class NoLabelYetWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
padding: const EdgeInsetsDirectional.only(top: 36),
|
padding: const EdgeInsetsDirectional.only(top: 36),
|
||||||
),
|
),
|
||||||
),
|
if (onCreateLabel != null)
|
||||||
if (onCreateLabel != null)
|
Container(
|
||||||
Container(
|
constraints: const BoxConstraints(minWidth: 186),
|
||||||
constraints: const BoxConstraints(minWidth: 186),
|
height: 48,
|
||||||
height: 48,
|
margin: const EdgeInsetsDirectional.only(top: 16),
|
||||||
margin: const EdgeInsetsDirectional.only(top: 16),
|
child: ConfirmDialogButton(
|
||||||
child: ConfirmDialogButton(
|
label: appLocalizations.createALabel,
|
||||||
label: appLocalizations.createALabel,
|
backgroundColor: Colors.white,
|
||||||
backgroundColor: Colors.white,
|
textColor: AppColor.primaryMain,
|
||||||
textColor: AppColor.primaryMain,
|
borderColor: AppColor.primaryMain,
|
||||||
borderColor: AppColor.primaryMain,
|
icon: imagePaths.icAddIdentity,
|
||||||
icon: imagePaths.icAddIdentity,
|
onTapAction: onCreateLabel!,
|
||||||
onTapAction: onCreateLabel!,
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user