From fa69cf3dff7688124be4de8b7977e043bd54c504 Mon Sep 17 00:00:00 2001 From: dab246 Date: Thu, 11 Dec 2025 12:25:49 +0700 Subject: [PATCH 1/7] TF-4195 Add `Label as` to email context menu --- assets/images/ic_thumbs_up.svg | 3 +++ core/lib/presentation/resources/image_paths.dart | 1 + .../extensions/email_action_type_extension.dart | 4 ++++ .../presentation/controller/single_email_controller.dart | 5 +++++ lib/features/email/presentation/email_view.dart | 2 ++ .../utils/email_action_reactor/email_action_reactor.dart | 3 +++ lib/l10n/intl_messages.arb | 8 +++++++- lib/main/localizations/app_localizations.dart | 7 +++++++ model/lib/email/email_action_type.dart | 1 + 9 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 assets/images/ic_thumbs_up.svg diff --git a/assets/images/ic_thumbs_up.svg b/assets/images/ic_thumbs_up.svg new file mode 100644 index 0000000000..6783c7c53f --- /dev/null +++ b/assets/images/ic_thumbs_up.svg @@ -0,0 +1,3 @@ + + + diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart index ce676362a1..798ae8d4e6 100644 --- a/core/lib/presentation/resources/image_paths.dart +++ b/core/lib/presentation/resources/image_paths.dart @@ -263,6 +263,7 @@ class ImagePaths { String get icUser => _getImagePath('ic_user.svg'); String get icTag => _getImagePath('ic_tag.svg'); String get icColorPicker => _getImagePath('ic_color_picker.svg'); + String get icThumbsUp => _getImagePath('ic_thumbs_up.svg'); String _getImagePath(String imageName) { return AssetsPaths.images + imageName; diff --git a/lib/features/composer/presentation/extensions/email_action_type_extension.dart b/lib/features/composer/presentation/extensions/email_action_type_extension.dart index 99263639d0..0a1ba74145 100644 --- a/lib/features/composer/presentation/extensions/email_action_type_extension.dart +++ b/lib/features/composer/presentation/extensions/email_action_type_extension.dart @@ -175,6 +175,8 @@ extension EmailActionTypeExtension on EmailActionType { case EmailActionType.moveToTrash: case EmailActionType.deletePermanently: return imagePaths.icDeleteComposer; + case EmailActionType.labelAs: + return imagePaths.icTag; default: return ''; } @@ -222,6 +224,8 @@ extension EmailActionTypeExtension on EmailActionType { return appLocalizations.move_to_trash; case EmailActionType.deletePermanently: return appLocalizations.delete_permanently; + case EmailActionType.labelAs: + return appLocalizations.labelAs; default: return ''; } diff --git a/lib/features/email/presentation/controller/single_email_controller.dart b/lib/features/email/presentation/controller/single_email_controller.dart index 0dc87d033e..f98216120f 100644 --- a/lib/features/email/presentation/controller/single_email_controller.dart +++ b/lib/features/email/presentation/controller/single_email_controller.dart @@ -1546,4 +1546,9 @@ class SingleEmailController extends BaseController with AppLoaderMixin { _threadDetailController?.onNextMobile(); } } + + bool get isLabelFeatureEnabled { + return mailboxDashBoardController.isLabelCapabilitySupported && + mailboxDashBoardController.labelController.isLabelSettingEnabled.isTrue; + } } \ No newline at end of file diff --git a/lib/features/email/presentation/email_view.dart b/lib/features/email/presentation/email_view.dart index baf1b26cc2..c6688fb059 100644 --- a/lib/features/email/presentation/email_view.dart +++ b/lib/features/email/presentation/email_view.dart @@ -102,6 +102,7 @@ class EmailView extends GetWidget { handleEmailAction: controller.handleEmailAction, additionalActions: [], emailIsRead: presentationEmail.hasRead, + isLabelFeatureEnabled: controller.isLabelFeatureEnabled, openBottomSheetContextMenu: controller.mailboxDashBoardController.openBottomSheetContextMenu, openPopupMenu: controller.mailboxDashBoardController.openPopupMenuActionGroup, ); @@ -307,6 +308,7 @@ class EmailView extends GetWidget { EmailActionType.deletePermanently, ], emailIsRead: presentationEmail.hasRead, + isLabelFeatureEnabled: controller.isLabelFeatureEnabled, openBottomSheetContextMenu: controller.mailboxDashBoardController.openBottomSheetContextMenu, openPopupMenu: controller.mailboxDashBoardController.openPopupMenuActionGroup, ), diff --git a/lib/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart b/lib/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart index 9a23d84e68..4e70798488 100644 --- a/lib/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart +++ b/lib/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart @@ -483,6 +483,7 @@ class EmailActionReactor { ) handleEmailAction, required List additionalActions, required bool emailIsRead, + required bool isLabelFeatureEnabled, required OpenBottomSheetContextMenuAction openBottomSheetContextMenu, required OpenPopupMenuActionGroup openPopupMenu, }) { @@ -499,6 +500,8 @@ class EmailActionReactor { if (EmailUtils.isReplyToListEnabled(presentationEmail.listPost ?? '') && additionalActions.contains(EmailActionType.replyToList)) EmailActionType.replyToList, + if (isLabelFeatureEnabled) + EmailActionType.labelAs, if (PlatformInfo.isWeb && PlatformInfo.isCanvasKit && additionalActions.contains(EmailActionType.printAll)) diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 056038c0b8..291f1db7ee 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2025-12-10T10:27:38.268515", + "@@last_modified": "2025-12-11T12:24:51.793065", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -5235,5 +5235,11 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "labelAs": "Label as", + "@labelAs": { + "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 941ee6111e..0b33ddc01a 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -5556,4 +5556,11 @@ class AppLocalizations { name: 'theLabelFeatureIsNowAvailable', ); } + + String get labelAs { + return Intl.message( + 'Label as', + name: 'labelAs', + ); + } } diff --git a/model/lib/email/email_action_type.dart b/model/lib/email/email_action_type.dart index b56c6d057b..30cba1d766 100644 --- a/model/lib/email/email_action_type.dart +++ b/model/lib/email/email_action_type.dart @@ -4,6 +4,7 @@ enum EmailActionType { replyToList(1), forward(1), replyAll(1), + labelAs(1), compose(), markAsRead(2), markAsUnread(2), From 0e9f7ca2e0c06deb938728565ce467faefc2bc8d Mon Sep 17 00:00:00 2001 From: dab246 Date: Thu, 11 Dec 2025 17:00:10 +0700 Subject: [PATCH 2/7] TF-4195 Show submenu when hover Label As item --- .../base/model/popup_menu_item_action.dart | 31 +++- .../popup_menu_action_group_widget.dart | 33 +++- .../popup_menu_item_action_widget.dart | 155 ++++++++++++++++-- .../popup_menu/popup_submenu_controller.dart | 80 +++++++++ .../email/presentation/email_view.dart | 2 + .../model/popup_menu_item_email_action.dart | 4 + .../email_action_reactor.dart | 35 +++- .../widgets/label_item_context_menu.dart | 58 +++++++ .../widgets/label_list_context_menu.dart | 38 +++++ .../model/popup_menu_item_mailbox_action.dart | 3 + ...menu_item_profile_setting_type_action.dart | 3 + 11 files changed, 419 insertions(+), 23 deletions(-) create mode 100644 lib/features/base/widget/popup_menu/popup_submenu_controller.dart create mode 100644 lib/features/labels/presentation/widgets/label_item_context_menu.dart create mode 100644 lib/features/labels/presentation/widgets/label_list_context_menu.dart diff --git a/lib/features/base/model/popup_menu_item_action.dart b/lib/features/base/model/popup_menu_item_action.dart index 1cbffb5ba0..9e53516689 100644 --- a/lib/features/base/model/popup_menu_item_action.dart +++ b/lib/features/base/model/popup_menu_item_action.dart @@ -3,16 +3,23 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; typedef OnPopupMenuActionClick = void Function(PopupMenuItemAction action); +typedef OnHoverShowSubmenu = void Function(GlobalKey key); abstract class PopupMenuItemAction with EquatableMixin { final T action; final String? key; final int category; + final Widget? submenu; - PopupMenuItemAction(this.action, {this.key, this.category = -1}); + PopupMenuItemAction( + this.action, { + this.key, + this.category = -1, + this.submenu, + }); @override - List get props => [action, key, category]; + List get props => [action, key, category, submenu]; String get actionName; @@ -40,9 +47,22 @@ mixin OptionalPopupSelectedIcon { double get selectedIconSize => 16.0; } +mixin OptionalPopupHoverIcon { + String get hoverIcon; + + Color get hoverIconColor => AppColor.steelGrayA540; + + double get hoverIconSize => 16.0; +} + abstract class PopupMenuItemActionRequiredIcon extends PopupMenuItemAction - with OptionalPopupIcon { - PopupMenuItemActionRequiredIcon(super.action, {super.key, super.category}); + with OptionalPopupIcon, OptionalPopupHoverIcon { + PopupMenuItemActionRequiredIcon( + super.action, { + super.key, + super.category, + super.submenu, + }); } abstract class PopupMenuItemActionRequiredSelectedIcon @@ -54,6 +74,7 @@ abstract class PopupMenuItemActionRequiredSelectedIcon this.selectedAction, { super.key, super.category, + super.submenu, }); } @@ -66,6 +87,7 @@ abstract class PopupMenuItemActionRequiredFull extends PopupMenuItemAction this.selectedAction, { super.key, super.category, + super.submenu, }); } @@ -79,5 +101,6 @@ abstract class PopupMenuItemActionRequiredIconWithMultipleSelected this.selectedActions, { super.key, super.category, + super.submenu, }); } diff --git a/lib/features/base/widget/popup_menu/popup_menu_action_group_widget.dart b/lib/features/base/widget/popup_menu/popup_menu_action_group_widget.dart index 04eab5b5b2..a3354ec899 100644 --- a/lib/features/base/widget/popup_menu/popup_menu_action_group_widget.dart +++ b/lib/features/base/widget/popup_menu/popup_menu_action_group_widget.dart @@ -4,6 +4,7 @@ import 'package:tmail_ui_user/features/base/extensions/popup_menu_action_list_ex import 'package:tmail_ui_user/features/base/mixin/popup_context_menu_action_mixin.dart'; import 'package:tmail_ui_user/features/base/model/popup_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/base/widget/popup_menu/popup_submenu_controller.dart'; typedef OnPopupMenuActionSelected = void Function(PopupMenuItemAction action); @@ -11,11 +12,13 @@ class PopupMenuActionGroupWidget with PopupContextMenuActionMixin { final List actions; final OnPopupMenuActionSelected onActionSelected; final double dividerOpacity; + final PopupSubmenuController? submenuController; - const PopupMenuActionGroupWidget({ + PopupMenuActionGroupWidget({ required this.actions, required this.onActionSelected, this.dividerOpacity = 0.12, + this.submenuController, }); Future show( @@ -37,6 +40,15 @@ class PopupMenuActionGroupWidget with PopupContextMenuActionMixin { Navigator.pop(context); onActionSelected(menuAction); }, + onHoverShowSubmenu: submenuController != null && menuAction.submenu != null + ? (itemKey) => _showPopupSubmenu( + context: context, + itemKey: itemKey, + submenuController: submenuController!, + submenu: menuAction.submenu!, + ) + : null, + onHoverOtherItem: submenuController?.hide, ), ), ), @@ -50,4 +62,23 @@ class PopupMenuActionGroupWidget with PopupContextMenuActionMixin { return openPopupMenuAction(context, position, popupMenuItems); } + + void _showPopupSubmenu({ + required BuildContext context, + required GlobalKey itemKey, + required PopupSubmenuController submenuController, + required Widget submenu, +}) { + final renderBox = itemKey.currentContext?.findRenderObject() as RenderBox?; + if (renderBox == null) return; + + final offset = renderBox.localToGlobal(Offset.zero); + final rect = offset & renderBox.size; + + submenuController.show( + context: context, + anchor: rect, + submenu: submenu, + ); + } } diff --git a/lib/features/base/widget/popup_menu/popup_menu_item_action_widget.dart b/lib/features/base/widget/popup_menu/popup_menu_item_action_widget.dart index 281534d1d8..752be30cb6 100644 --- a/lib/features/base/widget/popup_menu/popup_menu_item_action_widget.dart +++ b/lib/features/base/widget/popup_menu/popup_menu_item_action_widget.dart @@ -2,27 +2,52 @@ import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/utils/theme_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:model/email/email_action_type.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:tmail_ui_user/features/base/model/popup_menu_item_action.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/popup_menu_item_email_action.dart'; -class PopupMenuItemActionWidget extends StatelessWidget { +class PopupMenuItemActionWidget extends StatefulWidget { final PopupMenuItemAction menuAction; final OnPopupMenuActionClick menuActionClick; + final OnHoverShowSubmenu? onHoverShowSubmenu; + final VoidCallback? onHoverOtherItem; const PopupMenuItemActionWidget({ super.key, required this.menuAction, required this.menuActionClick, + this.onHoverShowSubmenu, + this.onHoverOtherItem, }); + @override + State createState() => + _PopupMenuItemActionWidgetState(); +} + +class _PopupMenuItemActionWidgetState extends State { + GlobalKey? _itemKey; + ValueNotifier? _hoverItemNotifier; + + @override + void initState() { + super.initState(); + if (widget.menuAction is PopupMenuItemEmailAction) { + _itemKey = GlobalKey(); + _hoverItemNotifier = ValueNotifier(false); + } + } + @override Widget build(BuildContext context) { Widget? iconWidget; Widget? selectedIconWidget; bool isSelected = false; - if (menuAction is PopupMenuItemActionRequiredIcon) { - final specificMenuAction = menuAction as PopupMenuItemActionRequiredIcon; + if (widget.menuAction is PopupMenuItemActionRequiredIcon) { + final specificMenuAction = + widget.menuAction as PopupMenuItemActionRequiredIcon; iconWidget = Padding( padding: const EdgeInsetsDirectional.only(end: 16), child: SvgPicture.asset( @@ -33,9 +58,9 @@ class PopupMenuItemActionWidget extends StatelessWidget { fit: BoxFit.fill, ), ); - } else if (menuAction is PopupMenuItemActionRequiredSelectedIcon) { + } else if (widget.menuAction is PopupMenuItemActionRequiredSelectedIcon) { final specificMenuAction = - menuAction as PopupMenuItemActionRequiredSelectedIcon; + widget.menuAction as PopupMenuItemActionRequiredSelectedIcon; selectedIconWidget = Padding( padding: const EdgeInsetsDirectional.only(start: 16), child: SvgPicture.asset( @@ -46,9 +71,11 @@ class PopupMenuItemActionWidget extends StatelessWidget { fit: BoxFit.fill, ), ); - isSelected = specificMenuAction.selectedAction == menuAction.action; - } else if (menuAction is PopupMenuItemActionRequiredFull) { - final specificMenuAction = menuAction as PopupMenuItemActionRequiredFull; + isSelected = + specificMenuAction.selectedAction == widget.menuAction.action; + } else if (widget.menuAction is PopupMenuItemActionRequiredFull) { + final specificMenuAction = + widget.menuAction as PopupMenuItemActionRequiredFull; iconWidget = Padding( padding: const EdgeInsetsDirectional.only(end: 16), child: SvgPicture.asset( @@ -69,11 +96,12 @@ class PopupMenuItemActionWidget extends StatelessWidget { fit: BoxFit.fill, ), ); - isSelected = specificMenuAction.selectedAction == menuAction.action; - } else if (menuAction + isSelected = + specificMenuAction.selectedAction == widget.menuAction.action; + } else if (widget.menuAction is PopupMenuItemActionRequiredIconWithMultipleSelected) { - final specificMenuAction = - menuAction as PopupMenuItemActionRequiredIconWithMultipleSelected; + final specificMenuAction = widget.menuAction + as PopupMenuItemActionRequiredIconWithMultipleSelected; iconWidget = Padding( padding: const EdgeInsetsDirectional.only(end: 16), child: SvgPicture.asset( @@ -95,27 +123,111 @@ class PopupMenuItemActionWidget extends StatelessWidget { ), ); isSelected = - specificMenuAction.selectedActions.contains(menuAction.action); + specificMenuAction.selectedActions.contains(widget.menuAction.action); + } + + if (widget.menuAction is PopupMenuItemEmailAction && + widget.menuAction.action == EmailActionType.labelAs) { + final specificMenuAction = widget.menuAction as PopupMenuItemEmailAction; + + return PointerInterceptor( + child: MouseRegion( + onEnter: (_) { + if (_hoverItemNotifier != null) { + _hoverItemNotifier?.value = true; + } + if (_itemKey != null) { + widget.onHoverShowSubmenu?.call(_itemKey!); + } else { + widget.onHoverOtherItem?.call(); + } + }, + onExit: (_) { + if (_hoverItemNotifier != null) { + _hoverItemNotifier?.value = false; + } + }, + child: Material( + type: MaterialType.transparency, + child: InkWell( + onTap: () => specificMenuAction.onClick(widget.menuActionClick), + hoverColor: AppColor.popupMenuItemHovered, + child: Container( + key: _itemKey, + height: 48, + width: double.infinity, + padding: specificMenuAction.itemPadding, + child: Row( + children: [ + if (iconWidget != null) iconWidget, + Expanded( + child: Text( + specificMenuAction.actionName, + style: ThemeUtils.textStyleBodyBody3( + color: specificMenuAction.actionNameColor, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + if (isSelected && selectedIconWidget != null) + selectedIconWidget, + if (_hoverItemNotifier != null) + ValueListenableBuilder( + valueListenable: _hoverItemNotifier!, + builder: (_, value, __) { + if (value) { + return Padding( + padding: + const EdgeInsetsDirectional.only(start: 16), + child: SvgPicture.asset( + specificMenuAction.hoverIcon, + width: specificMenuAction.hoverIconSize, + height: specificMenuAction.hoverIconSize, + colorFilter: specificMenuAction.hoverIconColor + .asFilter(), + fit: BoxFit.fill, + ), + ); + } else { + return const SizedBox.shrink(); + } + }, + ) + ], + ), + ), + ), + ), + ), + ); } return PointerInterceptor( child: Material( type: MaterialType.transparency, child: InkWell( - onTap: () => menuAction.onClick(menuActionClick), + onTap: () => widget.menuAction.onClick(widget.menuActionClick), hoverColor: AppColor.popupMenuItemHovered, + onHover: (_) { + if (_hoverItemNotifier != null) { + _hoverItemNotifier?.value = false; + } + widget.onHoverOtherItem?.call(); + }, child: Container( + key: _itemKey, height: 48, width: double.infinity, - padding: menuAction.itemPadding, + padding: widget.menuAction.itemPadding, child: Row( children: [ if (iconWidget != null) iconWidget, Expanded( child: Text( - menuAction.actionName, + widget.menuAction.actionName, style: ThemeUtils.textStyleBodyBody3( - color: menuAction.actionNameColor, + color: widget.menuAction.actionNameColor, ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -130,4 +242,13 @@ class PopupMenuItemActionWidget extends StatelessWidget { ), ); } + + @override + void dispose() { + if (_hoverItemNotifier != null) { + _hoverItemNotifier?.dispose(); + _hoverItemNotifier = null; + } + super.dispose(); + } } diff --git a/lib/features/base/widget/popup_menu/popup_submenu_controller.dart b/lib/features/base/widget/popup_menu/popup_submenu_controller.dart new file mode 100644 index 0000000000..24261dddd2 --- /dev/null +++ b/lib/features/base/widget/popup_menu/popup_submenu_controller.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; + +enum SubmenuDirection { left, right, auto } + +class PopupSubmenuController { + OverlayEntry? _submenuEntry; + + bool get isShowing => _submenuEntry != null; + + void show({ + required BuildContext context, + required Rect anchor, + required Widget submenu, + SubmenuDirection direction = SubmenuDirection.auto, + double submenuWidth = 249, + double submenuMaxHeight = 400, + double offset = 0, + }) { + hide(); + + final screenWidth = MediaQuery.of(context).size.width; + + final rightPosition = anchor.right + offset; + final leftPosition = anchor.left - submenuWidth - offset; + + bool canShowRight = rightPosition + submenuWidth <= screenWidth; + bool canShowLeft = leftPosition >= 0; + + double? finalLeft; + + if (direction == SubmenuDirection.right) { + finalLeft = rightPosition; + } else if (direction == SubmenuDirection.left) { + finalLeft = leftPosition; + } else { + if (canShowRight) { + finalLeft = rightPosition; + } else if (canShowLeft) { + finalLeft = leftPosition; + } else { + finalLeft = rightPosition; + } + } + + _submenuEntry = OverlayEntry( + builder: (_) { + return Positioned( + left: finalLeft, + top: anchor.top, + child: MouseRegion( + onExit: (_) => hide(), + child: Material( + elevation: 8, + color: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + child: SizedBox( + width: submenuWidth, + height: submenuMaxHeight, + child: submenu, + ), + ), + ), + ); + }, + ); + + Overlay.of(context).insert(_submenuEntry!); + } + + void hide() { + _submenuEntry?.remove(); + _submenuEntry = null; + } + + void dispose() { + hide(); + } +} diff --git a/lib/features/email/presentation/email_view.dart b/lib/features/email/presentation/email_view.dart index c6688fb059..f82303bab7 100644 --- a/lib/features/email/presentation/email_view.dart +++ b/lib/features/email/presentation/email_view.dart @@ -103,6 +103,7 @@ class EmailView extends GetWidget { additionalActions: [], emailIsRead: presentationEmail.hasRead, isLabelFeatureEnabled: controller.isLabelFeatureEnabled, + labels: controller.mailboxDashBoardController.labelController.labels, openBottomSheetContextMenu: controller.mailboxDashBoardController.openBottomSheetContextMenu, openPopupMenu: controller.mailboxDashBoardController.openPopupMenuActionGroup, ); @@ -309,6 +310,7 @@ class EmailView extends GetWidget { ], emailIsRead: presentationEmail.hasRead, isLabelFeatureEnabled: controller.isLabelFeatureEnabled, + labels: controller.mailboxDashBoardController.labelController.labels, openBottomSheetContextMenu: controller.mailboxDashBoardController.openBottomSheetContextMenu, openPopupMenu: controller.mailboxDashBoardController.openPopupMenuActionGroup, ), diff --git a/lib/features/email/presentation/model/popup_menu_item_email_action.dart b/lib/features/email/presentation/model/popup_menu_item_email_action.dart index 1071e4952c..a9dcbb8348 100644 --- a/lib/features/email/presentation/model/popup_menu_item_email_action.dart +++ b/lib/features/email/presentation/model/popup_menu_item_email_action.dart @@ -16,6 +16,7 @@ class PopupMenuItemEmailAction this.imagePaths, { super.key, super.category, + super.submenu, }); @override @@ -29,4 +30,7 @@ class PopupMenuItemEmailAction @override Color get actionNameColor => action.getPopupMenuTitleColor(); + + @override + String get hoverIcon => imagePaths.icThumbsUp; } diff --git a/lib/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart b/lib/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart index 4e70798488..50e53ac648 100644 --- a/lib/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart +++ b/lib/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart @@ -20,6 +20,7 @@ import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:labels/model/label.dart'; import 'package:model/email/email_action_type.dart'; import 'package:model/email/mark_star_action.dart'; import 'package:model/email/presentation_email.dart'; @@ -32,6 +33,7 @@ import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_manager.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_action_group_widget.dart'; +import 'package:tmail_ui_user/features/base/widget/popup_menu/popup_submenu_controller.dart'; import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart'; import 'package:tmail_ui_user/features/email/domain/model/email_print.dart'; import 'package:tmail_ui_user/features/email/domain/model/mark_read_action.dart'; @@ -50,6 +52,7 @@ import 'package:tmail_ui_user/features/email/presentation/model/popup_menu_item_ import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart'; import 'package:tmail_ui_user/features/email/presentation/widgets/email_address_bottom_sheet_builder.dart'; import 'package:tmail_ui_user/features/email/presentation/widgets/email_address_dialog_builder.dart'; +import 'package:tmail_ui_user/features/labels/presentation/widgets/label_list_context_menu.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/features/manage_account/domain/model/create_new_email_rule_filter_request.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/create_new_email_rule_filter_interactor.dart'; @@ -486,6 +489,7 @@ class EmailActionReactor { required bool isLabelFeatureEnabled, required OpenBottomSheetContextMenuAction openBottomSheetContextMenu, required OpenPopupMenuActionGroup openPopupMenu, + List