From 34c9c0cd0ed7319fa0d1d32f1e40586b4d5cc71a Mon Sep 17 00:00:00 2001 From: dab246 Date: Mon, 16 Sep 2024 13:20:43 +0700 Subject: [PATCH] TF-3002 [WEB] Apply new design for SearchFilterButton --- assets/images/ic_delete_selection.svg | 3 + .../extensions/color_extension.dart | 2 + .../presentation/resources/image_paths.dart | 1 + .../quick_search/quick_search_input_form.dart | 17 +- .../advanced_filter_controller.dart | 2 +- .../mailbox_dashboard_controller.dart | 45 ++--- .../controller/search_controller.dart | 27 --- .../mailbox_dashboard_view_web.dart | 162 +++++++----------- .../model/search/quick_search_filter.dart | 112 ++++++++---- .../styles/search_filter_button_style.dart | 17 ++ .../search_filters/search_filter_button.dart | 133 ++++++++++++++ .../widgets/search_input_form_widget.dart | 43 ++--- .../email/presentation/search_email_view.dart | 7 +- .../mailbox_dashboard_controller_test.dart | 4 +- 14 files changed, 345 insertions(+), 230 deletions(-) create mode 100644 assets/images/ic_delete_selection.svg create mode 100644 lib/features/mailbox_dashboard/presentation/styles/search_filter_button_style.dart create mode 100644 lib/features/mailbox_dashboard/presentation/widgets/search_filters/search_filter_button.dart diff --git a/assets/images/ic_delete_selection.svg b/assets/images/ic_delete_selection.svg new file mode 100644 index 000000000..91fd7df66 --- /dev/null +++ b/assets/images/ic_delete_selection.svg @@ -0,0 +1,3 @@ + + + diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart index 85813ca8b..b4eb31a01 100644 --- a/core/lib/presentation/extensions/color_extension.dart +++ b/core/lib/presentation/extensions/color_extension.dart @@ -225,6 +225,8 @@ extension AppColor on Color { static const loginViewShadowColor = Color(0x3DBCBCBC); static const colorEmailTileCheckboxUnhover = Color(0xFFAEB7C2); static const colorEmailTileHoverWeb = Color(0xFFDFEEFF); + static const colorSearchFilterButton = Color(0xFFECEEF1); + static const colorSuggestionSearchFilterButton = Color(0xFFEBEDF0); static const mapGradientColor = [ [Color(0xFF21D4FD), Color(0xFFB721FF)], diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart index eca728238..075cc5b54 100644 --- a/core/lib/presentation/resources/image_paths.dart +++ b/core/lib/presentation/resources/image_paths.dart @@ -218,6 +218,7 @@ class ImagePaths { String get icCheckboxSelected => _getImagePath('ic_checkbox_selected.svg'); String get icGoodSignature => _getImagePath('ic_good_signature.svg'); String get icBadSignature => _getImagePath('ic_bad_signature.svg'); + String get icDeleteSelection => _getImagePath('ic_delete_selection.svg'); String _getImagePath(String imageName) { return AssetsPaths.images + imageName; diff --git a/core/lib/presentation/views/quick_search/quick_search_input_form.dart b/core/lib/presentation/views/quick_search/quick_search_input_form.dart index 1a159229d..b037a14f6 100644 --- a/core/lib/presentation/views/quick_search/quick_search_input_form.dart +++ b/core/lib/presentation/views/quick_search/quick_search_input_form.dart @@ -1286,13 +1286,16 @@ class _SuggestionsListState extends State<_SuggestionsList> if (widget.buttonActionCallback != null) { return Padding( padding: const EdgeInsetsDirectional.only(end: 8, bottom: 8), - child: InkWell( - borderRadius: const BorderRadius.all(Radius.circular(10)), - onTap: () { - widget.buttonActionCallback?.call(action); - invalidateSuggestions(); - }, - child: widget.actionButtonBuilder!(context, action), + child: Material( + type: MaterialType.transparency, + child: InkWell( + borderRadius: const BorderRadius.all(Radius.circular(10)), + onTap: () { + widget.buttonActionCallback?.call(action); + invalidateSuggestions(); + }, + child: widget.actionButtonBuilder!(context, action), + ), ), ); } else { diff --git a/lib/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart index 616c4c786..25c9e8bc9 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart @@ -397,7 +397,7 @@ class AdvancedFilterController extends BaseController { ); } else if (action is ClearDateRangeToAdvancedSearch) { _updateDateRangeTime(action.receiveTime); - } else if (action is StartSearchEmailAction && action.filter == QuickSearchFilter.fromMe) { + } else if (action is StartSearchEmailAction && action.filter == QuickSearchFilter.from) { _updateFromField(); } else if (action is SearchEmailByFromFieldsAction) { searchController.clearSearchFilter(); diff --git a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart index f13d24084..a7d1679df 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -1575,17 +1575,6 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo _getAllIdentities(); } - void selectQuickSearchFilter(QuickSearchFilter filter) { - return searchController.selectQuickSearchFilter(filter); - } - - void selectQuickSearchFilterFrom(EmailAddress fromEmailFilter) { - return searchController.selectQuickSearchFilter( - QuickSearchFilter.fromMe, - fromEmailFilter: fromEmailFilter - ); - } - void addFilterToSuggestionForm(QuickSearchFilter filter) { searchController.addFilterToSuggestionForm(filter); } @@ -1667,24 +1656,26 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo } } - void selectQuickSearchFilterAction(QuickSearchFilter filter) async { - log('MailboxDashBoardController::selectQuickSearchFilterAction(): filter: $filter'); - if (filter == QuickSearchFilter.fromMe) { - if (accountId.value == null || sessionCurrent == null) { - logError('MailboxDashBoardController::selectQuickSearchFilterAction(): accountId or sessionCurrent is null'); - } - final listContactSelected = searchController.searchEmailFilter.value.from; - final arguments = ContactArguments(accountId.value!, sessionCurrent!, listContactSelected); + void selectHasAttachmentSearchFilter() { + searchController.updateFilterEmail(hasAttachment: true); + dispatchAction(StartSearchEmailAction()); + } - final newContact = await DialogRouter.pushGeneralDialog(routeName: AppRoutes.contact, arguments: arguments); + Future selectFromSearchFilter() async { + if (accountId.value == null || sessionCurrent == null) return; - if (newContact is EmailAddress) { - selectQuickSearchFilterFrom(newContact); - dispatchAction(StartSearchEmailAction(filter: filter)); - } - } else { - selectQuickSearchFilter(filter); - dispatchAction(StartSearchEmailAction(filter: filter)); + final contactArgument = ContactArguments( + accountId.value!, + sessionCurrent!, + searchController.searchEmailFilter.value.from); + + final newContact = await DialogRouter.pushGeneralDialog( + routeName: AppRoutes.contact, + arguments: contactArgument); + + if (newContact is EmailAddress) { + searchController.updateFilterEmail(fromOption: Some({newContact.emailAddress})); + dispatchAction(StartSearchEmailAction(filter: QuickSearchFilter.from)); } } diff --git a/lib/features/mailbox_dashboard/presentation/controller/search_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/search_controller.dart index c725cda47..8ee5a0aff 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/search_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/search_controller.dart @@ -11,7 +11,6 @@ import 'package:jmap_dart_client/jmap/core/sort/comparator.dart'; import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:jmap_dart_client/jmap/core/utc_date.dart'; -import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_comparator.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_comparator_property.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart'; @@ -71,32 +70,6 @@ class SearchController extends BaseController with DateRangePickerMixin { searchEmailFilter.value = SearchEmailFilter.initial(); } - void selectQuickSearchFilter( - QuickSearchFilter quickSearchFilter, - {EmailAddress? fromEmailFilter} - ) { - final isFilterSelected = quickSearchFilter.isSelected(searchEmailFilter.value); - - switch (quickSearchFilter) { - case QuickSearchFilter.hasAttachment: - updateFilterEmail(hasAttachment: !isFilterSelected); - return; - case QuickSearchFilter.last7Days: - updateFilterEmail(emailReceiveTimeType: EmailReceiveTimeType.allTime); - return; - case QuickSearchFilter.fromMe: - if (fromEmailFilter != null) { - final newListEmailAddress = (fromEmailFilter.email != null && fromEmailFilter.email!.isNotEmpty) - ? {fromEmailFilter.email!} - : {}; - updateFilterEmail(fromOption: Some(newListEmailAddress)); - } - return; - case QuickSearchFilter.sortBy: - return; - } - } - void addFilterToSuggestionForm(QuickSearchFilter filter) { if (listFilterOnSuggestionForm.contains(filter)) { listFilterOnSuggestionForm.remove(filter); 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 0aa186da8..a9858dbf1 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart @@ -21,6 +21,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/sear import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/download/download_task_item_widget.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/navigation_bar/navigation_bar_widget.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/recover_deleted_message_loading_banner_widget.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/search_filters/search_filter_button.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/search_input_form_widget.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/top_bar_thread_selection.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/extensions/vacation_response_extension.dart'; @@ -36,7 +37,6 @@ import 'package:tmail_ui_user/features/thread/presentation/widgets/banner_delete import 'package:tmail_ui_user/features/thread/presentation/widgets/banner_empty_trash_widget.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/utils/app_utils.dart'; class MailboxDashBoardView extends BaseMailboxDashBoardView { @@ -460,24 +460,23 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { }); } - bool supportListButtonQuickSearchFilter(BuildContext context) { - return controller.searchController.isSearchEmailRunning - && controller.dashboardRoute.value == DashboardRoutes.thread; - } - Widget _buildListButtonQuickSearchFilter(BuildContext context) { return Obx(() { - if (supportListButtonQuickSearchFilter(context)) { + if (controller.searchController.isSearchEmailRunning && + controller.dashboardRoute.value == DashboardRoutes.thread) { return Padding( padding: const EdgeInsetsDirectional.only(end: 16, top: 8), child: Row( children: [ - ...QuickSearchFilter.values - .where((filter) => filter != QuickSearchFilter.sortBy) - .map((filter) => _buildQuickSearchFilterButton(context, filter)), - const Spacer(), - _buildQuickSearchFilterButton(context, QuickSearchFilter.sortBy) - ] + _buildQuickSearchFilterButton(context, QuickSearchFilter.from), + const SizedBox(width: 8), + _buildQuickSearchFilterButton(context, QuickSearchFilter.dateTime), + const SizedBox(width: 8), + _buildQuickSearchFilterButton(context, QuickSearchFilter.hasAttachment), + const Spacer(), + _buildQuickSearchFilterButton(context, QuickSearchFilter.sortBy), + const SizedBox(width: 8), + ] ), ); } else { @@ -488,87 +487,64 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { Widget _buildQuickSearchFilterButton( BuildContext context, - QuickSearchFilter filter + QuickSearchFilter searchFilter ) { return Obx(() { - final isFilterSelected = filter.isSelected( - controller.searchController.searchEmailFilter.value, - ); + final searchEmailFilter = controller.searchController.searchEmailFilter.value; + final sortOrderType = controller.searchController.sortOrderFiltered.value; + final listAddressOfFrom = controller.searchController.searchEmailFilter.value.from; + final userName = controller.sessionCurrent?.username; + final startDate = controller.searchController.startDateFiltered; + final endDate = controller.searchController.endDateFiltered; + final receiveTimeType = controller.searchController.receiveTimeFiltered; - return Padding( - padding: EdgeInsets.only( - right: AppUtils.isDirectionRTL(context) ? 0 : 8, - left: AppUtils.isDirectionRTL(context) ? 8 : 0, - ), - child: InkWell( - onTap: () { - if (!filter.isTapOpenPopupMenu()) { - controller.selectQuickSearchFilterAction(filter); - } - }, - onTapDown: (detail) { - final screenSize = MediaQuery.of(context).size; - final offset = detail.globalPosition; - final position = RelativeRect.fromLTRB( - offset.dx, - offset.dy, - screenSize.width - offset.dx, - screenSize.height - offset.dy, - ); + final isSelected = searchFilter.isSelected( + context, + searchEmailFilter, + sortOrderType); - switch(filter) { - case QuickSearchFilter.last7Days: - _openPopupMenuDateFilter(context, position); - break; - case QuickSearchFilter.sortBy: - _openPopupMenuSortFilter(context, position); - break; - default: - break; - } - }, - borderRadius: const BorderRadius.all(Radius.circular(10)), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - color: filter.getBackgroundColor(isFilterSelected: isFilterSelected)), - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ - SvgPicture.asset( - filter.getIcon(controller.imagePaths, isFilterSelected: isFilterSelected), - width: 16, - height: 16, - fit: BoxFit.fill), - const SizedBox(width: 4), - Text( - filter == QuickSearchFilter.fromMe - ? _getQuickSearchFilterFromTitle(context) - : filter.getTitle( - context, - receiveTimeType: controller.searchController.receiveTimeFiltered, - startDate: controller.searchController.startDateFiltered, - endDate: controller.searchController.endDateFiltered, - sortOrderType: controller.searchController.sortOrderFiltered.value, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: filter.getTextStyle(isFilterSelected: isFilterSelected), - ), - if (filter == QuickSearchFilter.last7Days || filter == QuickSearchFilter.fromMe) - ... [ - const SizedBox(width: 4), - SvgPicture.asset( - controller.imagePaths.icChevronDown, - width: 16, - height: 16, - fit: BoxFit.fill), - ] - ])), - ), + return SearchFilterButton( + searchFilter: searchFilter, + imagePaths: controller.imagePaths, + isSelected: isSelected, + receiveTimeType: receiveTimeType, + startDate: startDate, + endDate: endDate, + sortOrderType: sortOrderType, + listAddressOfFrom: listAddressOfFrom, + userName: userName, + onSelectSearchFilterAction: _onSelectSearchFilterAction, ); }); } + void _onSelectSearchFilterAction( + BuildContext context, + QuickSearchFilter searchFilter, + {RelativeRect? buttonPosition} + ) async { + switch(searchFilter) { + case QuickSearchFilter.dateTime: + if (buttonPosition != null) { + _openPopupMenuDateFilter(context, buttonPosition); + } + break; + case QuickSearchFilter.sortBy: + if (buttonPosition != null) { + _openPopupMenuSortFilter(context, buttonPosition); + } + break; + case QuickSearchFilter.from: + controller.selectFromSearchFilter(); + break; + case QuickSearchFilter.hasAttachment: + controller.selectHasAttachmentSearchFilter(); + break; + default: + break; + } + } + List popupMenuEmailReceiveTimeType( BuildContext context, EmailReceiveTimeType? receiveTimeSelected, @@ -666,18 +642,4 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { ), ); } - - String _getQuickSearchFilterFromTitle(BuildContext context) { - final searchEmailFilterFromFiled = controller.searchController.searchEmailFilter.value.from; - if (searchEmailFilterFromFiled.length == 1) { - if (searchEmailFilterFromFiled.first == controller.sessionCurrent?.username.value && - controller.sessionCurrent?.username.value.isNotEmpty == true) { - return QuickSearchFilter.fromMe.getTitle(context); - } else { - return '${AppLocalizations.of(context).from_email_address_prefix} ${searchEmailFilterFromFiled.first}'; - } - } else { - return AppLocalizations.of(context).from_email_address_prefix; - } - } } \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart b/lib/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart index 2dc8a9e9f..6f7e04c8c 100644 --- a/lib/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart +++ b/lib/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart @@ -2,6 +2,7 @@ import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/resources/image_paths.dart'; import 'package:flutter/cupertino.dart'; +import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; @@ -11,7 +12,9 @@ enum QuickSearchFilter { hasAttachment, last7Days, fromMe, - sortBy; + sortBy, + dateTime, + from; String getName(BuildContext context) { switch(this) { @@ -23,6 +26,12 @@ enum QuickSearchFilter { return AppLocalizations.of(context).fromMe; case QuickSearchFilter.sortBy: return AppLocalizations.of(context).sortBy; + case QuickSearchFilter.dateTime: + return AppLocalizations.of(context).allTime; + case QuickSearchFilter.from: + return AppLocalizations.of(context).from_email_address_prefix; + default: + return ''; } } @@ -32,74 +41,109 @@ enum QuickSearchFilter { EmailSortOrderType? sortOrderType, DateTime? startDate, DateTime? endDate, + Set? listAddressOfFrom, + UserName? userName }) { switch(this) { case QuickSearchFilter.hasAttachment: return AppLocalizations.of(context).hasAttachment; case QuickSearchFilter.last7Days: - return receiveTimeType?.getTitle(context, startDate: startDate, endDate: endDate) - ?? AppLocalizations.of(context).allTime; + return AppLocalizations.of(context).last7Days; case QuickSearchFilter.fromMe: return AppLocalizations.of(context).fromMe; case QuickSearchFilter.sortBy: return sortOrderType?.getTitle(context) ?? AppLocalizations.of(context).mostRecent; + case QuickSearchFilter.dateTime: + return receiveTimeType?.getTitle( + context, + startDate: startDate, + endDate: endDate + ) ?? AppLocalizations.of(context).allTime; + case QuickSearchFilter.from: + if (listAddressOfFrom?.length != 1) { + return AppLocalizations.of(context).from_email_address_prefix; + } + + if (userName?.value.isNotEmpty == true && listAddressOfFrom?.first == userName?.value) { + return AppLocalizations.of(context).fromMe; + } else { + return '${AppLocalizations.of(context).from_email_address_prefix} ${listAddressOfFrom?.first}'; + } + default: + return ''; } } - String getIcon(ImagePaths imagePaths, {required bool isFilterSelected}) { - if (isFilterSelected) { - return imagePaths.icSelectedSB; - } else { - switch(this) { - case QuickSearchFilter.hasAttachment: - return imagePaths.icAttachmentSB; - case QuickSearchFilter.last7Days: - return imagePaths.icCalendarSB; - case QuickSearchFilter.fromMe: - return imagePaths.icUserSB; - case QuickSearchFilter.sortBy: - return imagePaths.icFilterSB; - } + String getIcon(ImagePaths imagePaths, {bool isSelected = false}) { + switch(this) { + case QuickSearchFilter.hasAttachment: + return isSelected ? imagePaths.icSelectedSB : imagePaths.icAttachmentSB; + case QuickSearchFilter.last7Days: + return isSelected ? imagePaths.icSelectedSB : imagePaths.icCalendarSB; + case QuickSearchFilter.fromMe: + return isSelected ? imagePaths.icSelectedSB : imagePaths.icUserSB; + case QuickSearchFilter.sortBy: + return imagePaths.icFilterSB; + case QuickSearchFilter.dateTime: + return imagePaths.icCalendarSB; + case QuickSearchFilter.from: + return imagePaths.icUserSB; } } - Color getBackgroundColor({required bool isFilterSelected}) { + Color getBackgroundColor({bool isFilterSelected = false}) { if (isFilterSelected) { - return AppColor.colorItemEmailSelectedDesktop; + return AppColor.primaryColor.withOpacity(0.06); } else { - return AppColor.colorButtonHeaderThread; + return AppColor.colorSearchFilterButton; } } - TextStyle getTextStyle({required bool isFilterSelected}) { + Color getSuggestionBackgroundColor({bool isFilterSelected = false}) { if (isFilterSelected) { - return const TextStyle( - fontSize: 13, - fontWeight: FontWeight.w500, - color: AppColor.colorTextButton); + return AppColor.primaryColor.withOpacity(0.06); } else { - return const TextStyle( - fontSize: 13, - fontWeight: FontWeight.normal, - color: AppColor.colorTextButtonHeaderThread); + return AppColor.colorSuggestionSearchFilterButton.withOpacity(0.6); + } + } + + Color getIconColor({bool isSelected = false}) { + if (isSelected) { + return AppColor.primaryColor; + } else { + return AppColor.colorTextBody; } } bool isApplied(List listFilter) => listFilter.contains(this); - bool isSelected(SearchEmailFilter filter) { + bool isSelected( + BuildContext context, + SearchEmailFilter searchFilter, + EmailSortOrderType sortOrderType + ) { switch (this) { case QuickSearchFilter.hasAttachment: - return filter.hasAttachment == true; + return searchFilter.hasAttachment; case QuickSearchFilter.last7Days: return true; case QuickSearchFilter.fromMe: - return filter.from.length == 1; + return searchFilter.from.length == 1; case QuickSearchFilter.sortBy: - return true; + return sortOrderType != EmailSortOrderType.mostRecent; + case QuickSearchFilter.dateTime: + return searchFilter.emailReceiveTimeType != EmailReceiveTimeType.allTime; + case QuickSearchFilter.from: + return searchFilter.from.isNotEmpty; } } - bool isTapOpenPopupMenu() => this == QuickSearchFilter.last7Days || this == QuickSearchFilter.sortBy; + bool isOnTapWithPositionActionSupported() => + this == QuickSearchFilter.dateTime || this == QuickSearchFilter.sortBy; + + bool isArrowDownIconSupported() => + this == QuickSearchFilter.dateTime || + this == QuickSearchFilter.from || + this == QuickSearchFilter.sortBy; } \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/styles/search_filter_button_style.dart b/lib/features/mailbox_dashboard/presentation/styles/search_filter_button_style.dart new file mode 100644 index 000000000..472873bfe --- /dev/null +++ b/lib/features/mailbox_dashboard/presentation/styles/search_filter_button_style.dart @@ -0,0 +1,17 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:flutter/material.dart'; + +class SearchFilterButtonStyle { + static const double iconSize = 16; + static const double deleteIconSize = 10; + static const double spaceSize = 4; + + static const EdgeInsetsGeometry buttonPadding = EdgeInsets.symmetric(horizontal: 12, vertical: 8); + static const EdgeInsetsGeometry elementPadding = EdgeInsetsDirectional.only(start: 8); + static const BorderRadius borderRadius = BorderRadius.all(Radius.circular(10)); + + static const TextStyle titleStyle = TextStyle( + fontSize: 13, + fontWeight: FontWeight.normal, + color: AppColor.colorTextButtonHeaderThread); +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/widgets/search_filters/search_filter_button.dart b/lib/features/mailbox_dashboard/presentation/widgets/search_filters/search_filter_button.dart new file mode 100644 index 000000000..b3613d3ef --- /dev/null +++ b/lib/features/mailbox_dashboard/presentation/widgets/search_filters/search_filter_button.dart @@ -0,0 +1,133 @@ + +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:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:jmap_dart_client/jmap/core/user_name.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/search_filter_button_style.dart'; + +typedef OnSelectSearchFilterAction = Function( + BuildContext context, + QuickSearchFilter searchFilter, + {RelativeRect? buttonPosition}); + +class SearchFilterButton extends StatelessWidget { + + final QuickSearchFilter searchFilter; + final bool isSelected; + final ImagePaths imagePaths; + final EmailReceiveTimeType? receiveTimeType; + final EmailSortOrderType? sortOrderType; + final DateTime? startDate; + final DateTime? endDate; + final Set? listAddressOfFrom; + final UserName? userName; + final Color? backgroundColor; + final OnSelectSearchFilterAction? onSelectSearchFilterAction; + + const SearchFilterButton({ + super.key, + required this.searchFilter, + required this.imagePaths, + this.isSelected = false, + this.receiveTimeType, + this.sortOrderType, + this.startDate, + this.endDate, + this.listAddressOfFrom, + this.userName, + this.backgroundColor, + this.onSelectSearchFilterAction, + }); + + @override + Widget build(BuildContext context) { + final childItem = Container( + decoration: BoxDecoration( + borderRadius: SearchFilterButtonStyle.borderRadius, + color: backgroundColor ?? searchFilter.getBackgroundColor(isFilterSelected: isSelected)), + padding: SearchFilterButtonStyle.buttonPadding, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + SvgPicture.asset( + searchFilter.getIcon(imagePaths, isSelected: isSelected), + width: SearchFilterButtonStyle.iconSize, + height: SearchFilterButtonStyle.iconSize, + colorFilter: searchFilter.getIconColor(isSelected: isSelected).asFilter(), + fit: BoxFit.fill), + const SizedBox(width: SearchFilterButtonStyle.spaceSize), + Text( + searchFilter.getTitle( + context, + receiveTimeType: receiveTimeType, + startDate: startDate, + endDate: startDate, + sortOrderType: sortOrderType, + listAddressOfFrom: listAddressOfFrom, + userName: userName), + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: SearchFilterButtonStyle.titleStyle, + ), + if (searchFilter.isArrowDownIconSupported()) + Padding( + padding: SearchFilterButtonStyle.elementPadding, + child: SvgPicture.asset( + imagePaths.icDropDown, + width: SearchFilterButtonStyle.iconSize, + height: SearchFilterButtonStyle.iconSize, + colorFilter: AppColor.colorTextBody.asFilter(), + fit: BoxFit.fill), + ), + if (isSelected) + TMailButtonWidget.fromIcon( + icon: imagePaths.icDeleteSelection, + iconSize: SearchFilterButtonStyle.deleteIconSize, + iconColor: AppColor.colorTextBody, + padding: EdgeInsets.zero, + backgroundColor: Colors.transparent, + margin: SearchFilterButtonStyle.elementPadding, + ), + ] + ) + ); + + if (onSelectSearchFilterAction != null) { + return Material( + type: MaterialType.transparency, + child: InkWell( + onTap: searchFilter.isOnTapWithPositionActionSupported() + ? null + : () => _onTapAction(context), + onTapDown: searchFilter.isOnTapWithPositionActionSupported() + ? (details) => _onTapDownAction(context, details) + : null, + borderRadius: SearchFilterButtonStyle.borderRadius, + child: childItem), + ); + } else { + return childItem; + } + } + + void _onTapAction(BuildContext context) { + onSelectSearchFilterAction?.call(context, searchFilter); + } + + 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); + + onSelectSearchFilterAction?.call(context, searchFilter, buttonPosition: position); + } +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/widgets/search_input_form_widget.dart b/lib/features/mailbox_dashboard/presentation/widgets/search_input_form_widget.dart index d8463069d..b3e388ed1 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/search_input_form_widget.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/search_input_form_widget.dart @@ -1,7 +1,6 @@ import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/resources/image_paths.dart'; -import 'package:core/presentation/utils/style_utils.dart'; import 'package:core/presentation/views/button/icon_button_web.dart'; import 'package:core/presentation/views/quick_search/quick_search_input_form.dart'; import 'package:core/utils/direction_utils.dart'; @@ -23,6 +22,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/ad import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/quick_search/contact_quick_search_item.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/quick_search/email_quick_search_item_tile_widget.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/quick_search/recent_search_item_tile_widget.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/search_filters/search_filter_button.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; class SearchInputFormWidget extends StatelessWidget with AppLoaderMixin { @@ -65,9 +65,13 @@ class SearchInputFormWidget extends StatelessWidget with AppLoaderMixin { borderRadius: BorderRadius.all(Radius.circular(16)), ), debounceDuration: const Duration(milliseconds: 300), - listActionButton: QuickSearchFilter.values, + listActionButton: const [ + QuickSearchFilter.hasAttachment, + QuickSearchFilter.last7Days, + QuickSearchFilter.fromMe, + ], actionButtonBuilder: (context, filterAction) { - if (filterAction is QuickSearchFilter && filterAction != QuickSearchFilter.sortBy) { + if (filterAction is QuickSearchFilter) { return buildListButtonForQuickSearchForm(context, filterAction); } else { return const SizedBox.shrink(); @@ -217,34 +221,15 @@ class SearchInputFormWidget extends StatelessWidget with AppLoaderMixin { ); } - Widget buildListButtonForQuickSearchForm(BuildContext context, QuickSearchFilter filter) { + Widget buildListButtonForQuickSearchForm(BuildContext context, QuickSearchFilter searchFilter) { return Obx(() { - final isFilterSelected = filter.isApplied(_searchController.listFilterOnSuggestionForm); + final isSelected = searchFilter.isApplied(_searchController.listFilterOnSuggestionForm); - return Chip( - labelPadding: const EdgeInsetsDirectional.only( - top: 2, - bottom: 2, - end: 10, - ), - label: Text( - filter.getName(context), - maxLines: 1, - overflow: CommonTextStyle.defaultTextOverFlow, - softWrap: CommonTextStyle.defaultSoftWrap, - style: filter.getTextStyle(isFilterSelected: isFilterSelected), - ), - avatar: SvgPicture.asset( - filter.getIcon(_imagePaths, isFilterSelected: isFilterSelected), - width: 16, - height: 16, - fit: BoxFit.fill), - labelStyle: filter.getTextStyle(isFilterSelected: isFilterSelected), - backgroundColor: filter.getBackgroundColor(isFilterSelected: isFilterSelected), - shape: RoundedRectangleBorder( - borderRadius: const BorderRadius.all(Radius.circular(10)), - side: BorderSide(color: filter.getBackgroundColor(isFilterSelected: isFilterSelected)), - ), + return SearchFilterButton( + searchFilter: searchFilter, + imagePaths: _imagePaths, + isSelected: isSelected, + backgroundColor: searchFilter.getSuggestionBackgroundColor(isFilterSelected: isSelected), ); }); } diff --git a/lib/features/search/email/presentation/search_email_view.dart b/lib/features/search/email/presentation/search_email_view.dart index 5ed097a53..cf0fb6756 100644 --- a/lib/features/search/email/presentation/search_email_view.dart +++ b/lib/features/search/email/presentation/search_email_view.dart @@ -257,7 +257,7 @@ class SearchEmailView extends GetWidget padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ SvgPicture.asset( - filter.getIcon(controller.imagePaths, isFilterSelected: filterSelected), + filter.getIcon(controller.imagePaths, isSelected: filterSelected), width: 16, height: 16, fit: BoxFit.fill), @@ -273,7 +273,10 @@ class SearchEmailView extends GetWidget maxLines: 1, overflow: CommonTextStyle.defaultTextOverFlow, softWrap: CommonTextStyle.defaultSoftWrap, - style: filter.getTextStyle(isFilterSelected: filterSelected), + style: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.normal, + color: AppColor.colorTextButtonHeaderThread), ), if (filter == QuickSearchFilter.last7Days || filter == QuickSearchFilter.sortBy) ... [ diff --git a/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart b/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart index 15c0a092f..d30443722 100644 --- a/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart +++ b/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart @@ -64,7 +64,6 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/advanced_search_filter.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; @@ -405,9 +404,8 @@ void main() { expect(searchController.sortOrderFiltered.value, EmailSortOrderType.oldest); // expect filter in search controller update as expected - mailboxDashboardController.selectQuickSearchFilterAction(QuickSearchFilter.hasAttachment); + mailboxDashboardController.selectHasAttachmentSearchFilter(); expect(searchController.searchEmailFilter.value.hasAttachment, true); - mailboxDashboardController.selectQuickSearchFilterAction(QuickSearchFilter.last7Days); mailboxDashboardController.selectReceiveTimeQuickSearchFilter(context, EmailReceiveTimeType.last30Days); expect(searchController.searchEmailFilter.value.emailReceiveTimeType, EmailReceiveTimeType.last30Days);