From d05904229b3b7796cf1a24580a499aa9adbb4559 Mon Sep 17 00:00:00 2001 From: dab246 Date: Fri, 26 Sep 2025 18:12:27 +0700 Subject: [PATCH] TF-4066 Display `Unread`, `Starred` in list quick search button in dashboard view --- .../extensions/color_extension.dart | 1 - .../advanced_filter_controller.dart | 2 + .../mailbox_dashboard_controller.dart | 7 ++++ .../controller/search_controller.dart | 6 +++ ...select_search_filter_action_extension.dart | 38 +++++++++++++++++++ .../mailbox_dashboard_view_web.dart | 21 +++++++++- .../model/search/quick_search_filter.dart | 15 +++++--- .../model/search/search_email_filter.dart | 10 ++++- .../presentation/search_email_controller.dart | 2 + 9 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 lib/features/mailbox_dashboard/presentation/extensions/select_search_filter_action_extension.dart diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart index 733e32dfa..fd70934ba 100644 --- a/core/lib/presentation/extensions/color_extension.dart +++ b/core/lib/presentation/extensions/color_extension.dart @@ -224,7 +224,6 @@ extension AppColor on Color { static const colorFilterMessageButton = Color(0xFFEBEDF0); static const colorFilterMessageIcon = Color(0xFF686E76); static const colorFilterMessageTitle = Color(0xFF686E76); - static const colorStarredSearchFilterIcon = Color(0xFFFFCC00); static const colorMobileSearchFilterButton = Color(0xFFEBEDF0); static const colorContactViewClearFilterButton = Color(0x001C3D0D); static const steelGrayA540 = Color(0xFF55687D); 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 d26859197..d569e5565 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart @@ -119,6 +119,7 @@ class AdvancedFilterController extends BaseController { Option? mailboxOption, Option? emailReceiveTimeTypeOption, Option? hasAttachmentOption, + Option? unreadOption, Option? beforeOption, Option? startDateOption, Option? endDateOption, @@ -134,6 +135,7 @@ class AdvancedFilterController extends BaseController { mailboxOption: mailboxOption, emailReceiveTimeTypeOption: emailReceiveTimeTypeOption, hasAttachmentOption: hasAttachmentOption, + unreadOption: unreadOption, beforeOption: beforeOption, startDateOption: startDateOption, endDateOption: endDateOption, 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 d65dd070d..48ee32249 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -125,6 +125,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/notify_thread_detail_setting_updated.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/extensions/reopen_composer_cache_extension.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/select_search_filter_action_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/set_error_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/update_current_emails_flags_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_paywall_extension.dart'; @@ -2240,6 +2241,12 @@ class MailboxDashBoardController extends ReloadableController case QuickSearchFilter.folder: _deleteFolderSearchFilter(); break; + case QuickSearchFilter.starred: + deleteStarredSearchFilter(); + break; + case QuickSearchFilter.unread: + deleteUnreadSearchFilter(); + break; default: break; } diff --git a/lib/features/mailbox_dashboard/presentation/controller/search_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/search_controller.dart index ec10a9464..dab5a15eb 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/search_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/search_controller.dart @@ -183,6 +183,7 @@ class SearchController extends BaseController with DateRangePickerMixin { Option? mailboxOption, Option? emailReceiveTimeTypeOption, Option? hasAttachmentOption, + Option? unreadOption, Option? beforeOption, Option? startDateOption, Option? endDateOption, @@ -199,6 +200,7 @@ class SearchController extends BaseController with DateRangePickerMixin { mailboxOption: mailboxOption, emailReceiveTimeTypeOption: emailReceiveTimeTypeOption, hasAttachmentOption: hasAttachmentOption, + unreadOption: unreadOption, beforeOption: beforeOption, startDateOption: startDateOption, endDateOption: endDateOption, @@ -220,6 +222,10 @@ class SearchController extends BaseController with DateRangePickerMixin { Set get listAddressOfFromFiltered => searchEmailFilter.value.from; + Set get listHasKeywordFiltered => searchEmailFilter.value.hasKeyword; + + bool get unreadFiltered => searchEmailFilter.value.unread; + EmailSortOrderType get sortOrderFiltered => searchEmailFilter.value.sortOrderType; bool isSearchActive() => diff --git a/lib/features/mailbox_dashboard/presentation/extensions/select_search_filter_action_extension.dart b/lib/features/mailbox_dashboard/presentation/extensions/select_search_filter_action_extension.dart new file mode 100644 index 000000000..8e0b44214 --- /dev/null +++ b/lib/features/mailbox_dashboard/presentation/extensions/select_search_filter_action_extension.dart @@ -0,0 +1,38 @@ +import 'package:dartz/dartz.dart'; +import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; + +extension SelectSearchFilterActionExtension on MailboxDashBoardController { + void selectStarredSearchFilter() { + final listHasKeywordFiltered = searchController.listHasKeywordFiltered; + if (!listHasKeywordFiltered.contains(KeyWordIdentifier.emailFlagged.value)) { + listHasKeywordFiltered.add(KeyWordIdentifier.emailFlagged.value); + } + searchController.updateFilterEmail( + hasKeywordOption: Some(listHasKeywordFiltered), + ); + dispatchAction(StartSearchEmailAction()); + } + + void selectUnreadSearchFilter() { + searchController.updateFilterEmail(unreadOption: const Some(true)); + dispatchAction(StartSearchEmailAction()); + } + + void deleteStarredSearchFilter() { + final listHasKeywordFiltered = searchController.listHasKeywordFiltered; + if (listHasKeywordFiltered.contains(KeyWordIdentifier.emailFlagged.value)) { + listHasKeywordFiltered.remove(KeyWordIdentifier.emailFlagged.value); + } + searchController.updateFilterEmail( + hasKeywordOption: Some(listHasKeywordFiltered), + ); + dispatchAction(StartSearchEmailAction()); + } + + void deleteUnreadSearchFilter() { + searchController.updateFilterEmail(unreadOption: const None()); + dispatchAction(StartSearchEmailAction()); + } +} 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 4a23bf2a6..847d3cbff 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; +import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart'; 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'; @@ -22,6 +23,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/base_mailb import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_open_context_menu_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_profile_setting_action_type_click_extension.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/extensions/select_search_filter_action_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/profile_setting/profile_setting_action_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; @@ -455,13 +457,14 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { style: ElevatedButton.styleFrom( backgroundColor: AppColor.colorFilterMessageButton.withValues(alpha: 0.6), shadowColor: Colors.transparent, - padding: const EdgeInsetsDirectional.symmetric(horizontal: 12, vertical: 8), + padding: const EdgeInsetsDirectional.symmetric(horizontal: 12), shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(10)), ), elevation: 0.0, foregroundColor: AppColor.colorTextButtonHeaderThread, maximumSize: const Size.fromWidth(250), + fixedSize: const Size.fromHeight(34), textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith( fontSize: 13, fontWeight: FontWeight.normal, @@ -663,6 +666,10 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { _buildQuickSearchFilterButton(context, QuickSearchFilter.dateTime), MailboxDashboardViewWebStyle.searchFilterSizeBoxMargin, _buildQuickSearchFilterButton(context, QuickSearchFilter.hasAttachment), + MailboxDashboardViewWebStyle.searchFilterSizeBoxMargin, + _buildQuickSearchFilterButton(context, QuickSearchFilter.starred), + MailboxDashboardViewWebStyle.searchFilterSizeBoxMargin, + _buildQuickSearchFilterButton(context, QuickSearchFilter.unread), ], ), ), @@ -713,6 +720,8 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { final receiveTimeType = controller.searchController.receiveTimeFiltered; final mailbox = controller.searchController.mailboxFiltered; final listAddressOfTo = controller.searchController.listAddressOfToFiltered; + final listHasKeywordFiltered = controller.searchController.listHasKeywordFiltered; + final unreadFiltered = controller.searchController.unreadFiltered; final isSelected = searchFilter.isSelected( context, @@ -730,7 +739,9 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { startDate != null || endDate != null || receiveTimeType != EmailReceiveTimeType.allTime || - mailbox != null; + mailbox != null || + listHasKeywordFiltered.contains(KeyWordIdentifier.emailFlagged.value) || + unreadFiltered; return SearchFilterButton( key: Key('${searchFilter.name}_search_filter_button'), @@ -790,6 +801,12 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { case QuickSearchFilter.folder: controller.selectFolderSearchFilter(); break; + case QuickSearchFilter.starred: + controller.selectStarredSearchFilter(); + break; + case QuickSearchFilter.unread: + controller.selectUnreadSearchFilter(); + break; default: break; } 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 ac0416439..a26a99034 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 @@ -17,6 +17,7 @@ enum QuickSearchFilter { last7Days, fromMe, starred, + unread, sortBy, dateTime, from, @@ -58,6 +59,8 @@ enum QuickSearchFilter { return AppLocalizations.of(context).to_email_address_prefix; case QuickSearchFilter.starred: return AppLocalizations.of(context).starred; + case QuickSearchFilter.unread: + return AppLocalizations.of(context).unread; } } @@ -80,7 +83,9 @@ enum QuickSearchFilter { case QuickSearchFilter.folder: return imagePaths.icFolderMailbox; case QuickSearchFilter.starred: - return isSelected ? imagePaths.icSelectedSB : imagePaths.icStar; + return isSelected ? imagePaths.icSelectedSB : imagePaths.icUnStar; + case QuickSearchFilter.unread: + return isSelected ? imagePaths.icSelectedSB : imagePaths.icUnread; } } @@ -112,11 +117,7 @@ enum QuickSearchFilter { if (isSelected) { return AppColor.primaryColor; } else { - if (this == QuickSearchFilter.starred) { - return AppColor.colorStarredSearchFilterIcon; - } else { - return AppColor.colorSearchFilterIcon; - } + return AppColor.colorSearchFilterIcon; } } @@ -149,6 +150,8 @@ enum QuickSearchFilter { return searchFilter.mailbox != null; case QuickSearchFilter.starred: return searchFilter.hasKeyword.contains(KeyWordIdentifier.emailFlagged.value) == true; + case QuickSearchFilter.unread: + return searchFilter.unread; } } diff --git a/lib/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart b/lib/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart index 5e50dfb5a..01fa2b4fd 100644 --- a/lib/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart +++ b/lib/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart @@ -29,6 +29,7 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin { final PresentationMailbox? mailbox; final EmailReceiveTimeType emailReceiveTimeType; final bool hasAttachment; + final bool unread; final UTCDate? before; final UTCDate? startDate; final UTCDate? endDate; @@ -52,6 +53,7 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin { Set? to, EmailReceiveTimeType? emailReceiveTimeType, bool? hasAttachment, + bool? unread, Set? notKeyword, Set? hasKeyword, EmailSortOrderType? sortOrderType, @@ -60,6 +62,7 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin { notKeyword = notKeyword ?? {}, hasKeyword = hasKeyword ?? {}, hasAttachment = hasAttachment ?? false, + unread = unread ?? false, emailReceiveTimeType = emailReceiveTimeType ?? EmailReceiveTimeType.allTime, sortOrderType = sortOrderType ?? defaultSortOrder; @@ -74,6 +77,7 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin { Option? mailboxOption, Option? emailReceiveTimeTypeOption, Option? hasAttachmentOption, + Option? unreadOption, Option? beforeOption, Option? startDateOption, Option? endDateOption, @@ -90,6 +94,7 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin { mailbox: getOptionParam(mailboxOption, mailbox), emailReceiveTimeType: getOptionParam(emailReceiveTimeTypeOption, emailReceiveTimeType), hasAttachment: getOptionParam(hasAttachmentOption, hasAttachment), + unread: getOptionParam(unreadOption, unread), before: getOptionParam(beforeOption, before), startDate: getOptionParam(startDateOption, startDate), endDate: getOptionParam(endDateOption, endDate), @@ -118,6 +123,7 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin { hasKeyword: hasKeyword.length == 1 ? hasKeyword.first : null, + notKeyword: unread ? KeyWordIdentifier.emailSeen.value : null, ); final listEmailCondition = { @@ -197,7 +203,8 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin { emailReceiveTimeType != EmailReceiveTimeType.allTime || sortOrderType != SearchEmailFilter.defaultSortOrder || (mailbox != null && mailbox?.id != PresentationMailbox.unifiedMailbox.id) || - hasAttachment; + hasAttachment || + unread; bool get isContainFlagged => hasKeyword.contains(KeyWordIdentifier.emailFlagged.value); @@ -212,6 +219,7 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin { mailbox, emailReceiveTimeType, hasAttachment, + unread, before, startDate, endDate, diff --git a/lib/features/search/email/presentation/search_email_controller.dart b/lib/features/search/email/presentation/search_email_controller.dart index 43bd6a3ec..af9bdaa30 100644 --- a/lib/features/search/email/presentation/search_email_controller.dart +++ b/lib/features/search/email/presentation/search_email_controller.dart @@ -769,6 +769,7 @@ class SearchEmailController extends BaseController Option? mailboxOption, Option? emailReceiveTimeTypeOption, Option? hasAttachmentOption, + Option? unreadOption, Option? beforeOption, Option? startDateOption, Option? endDateOption, @@ -783,6 +784,7 @@ class SearchEmailController extends BaseController mailboxOption: mailboxOption, emailReceiveTimeTypeOption: emailReceiveTimeTypeOption, hasAttachmentOption: hasAttachmentOption, + unreadOption: unreadOption, beforeOption: beforeOption, startDateOption: startDateOption, endDateOption: endDateOption,