From ac33424c30661bf80448a1981f5c28acd9d42f54 Mon Sep 17 00:00:00 2001 From: dab246 Date: Mon, 5 Jan 2026 17:00:28 +0700 Subject: [PATCH] TF-4233 Handle edit label on mobile --- .../mixin/label_context_menu_mixin.dart | 67 ++++++++++++++++++- .../models/context_item_label_action.dart | 29 ++++++++ .../widgets/create_new_label_modal.dart | 1 - .../presentation/base_mailbox_view.dart | 8 ++- .../handle_label_action_type_extension.dart | 19 +++++- .../labels/label_method_action_define.dart | 4 ++ .../widgets/labels/label_list_item.dart | 8 ++- .../widgets/labels/label_list_view.dart | 3 + 8 files changed, 131 insertions(+), 8 deletions(-) create mode 100644 lib/features/labels/presentation/models/context_item_label_action.dart diff --git a/lib/features/labels/presentation/mixin/label_context_menu_mixin.dart b/lib/features/labels/presentation/mixin/label_context_menu_mixin.dart index 6ca57e288..51a189a66 100644 --- a/lib/features/labels/presentation/mixin/label_context_menu_mixin.dart +++ b/lib/features/labels/presentation/mixin/label_context_menu_mixin.dart @@ -2,7 +2,9 @@ import 'package:core/presentation/resources/image_paths.dart'; import 'package:flutter/material.dart'; import 'package:labels/model/label.dart'; import 'package:tmail_ui_user/features/base/mixin/popup_context_menu_action_mixin.dart'; +import 'package:tmail_ui_user/features/base/widget/context_menu/context_menu_item_action.dart'; import 'package:tmail_ui_user/features/base/widget/popup_menu/popup_menu_item_action_widget.dart'; +import 'package:tmail_ui_user/features/labels/presentation/models/context_item_label_action.dart'; import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart'; import 'package:tmail_ui_user/features/labels/presentation/models/popup_menu_item_label_action.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/utils/labels/label_method_action_define.dart'; @@ -10,7 +12,7 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; mixin LabelContextMenuMixin on PopupContextMenuActionMixin { - Future openLabelContextMenuAction( + Future _openLabelPopupMenuAction( BuildContext context, ImagePaths imagePaths, Label label, @@ -68,4 +70,67 @@ mixin LabelContextMenuMixin on PopupContextMenuActionMixin { popBack(); onLabelActionTypeCallback(label, actionType); } + + ContextMenuItemAction _buildLabelContextMenuItem( + LabelActionType actionType, + AppLocalizations appLocalizations, + ImagePaths imagePaths, + ) { + return ContextMenuItemLabelAction( + actionType, + appLocalizations, + imagePaths, + ); + } + + Future _openLabelContextMenuAction( + BuildContext context, + ImagePaths imagePaths, + Label label, + OnLabelActionTypeCallback onLabelActionTypeCallback, + ) async { + final listLabelAction = [LabelActionType.edit]; + + final contextMenuActions = listLabelAction + .map((actionType) => _buildLabelContextMenuItem( + actionType, + AppLocalizations.of(context), + imagePaths, + )) + .toList(); + + return openBottomSheetContextMenuAction( + context: context, + itemActions: contextMenuActions, + onContextMenuActionClick: (menuAction) { + popBack(); + onLabelActionTypeCallback(label, menuAction.action); + }, + ); + } + + Future openLabelMenuAction( + BuildContext context, + ImagePaths imagePaths, + Label label, + OnLabelActionTypeCallback onLabelActionTypeCallback, + {RelativeRect? position} + ) async { + if (position == null) { + return _openLabelContextMenuAction( + context, + imagePaths, + label, + onLabelActionTypeCallback, + ); + } else { + return _openLabelPopupMenuAction( + context, + imagePaths, + label, + position, + onLabelActionTypeCallback, + ); + } + } } diff --git a/lib/features/labels/presentation/models/context_item_label_action.dart b/lib/features/labels/presentation/models/context_item_label_action.dart new file mode 100644 index 000000000..b2cfdb362 --- /dev/null +++ b/lib/features/labels/presentation/models/context_item_label_action.dart @@ -0,0 +1,29 @@ +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:flutter/material.dart'; +import 'package:tmail_ui_user/features/base/widget/context_menu/context_menu_item_action.dart'; +import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +class ContextMenuItemLabelAction + extends ContextMenuItemActionRequiredIcon { + final AppLocalizations appLocalizations; + final ImagePaths imagePaths; + + ContextMenuItemLabelAction( + super.action, + this.appLocalizations, + this.imagePaths, + ); + + @override + String get actionIcon => action.getContextMenuIcon(imagePaths); + + @override + String get actionName => action.getContextMenuTitle(appLocalizations); + + @override + Color get actionIconColor => action.getContextMenuIconColor(); + + @override + Color get actionNameColor => action.getContextMenuTitleColor(); +} diff --git a/lib/features/labels/presentation/widgets/create_new_label_modal.dart b/lib/features/labels/presentation/widgets/create_new_label_modal.dart index 057f0aacf..a1600c9d2 100644 --- a/lib/features/labels/presentation/widgets/create_new_label_modal.dart +++ b/lib/features/labels/presentation/widgets/create_new_label_modal.dart @@ -75,7 +75,6 @@ class _CreateNewLabelModalState extends State { if (selectedLabel != null) { _nameInputController.text = selectedLabel.safeDisplayName; _nameInputFocusNode.requestFocus(); - _labelSelectedColorNotifier.value = _selectedColor; _createLabelStateNotifier.value = true; } }); diff --git a/lib/features/mailbox/presentation/base_mailbox_view.dart b/lib/features/mailbox/presentation/base_mailbox_view.dart index 28e0f0b76..a4c9f6279 100644 --- a/lib/features/mailbox/presentation/base_mailbox_view.dart +++ b/lib/features/mailbox/presentation/base_mailbox_view.dart @@ -426,12 +426,18 @@ abstract class BaseMailboxView extends GetWidget PresentationLabelMailbox.initial(label), ), onOpenContextMenu: (label, position) => - dashboardController.openLabelContextMenuAction( + dashboardController.openLabelPopupMenuAction( context, controller.imagePaths, label, position, ), + onLongPressLabelItemAction: (label) => + dashboardController.openLabelContextMenuAction( + context, + controller.imagePaths, + label, + ), ) : const Offstage(), ); diff --git a/lib/features/mailbox/presentation/extensions/handle_label_action_type_extension.dart b/lib/features/mailbox/presentation/extensions/handle_label_action_type_extension.dart index 8dd82793f..ca46e4307 100644 --- a/lib/features/mailbox/presentation/extensions/handle_label_action_type_extension.dart +++ b/lib/features/mailbox/presentation/extensions/handle_label_action_type_extension.dart @@ -7,7 +7,7 @@ import 'package:tmail_ui_user/features/labels/presentation/models/label_action_t import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; extension HandleLabelActionTypeExtension on MailboxDashBoardController { - Future openLabelContextMenuAction( + Future openLabelPopupMenuAction( BuildContext context, ImagePaths imagePaths, Label label, @@ -17,11 +17,11 @@ extension HandleLabelActionTypeExtension on MailboxDashBoardController { isPopupMenuOpened.value = true; } return labelController - .openLabelContextMenuAction( + .openLabelMenuAction( context, imagePaths, label, - position, + position: position, _onPerformLabelActionType, ) .whenComplete(() { @@ -38,4 +38,17 @@ extension HandleLabelActionTypeExtension on MailboxDashBoardController { label: label, ); } + + Future openLabelContextMenuAction( + BuildContext context, + ImagePaths imagePaths, + Label label, + ) async { + return labelController.openLabelMenuAction( + context, + imagePaths, + label, + _onPerformLabelActionType, + ); + } } diff --git a/lib/features/mailbox/presentation/utils/labels/label_method_action_define.dart b/lib/features/mailbox/presentation/utils/labels/label_method_action_define.dart index 9e2671062..8c158af24 100644 --- a/lib/features/mailbox/presentation/utils/labels/label_method_action_define.dart +++ b/lib/features/mailbox/presentation/utils/labels/label_method_action_define.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import 'package:labels/labels.dart'; import 'package:tmail_ui_user/features/labels/presentation/models/label_action_type.dart'; +typedef OnOpenLabelCallback = void Function(Label label); + typedef OnOpenLabelContextMenuAction = Future Function( Label label, RelativeRect position, @@ -11,3 +13,5 @@ typedef OnLabelActionTypeCallback = void Function( Label label, LabelActionType actionType, ); + +typedef OnLongPressLabelItemAction = void Function(Label label); \ No newline at end of file diff --git a/lib/features/mailbox/presentation/widgets/labels/label_list_item.dart b/lib/features/mailbox/presentation/widgets/labels/label_list_item.dart index 210088ebd..49858237d 100644 --- a/lib/features/mailbox/presentation/widgets/labels/label_list_item.dart +++ b/lib/features/mailbox/presentation/widgets/labels/label_list_item.dart @@ -1,6 +1,7 @@ import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/views/button/tmail_button_widget.dart'; +import 'package:core/utils/platform_info.dart'; import 'package:flutter/material.dart'; import 'package:labels/extensions/label_extension.dart'; import 'package:labels/model/label.dart'; @@ -10,8 +11,6 @@ import 'package:tmail_ui_user/features/mailbox/presentation/utils/labels/label_m import 'package:tmail_ui_user/features/mailbox/presentation/widgets/labels/label_icon_widget.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/widgets/labels/label_name_widget.dart'; -typedef OnOpenLabelCallback = void Function(Label label); - class LabelListItem extends StatefulWidget { final Label label; final ImagePaths imagePaths; @@ -20,6 +19,7 @@ class LabelListItem extends StatefulWidget { final bool isMobileResponsive; final OnOpenLabelCallback onOpenLabelCallback; final OnOpenLabelContextMenuAction? onOpenContextMenu; + final OnLongPressLabelItemAction? onLongPressLabelItemAction; const LabelListItem({ super.key, @@ -30,6 +30,7 @@ class LabelListItem extends StatefulWidget { this.isSelected = false, this.isMobileResponsive = false, this.onOpenContextMenu, + this.onLongPressLabelItemAction, }); @override @@ -84,6 +85,9 @@ class _LabelListItemState extends State { borderRadius: _borderRadius, onHover: _handleHoverChanged, onTap: () => widget.onOpenLabelCallback(widget.label), + onLongPress: PlatformInfo.isWebTouchDevice || PlatformInfo.isMobile + ? () => widget.onLongPressLabelItemAction?.call(widget.label) + : null, child: Container( height: _itemHeight, padding: _itemPadding, diff --git a/lib/features/mailbox/presentation/widgets/labels/label_list_view.dart b/lib/features/mailbox/presentation/widgets/labels/label_list_view.dart index da9200a3d..5dd17c673 100644 --- a/lib/features/mailbox/presentation/widgets/labels/label_list_view.dart +++ b/lib/features/mailbox/presentation/widgets/labels/label_list_view.dart @@ -13,6 +13,7 @@ class LabelListView extends StatelessWidget { final OnOpenLabelCallback onOpenLabelCallback; final bool isMobileResponsive; final OnOpenLabelContextMenuAction? onOpenContextMenu; + final OnLongPressLabelItemAction? onLongPressLabelItemAction; const LabelListView({ super.key, @@ -23,6 +24,7 @@ class LabelListView extends StatelessWidget { this.labelIdSelected, this.isMobileResponsive = false, this.onOpenContextMenu, + this.onLongPressLabelItemAction, }); @override @@ -43,6 +45,7 @@ class LabelListView extends StatelessWidget { onOpenLabelCallback: onOpenLabelCallback, isMobileResponsive: isMobileResponsive, onOpenContextMenu: onOpenContextMenu, + onLongPressLabelItemAction: onLongPressLabelItemAction, ); }, );