diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart index dfc78168c..c6b7297e7 100644 --- a/core/lib/presentation/extensions/color_extension.dart +++ b/core/lib/presentation/extensions/color_extension.dart @@ -261,6 +261,7 @@ extension AppColor on Color { static const gray686E76 = Color(0xFF686E76); static const textSecondary = Color(0xFF1C1B1F); static const profileMenuDivider = Color(0xFF1D192B); + static const popupMenuItemHovered = Color(0xFFF8F8F8); static const mapGradientColor = [ [Color(0xFF21D4FD), Color(0xFFB721FF)], 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 e90235ff4..4b15ba5f3 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 @@ -76,6 +76,7 @@ class PopupMenuItemActionWidget extends StatelessWidget { type: MaterialType.transparency, child: InkWell( onTap: () => menuAction.onClick(menuActionClick), + hoverColor: AppColor.popupMenuItemHovered, child: Container( height: 48, width: double.infinity, diff --git a/lib/features/mailbox_dashboard/presentation/base_mailbox_dashboard_view.dart b/lib/features/mailbox_dashboard/presentation/base_mailbox_dashboard_view.dart index 3427ab5a1..51e79bc67 100644 --- a/lib/features/mailbox_dashboard/presentation/base_mailbox_dashboard_view.dart +++ b/lib/features/mailbox_dashboard/presentation/base_mailbox_dashboard_view.dart @@ -2,10 +2,8 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mixin/filter_email_popup_menu_mixin.dart'; abstract class BaseMailboxDashBoardView extends GetWidget - with FilterEmailPopupMenuMixin, - AppLoaderMixin { + with AppLoaderMixin { BaseMailboxDashBoardView({Key? key}) : super(key: key); } \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart index b68731f3e..3aed44d41 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart @@ -8,6 +8,7 @@ import 'package:model/extensions/presentation_mailbox_extension.dart'; import 'package:model/extensions/session_extension.dart'; import 'package:tmail_ui_user/features/base/widget/clean_messages_banner.dart'; import 'package:tmail_ui_user/features/base/widget/popup_item_no_icon_widget.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/scrollbar_list_view.dart'; import 'package:tmail_ui_user/features/composer/presentation/view/web/composer_overlay_view.dart'; import 'package:tmail_ui_user/features/email/presentation/email_view.dart'; @@ -42,9 +43,11 @@ import 'package:tmail_ui_user/features/quotas/presentation/widget/quotas_banner_ import 'package:tmail_ui_user/features/search/email/presentation/search_email_view.dart'; import 'package:tmail_ui_user/features/search/mailbox/presentation/search_mailbox_view.dart'; import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart'; +import 'package:tmail_ui_user/features/thread/presentation/model/popup_menu_item_filter_message_action.dart'; import 'package:tmail_ui_user/features/thread/presentation/thread_view.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/spam_banner/spam_report_banner_web_widget.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; +import 'package:tmail_ui_user/main/routes/route_navigation.dart'; class MailboxDashBoardView extends BaseMailboxDashBoardView { @@ -437,18 +440,30 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { FilterMessageOption filterMessageCurrent, RelativeRect buttonPosition ) { - controller.openPopupMenuAction( - context, - buttonPosition, - popupMenuFilterEmailActionTile( - context, - filterMessageCurrent, - (filterMessageSelected) { - controller.dispatchAction(FilterMessageAction(filterMessageSelected)); - }, - isSearchEmailRunning: controller.searchController.isSearchEmailRunning - ) - ); + final popupMenuItems = [ + if (!controller.searchController.isSearchEmailRunning) + FilterMessageOption.attachments, + FilterMessageOption.unread, + FilterMessageOption.starred, + ].map((filterOption) { + return PopupMenuItem( + padding: EdgeInsets.zero, + child: PopupMenuItemActionWidget( + menuAction: PopupMenuItemFilterMessageAction( + filterOption, + filterMessageCurrent, + AppLocalizations.of(context), + controller.imagePaths, + ), + menuActionClick: (menuAction) { + popBack(); + controller.dispatchAction(FilterMessageAction(menuAction.action)); + }, + ), + ); + }).toList(); + + controller.openPopupMenuAction(context, buttonPosition, popupMenuItems); } void _onDeleteFilterMessageOptionAction() { diff --git a/lib/features/mailbox_dashboard/presentation/mixin/filter_email_popup_menu_mixin.dart b/lib/features/mailbox_dashboard/presentation/mixin/filter_email_popup_menu_mixin.dart deleted file mode 100644 index 353095859..000000000 --- a/lib/features/mailbox_dashboard/presentation/mixin/filter_email_popup_menu_mixin.dart +++ /dev/null @@ -1,90 +0,0 @@ - -import 'package:core/presentation/extensions/color_extension.dart'; -import 'package:core/presentation/resources/image_paths.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:get/get.dart'; -import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart'; -import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; - -mixin FilterEmailPopupMenuMixin { - final _imagePaths = Get.find(); - - List popupMenuFilterEmailActionTile( - BuildContext context, - FilterMessageOption optionSelected, - Function(FilterMessageOption)? onCallBack, - { - bool isSearchEmailRunning = false - } - ) { - return [ - if (!isSearchEmailRunning) - ...[ - PopupMenuItem( - padding: EdgeInsets.zero, - child: _filterEmailAction( - context, - optionSelected, - FilterMessageOption.attachments, - onCallBack)), - const PopupMenuDivider(height: 0.5) - ], - PopupMenuItem( - padding: EdgeInsets.zero, - child: _filterEmailAction( - context, - optionSelected, - FilterMessageOption.unread, - onCallBack)), - const PopupMenuDivider(height: 0.5), - PopupMenuItem( - padding: EdgeInsets.zero, - child: _filterEmailAction( - context, - optionSelected, - FilterMessageOption.starred, - onCallBack)), - ]; - } - - Widget _filterEmailAction( - BuildContext context, - FilterMessageOption optionSelected, - FilterMessageOption option, - Function(FilterMessageOption)? onCallBack - ) { - return InkWell( - onTap: () => onCallBack?.call(option), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), - child: SizedBox( - child: Row(children: [ - SvgPicture.asset(option.getContextMenuIcon(_imagePaths), - width: 20, - height: 20, - fit: BoxFit.fill, - colorFilter: option != FilterMessageOption.starred - ? AppColor.colorTextButton.asFilter() - : null), - const SizedBox(width: 12), - Expanded(child: Text( - option.getName(AppLocalizations.of(context)), - style: const TextStyle( - fontSize: 15, - color: Colors.black, - fontWeight: FontWeight.w500))), - if (optionSelected == option) - ... [ - const SizedBox(width: 12), - SvgPicture.asset(_imagePaths.icFilterSelected, - width: 16, - height: 16, - fit: BoxFit.fill), - ] - ]) - ), - ) - ); - } -} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/widgets/search_filters/filter_message_button.dart b/lib/features/mailbox_dashboard/presentation/widgets/search_filters/filter_message_button.dart index 1c7322db2..1928d55dd 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/search_filters/filter_message_button.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/search_filters/filter_message_button.dart @@ -1,4 +1,3 @@ - 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'; @@ -7,57 +6,66 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/filter_message_button_style.dart'; import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart'; -typedef OnSelectFilterMessageOptionAction = Function( - BuildContext context, - FilterMessageOption filterMessageOption, - RelativeRect buttonPosition); -typedef OnDeleteFilterMessageOptionAction = Function(FilterMessageOption filterMessageOption); - -class FilterMessageButton extends StatelessWidget { +typedef OnSelectFilterMessageOptionAction = Function(BuildContext context, + FilterMessageOption filterMessageOption, RelativeRect buttonPosition); +typedef OnDeleteFilterMessageOptionAction = Function( + FilterMessageOption filterMessageOption); +class FilterMessageButton extends StatefulWidget { static const int _titleCharactersMaximum = 20; final FilterMessageOption filterMessageOption; final bool isSelected; final ImagePaths imagePaths; - final OnSelectFilterMessageOptionAction? onSelectFilterMessageOptionAction; - final OnDeleteFilterMessageOptionAction? onDeleteFilterMessageOptionAction; + final OnSelectFilterMessageOptionAction onSelectFilterMessageOptionAction; + final OnDeleteFilterMessageOptionAction onDeleteFilterMessageOptionAction; const FilterMessageButton({ super.key, required this.filterMessageOption, required this.imagePaths, this.isSelected = false, - this.onSelectFilterMessageOptionAction, - this.onDeleteFilterMessageOptionAction, + required this.onSelectFilterMessageOptionAction, + required this.onDeleteFilterMessageOptionAction, }); + @override + State createState() => _FilterMessageButtonState(); +} + +class _FilterMessageButtonState extends State { + final GlobalKey _buttonKey = GlobalKey(); + @override Widget build(BuildContext context) { - final filterMessageTitle = filterMessageOption.getTitle(context); + final filterMessageTitle = widget.filterMessageOption.getTitle(context); final deleteButtonWidget = TMailButtonWidget.fromIcon( - icon: imagePaths.icDeleteSelection, + icon: widget.imagePaths.icDeleteSelection, iconSize: FilterMessageButtonStyle.deleteIconSize, iconColor: AppColor.colorFilterMessageIcon, padding: const EdgeInsets.all(8), backgroundColor: Colors.transparent, - onTapActionCallback: () => onDeleteFilterMessageOptionAction?.call(filterMessageOption), + onTapActionCallback: () => widget.onDeleteFilterMessageOptionAction + .call(widget.filterMessageOption), ); final listComponentsWidget = [ SvgPicture.asset( - filterMessageOption.getIcon(imagePaths), + widget.filterMessageOption.getIcon(widget.imagePaths), width: FilterMessageButtonStyle.iconSize, height: FilterMessageButtonStyle.iconSize, - colorFilter: filterMessageOption.getIconColor().asFilter(), - fit: BoxFit.fill + colorFilter: widget.filterMessageOption.getIconColor().asFilter(), + fit: BoxFit.fill, ), const SizedBox(width: FilterMessageButtonStyle.spaceSize), Text( - filterMessageTitle.length > _titleCharactersMaximum - ? '${filterMessageTitle.substring(0, _titleCharactersMaximum)}...' - : filterMessageTitle, + filterMessageTitle.length > FilterMessageButton._titleCharactersMaximum + ? '${filterMessageTitle.substring( + 0, + FilterMessageButton._titleCharactersMaximum, + )}...' + : filterMessageTitle, maxLines: 1, overflow: TextOverflow.ellipsis, style: FilterMessageButtonStyle.titleStyle, @@ -65,78 +73,108 @@ class FilterMessageButton extends StatelessWidget { Padding( padding: FilterMessageButtonStyle.elementPadding, child: SvgPicture.asset( - imagePaths.icDropDown, + widget.imagePaths.icDropDown, width: FilterMessageButtonStyle.arrowDownIconSize, height: FilterMessageButtonStyle.arrowDownIconSize, colorFilter: AppColor.colorFilterMessageIcon.asFilter(), - fit: BoxFit.fill + fit: BoxFit.fill, ), ), ]; final childItem = _buildContainerForComponents( children: [ - ... listComponentsWidget, - if (isSelected) deleteButtonWidget, - ] + ...listComponentsWidget, + if (widget.isSelected) deleteButtonWidget, + ], ); - if (onSelectFilterMessageOptionAction == null) { - return childItem; - } - - if (!isSelected) { + if (widget.isSelected) { + return _buildContainerForComponents( + key: _buttonKey, + children: [ + InkWell( + onTapDown: (details) => _onTapDownAction(context, details), + borderRadius: FilterMessageButtonStyle.borderRadius, + child: Row( + mainAxisSize: MainAxisSize.min, + children: listComponentsWidget, + ), + ), + if (widget.isSelected) deleteButtonWidget, + ], + ); + } else { return Material( type: MaterialType.transparency, child: InkWell( + key: _buttonKey, onTapDown: (details) => _onTapDownAction(context, details), borderRadius: FilterMessageButtonStyle.borderRadius, - child: childItem + child: childItem, ), ); } - - return _buildContainerForComponents( - children: [ - InkWell( - onTapDown: (details) => _onTapDownAction(context, details), - borderRadius: FilterMessageButtonStyle.borderRadius, - child: Row( - mainAxisSize: MainAxisSize.min, - children: listComponentsWidget, - ), - ), - if (isSelected) deleteButtonWidget, - ] - ); } - Widget _buildContainerForComponents({required List children}) { + Widget _buildContainerForComponents({ + required List children, + Key? key, + }) { return Container( + key: key, decoration: BoxDecoration( borderRadius: FilterMessageButtonStyle.borderRadius, - color: filterMessageOption.getBackgroundColor(isSelected: isSelected) + color: widget.filterMessageOption.getBackgroundColor( + isSelected: widget.isSelected, + ), ), - padding: FilterMessageButtonStyle.getButtonPadding(isSelected), + padding: FilterMessageButtonStyle.getButtonPadding(widget.isSelected), child: Row( mainAxisSize: MainAxisSize.min, - children: children - ) + children: children, + ), ); } void _onTapDownAction(BuildContext context, TapDownDetails details) { - final screenSize = MediaQuery.sizeOf(context); - final offset = details.globalPosition; - final position = RelativeRect.fromLTRB( - offset.dx, - offset.dy, - screenSize.width - offset.dx, - screenSize.height - offset.dy); + RelativeRect? position; - onSelectFilterMessageOptionAction?.call( + final buttonBox = + _buttonKey.currentContext?.findRenderObject() as RenderBox?; + final overlayBox = + Overlay.maybeOf(context)?.context.findRenderObject() as RenderBox?; + + if (buttonBox == null || overlayBox == null) { + final screenSize = MediaQuery.sizeOf(context); + final offset = details.globalPosition; + position = RelativeRect.fromLTRB( + offset.dx, + offset.dy, + screenSize.width - offset.dx, + screenSize.height - offset.dy, + ); + } else { + final buttonPosition = buttonBox.localToGlobal( + Offset.zero, + ancestor: overlayBox, + ); + final buttonSize = buttonBox.size; + position = RelativeRect.fromRect( + Rect.fromLTWH( + buttonPosition.dx, + buttonPosition.dy + buttonSize.height + 8, + 0, + 0, + ), + Offset.zero & overlayBox.size, + ); + } + + widget.onSelectFilterMessageOptionAction.call( context, - filterMessageOption, - position); + widget.filterMessageOption, + position, + ); } -} \ No newline at end of file +} diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index 2c869fe36..1b407ee11 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -17,7 +17,6 @@ import 'package:tmail_ui_user/features/email/presentation/model/popup_menu_item_ import 'package:tmail_ui_user/features/mailbox/domain/state/clear_mailbox_state.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mixin/filter_email_popup_menu_mixin.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/recover_deleted_message_loading_banner_widget.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/extensions/vacation_response_extension.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/vacation/widgets/vacation_notification_message_widget.dart'; @@ -50,9 +49,7 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; class ThreadView extends GetWidget - with AppLoaderMixin, - FilterEmailPopupMenuMixin, - PopupMenuWidgetMixin { + with AppLoaderMixin, PopupMenuWidgetMixin { ThreadView({Key? key}) : super(key: key);