From 6554ca5f23aac0b4b532e3e45d2d7430113b6850 Mon Sep 17 00:00:00 2001 From: dab246 Date: Wed, 26 Oct 2022 15:00:48 +0700 Subject: [PATCH] TF-899 Implement create mailbox folder in destination picker --- assets/images/ic_create_new_folder.svg | 8 + .../extensions/color_extension.dart | 3 + .../presentation/resources/image_paths.dart | 1 + .../base/base_mailbox_controller.dart | 7 + .../destination_picker_bindings.dart | 9 + .../destination_picker_controller.dart | 309 +++++++++++++++++- .../presentation/destination_picker_view.dart | 293 +++++++++++------ .../model/destination_screen_type.dart | 17 + .../top_bar_destination_picker_builder.dart | 166 +++++++++- .../presentation/mailbox_controller.dart | 3 +- .../mailbox/presentation/mailbox_view.dart | 2 +- .../presentation/mailbox_view_web.dart | 3 +- .../presentation/model/mailbox_node.dart | 2 + .../widgets/mailbox_folder_tile_builder.dart | 10 +- .../widgets/mailbox_search_tile_builder.dart | 60 ++-- .../mailbox_creator_controller.dart | 20 +- .../presentation/mailbox_creator_view.dart | 2 + lib/l10n/intl_messages.arb | 14 +- lib/main/localizations/app_localizations.dart | 12 + 19 files changed, 775 insertions(+), 166 deletions(-) create mode 100644 assets/images/ic_create_new_folder.svg create mode 100644 lib/features/destination_picker/presentation/model/destination_screen_type.dart diff --git a/assets/images/ic_create_new_folder.svg b/assets/images/ic_create_new_folder.svg new file mode 100644 index 000000000..65fd73bab --- /dev/null +++ b/assets/images/ic_create_new_folder.svg @@ -0,0 +1,8 @@ + + + + diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart index b6c3a3fb4..77d34b6aa 100644 --- a/core/lib/presentation/extensions/color_extension.dart +++ b/core/lib/presentation/extensions/color_extension.dart @@ -154,6 +154,9 @@ extension AppColor on Color { static const colorEmailAddressFull = Color(0xFF818C99); static const colorTitleHeaderAttachment = Color(0xFF818C99); static const colorAttachmentIcon = Color(0xFFAEB7C2); + static const colorDividerDestinationPicker = Color(0x1F000000); + static const colorItemAlreadySelected = Color(0xFF818C99); + static const colorItemSelected = Color(0xFFF2F3F5); static const mapGradientColor = [ [Color(0xFF21D4FD), Color(0xFFB721FF)], diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart index 0c7cd3fc9..7fbbeb054 100644 --- a/core/lib/presentation/resources/image_paths.dart +++ b/core/lib/presentation/resources/image_paths.dart @@ -166,6 +166,7 @@ class ImagePaths { String get icAppDashboard => _getImagePath('ic_app_dashboard.svg'); String get icRequestReadReceipt => _getImagePath('ic_request_read_receipt.svg'); String get icReadReceiptMessage => _getImagePath('ic_read_receipt_message.svg'); + String get icCreateNewFolder => _getImagePath('ic_create_new_folder.svg'); String _getImagePath(String imageName) { return AssetsPaths.images + imageName; diff --git a/lib/features/base/base_mailbox_controller.dart b/lib/features/base/base_mailbox_controller.dart index a430aaecf..3745a716c 100644 --- a/lib/features/base/base_mailbox_controller.dart +++ b/lib/features/base/base_mailbox_controller.dart @@ -74,6 +74,13 @@ abstract class BaseMailboxController extends BaseController { } } + void unAllSelectedMailboxNode() { + defaultMailboxTree.value.updateNodesUIMode(selectMode: SelectMode.INACTIVE); + folderMailboxTree.value.updateNodesUIMode(selectMode: SelectMode.INACTIVE); + defaultMailboxTree.refresh(); + folderMailboxTree.refresh(); + } + MailboxNode? findMailboxNodeById(MailboxId mailboxId) { final mailboxNode = defaultMailboxTree.value.findNode((node) => node.item.id == mailboxId); if (mailboxNode != null) { diff --git a/lib/features/destination_picker/presentation/destination_picker_bindings.dart b/lib/features/destination_picker/presentation/destination_picker_bindings.dart index bccf76b05..8f64400c6 100644 --- a/lib/features/destination_picker/presentation/destination_picker_bindings.dart +++ b/lib/features/destination_picker/presentation/destination_picker_bindings.dart @@ -16,9 +16,12 @@ import 'package:tmail_ui_user/features/mailbox/data/local/mailbox_cache_manager. import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_isolate_worker.dart'; import 'package:tmail_ui_user/features/mailbox/data/repository/mailbox_repository_impl.dart'; import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/usecases/create_new_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/get_all_mailbox_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/usecases/refresh_all_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/search_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree_builder.dart'; +import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_name_interactor.dart'; import 'package:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart'; import 'package:tmail_ui_user/features/thread/data/datasource_impl/thread_datasource_impl.dart'; import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart'; @@ -41,6 +44,9 @@ class DestinationPickerBindings extends BaseBindings { Get.lazyPut(() => DestinationPickerController( Get.find(), Get.find(), + Get.find(), + Get.find(), + Get.find(), Get.find(), )); } @@ -66,6 +72,9 @@ class DestinationPickerBindings extends BaseBindings { void bindingsInteractor() { Get.lazyPut(() => GetAllMailboxInteractor(Get.find())); Get.lazyPut(() => SearchMailboxInteractor()); + Get.lazyPut(() => CreateNewMailboxInteractor(Get.find())); + Get.lazyPut(() => RefreshAllMailboxInteractor(Get.find())); + Get.lazyPut(() => VerifyNameInteractor()); } @override diff --git a/lib/features/destination_picker/presentation/destination_picker_controller.dart b/lib/features/destination_picker/presentation/destination_picker_controller.dart index e1fe0f740..be8b4df03 100644 --- a/lib/features/destination_picker/presentation/destination_picker_controller.dart +++ b/lib/features/destination_picker/presentation/destination_picker_controller.dart @@ -1,53 +1,106 @@ -import 'package:core/core.dart'; +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:core/presentation/utils/app_toast.dart'; +import 'package:core/presentation/utils/responsive_utils.dart'; +import 'package:core/utils/build_utils.dart'; import 'package:dartz/dartz.dart'; -import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/error/method/error_method_response.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; -import 'package:model/model.dart'; +import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; +import 'package:model/mailbox/expand_mode.dart'; +import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:tmail_ui_user/features/base/base_mailbox_controller.dart'; import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart'; +import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_screen_type.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/state/create_new_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/get_all_mailboxes_state.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/state/refresh_changes_all_mailboxes_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/search_mailbox_state.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/usecases/create_new_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/get_all_mailbox_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/usecases/refresh_all_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/domain/usecases/search_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_categories.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_categories_expand_mode.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.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/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/presentation/extensions/validator_failure_extension.dart'; import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; import 'package:tmail_ui_user/features/thread/presentation/model/search_state.dart'; import 'package:tmail_ui_user/features/thread/presentation/model/search_status.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; +import 'package:uuid/uuid.dart'; typedef OnSelectedMailboxCallback = Function(PresentationMailbox? destinationMailbox); class DestinationPickerController extends BaseMailboxController { + final _uuid = Get.find(); + final _appToast = Get.find(); + final _imagePaths = Get.find(); + final _responsiveUtils = Get.find(); + final GetAllMailboxInteractor _getAllMailboxInteractor; final SearchMailboxInteractor _searchMailboxInteractor; + final CreateNewMailboxInteractor _createNewMailboxInteractor; + final RefreshAllMailboxInteractor _refreshAllMailboxInteractor; + final VerifyNameInteractor _verifyNameInteractor; final mailboxAction = Rxn(); final listMailboxSearched = [].obs; final searchState = SearchState.initial().obs; final searchQuery = SearchQuery.initial().obs; final mailboxCategoriesExpandMode = MailboxCategoriesExpandMode.initial().obs; + final destinationScreenType = DestinationScreenType.destinationPicker.obs; + final mailboxDestination = Rxn(); + final newNameMailbox = Rxn(); + FocusNode? nameInputFocusNode; + TextEditingController? nameInputController; + TextEditingController? searchInputController; + FocusNode? searchFocus; DestinationPickerArguments? arguments; AccountId? accountId; MailboxId? mailboxIdSelected; OnSelectedMailboxCallback? onSelectedMailboxCallback; VoidCallback? onDismissDestinationPicker; - final searchInputController = TextEditingController(); - final searchFocus = FocusNode(); + List listMailboxNameAsStringExist = []; DestinationPickerController( this._getAllMailboxInteractor, this._searchMailboxInteractor, + this._createNewMailboxInteractor, + this._refreshAllMailboxInteractor, + this._verifyNameInteractor, treeBuilder, ) : super(treeBuilder); + @override + void onInit() { + super.onInit(); + nameInputFocusNode = FocusNode(); + nameInputController = TextEditingController(); + searchInputController = TextEditingController(); + searchFocus = FocusNode(); + searchFocus?.unfocus(); + nameInputFocusNode?.unfocus(); + } + @override void onReady() { super.onReady(); @@ -69,6 +122,8 @@ class DestinationPickerController extends BaseMailboxController { } else { buildTree(success.mailboxList); } + } else if (success is RefreshChangesAllMailboxSuccess) { + refreshTree(success.mailboxList); } }); } @@ -79,11 +134,15 @@ class DestinationPickerController extends BaseMailboxController { (failure) { if (failure is SearchMailboxFailure) { _searchMailboxFailure(failure); + } else if (failure is CreateNewMailboxFailure) { + _createNewMailboxFailure(failure); } }, (success) { if (success is SearchMailboxSuccess) { _searchMailboxSuccess(success); + } else if (success is CreateNewMailboxSuccess) { + _createNewMailboxSuccess(success); } } ); @@ -94,8 +153,7 @@ class DestinationPickerController extends BaseMailboxController { @override void onClose() { - searchInputController.dispose(); - searchFocus.dispose(); + _disposeWidget(); super.onClose(); } @@ -105,6 +163,12 @@ class DestinationPickerController extends BaseMailboxController { } } + void _refreshMailboxChanges(jmap.State currentMailboxState) { + if (accountId != null) { + consumeState(_refreshAllMailboxInteractor.execute(accountId!, currentMailboxState)); + } + } + void toggleMailboxCategories(MailboxCategories categories) { switch(categories) { case MailboxCategories.exchange: @@ -128,17 +192,84 @@ class DestinationPickerController extends BaseMailboxController { searchState.value = searchState.value.enableSearchState(); } + void setNewNameMailbox(String newName) => newNameMailbox.value = newName; + + String? getErrorInputNameString(BuildContext context) { + final nameMailbox = newNameMailbox.value; + + if (nameInputFocusNode?.hasFocus == false && nameMailbox == null) { + return null; + } + + return _verifyNameInteractor.execute( + nameMailbox, + [ + EmptyNameValidator(), + DuplicateNameValidator(listMailboxNameAsStringExist), + ] + ).fold( + (failure) { + if (failure is VerifyNameFailure) { + return failure.getMessage(context); + } else { + return null; + } + }, + (success) => null + ); + } + + bool isCreateMailboxValidated(BuildContext context) { + final nameValidated = getErrorInputNameString(context); + + if (nameInputFocusNode?.hasFocus == false && newNameMailbox.value == null) { + return false; + } + + if (nameValidated?.isNotEmpty == true) { + return false; + } + return true; + } + + void _createListMailboxNameAsStringInMailboxLocation() { + if (mailboxDestination.value == null || + mailboxDestination.value == PresentationMailbox.unifiedMailbox) { + final allChildrenAtMailboxLocation = (defaultMailboxTree.value.root.childrenItems ?? []) + + (folderMailboxTree.value.root.childrenItems ?? []); + if (allChildrenAtMailboxLocation.isNotEmpty) { + listMailboxNameAsStringExist = allChildrenAtMailboxLocation + .where((mailboxNode) => mailboxNode.nameNotEmpty) + .map((mailboxNode) => mailboxNode.mailboxNameAsString) + .toList(); + } else { + listMailboxNameAsStringExist = []; + } + } else { + final mailboxNodeLocation = findMailboxNodeById(mailboxDestination.value!.id); + if (mailboxNodeLocation != null && mailboxNodeLocation.childrenItems?.isNotEmpty == true) { + final allChildrenAtMailboxLocation = mailboxNodeLocation.childrenItems!; + listMailboxNameAsStringExist = allChildrenAtMailboxLocation + .where((mailboxNode) => mailboxNode.nameNotEmpty) + .map((mailboxNode) => mailboxNode.mailboxNameAsString) + .toList(); + } else { + listMailboxNameAsStringExist = []; + } + } + } + void disableSearch(BuildContext context) { listMailboxSearched.clear(); searchState.value = searchState.value.disableSearchState(); searchQuery.value = SearchQuery.initial(); - searchInputController.clear(); + searchInputController?.clear(); FocusScope.of(context).unfocus(); } void clearSearchText() { searchQuery.value = SearchQuery.initial(); - searchFocus.requestFocus(); + searchFocus?.requestFocus(); listMailboxSearched.clear(); } @@ -164,16 +295,166 @@ class DestinationPickerController extends BaseMailboxController { listMailboxSearched.clear(); } - void selectMailboxAction(PresentationMailbox? destinationMailbox) { - if (BuildUtils.isWeb) { - onSelectedMailboxCallback?.call(destinationMailbox); + void openCreateNewMailboxView(BuildContext context) async { + if (mailboxDestination.value == null) { + _appToast.showBottomToast( + currentOverlayContext!, + AppLocalizations.of(context).toastMessageErrorNotSelectedFolderWhenCreateNewMailbox, + leadingIcon: SvgPicture.asset( + _imagePaths.icNotConnection, + width: 24, + height: 24, + color: Colors.white, + fit: BoxFit.fill), + backgroundColor: AppColor.toastErrorBackgroundColor, + textColor: Colors.white, + textActionColor: Colors.white, + maxWidth: _responsiveUtils.getMaxWidthToast(currentContext!)); } else { - popBack(result: destinationMailbox); + destinationScreenType.value = DestinationScreenType.createNewMailbox; + _createListMailboxNameAsStringInMailboxLocation(); } } - void closeDestinationPicker() { + void _dispatchCreateNewMailboxFolder( + AccountId accountId, + CreateNewMailboxRequest request + ) async { + consumeState(_createNewMailboxInteractor.execute(accountId, request)); + } + + void _createNewMailboxSuccess(CreateNewMailboxSuccess success) { + if (currentContext != null) { + disableSearch(currentContext!); + } + + if (success.currentMailboxState != null) { + _refreshMailboxChanges(success.currentMailboxState!); + } else { + getAllMailboxAction(); + } + } + + void _createNewMailboxFailure(CreateNewMailboxFailure failure) { + if (currentOverlayContext != null && currentContext != null) { + final exception = failure.exception; + var messageError = AppLocalizations.of(currentContext!).create_new_mailbox_failure; + if (exception is ErrorMethodResponse) { + messageError = exception.description ?? AppLocalizations.of(currentContext!).create_new_mailbox_failure; + } + + _appToast.showBottomToast( + currentOverlayContext!, + messageError, + leadingIcon: SvgPicture.asset( + _imagePaths.icNotConnection, + width: 24, + height: 24, + color: Colors.white, + fit: BoxFit.fill), + backgroundColor: AppColor.toastErrorBackgroundColor, + textColor: Colors.white, + textActionColor: Colors.white, + maxWidth: _responsiveUtils.getMaxWidthToast(currentContext!)); + } + } + + void selectMailboxAction( + PresentationMailbox? presentationMailbox, + {MailboxNode? mailboxNode} + ) { + if (mailboxDestination.value == presentationMailbox) { + return; + } + mailboxDestination.value = presentationMailbox; + if (presentationMailbox == null || + presentationMailbox.id == PresentationMailbox.unifiedMailbox.id) { + unAllSelectedMailboxNode(); + } else { + if (mailboxNode != null) { + unAllSelectedMailboxNode(); + selectMailboxNode(mailboxNode); + } else { + final matchedMailboxNode = findMailboxNodeById(presentationMailbox.id); + if (matchedMailboxNode != null) { + unAllSelectedMailboxNode(); + selectMailboxNode(matchedMailboxNode); + } + } + } + } + + void dispatchSelectMailboxDestination(BuildContext context) { + FocusScope.of(context).unfocus(); + + if (mailboxDestination.value == null) { + _appToast.showBottomToast( + currentOverlayContext!, + AppLocalizations.of(context).toastMessageErrorNotSelectedFolderWhenCreateNewMailbox, + leadingIcon: SvgPicture.asset( + _imagePaths.icNotConnection, + width: 24, + height: 24, + color: Colors.white, + fit: BoxFit.fill), + backgroundColor: AppColor.toastErrorBackgroundColor, + textColor: Colors.white, + textActionColor: Colors.white, + maxWidth: _responsiveUtils.getMaxWidthToast(currentContext!)); + return; + } + if (BuildUtils.isWeb) { + _disposeWidget(); + onSelectedMailboxCallback?.call(mailboxDestination.value); + } else { + popBack(result: mailboxDestination.value); + } + } + + void createNewMailboxAction(BuildContext context) { + FocusScope.of(context).unfocus(); + + final nameMailbox = newNameMailbox.value; + if (nameMailbox != null && nameMailbox.isNotEmpty) { + final generateCreateId = Id(_uuid.v1()); + final parentId = mailboxDestination.value == PresentationMailbox.unifiedMailbox + ? null + : mailboxDestination.value?.id; + + _dispatchCreateNewMailboxFolder( + accountId!, + CreateNewMailboxRequest( + generateCreateId, + MailboxName(nameMailbox), + parentId: parentId)); + } + + backToDestinationScreen(context); + } + + void backToDestinationScreen(BuildContext context) { + nameInputController?.clear(); + FocusScope.of(context).unfocus(); + destinationScreenType.value = DestinationScreenType.destinationPicker; + } + + void _disposeWidget() { + nameInputFocusNode?.dispose(); + nameInputFocusNode = null; + nameInputController?.dispose(); + nameInputController = null; + searchFocus?.dispose(); + searchFocus = null; + searchInputController?.dispose(); + searchInputController = null; + } + + void closeDestinationPicker(BuildContext context) { + FocusScope.of(context).unfocus(); + + if (BuildUtils.isWeb) { + _disposeWidget(); onDismissDestinationPicker?.call(); } else { popBack(); diff --git a/lib/features/destination_picker/presentation/destination_picker_view.dart b/lib/features/destination_picker/presentation/destination_picker_view.dart index ef0fdcfef..7abca86af 100644 --- a/lib/features/destination_picker/presentation/destination_picker_view.dart +++ b/lib/features/destination_picker/presentation/destination_picker_view.dart @@ -6,6 +6,7 @@ import 'package:core/presentation/utils/style_utils.dart'; import 'package:core/presentation/views/button/icon_button_web.dart'; import 'package:core/presentation/views/list/tree_view.dart'; import 'package:core/presentation/views/search/search_bar_view.dart'; +import 'package:core/presentation/views/text/text_field_builder.dart'; import 'package:core/utils/build_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -18,6 +19,7 @@ import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; import 'package:tmail_ui_user/features/destination_picker/presentation/destination_picker_controller.dart'; import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart'; +import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_screen_type.dart'; import 'package:tmail_ui_user/features/destination_picker/presentation/widgets/top_bar_destination_picker_builder.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_categories.dart'; @@ -25,6 +27,7 @@ import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_displa import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_folder_tile_builder.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_search_tile_builder.dart'; +import 'package:tmail_ui_user/features/mailbox_creator/presentation/widgets/create_mailbox_name_input_decoration_builder.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/search_app_bar_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; @@ -60,7 +63,7 @@ class DestinationPickerView extends GetWidget return PointerInterceptor( child: GestureDetector( - onTap: () => controller.closeDestinationPicker(), + onTap: () => controller.closeDestinationPicker(context), child: Card( margin: EdgeInsets.zero, borderOnForeground: false, @@ -101,11 +104,47 @@ class DestinationPickerView extends GetWidget right: !BuildUtils.isWeb && _responsiveUtils.isLandscapeMobile(context), child: Column(children: [ Obx(() => TopBarDestinationPickerBuilder( - controller.mailboxAction.value, - onCloseAction: () => controller.closeDestinationPicker())), - Obx(() => controller.isSearchActive() - ? _buildInputSearchFormWidget(context) - : const SizedBox.shrink()), + controller.mailboxAction.value, + controller.destinationScreenType.value, + mailboxIdDestination: controller.mailboxDestination.value?.id, + isCreateMailboxValidated: controller.isCreateMailboxValidated(context), + onCloseAction: () => + controller.closeDestinationPicker(context), + onBackToAction: () => + controller.backToDestinationScreen(context), + onSelectedMailboxDestinationAction: () => + controller.dispatchSelectMailboxDestination(context), + onCreateNewMailboxAction: () => + controller.createNewMailboxAction(context), + onOpenCreateNewMailboxScreenAction: () => + controller.openCreateNewMailboxView(context))), + const Divider( + color: AppColor.colorDividerDestinationPicker, + height: 1), + Obx(() { + if (controller.destinationScreenType.value == DestinationScreenType.destinationPicker) { + return controller.isSearchActive() + ? _buildInputSearchFormWidget(context) + : const SizedBox.shrink(); + } else { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildCreateMailboxNameInput(context), + Padding( + padding: const EdgeInsets.only(left: 24, right: 24, bottom: 12), + child: Text( + AppLocalizations.of(context).selectParentFolder, + textAlign: TextAlign.left, + style: const TextStyle( + fontSize: 13, + color: AppColor.colorHintSearchBar, + fontWeight: FontWeight.normal)), + ) + ]); + } + }), Expanded(child: Container( color: Colors.white, child: RefreshIndicator( @@ -126,6 +165,29 @@ class DestinationPickerView extends GetWidget ); } + Widget _buildCreateMailboxNameInput(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + child: Obx(() => (TextFieldBuilder() + ..key(const Key('create_mailbox_name_input')) + ..onChange((value) => controller.setNewNameMailbox(value)) + ..keyboardType(TextInputType.visiblePassword) + ..cursorColor(AppColor.colorTextButton) + ..addController(controller.nameInputController) + ..maxLines(1) + ..textStyle(const TextStyle( + color: AppColor.colorNameEmail, + fontSize: 16, + overflow: CommonTextStyle.defaultTextOverFlow)) + ..addFocusNode(controller.nameInputFocusNode) + ..textDecoration((CreateMailboxNameInputDecorationBuilder() + ..setHintText(AppLocalizations.of(context).hint_input_create_new_mailbox) + ..setErrorText(controller.getErrorInputNameString(context))) + .build())) + .build()) + ); + } + Widget _buildLoadingView() { return Obx(() => controller.viewState.value.fold( (failure) => const SizedBox.shrink(), @@ -144,7 +206,8 @@ class DestinationPickerView extends GetWidget return SingleChildScrollView( physics: const ClampingScrollPhysics(), child: Column(children: [ - if (actions?.canSearch() == true) + if (actions?.canSearch() == true && + controller.destinationScreenType.value == DestinationScreenType.destinationPicker) SearchBarView( _imagePaths, margin: const EdgeInsets.all(12), @@ -154,21 +217,21 @@ class DestinationPickerView extends GetWidget if (actions?.hasAllMailboxDefault() == true) _buildAllMailboxes(context, actions, mailboxIdSelected), Obx(() => controller.defaultMailboxHasChild - ? _buildMailboxCategory( - context, - MailboxCategories.exchange, - controller.defaultRootNode, - actions, - mailboxIdSelected) - : const SizedBox.shrink()), + ? _buildMailboxCategory( + context, + MailboxCategories.exchange, + controller.defaultRootNode, + actions, + mailboxIdSelected) + : const SizedBox.shrink()), Obx(() => controller.folderMailboxHasChild - ? _buildMailboxCategory( - context, - MailboxCategories.folders, - controller.folderRootNode, - actions, - mailboxIdSelected) - : const SizedBox.shrink()), + ? _buildMailboxCategory( + context, + MailboxCategories.folders, + controller.folderRootNode, + actions, + mailboxIdSelected) + : const SizedBox.shrink()), const SizedBox(height: 12), ]) ); @@ -182,15 +245,17 @@ class DestinationPickerView extends GetWidget MailboxId? mailboxIdSelected ) { return Container( - color: Colors.white, - child: TreeView( - key: Key('${categories.keyValue}_mailbox_list'), - children: _buildListChildTileWidget( - context, - mailboxNode, - mailboxIdSelected, - lastNode: mailboxNode.childrenItems?.last, - actions: actions))); + color: Colors.white, + child: TreeView( + key: Key('${categories.keyValue}_mailbox_list'), + children: _buildListChildTileWidget( + context, + mailboxNode, + mailboxIdSelected, + lastNode: mailboxNode.childrenItems?.last, + actions: actions) + ) + ); } List _buildListChildTileWidget( @@ -203,34 +268,41 @@ class DestinationPickerView extends GetWidget } ) { return parentNode.childrenItems - ?.map((mailboxNode) => mailboxNode.hasChildren() - ? TreeViewChild( - context, - key: const Key('children_tree_mailbox_child'), - isExpanded: mailboxNode.expandMode == ExpandMode.EXPAND, - parent: (MailBoxFolderTileBuilder( - context, - _imagePaths, - mailboxNode, - lastNode: lastNode, - mailboxActions: actions, - mailboxIdSelected: mailboxIdSelected, - mailboxDisplayed: MailboxDisplayed.destinationPicker) - ..addOnOpenMailboxFolderClick(_handleOpenMailboxClick) - ..addOnExpandFolderActionClick((mailboxNode) => controller.toggleMailboxFolder(mailboxNode))) - .build(), - children: _buildListChildTileWidget(context, mailboxNode, mailboxIdSelected, actions: actions) - ).build() - : (MailBoxFolderTileBuilder( - context, - _imagePaths, - mailboxNode, - lastNode: lastNode, - mailboxDisplayed: MailboxDisplayed.destinationPicker, - mailboxIdSelected: mailboxIdSelected, - mailboxActions: actions) - ..addOnOpenMailboxFolderClick(_handleOpenMailboxClick)) - .build()) + ?.map((mailboxNode) { + if (mailboxNode.hasChildren()) { + return TreeViewChild( + context, + key: const Key('children_tree_mailbox_child'), + isExpanded: mailboxNode.expandMode == ExpandMode.EXPAND, + parent: (MailBoxFolderTileBuilder( + context, + _imagePaths, + mailboxNode, + lastNode: lastNode, + mailboxActions: actions, + mailboxIdAlreadySelected: mailboxIdSelected, + mailboxDisplayed: MailboxDisplayed.destinationPicker) + ..addOnOpenMailboxFolderClick(_handleOpenMailboxNodeClick) + ..addOnExpandFolderActionClick((mailboxNode) => controller.toggleMailboxFolder(mailboxNode)) + ).build(), + children: _buildListChildTileWidget( + context, + mailboxNode, + mailboxIdSelected, + actions: actions) + ).build(); + } else { + return (MailBoxFolderTileBuilder( + context, + _imagePaths, + mailboxNode, + lastNode: lastNode, + mailboxDisplayed: MailboxDisplayed.destinationPicker, + mailboxIdAlreadySelected: mailboxIdSelected, + mailboxActions: actions) + ..addOnOpenMailboxFolderClick(_handleOpenMailboxNodeClick) + ).build(); + }}) .toList() ?? []; } @@ -250,7 +322,6 @@ class DestinationPickerView extends GetWidget color: Colors.white) : null, child: ListView.builder( - padding: const EdgeInsets.only(left: 16, right: 8), key: const Key('list_mailbox_searched'), itemCount: controller.listMailboxSearched.length, shrinkWrap: true, @@ -262,10 +333,11 @@ class DestinationPickerView extends GetWidget _responsiveUtils, controller.listMailboxSearched[index], mailboxActions: actions, - mailboxIdSelected: mailboxIdSelected, + mailboxIdAlreadySelected: mailboxIdSelected, mailboxDisplayed: MailboxDisplayed.destinationPicker, - lastMailbox: controller.listMailboxSearched.last) - ..addOnOpenMailboxAction((mailbox) => controller.selectMailboxAction(mailbox))) + mailboxIdDestination: controller.mailboxDestination.value?.id) + ..addOnOpenMailboxAction((mailbox) => + _handleOpenPresentationMailboxClick(context, mailbox))) .build()) ) )); @@ -276,47 +348,54 @@ class DestinationPickerView extends GetWidget MailboxActions? actions, MailboxId? mailboxIdSelected ) { - return Material( + return Obx(() { + return Material( color: Colors.transparent, child: InkWell( - onTap: () => controller.selectMailboxAction(PresentationMailbox.unifiedMailbox), + onTap: () => _handleOpenPresentationMailboxClick( + context, + PresentationMailbox.unifiedMailbox), child: Container( - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), - child: Row(children: [ - SvgPicture.asset( - _imagePaths.icFolderMailbox, - width: BuildUtils.isWeb ? 20 : 24, - height: BuildUtils.isWeb ? 20 : 24, + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), + color: controller.mailboxDestination.value == PresentationMailbox.unifiedMailbox + ? AppColor.colorItemSelected + : Colors.transparent, + child: Row(children: [ + SvgPicture.asset( + _imagePaths.icFolderMailbox, + width: BuildUtils.isWeb ? 20 : 24, + height: BuildUtils.isWeb ? 20 : 24, + fit: BoxFit.fill), + const SizedBox(width: 8), + Expanded(child: Text( + AppLocalizations.of(context).allMailboxes, + maxLines: 1, + softWrap: CommonTextStyle.defaultSoftWrap, + overflow: CommonTextStyle.defaultTextOverFlow, + style: const TextStyle( + fontSize: 15, + color: AppColor.colorNameEmail, + fontWeight: FontWeight.normal), + )), + const SizedBox(width: 8), + if (actions == MailboxActions.select && (mailboxIdSelected == null || + mailboxIdSelected == PresentationMailbox.unifiedMailbox.id)) + Padding( + padding: const EdgeInsets.only(right: 30.0), + child: SvgPicture.asset( + _imagePaths.icFilterSelected, + width: 20, + height: 20, fit: BoxFit.fill), - const SizedBox(width: 8), - Expanded(child: Text( - AppLocalizations.of(context).allMailboxes, - maxLines: 1, - softWrap: CommonTextStyle.defaultSoftWrap, - overflow: CommonTextStyle.defaultTextOverFlow, - style: const TextStyle( - fontSize: 15, - color: AppColor.colorNameEmail, - fontWeight: FontWeight.normal), - )), - const SizedBox(width: 8), - if (actions == MailboxActions.select && (mailboxIdSelected == null || - mailboxIdSelected == PresentationMailbox.unifiedMailbox.id)) - Padding( - padding: const EdgeInsets.only(right: 30.0), - child: SvgPicture.asset( - _imagePaths.icFilterSelected, - width: 20, - height: 20, - fit: BoxFit.fill), - ) - ]) + ) + ]) ), ), ); + }); } - void _handleOpenMailboxClick(MailboxNode mailboxNode) { + void _handleOpenMailboxNodeClick(MailboxNode mailboxNode) { PresentationMailbox presentationMailbox; final path = controller.findNodePath(mailboxNode.item.id) ?? mailboxNode.item.name?.name; @@ -326,13 +405,33 @@ class DestinationPickerView extends GetWidget } else { presentationMailbox = mailboxNode.item; } - controller.selectMailboxAction(presentationMailbox); + controller.selectMailboxAction(presentationMailbox, mailboxNode: mailboxNode); + } + + void _handleOpenPresentationMailboxClick( + BuildContext context, + PresentationMailbox presentationMailbox + ) { + PresentationMailbox newPresentationMailbox; + if (presentationMailbox.id == PresentationMailbox.unifiedMailbox.id) { + newPresentationMailbox = presentationMailbox + .toPresentationMailboxWithMailboxPath(AppLocalizations.of(context).allMailboxes); + } else { + final path = controller.findNodePath(presentationMailbox.id) + ?? presentationMailbox.name?.name; + if (path != null) { + newPresentationMailbox = presentationMailbox.toPresentationMailboxWithMailboxPath(path); + } else { + newPresentationMailbox = presentationMailbox; + } + } + + controller.selectMailboxAction(newPresentationMailbox); } Widget _buildInputSearchFormWidget(BuildContext context) { return Padding( - padding: const EdgeInsets.symmetric( - vertical: BuildUtils.isWeb ? 0 : 16), + padding: const EdgeInsets.symmetric(vertical: 16), child: Row( children: [ Padding(padding: const EdgeInsets.only(left: 5), child: buildIconWeb( diff --git a/lib/features/destination_picker/presentation/model/destination_screen_type.dart b/lib/features/destination_picker/presentation/model/destination_screen_type.dart new file mode 100644 index 000000000..066f95f7d --- /dev/null +++ b/lib/features/destination_picker/presentation/model/destination_screen_type.dart @@ -0,0 +1,17 @@ + +import 'package:flutter/cupertino.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +enum DestinationScreenType { + destinationPicker, + createNewMailbox; + + String getTitle(BuildContext context, String mailboxActionTitle) { + switch(this) { + case DestinationScreenType.destinationPicker: + return mailboxActionTitle; + case DestinationScreenType.createNewMailbox: + return AppLocalizations.of(context).createNewMailbox; + } + } +} \ No newline at end of file diff --git a/lib/features/destination_picker/presentation/widgets/top_bar_destination_picker_builder.dart b/lib/features/destination_picker/presentation/widgets/top_bar_destination_picker_builder.dart index 7c526b39c..1562a214f 100644 --- a/lib/features/destination_picker/presentation/widgets/top_bar_destination_picker_builder.dart +++ b/lib/features/destination_picker/presentation/widgets/top_bar_destination_picker_builder.dart @@ -3,26 +3,46 @@ import 'package:core/core.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_screen_type.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; +typedef OnCreateNewMailboxAction = Function(); +typedef OnOpenCreateNewMailboxScreenAction = Function(); +typedef OnSelectedMailboxDestinationAction = Function(); + class TopBarDestinationPickerBuilder extends StatelessWidget { + final _imagePaths = Get.find(); + final MailboxActions? _mailboxAction; final VoidCallback? onCloseAction; + final VoidCallback? onBackToAction; + final OnCreateNewMailboxAction? onCreateNewMailboxAction; + final OnOpenCreateNewMailboxScreenAction? onOpenCreateNewMailboxScreenAction; + final OnSelectedMailboxDestinationAction? onSelectedMailboxDestinationAction; + final DestinationScreenType _destinationScreenType; + final MailboxId? mailboxIdDestination; + final bool isCreateMailboxValidated; - const TopBarDestinationPickerBuilder( - this._mailboxAction, - { - Key? key, - this.onCloseAction, - } + TopBarDestinationPickerBuilder( + this._mailboxAction, + this._destinationScreenType, + { + Key? key, + this.mailboxIdDestination, + this.isCreateMailboxValidated = false, + this.onCloseAction, + this.onBackToAction, + this.onCreateNewMailboxAction, + this.onOpenCreateNewMailboxScreenAction, + this.onSelectedMailboxDestinationAction, + } ) : super(key: key); @override Widget build(BuildContext context) { - final _imagePaths = Get.find(); - return Container( color: Colors.white, height: 52, @@ -30,7 +50,7 @@ class TopBarDestinationPickerBuilder extends StatelessWidget { children: [ Center( child: Text( - _mailboxAction?.getTitle(context) ?? '', + _destinationScreenType.getTitle(context, _mailboxAction?.getTitle(context) ?? ''), maxLines: 1, softWrap: CommonTextStyle.defaultSoftWrap, overflow: CommonTextStyle.defaultTextOverFlow, @@ -39,11 +59,12 @@ class TopBarDestinationPickerBuilder extends StatelessWidget { fontSize: 20, color: Colors.black, fontWeight: FontWeight.w700))), - Padding( - padding: const EdgeInsets.only(left: 8.0), - child: Align( - alignment: Alignment.centerLeft, - child: buildIconWeb( + if (_destinationScreenType == DestinationScreenType.destinationPicker) + Padding( + padding: const EdgeInsets.only(left: 8.0), + child: Align( + alignment: Alignment.centerLeft, + child: buildIconWeb( iconSize: 24, colorSelected: Colors.white, splashRadius: 15, @@ -51,10 +72,125 @@ class TopBarDestinationPickerBuilder extends StatelessWidget { icon: SvgPicture.asset(_imagePaths.icComposerClose, fit: BoxFit.fill), tooltip: AppLocalizations.of(context).close, onTap: () => onCloseAction?.call()), + ), + ) + else + Padding( + padding: const EdgeInsets.only(left: 8.0), + child: Align( + alignment: Alignment.centerLeft, + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: onBackToAction, + customBorder: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8))), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 8), + child: Row(mainAxisSize: MainAxisSize.min, children: [ + SvgPicture.asset( + _imagePaths.icBack, + width: 14, + height: 14, + color: AppColor.primaryColor, + fit: BoxFit.fill), + const SizedBox(width: 5), + Text( + AppLocalizations.of(context).back, + maxLines: 1, + softWrap: CommonTextStyle.defaultSoftWrap, + overflow: CommonTextStyle.defaultTextOverFlow, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 15, + color: AppColor.colorTextButton, + fontWeight: FontWeight.normal)) + ]), + ), + ), + ), + ), ), - ), + Align( + alignment: Alignment.centerRight, + child: Row(mainAxisSize: MainAxisSize.min, children: [ + if (_destinationScreenType == DestinationScreenType.destinationPicker && + _mailboxAction != MailboxActions.create) + _buildIconCreateButton(context), + if (_destinationScreenType == DestinationScreenType.destinationPicker) + _buildDoneButton(context) + else + _buildSaveButton(context), + const SizedBox(width: 8) + ]), + ) ] ), ); } + + Widget _buildIconCreateButton(BuildContext context) { + return buildIconWeb( + iconSize: 24, + colorSelected: Colors.white, + splashRadius: 15, + iconPadding: const EdgeInsets.all(3), + icon: SvgPicture.asset( + _imagePaths.icCreateNewFolder, + color: mailboxIdDestination != null + ? AppColor.colorTextButton + : AppColor.colorDisableMailboxCreateButton, + fit: BoxFit.fill), + tooltip: AppLocalizations.of(context).create, + onTap: onOpenCreateNewMailboxScreenAction + ); + } + + Widget _buildDoneButton(BuildContext context) { + return Material( + color: Colors.transparent, + child: InkWell( + customBorder: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8))), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 8), + child: Text( + AppLocalizations.of(context).done, + style: TextStyle( + fontSize: 15, + color: mailboxIdDestination != null + ? AppColor.colorTextButton + : AppColor.colorDisableMailboxCreateButton))), + onTap: onSelectedMailboxDestinationAction + ) + ); + } + + Widget _buildSaveButton(BuildContext context) { + return Material( + color: Colors.transparent, + child: InkWell( + customBorder: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8))), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 8), + child: Text( + AppLocalizations.of(context).save, + style: TextStyle( + fontSize: 15, + color: isCreateMailboxValidated + ? AppColor.colorTextButton + : AppColor.colorDisableMailboxCreateButton) + ) + ), + onTap: isCreateMailboxValidated ? onCreateNewMailboxAction : null + ) + ); + } } \ No newline at end of file diff --git a/lib/features/mailbox/presentation/mailbox_controller.dart b/lib/features/mailbox/presentation/mailbox_controller.dart index b79e872b3..4b82c2f22 100644 --- a/lib/features/mailbox/presentation/mailbox_controller.dart +++ b/lib/features/mailbox/presentation/mailbox_controller.dart @@ -529,8 +529,7 @@ class MailboxController extends BaseMailboxController { .map((mailbox) => mailbox.toSelectedPresentationMailbox(selectMode: SelectMode.INACTIVE)) .toList(); } else { - defaultMailboxTree.value.updateNodesUIMode(selectMode: SelectMode.INACTIVE); - folderMailboxTree.value.updateNodesUIMode(selectMode: SelectMode.INACTIVE); + unAllSelectedMailboxNode(); } currentSelectMode.value = SelectMode.INACTIVE; } diff --git a/lib/features/mailbox/presentation/mailbox_view.dart b/lib/features/mailbox/presentation/mailbox_view.dart index 11f5efbef..a3a52c0e2 100644 --- a/lib/features/mailbox/presentation/mailbox_view.dart +++ b/lib/features/mailbox/presentation/mailbox_view.dart @@ -361,7 +361,7 @@ class MailboxView extends GetWidget { _responsiveUtils, controller.listMailboxSearched[index], allSelectMode: controller.currentSelectMode.value, - lastMailbox: controller.listMailboxSearched.last) + lastMailboxIdInSearchedList: controller.listMailboxSearched.last.id) ..addOnOpenMailboxAction((mailbox) => controller.openMailbox(context, mailbox)) ..addOnSelectMailboxActionClick((mailbox) => controller.selectMailboxSearched(context, mailbox))) .build()) diff --git a/lib/features/mailbox/presentation/mailbox_view_web.dart b/lib/features/mailbox/presentation/mailbox_view_web.dart index f427268bb..f7bd4bd9f 100644 --- a/lib/features/mailbox/presentation/mailbox_view_web.dart +++ b/lib/features/mailbox/presentation/mailbox_view_web.dart @@ -361,8 +361,7 @@ class MailboxView extends GetWidget with AppLoaderMixin, Popu context, _imagePaths, _responsiveUtils, - listMailbox[index], - lastMailbox: controller.listMailboxSearched.last) + listMailbox[index]) ..addOnDragItemAccepted(controller.mailboxDashBoardController.dragSelectedMultipleEmailToMailboxAction) ..addOnOpenMailboxAction((mailbox) => controller.openMailbox(context, mailbox)) ..addOnMenuActionClick((position, mailbox) => _openMailboxMenuAction(context, position, mailbox)) diff --git a/lib/features/mailbox/presentation/model/mailbox_node.dart b/lib/features/mailbox/presentation/model/mailbox_node.dart index bbfe686fd..d454e4e24 100644 --- a/lib/features/mailbox/presentation/model/mailbox_node.dart +++ b/lib/features/mailbox/presentation/model/mailbox_node.dart @@ -23,6 +23,8 @@ class MailboxNode with EquatableMixin{ bool get isActivated => nodeState == MailboxState.activated; + bool get isSelected => selectMode == SelectMode.ACTIVE; + MailboxNode( this.item, { diff --git a/lib/features/mailbox/presentation/widgets/mailbox_folder_tile_builder.dart b/lib/features/mailbox/presentation/widgets/mailbox_folder_tile_builder.dart index be69f2512..9456dcb50 100644 --- a/lib/features/mailbox/presentation/widgets/mailbox_folder_tile_builder.dart +++ b/lib/features/mailbox/presentation/widgets/mailbox_folder_tile_builder.dart @@ -28,7 +28,7 @@ class MailBoxFolderTileBuilder { final MailboxNode? lastNode; final PresentationMailbox? mailboxNodeSelected; final MailboxActions? mailboxActions; - final MailboxId? mailboxIdSelected; + final MailboxId? mailboxIdAlreadySelected; OnExpandFolderActionClick? _onExpandFolderActionClick; OnOpenMailboxFolderClick? _onOpenMailboxFolderClick; @@ -48,7 +48,7 @@ class MailBoxFolderTileBuilder { this.lastNode, this.mailboxNodeSelected, this.mailboxActions, - this.mailboxIdSelected, + this.mailboxIdAlreadySelected } ); @@ -159,8 +159,10 @@ class MailBoxFolderTileBuilder { child: InkWell( onTap: () => _onOpenMailboxFolderClick?.call(_mailboxNode), child: Container( - color: Colors.transparent, padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 24), + color: mailboxDisplayed == MailboxDisplayed.destinationPicker && _mailboxNode.isSelected + ? AppColor.colorItemSelected + : Colors.transparent, child: Row(children: [ _buildLeadingMailboxItem(), const SizedBox(width: 8), @@ -399,7 +401,7 @@ class MailBoxFolderTileBuilder { } Widget _buildSelectedIcon() { - if (_mailboxNode.item.id == mailboxIdSelected && + if (_mailboxNode.item.id == mailboxIdAlreadySelected && mailboxDisplayed == MailboxDisplayed.destinationPicker && (mailboxActions == MailboxActions.select || mailboxActions == MailboxActions.create)) { diff --git a/lib/features/mailbox/presentation/widgets/mailbox_search_tile_builder.dart b/lib/features/mailbox/presentation/widgets/mailbox_search_tile_builder.dart index 395151ce6..930d59b68 100644 --- a/lib/features/mailbox/presentation/widgets/mailbox_search_tile_builder.dart +++ b/lib/features/mailbox/presentation/widgets/mailbox_search_tile_builder.dart @@ -15,14 +15,15 @@ typedef OnDragItemAccepted = void Function(List, Presentation class MailboxSearchTileBuilder { final PresentationMailbox _presentationMailbox; - final PresentationMailbox? lastMailbox; final SelectMode allSelectMode; final ImagePaths _imagePaths; final ResponsiveUtils _responsiveUtils; final BuildContext _context; final MailboxDisplayed mailboxDisplayed; final MailboxActions? mailboxActions; - final MailboxId? mailboxIdSelected; + final MailboxId? mailboxIdAlreadySelected; + final MailboxId? lastMailboxIdInSearchedList; + final MailboxId? mailboxIdDestination; bool isHoverItem = false; @@ -38,10 +39,11 @@ class MailboxSearchTileBuilder { this._presentationMailbox, { this.allSelectMode = SelectMode.INACTIVE, - this.lastMailbox, + this.lastMailboxIdInSearchedList, this.mailboxDisplayed = MailboxDisplayed.mailbox, this.mailboxActions, - this.mailboxIdSelected, + this.mailboxIdAlreadySelected, + this.mailboxIdDestination, } ); @@ -111,19 +113,28 @@ class MailboxSearchTileBuilder { absorbing: !_presentationMailbox.isActivated, child: Opacity( opacity: _presentationMailbox.isActivated ? 1.0 : 0.3, - child: ListTile( - contentPadding: EdgeInsets.zero, - onTap: () => allSelectMode == SelectMode.ACTIVE - ? _onSelectMailboxActionClick?.call(_presentationMailbox) - : _onOpenMailboxActionClick?.call(_presentationMailbox), - leading: _buildLeadingIcon(), - title: _buildTitleItem(), - subtitle: _buildSubtitleItem(), - trailing: _buildSelectedIcon(), + child: Container( + color: _presentationMailbox.id == mailboxIdDestination && + mailboxDisplayed == MailboxDisplayed.destinationPicker + ? AppColor.colorItemSelected + : Colors.transparent, + padding: mailboxDisplayed == MailboxDisplayed.destinationPicker + ? const EdgeInsets.only(left: 16, right: 8) + : EdgeInsets.zero, + child: ListTile( + contentPadding: EdgeInsets.zero, + onTap: () => allSelectMode == SelectMode.ACTIVE + ? _onSelectMailboxActionClick?.call(_presentationMailbox) + : _onOpenMailboxActionClick?.call(_presentationMailbox), + leading: _buildLeadingIcon(), + title: _buildTitleItem(), + subtitle: _buildSubtitleItem(), + trailing: _buildSelectedIcon(), + ), ), ), ), - if (lastMailbox?.id != _presentationMailbox.id) + if (lastMailboxIdInSearchedList != _presentationMailbox.id) Padding( padding: EdgeInsets.only(left: allSelectMode == SelectMode.ACTIVE ? 50 : 35), child: const Divider(color: AppColor.lineItemListColor, height: 0.5, thickness: 0.2)), @@ -221,21 +232,26 @@ class MailboxSearchTileBuilder { } Color get backgroundColorItem { - if (isHoverItem) { - return AppColor.colorBgMailboxSelected; + if (_presentationMailbox.id == mailboxIdDestination && + mailboxDisplayed == MailboxDisplayed.destinationPicker) { + return AppColor.colorItemSelected; } else { - if (mailboxDisplayed == MailboxDisplayed.mailbox) { - return _responsiveUtils.isDesktop(_context) - ? AppColor.colorBgDesktop - : Colors.white; + if (isHoverItem) { + return AppColor.colorBgMailboxSelected; } else { - return Colors.white; + if (mailboxDisplayed == MailboxDisplayed.mailbox) { + return _responsiveUtils.isDesktop(_context) + ? AppColor.colorBgDesktop + : Colors.white; + } else { + return Colors.white; + } } } } Widget? _buildSelectedIcon() { - if (_presentationMailbox.id == mailboxIdSelected && + if (_presentationMailbox.id == mailboxIdAlreadySelected && mailboxDisplayed == MailboxDisplayed.destinationPicker && (mailboxActions == MailboxActions.select || mailboxActions == MailboxActions.create)) { diff --git a/lib/features/mailbox_creator/presentation/mailbox_creator_controller.dart b/lib/features/mailbox_creator/presentation/mailbox_creator_controller.dart index c2a324fe5..3508ed4b8 100644 --- a/lib/features/mailbox_creator/presentation/mailbox_creator_controller.dart +++ b/lib/features/mailbox_creator/presentation/mailbox_creator_controller.dart @@ -30,6 +30,7 @@ class MailboxCreatorController extends BaseController { final newNameMailbox = Rxn(); FocusNode? nameInputFocusNode; + TextEditingController? nameInputController; MailboxCreatorArguments? arguments; AccountId? accountId; @@ -48,12 +49,12 @@ class MailboxCreatorController extends BaseController { void onInit() { super.onInit(); nameInputFocusNode = FocusNode(); + nameInputController = TextEditingController(); } @override void onReady() { super.onReady(); - log('MailboxCreatorController::onReady(): '); if (arguments != null) { folderMailboxTree = arguments!.folderMailboxTree; defaultMailboxTree = arguments!.defaultMailboxTree; @@ -71,7 +72,7 @@ class MailboxCreatorController extends BaseController { @override void onClose() { - nameInputFocusNode?.dispose(); + _disposeWidget(); super.onClose(); } @@ -184,6 +185,13 @@ class MailboxCreatorController extends BaseController { } } + void _disposeWidget() { + nameInputFocusNode?.dispose(); + nameInputFocusNode = null; + nameInputController?.dispose(); + nameInputController = null; + } + void createNewMailbox(BuildContext context) { FocusScope.of(context).unfocus(); @@ -194,9 +202,7 @@ class MailboxCreatorController extends BaseController { mailboxLocation: selectedMailbox.value); if (BuildUtils.isWeb) { - nameInputFocusNode?.dispose(); - nameInputFocusNode = null; - + _disposeWidget(); onCreatedMailboxCallback?.call(newMailboxArguments); } else { popBack(result: newMailboxArguments); @@ -208,9 +214,7 @@ class MailboxCreatorController extends BaseController { FocusScope.of(context).unfocus(); if (BuildUtils.isWeb) { - nameInputFocusNode?.dispose(); - nameInputFocusNode = null; - + _disposeWidget(); onDismissMailboxCreator?.call(); } else { popBack(); diff --git a/lib/features/mailbox_creator/presentation/mailbox_creator_view.dart b/lib/features/mailbox_creator/presentation/mailbox_creator_view.dart index e90bbddd8..91f97e035 100644 --- a/lib/features/mailbox_creator/presentation/mailbox_creator_view.dart +++ b/lib/features/mailbox_creator/presentation/mailbox_creator_view.dart @@ -84,6 +84,7 @@ class MailboxCreatorView extends GetWidget { right: !BuildUtils.isWeb && _responsiveUtils.isLandscapeMobile(context), child: Column(children: [ _buildAppBar(context), + const Divider(color: AppColor.colorDividerDestinationPicker, height: 1), _buildCreateMailboxNameInput(context), _buildMailboxLocation(context), ]), @@ -116,6 +117,7 @@ class MailboxCreatorView extends GetWidget { ..onChange((value) => controller.setNewNameMailbox(value)) ..keyboardType(TextInputType.visiblePassword) ..cursorColor(AppColor.colorTextButton) + ..addController(controller.nameInputController) ..maxLines(1) ..textStyle(const TextStyle( color: AppColor.colorNameEmail, diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 72975b0fd..9e5b1a127 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-10-26T13:43:22.314355", + "@@last_modified": "2022-10-27T15:02:38.515955", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -2393,5 +2393,17 @@ "placeholders": { "numberOfMailbox": {} } + }, + "toastMessageErrorNotSelectedFolderWhenCreateNewMailbox": "You have not selected a save folder to save", + "@toastMessageErrorNotSelectedFolderWhenCreateNewMailbox": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "createNewMailbox": "Create new mailbox", + "@createNewMailbox": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index e78095a74..b0f3602b3 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -2463,4 +2463,16 @@ class AppLocalizations { args: [numberOfMailbox] ); } + + String get toastMessageErrorNotSelectedFolderWhenCreateNewMailbox { + return Intl.message( + 'You have not selected a save folder to save', + name: 'toastMessageErrorNotSelectedFolderWhenCreateNewMailbox'); + } + + String get createNewMailbox { + return Intl.message( + 'Create new mailbox', + name: 'createNewMailbox'); + } } \ No newline at end of file