diff --git a/core/lib/presentation/views/list/tree_view.dart b/core/lib/presentation/views/list/tree_view.dart index b3c2db256..7a2bb5910 100644 --- a/core/lib/presentation/views/list/tree_view.dart +++ b/core/lib/presentation/views/list/tree_view.dart @@ -52,24 +52,20 @@ class _TreeViewData extends StatelessWidget { } class TreeViewChild { - final BuildContext context; final bool? isExpanded; final Widget parent; final List children; final VoidCallback? onTap; final EdgeInsetsGeometry? paddingChild; - TreeViewChild( - this.context, - { - required this.parent, - required this.children, - this.isExpanded, - this.onTap, - this.paddingChild, - Key? key, - } - ); + TreeViewChild({ + required this.parent, + required this.children, + this.isExpanded, + this.onTap, + this.paddingChild, + Key? key, + }); Widget build() { return Column( diff --git a/lib/features/base/base_mailbox_controller.dart b/lib/features/base/base_mailbox_controller.dart index ced48e80f..b71e66b60 100644 --- a/lib/features/base/base_mailbox_controller.dart +++ b/lib/features/base/base_mailbox_controller.dart @@ -19,6 +19,7 @@ import 'package:tmail_ui_user/features/base/action/update_mailbox_properties_act import 'package:tmail_ui_user/features/base/action/update_mailbox_properties_action/update_mailbox_total_emails_count_action.dart'; import 'package:tmail_ui_user/features/base/action/update_mailbox_properties_action/update_mailbox_unread_count_action.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; +import 'package:tmail_ui_user/features/base/mixin/expand_folder_trigger_scrollable_mixin.dart'; import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_manager.dart'; import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/mailbox_subscribe_action_state.dart'; @@ -55,7 +56,8 @@ typedef MovingMailboxActionCallback = void Function(PresentationMailbox mailboxS typedef DeleteMailboxActionCallback = void Function(PresentationMailbox mailbox); typedef AllowSubaddressingActionCallback = void Function(MailboxId, Map?>?, MailboxActions); -abstract class BaseMailboxController extends BaseController { +abstract class BaseMailboxController extends BaseController + with ExpandFolderTriggerScrollableMixin { final TreeBuilder _treeBuilder; final VerifyNameInteractor verifyNameInteractor; final GetAllMailboxInteractor? getAllMailboxInteractor; @@ -159,44 +161,6 @@ abstract class BaseMailboxController extends BaseController { } } - void triggerScrollWhenExpandFolder( - ExpandMode expandMode, - GlobalKey itemKey, - ScrollController scrollController, - ) { - if (expandMode == ExpandMode.COLLAPSE) return; - - final context = itemKey.currentContext; - if (context == null) return; - - WidgetsBinding.instance.addPostFrameCallback((_) { - _scrollToSelfIfNeeded(context, scrollController); - }); - } - - void _scrollToSelfIfNeeded( - BuildContext context, - ScrollController scrollController, - ) { - final renderBox = context.findRenderObject() as RenderBox?; - if (renderBox == null) return; - - final position = renderBox.localToGlobal(Offset.zero); - final screenHeight = MediaQuery.of(context).size.height; - const offsetY = 200; - final bottomY = position.dy + renderBox.size.height + offsetY; - log('BaseMailboxController::_scrollToSelfIfNeeded:position = $position |screenHeight = $screenHeight | bottomY = $bottomY'); - if (bottomY > screenHeight) { - final scrollOffset = scrollController.offset + bottomY - screenHeight + 40; - log('BaseMailboxController::_scrollToSelfIfNeeded:scrollOffset = $scrollOffset:'); - scrollController.animateTo( - scrollOffset, - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - } - } - void selectMailboxNode(MailboxNode mailboxNodeSelected) { final newSelectMode = mailboxNodeSelected.selectMode == SelectMode.INACTIVE ? SelectMode.ACTIVE diff --git a/lib/features/base/mixin/expand_folder_trigger_scrollable_mixin.dart b/lib/features/base/mixin/expand_folder_trigger_scrollable_mixin.dart new file mode 100644 index 000000000..18ade8c88 --- /dev/null +++ b/lib/features/base/mixin/expand_folder_trigger_scrollable_mixin.dart @@ -0,0 +1,44 @@ +import 'package:core/utils/app_logger.dart'; +import 'package:flutter/material.dart'; +import 'package:model/mailbox/expand_mode.dart'; + +mixin ExpandFolderTriggerScrollableMixin { + void triggerScrollWhenExpandFolder( + ExpandMode expandMode, + GlobalKey itemKey, + ScrollController scrollController, + ) { + if (expandMode == ExpandMode.COLLAPSE) return; + + final context = itemKey.currentContext; + if (context == null) return; + + WidgetsBinding.instance.addPostFrameCallback((_) { + _scrollToSelfIfNeeded(context, scrollController); + }); + } + + void _scrollToSelfIfNeeded( + BuildContext context, + ScrollController scrollController, + ) { + final renderBox = context.findRenderObject() as RenderBox?; + if (renderBox == null) return; + + final position = renderBox.localToGlobal(Offset.zero); + final screenHeight = MediaQuery.of(context).size.height; + const offsetY = 200; + final bottomY = position.dy + renderBox.size.height + offsetY; + log('$runtimeType::_scrollToSelfIfNeeded:position = $position |screenHeight = $screenHeight | bottomY = $bottomY'); + if (bottomY > screenHeight) { + final scrollOffset = + scrollController.offset + bottomY - screenHeight + 40; + log('$runtimeType::_scrollToSelfIfNeeded:scrollOffset = $scrollOffset:'); + scrollController.animateTo( + scrollOffset, + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + } + } +} diff --git a/lib/features/base/widget/default_field/default_button_arrow_down_field_widget.dart b/lib/features/base/widget/default_field/default_button_arrow_down_field_widget.dart index 1fa1f377b..a7feff3a0 100644 --- a/lib/features/base/widget/default_field/default_button_arrow_down_field_widget.dart +++ b/lib/features/base/widget/default_field/default_button_arrow_down_field_widget.dart @@ -7,12 +7,14 @@ class DefaultButtonArrowDownFieldWidget extends StatelessWidget { final String text; final String iconArrowDown; final VoidCallback onTap; + final Widget? icon; const DefaultButtonArrowDownFieldWidget({ super.key, required this.text, required this.iconArrowDown, required this.onTap, + this.icon, }); @override @@ -35,6 +37,7 @@ class DefaultButtonArrowDownFieldWidget extends StatelessWidget { padding: const EdgeInsetsDirectional.only(start: 12, end: 8), child: Row( children: [ + if (icon != null) icon!, Expanded( child: Text( text, diff --git a/lib/features/base/widget/modal_list_action_button_widget.dart b/lib/features/base/widget/modal_list_action_button_widget.dart new file mode 100644 index 000000000..48eaed6d8 --- /dev/null +++ b/lib/features/base/widget/modal_list_action_button_widget.dart @@ -0,0 +1,58 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/views/dialog/confirm_dialog_button.dart'; +import 'package:flutter/material.dart'; + +class ModalListActionButtonWidget extends StatelessWidget { + final String positiveLabel; + final String negativeLabel; + final VoidCallback onNegativeAction; + final VoidCallback onPositiveAction; + final EdgeInsetsGeometry? padding; + + const ModalListActionButtonWidget({ + super.key, + required this.positiveLabel, + required this.negativeLabel, + required this.onPositiveAction, + required this.onNegativeAction, + this.padding, + }); + + @override + Widget build(BuildContext context) { + Widget negativeButton = Container( + constraints: const BoxConstraints(minWidth: 67), + height: 48, + child: ConfirmDialogButton( + label: negativeLabel, + onTapAction: onNegativeAction, + ), + ); + + Widget positiveButton = Container( + constraints: const BoxConstraints(minWidth: 153), + height: 48, + child: ConfirmDialogButton( + label: positiveLabel, + backgroundColor: AppColor.primaryMain, + textColor: Colors.white, + onTapAction: onPositiveAction, + ), + ); + + Widget bodyWidget = Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Flexible(child: negativeButton), + const SizedBox(width: 8), + Flexible(child: positiveButton), + ], + ); + + if (padding != null) { + return Padding(padding: padding!, child: bodyWidget); + } else { + return bodyWidget; + } + } +} diff --git a/lib/features/destination_picker/presentation/destination_picker_view.dart b/lib/features/destination_picker/presentation/destination_picker_view.dart index 9dc6451f0..8094faeca 100644 --- a/lib/features/destination_picker/presentation/destination_picker_view.dart +++ b/lib/features/destination_picker/presentation/destination_picker_view.dart @@ -32,7 +32,7 @@ import 'package:tmail_ui_user/features/mailbox/presentation/styles/mailbox_icon_ import 'package:tmail_ui_user/features/mailbox/presentation/styles/mailbox_item_widget_styles.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_category_widget.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_item_widget.dart'; -import 'package:tmail_ui_user/features/mailbox_creator/presentation/widgets/create_mailbox_name_input_decoration_builder.dart'; +import 'package:tmail_ui_user/features/destination_picker/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'; import 'package:tmail_ui_user/main/utils/app_utils.dart'; @@ -413,7 +413,6 @@ class DestinationPickerView extends GetWidget ?.map((mailboxNode) { if (mailboxNode.hasChildren()) { return TreeViewChild( - context, key: const Key('children_tree_mailbox_child'), isExpanded: mailboxNode.expandMode == ExpandMode.EXPAND, paddingChild: const EdgeInsetsDirectional.only(start: 14), @@ -422,7 +421,9 @@ class DestinationPickerView extends GetWidget mailboxActions: actions, mailboxIdAlreadySelected: mailboxIdSelected, mailboxDisplayed: MailboxDisplayed.destinationPicker, - onOpenMailboxFolderClick: (node) => _pickMailboxNode(context, node), + onOpenMailboxFolderClick: (node) => node != null + ? _pickMailboxNode(context, node) + : null, onExpandFolderActionClick: (mailboxNode, itemKey) => controller.toggleMailboxFolder( mailboxNode, @@ -443,7 +444,9 @@ class DestinationPickerView extends GetWidget mailboxDisplayed: MailboxDisplayed.destinationPicker, mailboxIdAlreadySelected: mailboxIdSelected, mailboxActions: actions, - onOpenMailboxFolderClick: (node) => _pickMailboxNode(context, node), + onOpenMailboxFolderClick: (node) => node != null + ? _pickMailboxNode(context, node) + : null, ); }}) .toList() ?? []; diff --git a/lib/features/mailbox_creator/presentation/widgets/create_mailbox_name_input_decoration_builder.dart b/lib/features/destination_picker/presentation/widgets/create_mailbox_name_input_decoration_builder.dart similarity index 100% rename from lib/features/mailbox_creator/presentation/widgets/create_mailbox_name_input_decoration_builder.dart rename to lib/features/destination_picker/presentation/widgets/create_mailbox_name_input_decoration_builder.dart diff --git a/lib/features/mailbox/presentation/base_mailbox_view.dart b/lib/features/mailbox/presentation/base_mailbox_view.dart index b8b0fc557..c5fd99e15 100644 --- a/lib/features/mailbox/presentation/base_mailbox_view.dart +++ b/lib/features/mailbox/presentation/base_mailbox_view.dart @@ -199,7 +199,9 @@ abstract class BaseMailboxView extends GetWidget .selectedMailbox .value, onOpenMailboxFolderClick: (mailboxNode) => - controller.openMailbox(context, mailboxNode.item), + mailboxNode != null + ? controller.openMailbox(context, mailboxNode.item) + : null, onExpandFolderActionClick: mailboxNode.hasChildren() ? (mailboxNode, itemKey) => controller.toggleMailboxFolder( mailboxNode, @@ -228,7 +230,6 @@ abstract class BaseMailboxView extends GetWidget if (mailboxNode.hasChildren()) { return TreeViewChild( - context, key: const Key('children_tree_mailbox_child'), isExpanded: mailboxNode.expandMode == ExpandMode.EXPAND, paddingChild: const EdgeInsetsDirectional.only(start: 12), diff --git a/lib/features/mailbox/presentation/mailbox_controller.dart b/lib/features/mailbox/presentation/mailbox_controller.dart index 47fde5ffb..1a7ccee74 100644 --- a/lib/features/mailbox/presentation/mailbox_controller.dart +++ b/lib/features/mailbox/presentation/mailbox_controller.dart @@ -843,15 +843,12 @@ class MailboxController extends BaseMailboxController } void goToCreateNewMailboxView(BuildContext context, {PresentationMailbox? parentMailbox}) async { - if (session !=null && accountId != null) { + if (session != null && accountId != null) { final arguments = MailboxCreatorArguments( - accountId!, - defaultMailboxTree.value, - personalMailboxTree.value, - teamMailboxesTree.value, - mailboxDashBoardController.sessionCurrent!, - parentMailbox - ); + defaultMailboxTree.value, + personalMailboxTree.value, + parentMailbox, + ); final result = PlatformInfo.isWeb ? await DialogRouter.pushGeneralDialog(routeName: AppRoutes.mailboxCreator, arguments: arguments) diff --git a/lib/features/mailbox/presentation/model/mailbox_displayed.dart b/lib/features/mailbox/presentation/model/mailbox_displayed.dart index ec77b663c..75238255e 100644 --- a/lib/features/mailbox/presentation/model/mailbox_displayed.dart +++ b/lib/features/mailbox/presentation/model/mailbox_displayed.dart @@ -1,5 +1,6 @@ enum MailboxDisplayed { mailbox, - destinationPicker + destinationPicker, + modalFolder; } \ No newline at end of file diff --git a/lib/features/mailbox/presentation/utils/mailbox_method_action_define.dart b/lib/features/mailbox/presentation/utils/mailbox_method_action_define.dart index f9ac67a5b..5f6af9431 100644 --- a/lib/features/mailbox/presentation/utils/mailbox_method_action_define.dart +++ b/lib/features/mailbox/presentation/utils/mailbox_method_action_define.dart @@ -11,7 +11,7 @@ typedef OnDragEmailToMailboxAccepted = void Function(List, Pr typedef OnLongPressMailboxAction = void Function(PresentationMailbox); typedef OnClickExpandMailboxNodeAction = void Function(MailboxNode, GlobalKey itemKey); -typedef OnClickOpenMailboxNodeAction = void Function(MailboxNode); +typedef OnClickOpenMailboxNodeAction = void Function(MailboxNode?); typedef OnSelectMailboxNodeAction = void Function(MailboxNode); typedef OnClickOpenMenuMailboxNodeAction = Future Function(RelativeRect, MailboxNode); typedef OnLongPressMailboxNodeAction = void Function(MailboxNode); diff --git a/lib/features/mailbox/presentation/widgets/folder_item_widget.dart b/lib/features/mailbox/presentation/widgets/folder_item_widget.dart new file mode 100644 index 000000000..368adaa2e --- /dev/null +++ b/lib/features/mailbox/presentation/widgets/folder_item_widget.dart @@ -0,0 +1,84 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/utils/theme_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/styles/mailbox_icon_widget_styles.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/styles/mailbox_item_widget_styles.dart'; + +class FolderItemWidget extends StatelessWidget { + final ImagePaths imagePaths; + final String folderName; + final bool isSelected; + final VoidCallback? onTapAction; + + const FolderItemWidget({ + super.key, + required this.imagePaths, + required this.folderName, + this.isSelected = false, + this.onTapAction, + }); + + @override + Widget build(BuildContext context) { + Widget itemWidget = Container( + padding: const EdgeInsetsDirectional.symmetric( + horizontal: MailboxItemWidgetStyles.itemPadding, + ), + height: MailboxItemWidgetStyles.height, + child: Row( + children: [ + SvgPicture.asset( + imagePaths.icFolderMailbox, + width: MailboxIconWidgetStyles.iconSize, + height: MailboxIconWidgetStyles.iconSize, + colorFilter: AppColor.primaryLinShare.asFilter(), + fit: BoxFit.fill, + ), + const SizedBox(width: MailboxItemWidgetStyles.labelIconSpace), + Expanded( + child: Text( + folderName, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: isSelected + ? ThemeUtils.textStyleInter700( + color: Colors.black, + fontSize: 14, + ) + : ThemeUtils.textStyleBodyBody3( + color: Colors.black, + ), + ), + ), + const SizedBox(width: MailboxItemWidgetStyles.labelIconSpace), + if (isSelected) + SvgPicture.asset( + imagePaths.icCheck, + width: MailboxIconWidgetStyles.iconSize, + height: MailboxIconWidgetStyles.iconSize, + colorFilter: AppColor.primaryLinShare.asFilter(), + fit: BoxFit.fill, + ), + ], + ), + ); + + if (onTapAction != null) { + return Material( + type: MaterialType.transparency, + child: InkWell( + borderRadius: const BorderRadius.all( + Radius.circular(MailboxItemWidgetStyles.borderRadius), + ), + hoverColor: AppColor.blue100, + onTap: onTapAction, + child: itemWidget, + ), + ); + } else { + return itemWidget; + } + } +} diff --git a/lib/features/mailbox/presentation/widgets/label_mailbox_item_widget.dart b/lib/features/mailbox/presentation/widgets/label_mailbox_item_widget.dart index 304e8f114..a5b82af57 100644 --- a/lib/features/mailbox/presentation/widgets/label_mailbox_item_widget.dart +++ b/lib/features/mailbox/presentation/widgets/label_mailbox_item_widget.dart @@ -24,6 +24,7 @@ class LabelMailboxItemWidget extends StatefulWidget { final bool showTrailing; final bool isItemHovered; final bool isSelected; + final bool isSelectedFolderInModal; final OnClickOpenMenuMailboxNodeAction? onMenuActionClick; final OnEmptyMailboxActionCallback? onEmptyMailboxActionCallback; final OnClickExpandMailboxNodeAction? onClickExpandMailboxNodeAction; @@ -35,6 +36,7 @@ class LabelMailboxItemWidget extends StatefulWidget { this.showTrailing = true, this.isItemHovered = false, this.isSelected = false, + this.isSelectedFolderInModal = false, this.onMenuActionClick, this.onEmptyMailboxActionCallback, this.onClickExpandMailboxNodeAction, @@ -219,6 +221,11 @@ class _LabelMailboxItemWidgetState extends State { color: _responsiveUtils.isDesktop(context) ? null : AppColor.iconFolder, fontSize: 14, ); + } else if (widget.isSelectedFolderInModal) { + return ThemeUtils.textStyleInter700( + color: Colors.black, + fontSize: 14, + ); } else { return _responsiveUtils.isWebDesktop(context) ? ThemeUtils.textStyleBodyBody3(color: Colors.black) diff --git a/lib/features/mailbox/presentation/widgets/mailbox_item_widget.dart b/lib/features/mailbox/presentation/widgets/mailbox_item_widget.dart index 0a5cdbcbb..9df46f0a6 100644 --- a/lib/features/mailbox/presentation/widgets/mailbox_item_widget.dart +++ b/lib/features/mailbox/presentation/widgets/mailbox_item_widget.dart @@ -25,6 +25,7 @@ class MailboxItemWidget extends StatefulWidget { final PresentationMailbox? mailboxNodeSelected; final MailboxActions? mailboxActions; final MailboxId? mailboxIdAlreadySelected; + final Color? hoverColor; final OnClickExpandMailboxNodeAction? onExpandFolderActionClick; final OnClickOpenMailboxNodeAction? onOpenMailboxFolderClick; @@ -41,6 +42,7 @@ class MailboxItemWidget extends StatefulWidget { this.mailboxNodeSelected, this.mailboxActions, this.mailboxIdAlreadySelected, + this.hoverColor, this.onExpandFolderActionClick, this.onOpenMailboxFolderClick, this.onSelectMailboxFolderClick, @@ -229,11 +231,11 @@ class _MailboxItemWidgetState extends State { child: Material( color: Colors.transparent, child: InkWell( - onTap: () => !_isSelectActionNoValid - ? widget.onOpenMailboxFolderClick?.call(widget.mailboxNode) + onTap: !isSelectedFolderInModal + ? () => widget.onOpenMailboxFolderClick?.call(widget.mailboxNode) : null, customBorder: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(8))), - hoverColor: AppColor.colorMailboxHovered, + hoverColor: widget.hoverColor ?? AppColor.colorMailboxHovered, child: Container( padding: const EdgeInsetsDirectional.symmetric( horizontal: MailboxItemWidgetStyles.itemPadding, @@ -255,14 +257,17 @@ class _MailboxItemWidgetState extends State { showTrailing: false, isSelected: _isSelected, isItemHovered: _isItemHovered, + isSelectedFolderInModal: isSelectedFolderInModal, onMenuActionClick: widget.onMenuActionClick, onEmptyMailboxActionCallback: widget.onEmptyMailboxActionCallback, onClickExpandMailboxNodeAction: widget.onExpandFolderActionClick, ) ), - if (_isSelectActionNoValid) + if (isSelectedFolderInModal) SvgPicture.asset( - _imagePaths.icSelectedSB, + isFolderModalActive + ? _imagePaths.icCheck + : _imagePaths.icSelectedSB, width: MailboxItemWidgetStyles.selectionIconSize, height: MailboxItemWidgetStyles.selectionIconSize, fit: BoxFit.fill @@ -282,24 +287,25 @@ class _MailboxItemWidgetState extends State { widget.mailboxNodeSelected?.id == widget.mailboxNode.item.id; Color get backgroundColorItem { - if (widget.mailboxDisplayed == MailboxDisplayed.destinationPicker) { - return Colors.white; - } else { - if (_isSelected) { - return AppColor.blue100; - } else { - return Colors.transparent; - } + if (widget.mailboxDisplayed == MailboxDisplayed.mailbox) { + return _isSelected ? AppColor.blue100 : Colors.transparent; } + return Colors.white; } - bool get _isSelectActionNoValid => widget.mailboxNode.item.id == widget.mailboxIdAlreadySelected && - widget.mailboxDisplayed == MailboxDisplayed.destinationPicker && - ( - widget.mailboxActions == MailboxActions.select || - widget.mailboxActions == MailboxActions.create || - widget.mailboxActions == MailboxActions.moveEmail - ); + bool get isFolderModalActive => + widget.mailboxDisplayed == MailboxDisplayed.modalFolder; + + bool get isSelectedFolderInModal { + final isDestinationPickerActive = + widget.mailboxDisplayed == MailboxDisplayed.destinationPicker && + (widget.mailboxActions == MailboxActions.select || + widget.mailboxActions == MailboxActions.create || + widget.mailboxActions == MailboxActions.moveEmail); + + return widget.mailboxNode.item.id == widget.mailboxIdAlreadySelected && + (isDestinationPickerActive || isFolderModalActive); + } bool get _isIconDisplayed => widget.mailboxNode.item.isPersonal || diff --git a/lib/features/mailbox_creator/presentation/mailbox_creator_controller.dart b/lib/features/mailbox_creator/presentation/mailbox_creator_controller.dart index 92fd4f160..ef6c3fd9d 100644 --- a/lib/features/mailbox_creator/presentation/mailbox_creator_controller.dart +++ b/lib/features/mailbox_creator/presentation/mailbox_creator_controller.dart @@ -1,14 +1,10 @@ - -import 'package:core/core.dart'; import 'package:flutter/cupertino.dart'; import 'package:get/get.dart'; -import 'package:jmap_dart_client/jmap/account_id.dart'; -import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; -import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart'; -import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; +import 'package:tmail_ui_user/features/base/mixin/expand_folder_trigger_scrollable_mixin.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/extensions/expand_mode_extension.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart'; import 'package:tmail_ui_user/features/mailbox_creator/domain/model/verification/duplicate_name_validator.dart'; @@ -20,27 +16,24 @@ import 'package:tmail_ui_user/features/mailbox_creator/domain/usecases/verify_na import 'package:tmail_ui_user/features/mailbox_creator/presentation/extensions/validator_failure_extension.dart'; import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/mailbox_creator_arguments.dart'; import 'package:tmail_ui_user/features/mailbox_creator/presentation/model/new_mailbox_arguments.dart'; -import 'package:tmail_ui_user/main/routes/app_routes.dart'; -import 'package:tmail_ui_user/main/routes/dialog_router.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; -class MailboxCreatorController extends BaseController { +class MailboxCreatorController extends BaseController + with ExpandFolderTriggerScrollableMixin { final VerifyNameInteractor _verifyNameInteractor; final selectedMailbox = Rxn(); final newNameMailbox = Rxn(); + final isFolderModalEnabled = RxBool(false); bool _createdMailbox = false; + final personalMailboxTree = MailboxTree(MailboxNode.root()).obs; + final defaultMailboxTree = MailboxTree(MailboxNode.root()).obs; + final FocusNode nameInputFocusNode = FocusNode(); final TextEditingController nameInputController = TextEditingController(); - - MailboxCreatorArguments? arguments; - AccountId? accountId; - Session? _session; - MailboxTree? defaultMailboxTree; - MailboxTree? personalMailboxTree; - MailboxTree? teamMailboxesTre; + final ScrollController listFolderScrollController = ScrollController(); List listMailboxNameAsStringExist = []; @@ -51,47 +44,35 @@ class MailboxCreatorController extends BaseController { @override void onInit() { super.onInit(); - log('MailboxCreatorController::onInit():arguments: ${Get.arguments}'); - arguments = Get.arguments; - } - - @override - void onReady() { - super.onReady(); - log('MailboxCreatorController::onReady():'); + MailboxCreatorArguments? arguments = Get.arguments; if (arguments != null) { - personalMailboxTree = arguments!.personalMailboxTree; - defaultMailboxTree = arguments!.defaultMailboxTree; - teamMailboxesTre = arguments!.teamMailboxesTree; - accountId = arguments!.accountId; - _session = arguments!.session; - if (arguments!.selectedMailbox != null) { - selectedMailbox.value = arguments!.selectedMailbox; - } + personalMailboxTree.value = arguments.personalMailboxTree; + defaultMailboxTree.value = arguments.defaultMailboxTree; + selectedMailbox.value = arguments.selectedMailbox; _createListMailboxNameAsStringInMailboxLocation(); } } @override void onClose() { - log('MailboxCreatorController::onClose():'); + listMailboxNameAsStringExist = []; + _createdMailbox = false; nameInputFocusNode.dispose(); nameInputController.dispose(); + listFolderScrollController.dispose(); super.onClose(); } MailboxNode? _findMailboxNodeById(MailboxId mailboxId) { - final mailboxNode = defaultMailboxTree?.findNode((node) => node.item.id == mailboxId) - ?? personalMailboxTree?.findNode((node) => node.item.id == mailboxId) - ?? teamMailboxesTre?.findNode((node) => node.item.id == mailboxId); + final mailboxNode = defaultMailboxTree.value.findNode((node) => node.item.id == mailboxId) + ?? personalMailboxTree.value.findNode((node) => node.item.id == mailboxId); return mailboxNode; } void _createListMailboxNameAsStringInMailboxLocation() { if (selectedMailbox.value == null) { - final allChildrenAtMailboxLocation = (defaultMailboxTree?.root.childrenItems ?? []) - + (personalMailboxTree?.root.childrenItems ?? []) - + (teamMailboxesTre?.root.childrenItems ?? []); + final allChildrenAtMailboxLocation = (defaultMailboxTree.value.root.childrenItems ?? []) + + (personalMailboxTree.value.root.childrenItems ?? []); if (allChildrenAtMailboxLocation.isNotEmpty) { listMailboxNameAsStringExist = allChildrenAtMailboxLocation .where((mailboxNode) => mailboxNode.nameNotEmpty) @@ -140,33 +121,22 @@ class MailboxCreatorController extends BaseController { ); } - void selectMailboxLocation(BuildContext context) async { - KeyboardUtils.hideKeyboard(context); + void openFolderModal(BuildContext context) { + nameInputFocusNode.unfocus(); - if (accountId != null) { - final arguments = DestinationPickerArguments( - accountId!, - MailboxActions.create, - _session, - mailboxIdSelected: selectedMailbox.value?.id); - - final destinationMailbox = PlatformInfo.isWeb - ? await DialogRouter.pushGeneralDialog(routeName: AppRoutes.destinationPicker, arguments: arguments) - : await push(AppRoutes.destinationPicker, arguments: arguments); - - if (destinationMailbox is PresentationMailbox) { - final mailboxDestination = destinationMailbox == PresentationMailbox.unifiedMailbox - ? null - : destinationMailbox; - - selectedMailbox.value = mailboxDestination; - _createListMailboxNameAsStringInMailboxLocation(); - } + if (!responsiveUtils.isMobile(context)) { + isFolderModalEnabled.value = true; } } + void selectMailboxLocation(MailboxNode? mailboxDestination) { + isFolderModalEnabled.value = false; + selectedMailbox.value = mailboxDestination?.item; + _createListMailboxNameAsStringInMailboxLocation(); + } + void createNewMailbox(BuildContext context) { - KeyboardUtils.hideKeyboard(context); + nameInputFocusNode.unfocus(); final nameMailbox = newNameMailbox.value; final nameValidated = getErrorInputNameString(context); @@ -180,11 +150,35 @@ class MailboxCreatorController extends BaseController { MailboxName(nameMailbox), mailboxLocation: selectedMailbox.value); popBack(result: newMailboxArguments); + } else { + newNameMailbox.refresh(); } } - void closeMailboxCreator(BuildContext context) { - KeyboardUtils.hideKeyboard(context); + void toggleFolder(MailboxNode selectedMailboxNode, GlobalKey itemKey) { + final newExpandMode = selectedMailboxNode.expandMode.toggle(); + + if (defaultMailboxTree.value.updateExpandedNode(selectedMailboxNode, newExpandMode) != null) { + defaultMailboxTree.refresh(); + triggerScrollWhenExpandFolder( + selectedMailboxNode.expandMode, + itemKey, + listFolderScrollController, + ); + } + + if (personalMailboxTree.value.updateExpandedNode(selectedMailboxNode, newExpandMode) != null) { + personalMailboxTree.refresh(); + triggerScrollWhenExpandFolder( + selectedMailboxNode.expandMode, + itemKey, + listFolderScrollController, + ); + } + } + + void closeView() { + nameInputFocusNode.unfocus(); popBack(); } } \ No newline at end of file diff --git a/lib/features/mailbox_creator/presentation/mailbox_creator_view.dart b/lib/features/mailbox_creator/presentation/mailbox_creator_view.dart index 41e20009a..176138176 100644 --- a/lib/features/mailbox_creator/presentation/mailbox_creator_view.dart +++ b/lib/features/mailbox_creator/presentation/mailbox_creator_view.dart @@ -1,24 +1,20 @@ +import 'dart:math'; + import 'package:core/presentation/extensions/color_extension.dart'; -import 'package:core/presentation/utils/style_utils.dart'; import 'package:core/presentation/utils/theme_utils.dart'; -import 'package:core/presentation/views/text/text_field_builder.dart'; -import 'package:core/utils/app_logger.dart'; -import 'package:core/utils/direction_utils.dart'; -import 'package:core/utils/platform_info.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; -import 'package:pointer_interceptor/pointer_interceptor.dart'; +import 'package:tmail_ui_user/features/base/widget/default_field/default_button_arrow_down_field_widget.dart'; +import 'package:tmail_ui_user/features/base/widget/default_field/default_close_button_widget.dart'; +import 'package:tmail_ui_user/features/base/widget/label_input_field_builder.dart'; +import 'package:tmail_ui_user/features/base/widget/modal_list_action_button_widget.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/extensions/presentation_mailbox_extension.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_icon_widget.dart'; import 'package:tmail_ui_user/features/mailbox_creator/presentation/mailbox_creator_controller.dart'; -import 'package:tmail_ui_user/features/mailbox_creator/presentation/widgets/app_bar_mailbox_creator_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/mailbox_creator/presentation/widgets/modal_folder_tree_list_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; class MailboxCreatorView extends GetWidget { - - final _maxHeight = 656.0; - @override final controller = Get.find(); @@ -26,234 +22,181 @@ class MailboxCreatorView extends GetWidget { @override Widget build(BuildContext context) { - log('MailboxCreatorView::build():'); - return PointerInterceptor( - child: GestureDetector( - onTap: () => controller.closeMailboxCreator(context), - child: Card( - margin: EdgeInsets.zero, - borderOnForeground: false, - color: Colors.transparent, - child: SafeArea( - top: PlatformInfo.isMobile && controller.responsiveUtils.isPortraitMobile(context), - bottom: false, - left: false, - right: false, - child: Center( - child: GestureDetector( - onTap: () => FocusScope.of(context).unfocus(), - child: Container( - margin: _getMarginView(context), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: _getRadiusView(context), - boxShadow: const [ - BoxShadow( - color: AppColor.colorShadowLayerBottom, - blurRadius: 96, - spreadRadius: 96, - offset: Offset.zero), - BoxShadow( - color: AppColor.colorShadowLayerTop, - blurRadius: 2, - spreadRadius: 2, - offset: Offset.zero), - ]), - width: _getWidthView(context), - height: _getHeightView(context), - child: ClipRRect( - borderRadius: _getRadiusView(context), - child: SafeArea( - top: false, - bottom: false, - left: PlatformInfo.isMobile && controller.responsiveUtils.isLandscapeMobile(context), - right: PlatformInfo.isMobile && controller.responsiveUtils.isLandscapeMobile(context), - child: Column(children: [ - _buildAppBar(context), - const Divider(color: AppColor.colorDividerDestinationPicker, height: 1), - _buildCreateMailboxNameInput(context), - _buildMailboxLocation(context), - ]), - ) - ) - ), - ) - ), + final appLocalizations = AppLocalizations.of(context); + final theme = Theme.of(context); + + Widget bodyWidget = Container( + decoration: 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, + ), + ], + ), + width: min( + controller.responsiveUtils.getSizeScreenWidth(context) - 32, + 554, + ), + constraints: BoxConstraints( + maxHeight: min( + controller.responsiveUtils.getSizeScreenHeight(context) - 100, + 671, ), ), - ); - } + clipBehavior: Clip.antiAlias, + child: Stack( + children: [ + Padding( + padding: EdgeInsetsDirectional.only( + start: 32, + end: 32, + bottom: controller.responsiveUtils.isMobile(context) ? 0 : 16, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + height: 64, + alignment: Alignment.center, + child: Text( + appLocalizations.createANewFolder, + style: theme.textTheme.headlineSmall?.copyWith( + color: AppColor.m3SurfaceBackground, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + Obx(() { + final folderName = + controller.selectedMailbox.value?.getDisplayName(context) ?? + appLocalizations.personalFolders; + return Center( + child: Text( + appLocalizations + .subtitleDisplayTheFolderNameLocationInFolderCreationModal( + folderName, + ), + style: ThemeUtils.textStyleInter400.copyWith( + color: AppColor.steelGrayA540, + fontSize: 13, + height: 20 / 13, + ), + textAlign: TextAlign.center, + ), + ); + }), + const SizedBox(height: 32), + Obx( + () => LabelInputFieldBuilder( + label: AppLocalizations.of(context).folderName, + hintText: AppLocalizations.of(context).enterTheFolderName, + textEditingController: controller.nameInputController, + focusNode: controller.nameInputFocusNode, + errorText: controller.getErrorInputNameString(context), + arrangeHorizontally: false, + isLabelHasColon: false, + labelStyle: ThemeUtils.textStyleInter600().copyWith( + fontSize: 14, + height: 18 / 14, + color: Colors.black, + ), + runSpacing: 16, + inputFieldMaxWidth: double.infinity, + onTextChange: controller.setNewNameMailbox, + ), + ), + Padding( + padding: const EdgeInsets.only(top: 26, bottom: 16), + child: Text( + appLocalizations.selectTheFolderLocation, + style: ThemeUtils.textStyleInter600().copyWith( + fontSize: 14, + height: 18 / 14, + color: Colors.black, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + Obx(() { + final selectedMailbox = controller.selectedMailbox.value; + final isFolderModalEnabled = + controller.isFolderModalEnabled.isTrue; - Widget _buildAppBar(BuildContext context) { - return (AppBarMailboxCreatorBuilder( - context, - title: AppLocalizations.of(context).newFolder, - isValidated: true) - ..addOnCancelActionClick(() => controller.closeMailboxCreator(context)) - ..addOnDoneActionClick(() => controller.createNewMailbox(context))) - .build(); - } - - Widget _buildCreateMailboxNameInput(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), - child: Obx(() => TextFieldBuilder( - onTextChange: controller.setNewNameMailbox, - keyboardType: TextInputType.visiblePassword, - cursorColor: AppColor.colorTextButton, - controller: controller.nameInputController, - textDirection: DirectionUtils.getDirectionByLanguage(context), - maxLines: 1, - textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith( - color: AppColor.colorNameEmail, - fontSize: 16, - overflow: CommonTextStyle.defaultTextOverFlow), - focusNode: controller.nameInputFocusNode, - decoration: (CreateMailboxNameInputDecorationBuilder() - ..setHintText(AppLocalizations.of(context).hintInputCreateNewFolder) - ..setErrorText(controller.getErrorInputNameString(context))) - .build(), - )) - ); - } - - Widget _buildMailboxLocation(BuildContext context) { - return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), - child: Text( - AppLocalizations.of(context).selectParentFolder, - textAlign: TextAlign.left, - style: ThemeUtils.defaultTextStyleInterFont.copyWith( - fontSize: 13, - color: AppColor.colorHintSearchBar, - fontWeight: FontWeight.normal)), - ), - Container( - alignment: Alignment.center, - margin: const EdgeInsets.symmetric(horizontal: 16), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(14), - color: AppColor.colorBgMailbox + return Flexible( + child: AnimatedSize( + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + alignment: Alignment.topCenter, + child: isFolderModalEnabled + ? Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all( + Radius.circular(10), + ), + border: Border.all( + color: AppColor.m3Neutral90, + width: 1, + ), + ), + padding: const EdgeInsetsDirectional.only( + start: 14, + top: 13, + bottom: 13, + end: 4, + ), + child: Obx(() => ModalFolderTreeListWidget( + defaultTree: controller.defaultMailboxTree.value, + personalTree: controller.personalMailboxTree.value, + imagePaths: controller.imagePaths, + listScrollController: + controller.listFolderScrollController, + mailboxIdSelected: selectedMailbox?.id, + onSelectFolderAction: controller.selectMailboxLocation, + onToggleFolderAction: controller.toggleFolder, + )), + ) + : DefaultButtonArrowDownFieldWidget( + text: selectedMailbox?.getDisplayName(context) ?? + appLocalizations.personalFolders, + icon: MailboxIconWidget( + icon: selectedMailbox?.getMailboxIcon(controller.imagePaths) + ?? controller.imagePaths.icFolderMailbox, + ), + iconArrowDown: controller.imagePaths.icDropDown, + onTap: () => controller.openFolderModal(context), + ), + ), + ); + }), + ModalListActionButtonWidget( + positiveLabel: appLocalizations.createFolder, + negativeLabel: appLocalizations.cancel, + padding: const EdgeInsets.symmetric(vertical: 25), + onPositiveAction: () => controller.createNewMailbox(context), + onNegativeAction: controller.closeView, + ) + ], + ), ), - child: Material( - color: Colors.transparent, - child: InkWell( - onTap: () => controller.selectMailboxLocation(context), - customBorder: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), - child: Row(children: [ - const SizedBox(width: 12), - Obx(() => SvgPicture.asset( - controller.selectedMailbox.value?.getMailboxIcon(controller.imagePaths) ?? controller.imagePaths.icFolderMailbox, - width: PlatformInfo.isWeb ? 20 : 24, - height: PlatformInfo.isWeb ? 20 : 24, - fit: BoxFit.fill)), - const SizedBox(width: 12), - Expanded(child: Obx(() => Text( - controller.selectedMailbox.value?.getDisplayName(context) ?? AppLocalizations.of(context).allFolders, - maxLines: 1, - softWrap: CommonTextStyle.defaultSoftWrap, - overflow: CommonTextStyle.defaultTextOverFlow, - style: ThemeUtils.defaultTextStyleInterFont.copyWith( - fontSize: 15, - color: controller.selectedMailbox.value == null - ? AppColor.colorHintSearchBar - : AppColor.colorNameEmail), - ))), - const SizedBox(width: 12), - IconButton( - color: AppColor.primaryColor, - icon: SvgPicture.asset( - DirectionUtils.isDirectionRTLByLanguage(context) - ? controller.imagePaths.icBack - : controller.imagePaths.icCollapseFolder, - colorFilter: AppColor.colorCollapseMailbox.asFilter(), - fit: BoxFit.fill), - onPressed: () => controller.selectMailboxLocation(context)) - ])), - ) - ) - ]); - } + DefaultCloseButtonWidget( + iconClose: controller.imagePaths.icCloseDialog, + onTapActionCallback: controller.closeView, + ), + ], + ), + ); - double _getWidthView(BuildContext context) { - if (PlatformInfo.isWeb) { - if (controller.responsiveUtils.isMobile(context)) { - return double.infinity; - } else { - return 556; - } - } else { - if (controller.responsiveUtils.isLandscapeMobile(context) || - controller.responsiveUtils.isPortraitMobile(context)) { - return double.infinity; - } else { - return 556; - } - } + return Center(child: bodyWidget); } - - double _getHeightView(BuildContext context) { - if (PlatformInfo.isWeb) { - if (controller.responsiveUtils.isMobile(context)) { - return double.infinity; - } else { - if (controller.responsiveUtils.getSizeScreenHeight(context) > _maxHeight) { - return _maxHeight; - } else { - return double.infinity; - } - } - } else { - if (controller.responsiveUtils.isLandscapeMobile(context) || - controller.responsiveUtils.isPortraitMobile(context)) { - return double.infinity; - } else { - if (controller.responsiveUtils.getSizeScreenHeight(context) > _maxHeight) { - return _maxHeight; - } else { - return double.infinity; - } - } - } - - } - - EdgeInsets _getMarginView(BuildContext context) { - if (PlatformInfo.isWeb) { - if (controller.responsiveUtils.isMobile(context)) { - return EdgeInsets.zero; - } else { - if (controller.responsiveUtils.getSizeScreenHeight(context) > _maxHeight) { - return const EdgeInsets.symmetric(vertical: 12); - } else { - return const EdgeInsets.symmetric(vertical: 50); - } - } - } else { - if (controller.responsiveUtils.isLandscapeMobile(context) || - controller.responsiveUtils.isPortraitMobile(context)) { - return EdgeInsets.zero; - } else { - if (controller.responsiveUtils.getSizeScreenHeight(context) > _maxHeight) { - return const EdgeInsets.symmetric(vertical: 12); - } else { - return const EdgeInsets.symmetric(vertical: 50); - } - } - } - } - - BorderRadius _getRadiusView(BuildContext context) { - if (PlatformInfo.isMobile && controller.responsiveUtils.isLandscapeMobile(context)) { - return BorderRadius.zero; - } else if (controller.responsiveUtils.isMobile(context)) { - return const BorderRadius.only( - topRight: Radius.circular(16), - topLeft: Radius.circular(16)); - } else { - return const BorderRadius.all(Radius.circular(16)); - } - } -} \ No newline at end of file +} diff --git a/lib/features/mailbox_creator/presentation/model/mailbox_creator_arguments.dart b/lib/features/mailbox_creator/presentation/model/mailbox_creator_arguments.dart index 9a34473cd..477681544 100644 --- a/lib/features/mailbox_creator/presentation/model/mailbox_creator_arguments.dart +++ b/lib/features/mailbox_creator/presentation/model/mailbox_creator_arguments.dart @@ -1,34 +1,23 @@ import 'package:equatable/equatable.dart'; -import 'package:jmap_dart_client/jmap/account_id.dart'; -import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart'; class MailboxCreatorArguments with EquatableMixin{ - final AccountId accountId; final MailboxTree personalMailboxTree; final MailboxTree defaultMailboxTree; - final MailboxTree teamMailboxesTree; - final Session session; final PresentationMailbox? selectedMailbox; MailboxCreatorArguments( - this.accountId, - this.defaultMailboxTree, + this.defaultMailboxTree, this.personalMailboxTree, - this.teamMailboxesTree, - this.session, - this.selectedMailbox + this.selectedMailbox, ); @override List get props => [ - accountId, - defaultMailboxTree, + defaultMailboxTree, personalMailboxTree, - teamMailboxesTree, - session, - selectedMailbox + selectedMailbox, ]; } \ No newline at end of file diff --git a/lib/features/mailbox_creator/presentation/widgets/app_bar_mailbox_creator_builder.dart b/lib/features/mailbox_creator/presentation/widgets/app_bar_mailbox_creator_builder.dart deleted file mode 100644 index edc326cf2..000000000 --- a/lib/features/mailbox_creator/presentation/widgets/app_bar_mailbox_creator_builder.dart +++ /dev/null @@ -1,92 +0,0 @@ - -import 'package:core/core.dart'; -import 'package:flutter/material.dart'; -import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; - -typedef OnCancelActionClick = void Function(); -typedef OnDoneActionClick = void Function(); - -class AppBarMailboxCreatorBuilder { - OnCancelActionClick? _cancelActionClick; - OnDoneActionClick? _onDoneActionClick; - - final BuildContext _context; - final bool isValidated; - final String? title; - - AppBarMailboxCreatorBuilder( - this._context, - { - this.title, - this.isValidated = false - } - ); - - void addOnCancelActionClick(OnCancelActionClick onCancelActionClick) { - _cancelActionClick = onCancelActionClick; - } - - void addOnDoneActionClick(OnDoneActionClick onDoneActionClick) { - _onDoneActionClick = onDoneActionClick; - } - - Widget build() { - return Container( - color: Colors.white, - height: 52, - padding: const EdgeInsets.symmetric(horizontal: 12), - child: Row(children: [ - _buildCancelButton(), - Expanded(child: _buildTitle()), - _buildCreateButton() - ]) - ); - } - - Widget _buildCancelButton() { - return Material( - borderRadius: BorderRadius.circular(20), - color: Colors.transparent, - child: TextButton( - child: Text( - AppLocalizations.of(_context).cancel, - style: ThemeUtils.defaultTextStyleInterFont.copyWith(fontSize: 17, color: AppColor.colorTextButton), - ), - onPressed: () => _cancelActionClick?.call() - ) - ); - } - - Widget _buildCreateButton() { - return Material( - borderRadius: BorderRadius.circular(20), - color: Colors.transparent, - child: TextButton( - child: Text( - AppLocalizations.of(_context).done, - style: ThemeUtils.defaultTextStyleInterFont.copyWith( - fontSize: 17, - color: isValidated - ? AppColor.colorTextButton - : AppColor.colorDisableMailboxCreateButton), - ), - onPressed: () => isValidated ? _onDoneActionClick?.call() : null - ) - ); - } - - Widget _buildTitle() { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Text( - title ?? '', - textAlign: TextAlign.center, - maxLines: 1, - softWrap: CommonTextStyle.defaultSoftWrap, - overflow: CommonTextStyle.defaultTextOverFlow, - style: ThemeUtils.defaultTextStyleInterFont.copyWith( - fontSize: 20, - color: AppColor.colorNameEmail, - fontWeight: FontWeight.bold))); - } -} \ No newline at end of file diff --git a/lib/features/mailbox_creator/presentation/widgets/modal_folder_tree_list_widget.dart b/lib/features/mailbox_creator/presentation/widgets/modal_folder_tree_list_widget.dart new file mode 100644 index 000000000..781cf3184 --- /dev/null +++ b/lib/features/mailbox_creator/presentation/widgets/modal_folder_tree_list_widget.dart @@ -0,0 +1,114 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/views/list/tree_view.dart'; +import 'package:flutter/material.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:model/mailbox/expand_mode.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_displayed.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/utils/mailbox_method_action_define.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/widgets/folder_item_widget.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/widgets/mailbox_item_widget.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +class ModalFolderTreeListWidget extends StatelessWidget { + + final MailboxTree defaultTree; + final MailboxTree personalTree; + final ScrollController listScrollController; + final ImagePaths imagePaths; + final OnClickOpenMailboxNodeAction onSelectFolderAction; + final OnClickExpandMailboxNodeAction onToggleFolderAction; + final MailboxId? mailboxIdSelected; + + const ModalFolderTreeListWidget({ + super.key, + required this.defaultTree, + required this.personalTree, + required this.listScrollController, + required this.imagePaths, + required this.onSelectFolderAction, + required this.onToggleFolderAction, + this.mailboxIdSelected, + }); + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + physics: const ClampingScrollPhysics(), + controller: listScrollController, + padding: const EdgeInsetsDirectional.only(end: 14), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + FolderItemWidget( + folderName: AppLocalizations.of(context).personalFolders, + imagePaths: imagePaths, + isSelected: mailboxIdSelected == null, + onTapAction: () => onSelectFolderAction(null), + ), + if (defaultTree.root.hasChildren()) + _buildFolderTree( + mailboxTree: defaultTree, + mailboxIdSelected: mailboxIdSelected, + ), + if (personalTree.root.hasChildren()) + _buildFolderTree( + mailboxTree: personalTree, + mailboxIdSelected: mailboxIdSelected, + ), + ], + ), + ); + } + + Widget _buildFolderTree({ + required MailboxTree mailboxTree, + MailboxId? mailboxIdSelected, + }) { + return TreeView( + children: _buildListChildTileWidget( + parentNode: mailboxTree.root, + mailboxIdSelected: mailboxIdSelected, + ), + ); + } + + List _buildListChildTileWidget({ + required MailboxNode parentNode, + MailboxId? mailboxIdSelected, + }) { + return parentNode.childrenItems?.map( + (mailboxNode) { + if (mailboxNode.hasChildren()) { + return TreeViewChild( + isExpanded: mailboxNode.expandMode == ExpandMode.EXPAND, + paddingChild: const EdgeInsetsDirectional.only(start: 14), + parent: MailboxItemWidget( + mailboxNode: mailboxNode, + mailboxIdAlreadySelected: mailboxIdSelected, + mailboxDisplayed: MailboxDisplayed.modalFolder, + hoverColor: AppColor.blue100, + onOpenMailboxFolderClick: onSelectFolderAction, + onExpandFolderActionClick: onToggleFolderAction, + ), + children: _buildListChildTileWidget( + parentNode: mailboxNode, + mailboxIdSelected: mailboxIdSelected, + ), + ).build(); + } else { + return MailboxItemWidget( + mailboxNode: mailboxNode, + mailboxDisplayed: MailboxDisplayed.modalFolder, + mailboxIdAlreadySelected: mailboxIdSelected, + hoverColor: AppColor.blue100, + onOpenMailboxFolderClick: onSelectFolderAction, + onExpandFolderActionClick: onToggleFolderAction, + ); + } + }, + ).toList() ?? []; + } +} diff --git a/lib/features/manage_account/presentation/mailbox_visibility/mailbox_visibility_view.dart b/lib/features/manage_account/presentation/mailbox_visibility/mailbox_visibility_view.dart index b0f8b60eb..d7589bc14 100644 --- a/lib/features/manage_account/presentation/mailbox_visibility/mailbox_visibility_view.dart +++ b/lib/features/manage_account/presentation/mailbox_visibility/mailbox_visibility_view.dart @@ -272,7 +272,6 @@ class MailboxVisibilityView extends GetWidget return parentNode.childrenItems ?.map((mailboxNode) => mailboxNode.hasChildren() ? TreeViewChild( - context, key: const Key('children_tree_mailbox_child'), isExpanded: mailboxNode.expandMode == ExpandMode.EXPAND, paddingChild: const EdgeInsetsDirectional.only(start: 10), diff --git a/lib/features/search/mailbox/presentation/search_mailbox_controller.dart b/lib/features/search/mailbox/presentation/search_mailbox_controller.dart index 1573e55a2..d7459b1a1 100644 --- a/lib/features/search/mailbox/presentation/search_mailbox_controller.dart +++ b/lib/features/search/mailbox/presentation/search_mailbox_controller.dart @@ -756,12 +756,9 @@ class SearchMailboxController extends BaseMailboxController with MailboxActionHa void goToCreateNewMailboxView(BuildContext context, {PresentationMailbox? parentMailbox}) async { if (session != null && accountId != null) { final arguments = MailboxCreatorArguments( - accountId!, defaultMailboxTree.value, personalMailboxTree.value, - teamMailboxesTree.value, - session!, - parentMailbox + parentMailbox, ); final result = PlatformInfo.isWeb diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 753c6abdf..1865c49d5 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -4789,5 +4789,45 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "createANewFolder": "Create a New Folder", + "@createANewFolder": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "subtitleDisplayTheFolderNameLocationInFolderCreationModal": "New folder will be created inside {folderName}", + "@subtitleDisplayTheFolderNameLocationInFolderCreationModal": { + "type": "text", + "placeholders_order": [ + "folderName" + ], + "placeholders": { + "folderName": {} + } + }, + "folderName": "Folder Name", + "@folderName": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "enterTheFolderName": "Enter the folder name", + "@enterTheFolderName": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "selectTheFolderLocation": "Select the folder location", + "@selectTheFolderLocation": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "createFolder": "Create folder", + "@createFolder": { + "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 ac67efb07..4c1e55c30 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -5059,4 +5059,47 @@ class AppLocalizations { name: 'actions', ); } + + String get createANewFolder { + return Intl.message( + 'Create a New Folder', + name: 'createANewFolder', + ); + } + + String subtitleDisplayTheFolderNameLocationInFolderCreationModal(String folderName) { + return Intl.message( + 'New folder will be created inside $folderName', + name: 'subtitleDisplayTheFolderNameLocationInFolderCreationModal', + args: [folderName] + ); + } + + String get folderName { + return Intl.message( + 'Folder Name', + name: 'folderName', + ); + } + + String get enterTheFolderName { + return Intl.message( + 'Enter the folder name', + name: 'enterTheFolderName', + ); + } + + String get selectTheFolderLocation { + return Intl.message( + 'Select the folder location', + name: 'selectTheFolderLocation', + ); + } + + String get createFolder { + return Intl.message( + 'Create folder', + name: 'createFolder', + ); + } }