diff --git a/lib/features/base/extensions/popup_menu_action_list_extension.dart b/lib/features/base/extensions/popup_menu_action_list_extension.dart new file mode 100644 index 000000000..a8e0b1890 --- /dev/null +++ b/lib/features/base/extensions/popup_menu_action_list_extension.dart @@ -0,0 +1,8 @@ +import 'package:core/presentation/extensions/iterable_extension.dart'; +import 'package:tmail_ui_user/features/base/model/popup_menu_item_action.dart'; + +extension PopupMenuActionListExtension on List { + Map> groupByCategory() { + return groupBy((action) => action.category, sortKeys: true); + } +} diff --git a/lib/features/base/model/popup_menu_item_action.dart b/lib/features/base/model/popup_menu_item_action.dart index feabf2200..1cbffb5ba 100644 --- a/lib/features/base/model/popup_menu_item_action.dart +++ b/lib/features/base/model/popup_menu_item_action.dart @@ -6,11 +6,13 @@ typedef OnPopupMenuActionClick = void Function(PopupMenuItemAction action); abstract class PopupMenuItemAction with EquatableMixin { final T action; + final String? key; + final int category; - PopupMenuItemAction(this.action); + PopupMenuItemAction(this.action, {this.key, this.category = -1}); @override - List get props => [action]; + List get props => [action, key, category]; String get actionName; @@ -40,21 +42,31 @@ mixin OptionalPopupSelectedIcon { abstract class PopupMenuItemActionRequiredIcon extends PopupMenuItemAction with OptionalPopupIcon { - PopupMenuItemActionRequiredIcon(super.action); + PopupMenuItemActionRequiredIcon(super.action, {super.key, super.category}); } abstract class PopupMenuItemActionRequiredSelectedIcon extends PopupMenuItemAction with OptionalPopupSelectedIcon { final T? selectedAction; - PopupMenuItemActionRequiredSelectedIcon(super.action, this.selectedAction); + PopupMenuItemActionRequiredSelectedIcon( + super.action, + this.selectedAction, { + super.key, + super.category, + }); } abstract class PopupMenuItemActionRequiredFull extends PopupMenuItemAction with OptionalPopupIcon, OptionalPopupSelectedIcon { final T selectedAction; - PopupMenuItemActionRequiredFull(super.action, this.selectedAction); + PopupMenuItemActionRequiredFull( + super.action, + this.selectedAction, { + super.key, + super.category, + }); } abstract class PopupMenuItemActionRequiredIconWithMultipleSelected @@ -64,6 +76,8 @@ abstract class PopupMenuItemActionRequiredIconWithMultipleSelected PopupMenuItemActionRequiredIconWithMultipleSelected( super.action, - this.selectedActions, - ); + this.selectedActions, { + super.key, + super.category, + }); } 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 c39bbebc9..1071e4952 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 @@ -13,8 +13,10 @@ class PopupMenuItemEmailAction PopupMenuItemEmailAction( super.action, this.appLocalizations, - this.imagePaths, - ); + this.imagePaths, { + super.key, + super.category, + }); @override String get actionIcon => action.getIcon(imagePaths); 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 00628c316..b973d62d1 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 @@ -29,6 +29,7 @@ import 'package:model/extensions/list_email_address_extension.dart'; import 'package:model/extensions/presentation_email_extension.dart'; import 'package:model/extensions/presentation_mailbox_extension.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; +import 'package:tmail_ui_user/features/base/extensions/popup_menu_action_list_extension.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_item_action_widget.dart'; @@ -591,23 +592,41 @@ class EmailActionReactor { EmailActionType action, ) handleEmailAction, }) { - return actionTypes.map((action) { - return PopupMenuItem( - key: Key('${action.name}_action'), - padding: EdgeInsets.zero, - child: PopupMenuItemActionWidget( - menuAction: PopupMenuItemEmailAction( - action, - AppLocalizations.of(context), - imagePaths, - ), - menuActionClick: (_) { - popBack(); - handleEmailAction(presentationEmail, action); - } - ) + final popupMenuItemEmailActions = actionTypes.map((actionType) { + return PopupMenuItemEmailAction( + actionType, + AppLocalizations.of(context), + imagePaths, + key: '${actionType.name}_action', + category: actionType.category, ); }).toList(); + + final groupedActions = popupMenuItemEmailActions.groupByCategory(); + final entries = groupedActions.entries.toList(); + + final popupMenuItems = [ + for (var i = 0; i < entries.length; i++) ...[ + ...entries[i].value.map((menuAction) => PopupMenuItem( + key: menuAction.key != null ? Key(menuAction.key!) : null, + padding: EdgeInsets.zero, + child: PopupMenuItemActionWidget( + menuAction: menuAction, + menuActionClick: (menuAction) { + popBack(); + handleEmailAction(presentationEmail, menuAction.action); + }, + ), + )), + if (i < entries.length - 1) + PopupMenuDivider( + height: 1, + color: AppColor.gray424244.withValues(alpha: 0.12), + ), + ], + ]; + + return popupMenuItems; } Future openEmailAddressDialog( diff --git a/lib/features/search/email/presentation/extension/handle_email_more_action_extension.dart b/lib/features/search/email/presentation/extension/handle_email_more_action_extension.dart index 0056c964c..cc71bc0da 100644 --- a/lib/features/search/email/presentation/extension/handle_email_more_action_extension.dart +++ b/lib/features/search/email/presentation/extension/handle_email_more_action_extension.dart @@ -1,7 +1,9 @@ +import 'package:core/presentation/extensions/color_extension.dart'; import 'package:flutter/material.dart'; import 'package:model/email/email_action_type.dart'; import 'package:model/email/presentation_email.dart'; import 'package:model/extensions/presentation_mailbox_extension.dart'; +import 'package:tmail_ui_user/features/base/extensions/popup_menu_action_list_extension.dart'; import 'package:tmail_ui_user/features/base/widget/popup_menu/popup_menu_item_action_widget.dart'; import 'package:tmail_ui_user/features/email/presentation/model/context_item_email_action.dart'; import 'package:tmail_ui_user/features/email/presentation/model/popup_menu_item_email_action.dart'; @@ -57,28 +59,44 @@ extension HandleEmailMoreActionExtension on SearchEmailController { useGroupedActions: true, ); } else { - final popupMenuItems = listEmailActions.map((actionType) { - return PopupMenuItem( - padding: EdgeInsets.zero, - child: PopupMenuItemActionWidget( - menuAction: PopupMenuItemEmailAction( - actionType, - AppLocalizations.of(context), - imagePaths, - ), - menuActionClick: (menuAction) { - popBack(); - pressEmailAction( - context, - menuAction.action, - presentationEmail, - mailboxContain: mailboxContain, - ); - }, - ), + final popupMenuItemEmailActions = listEmailActions.map((actionType) { + return PopupMenuItemEmailAction( + actionType, + AppLocalizations.of(context), + imagePaths, + category: actionType.category, ); }).toList(); + final groupedActions = popupMenuItemEmailActions.groupByCategory(); + final entries = groupedActions.entries.toList(); + + final popupMenuItems = [ + for (var i = 0; i < entries.length; i++) + ...[ + ...entries[i].value.map((menuAction) => PopupMenuItem( + padding: EdgeInsets.zero, + child: PopupMenuItemActionWidget( + menuAction: menuAction, + menuActionClick: (menuAction) { + popBack(); + pressEmailAction( + context, + menuAction.action, + presentationEmail, + mailboxContain: mailboxContain, + ); + }, + ), + )), + if (i < entries.length - 1) + PopupMenuDivider( + height: 1, + color: AppColor.gray424244.withValues(alpha: 0.12), + ), + ], + ]; + return openPopupMenuAction(context, position, popupMenuItems); } } diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index 285c2f80b..88584c18e 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -6,6 +6,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:model/model.dart'; +import 'package:tmail_ui_user/features/base/extensions/popup_menu_action_list_extension.dart'; import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; import 'package:tmail_ui_user/features/base/mixin/popup_menu_widget_mixin.dart'; import 'package:tmail_ui_user/features/base/widget/clean_messages_banner.dart'; @@ -668,27 +669,42 @@ class ThreadView extends GetWidget useGroupedActions: true, ); } else { - final popupMenuItems = listEmailActions.map((actionType) { - return PopupMenuItem( - padding: EdgeInsets.zero, - child: PopupMenuItemActionWidget( - menuAction: PopupMenuItemEmailAction( - actionType, - AppLocalizations.of(context), - controller.imagePaths, - ), - menuActionClick: (menuAction) { - popBack(); - controller.handleEmailActionType( - menuAction.action, - presentationEmail, - mailboxContain: mailboxContain, - ); - }, - ), + final popupMenuItemEmailActions = listEmailActions.map((actionType) { + return PopupMenuItemEmailAction( + actionType, + AppLocalizations.of(context), + controller.imagePaths, + category: actionType.category, ); }).toList(); + final groupedActions = popupMenuItemEmailActions.groupByCategory(); + final entries = groupedActions.entries.toList(); + + final popupMenuItems = [ + for (var i = 0; i < entries.length; i++) ...[ + ...entries[i].value.map((menuAction) => PopupMenuItem( + padding: EdgeInsets.zero, + child: PopupMenuItemActionWidget( + menuAction: menuAction, + menuActionClick: (menuAction) { + popBack(); + controller.handleEmailActionType( + menuAction.action, + presentationEmail, + mailboxContain: mailboxContain, + ); + }, + ), + )), + if (i < entries.length - 1) + PopupMenuDivider( + height: 1, + color: AppColor.gray424244.withValues(alpha: 0.12), + ), + ], + ]; + return controller.mailboxDashBoardController.openPopupMenuAction( context, position, diff --git a/lib/features/thread_detail/presentation/extension/on_thread_detail_action_click.dart b/lib/features/thread_detail/presentation/extension/on_thread_detail_action_click.dart index baf84e621..ed5b7e814 100644 --- a/lib/features/thread_detail/presentation/extension/on_thread_detail_action_click.dart +++ b/lib/features/thread_detail/presentation/extension/on_thread_detail_action_click.dart @@ -1,3 +1,4 @@ +import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/utils/platform_info.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter/material.dart'; @@ -6,13 +7,14 @@ import 'package:model/email/email_action_type.dart'; import 'package:model/email/mark_star_action.dart'; import 'package:model/email/read_actions.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; +import 'package:tmail_ui_user/features/base/extensions/popup_menu_action_list_extension.dart'; import 'package:tmail_ui_user/features/base/widget/popup_menu/popup_menu_item_action_widget.dart'; import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart'; import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart'; import 'package:tmail_ui_user/features/email/presentation/model/context_item_email_action.dart'; import 'package:tmail_ui_user/features/email/presentation/model/popup_menu_item_email_action.dart'; -import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart'; +import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_open_context_menu_extension.dart'; import 'package:tmail_ui_user/features/thread/domain/state/mark_as_multiple_email_read_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/mark_as_star_multiple_email_state.dart'; @@ -158,26 +160,44 @@ extension OnThreadDetailActionClick on ThreadDetailController { useGroupedActions: true, ); } else { + final popupMenuItemEmailActions = moreActions.map((actionType) { + return PopupMenuItemEmailAction( + actionType, + AppLocalizations.of(currentContext!), + imagePaths, + key: '${actionType.name}_action', + category: actionType.category, + ); + }).toList(); + + final groupedActions = popupMenuItemEmailActions.groupByCategory(); + final entries = groupedActions.entries.toList(); + + final popupMenuItems = [ + for (var i = 0; i < entries.length; i++) ...[ + ...entries[i].value.map((menuAction) => PopupMenuItem( + key: menuAction.key != null ? Key(menuAction.key!) : null, + padding: EdgeInsets.zero, + child: PopupMenuItemActionWidget( + menuAction: menuAction, + menuActionClick: (menuAction) { + popBack(); + onThreadDetailActionClick(menuAction.action); + }, + ), + )), + if (i < entries.length - 1) + PopupMenuDivider( + height: 1, + color: AppColor.gray424244.withValues(alpha: 0.12), + ), + ], + ]; + mailboxDashBoardController.openPopupMenu( currentContext!, position, - moreActions.map((action) { - return PopupMenuItem( - key: Key('${action.name}_action'), - padding: EdgeInsets.zero, - child: PopupMenuItemActionWidget( - menuAction: PopupMenuItemEmailAction( - action, - AppLocalizations.of(currentContext!), - imagePaths, - ), - menuActionClick: (_) { - popBack(); - onThreadDetailActionClick(action); - }, - ), - ); - }).toList(), + popupMenuItems, ); } }