TF-3759 Update style popup context menu for filter message option on mailbox dashboard view
Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
@@ -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)],
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<MailboxDashBoardController>
|
||||
with FilterEmailPopupMenuMixin,
|
||||
AppLoaderMixin {
|
||||
with AppLoaderMixin {
|
||||
BaseMailboxDashBoardView({Key? key}) : super(key: key);
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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<ImagePaths>();
|
||||
|
||||
List<PopupMenuEntry> 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),
|
||||
]
|
||||
])
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
+100
-62
@@ -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<FilterMessageButton> createState() => _FilterMessageButtonState();
|
||||
}
|
||||
|
||||
class _FilterMessageButtonState extends State<FilterMessageButton> {
|
||||
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<Widget> children}) {
|
||||
Widget _buildContainerForComponents({
|
||||
required List<Widget> 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ThreadController>
|
||||
with AppLoaderMixin,
|
||||
FilterEmailPopupMenuMixin,
|
||||
PopupMenuWidgetMixin {
|
||||
with AppLoaderMixin, PopupMenuWidgetMixin {
|
||||
|
||||
ThreadView({Key? key}) : super(key: key);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user