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 025422d7f..34cb7f6cd 100644 --- a/lib/features/mailbox_dashboard/presentation/base_mailbox_dashboard_view.dart +++ b/lib/features/mailbox_dashboard/presentation/base_mailbox_dashboard_view.dart @@ -33,7 +33,7 @@ abstract class BaseMailboxDashBoardView extends GetWidget[].obs; final listFilterQuickSearch = RxList(); + final emailReceiveTimeType = Rxn(); SearchQuery? searchQuery; Session? sessionCurrent; @@ -430,8 +432,6 @@ class MailboxDashBoardController extends ReloadableController { Future> quickSearchEmailsAction(String pattern) async { if (accountId.value != null && selectedMailbox.value != null) { - log('MailboxDashBoardController::quickSearchEmailsAction(): DATE_NOW: ${DateTime.now()}'); - log('MailboxDashBoardController::quickSearchEmailsAction(): DATE: ${DateTime.now().subtract(const Duration(days: 7))}'); return await _quickSearchEmailInteractor.execute( accountId.value!, limit: UnsignedInt(5), @@ -454,14 +454,24 @@ class MailboxDashBoardController extends ReloadableController { return []; } - void selectQuickSearchFilter(QuickSearchFilter quickSearchFilter) { + void selectQuickSearchFilter(QuickSearchFilter quickSearchFilter, {bool fromSuggestionBox = false}) { if (listFilterQuickSearch.contains(quickSearchFilter)) { listFilterQuickSearch.remove(quickSearchFilter); + if (fromSuggestionBox && quickSearchFilter == QuickSearchFilter.last7Days) { + setEmailReceiveTimeType(null); + } } else { listFilterQuickSearch.add(quickSearchFilter); + if (fromSuggestionBox && quickSearchFilter == QuickSearchFilter.last7Days) { + setEmailReceiveTimeType(EmailReceiveTimeType.last7Days); + } } } + void setEmailReceiveTimeType(EmailReceiveTimeType? receiveTimeType) { + emailReceiveTimeType.value = receiveTimeType; + } + void composeEmailAction() { if (kIsWeb) { if (dashBoardAction.value is! ComposeEmailAction) { 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 c85e28478..3a4085da4 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart @@ -157,14 +157,11 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { Obx(() => !controller.isSearchActive() ? const Spacer() : const SizedBox.shrink()), Obx(() => controller.isSearchActive() ? Expanded(child: _buildSearchForm(context)) - : const SizedBox.shrink()), - Obx(() => !controller.isSearchActive() - ? (SearchBarView(imagePaths) - ..hintTextSearch(AppLocalizations.of(context).search_emails) - ..maxSizeWidth(240) - ..addOnOpenSearchViewAction(() => controller.enableSearch())) - .build() - : const SizedBox.shrink()), + : (SearchBarView(imagePaths) + ..hintTextSearch(AppLocalizations.of(context).search_emails) + ..maxSizeWidth(240) + ..addOnOpenSearchViewAction(() => controller.enableSearch())) + .build()), Obx(() => !controller.isSearchActive() ? const SizedBox(width: 16) : const SizedBox.shrink()), Obx(() => (AvatarBuilder() ..text(controller.userProfile.value?.getAvatarText() ?? '') @@ -452,6 +449,7 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { constraints: BoxConstraints( maxWidth: responsiveUtils.isDesktop(context) ? 556 : double.infinity), ), + debounceDuration: const Duration(milliseconds: 500), listActionButton: const [ QuickSearchFilter.hasAttachment, QuickSearchFilter.last7Days, @@ -465,7 +463,7 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView { }, buttonActionCallback: (filterAction) { if (filterAction is QuickSearchFilter) { - return controller.selectQuickSearchFilter(filterAction); + controller.selectQuickSearchFilter(filterAction, fromSuggestionBox: true); } }, listActionPadding: const EdgeInsets.only(left: 24, top: 24, right: 24, bottom: 16), diff --git a/lib/features/mailbox_dashboard/presentation/model/email_receive_time_type.dart b/lib/features/mailbox_dashboard/presentation/model/email_receive_time_type.dart new file mode 100644 index 000000000..49a2de6f8 --- /dev/null +++ b/lib/features/mailbox_dashboard/presentation/model/email_receive_time_type.dart @@ -0,0 +1,45 @@ + +import 'package:flutter/material.dart'; +import 'package:jmap_dart_client/jmap/core/utc_date.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +enum EmailReceiveTimeType { + anyTime, + last7Days, + last30Days, + last6Months, + lastYears, +} + +extension EmailReceiveTimeTypeExtension on EmailReceiveTimeType { + + String getTitle(BuildContext context) { + switch(this) { + case EmailReceiveTimeType.anyTime: + return AppLocalizations.of(context).anyTime; + case EmailReceiveTimeType.last7Days: + return AppLocalizations.of(context).last7Days; + case EmailReceiveTimeType.last30Days: + return AppLocalizations.of(context).last30Days; + case EmailReceiveTimeType.last6Months: + return AppLocalizations.of(context).last6Months; + case EmailReceiveTimeType.lastYears: + return AppLocalizations.of(context).lastYears; + } + } + + UTCDate? toUTCDate() { + switch(this) { + case EmailReceiveTimeType.anyTime: + return null; + case EmailReceiveTimeType.last7Days: + return UTCDate(DateTime.now().subtract(const Duration(days: 7))); + case EmailReceiveTimeType.last30Days: + return UTCDate(DateTime.now().subtract(const Duration(days: 30))); + case EmailReceiveTimeType.last6Months: + return UTCDate(DateTime.now().subtract(const Duration(days: 180))); + case EmailReceiveTimeType.lastYears: + return UTCDate(DateTime.now().subtract(const Duration(days: 365))); + } + } +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/model/quick_search_filter.dart b/lib/features/mailbox_dashboard/presentation/model/quick_search_filter.dart index e372e72ce..f529943a8 100644 --- a/lib/features/mailbox_dashboard/presentation/model/quick_search_filter.dart +++ b/lib/features/mailbox_dashboard/presentation/model/quick_search_filter.dart @@ -1,6 +1,7 @@ import 'package:core/core.dart'; import 'package:flutter/cupertino.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/email_receive_time_type.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; enum QuickSearchFilter { @@ -11,11 +12,14 @@ enum QuickSearchFilter { extension QuickSearchFilterExtension on QuickSearchFilter { - String getTitle(BuildContext context) { + String getTitle(BuildContext context, {EmailReceiveTimeType? receiveTimeType}) { switch(this) { case QuickSearchFilter.hasAttachment: return AppLocalizations.of(context).hasAttachment; case QuickSearchFilter.last7Days: + if (receiveTimeType != null) { + return receiveTimeType.getTitle(context); + } return AppLocalizations.of(context).last7Days; case QuickSearchFilter.fromMe: return AppLocalizations.of(context).fromMe; diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index 20645914f..2d1a026dd 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -8,6 +8,7 @@ import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/sort/comparator.dart'; import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; +import 'package:jmap_dart_client/jmap/core/utc_date.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_comparator.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_comparator_property.dart'; @@ -38,6 +39,8 @@ import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_action import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/remove_email_drafts_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/email_receive_time_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/quick_search_filter.dart'; import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart'; import 'package:tmail_ui_user/features/thread/domain/model/get_email_request.dart'; import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; @@ -845,13 +848,27 @@ class ThreadController extends BaseController { emailListSearch.clear(); if (_accountId != null && searchQuery != null) { + UTCDate? afterTime; + if (mailboxDashBoardController.listFilterQuickSearch.contains(QuickSearchFilter.last7Days) + && mailboxDashBoardController.emailReceiveTimeType.value != null) { + afterTime = mailboxDashBoardController.emailReceiveTimeType.value!.toUTCDate(); + } + + final emailFilterCondition = EmailFilterCondition( + text: searchQuery!.value, + hasAttachment: mailboxDashBoardController.listFilterQuickSearch.contains(QuickSearchFilter.hasAttachment) + ? true : null, + from: mailboxDashBoardController.listFilterQuickSearch.contains(QuickSearchFilter.fromMe) + ? mailboxDashBoardController.userProfile.value!.email + : null, + after: afterTime, + inMailbox: _currentMailboxId); + consumeState(_searchEmailInteractor.execute( _accountId!, limit: ThreadConstants.defaultLimit, sort: _sortOrder, - filter: EmailFilterCondition( - text: searchQuery!.value, - inMailbox: _currentMailboxId), + filter: emailFilterCondition, properties: ThreadConstants.propertiesDefault, )); } @@ -1229,6 +1246,32 @@ class ThreadController extends BaseController { refreshAllEmail(); } + void selectQuickSearchFilter(QuickSearchFilter filter) { + mailboxDashBoardController.selectQuickSearchFilter(filter); + _searchEmail(); + } + + void openPopupReceiveTimeQuickSearchFilter( + BuildContext context, + RelativeRect position, + List entries) { + openPopupMenuAction(context, position, entries); + } + + void selectReceiveTimeQuickSearchFilter(EmailReceiveTimeType? emailReceiveTimeType) { + popBack(); + + if (emailReceiveTimeType != null) { + if (!mailboxDashBoardController.listFilterQuickSearch.contains(QuickSearchFilter.last7Days)) { + mailboxDashBoardController.selectQuickSearchFilter(QuickSearchFilter.last7Days); + } + } else { + mailboxDashBoardController.listFilterQuickSearch.remove(QuickSearchFilter.last7Days); + } + mailboxDashBoardController.setEmailReceiveTimeType(emailReceiveTimeType); + _searchEmail(); + } + void openMailboxLeftMenu() { mailboxDashBoardController.openMailboxMenuDrawer(); } diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index da33d4f0c..86969a0c8 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -5,6 +5,8 @@ import 'package:get/get.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; import 'package:tmail_ui_user/features/email/presentation/widgets/email_action_cupertino_action_sheet_action_builder.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/email_receive_time_type.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/quick_search_filter.dart'; import 'package:tmail_ui_user/features/thread/domain/state/search_email_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/search_more_email_state.dart'; import 'package:tmail_ui_user/features/thread/presentation/model/delete_action_type.dart'; @@ -56,6 +58,9 @@ class ThreadView extends GetWidget with AppLoaderMixin { Obx(() => controller.isMailboxTrash && controller.emailList.isNotEmpty && !controller.isSearchActive() ? _buildEmptyTrashButton(context) : const SizedBox.shrink()), + Obx(() => controller.isSearchActive() && _responsiveUtils.isDesktop(context) && BuildUtils.isWeb + ? _buildListButtonQuickSearchFilter(context) + : const SizedBox.shrink()), Expanded(child: Container( color: BuildUtils.isWeb ? AppColor.colorBgDesktop : Colors.white, padding: BuildUtils.isWeb && _responsiveUtils.isDesktop(context) @@ -75,7 +80,8 @@ class ThreadView extends GetWidget with AppLoaderMixin { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildStatusResultSearch(context), + if (_responsiveUtils.isMobile(context)) + _buildStatusResultSearch(context), _buildLoadingView(), Expanded(child: _buildListEmail(context)), _buildLoadingViewLoadMore(), @@ -333,7 +339,7 @@ class ThreadView extends GetWidget with AppLoaderMixin { return Container( margin: BuildUtils.isWeb && _responsiveUtils.isDesktop(context) ? EdgeInsets.symmetric(vertical: controller.isSelectionEnabled() ? 4 : 12, horizontal: 4) - : EdgeInsets.only(top: controller.isSearchActive() ? 8 : 0), + : EdgeInsets.zero, alignment: Alignment.center, padding: EdgeInsets.zero, color: Colors.white, @@ -537,4 +543,122 @@ class ThreadView extends GetWidget with AppLoaderMixin { PopupMenuItem(padding: const EdgeInsets.symmetric(horizontal: 8), child: _markAsEmailSpamOrUnSpamAction(context, email)), ]; } + + Widget _buildListButtonQuickSearchFilter(BuildContext context) { + final listQuickSearchFilter = [ + QuickSearchFilter.hasAttachment, + QuickSearchFilter.last7Days, + QuickSearchFilter.fromMe, + ]; + return Padding( + padding: EdgeInsets.only( + left: BuildUtils.isWeb && _responsiveUtils.isDesktop(context) ? 32 : 16, + right: BuildUtils.isWeb && _responsiveUtils.isDesktop(context) ? 20 : 16, + bottom: BuildUtils.isWeb && _responsiveUtils.isDesktop(context) ? 0 : 16, + top: BuildUtils.isWeb && _responsiveUtils.isDesktop(context) ? 16 : 0), + child: Row( + children: listQuickSearchFilter + .map((filter) => _buildQuickSearchFilterButton(context, filter)) + .toList()), + ); + } + + Widget _buildQuickSearchFilterButton( + BuildContext context, QuickSearchFilter filter) { + return Obx(() { + return Padding( + padding: const EdgeInsets.only(right: 8), + child: InkWell( + onTap: () { + if (filter != QuickSearchFilter.last7Days) { + controller.selectQuickSearchFilter(filter); + } + }, + onTapDown: (detail) { + if (filter == QuickSearchFilter.last7Days) { + 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, + ); + controller.openPopupReceiveTimeQuickSearchFilter(context, position, + popupMenuEmailReceiveTimeType(context, + controller.mailboxDashBoardController.emailReceiveTimeType.value, + (receiveTime) => controller.selectReceiveTimeQuickSearchFilter(receiveTime))); + } + }, + borderRadius: const BorderRadius.all(Radius.circular(10)), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: filter.getBackgroundColor(controller.mailboxDashBoardController.listFilterQuickSearch)), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + SvgPicture.asset( + filter.getIcon(_imagePaths, controller.mailboxDashBoardController.listFilterQuickSearch), + width: 16, + height: 16, + fit: BoxFit.fill), + const SizedBox(width: 4), + Text( + filter.getTitle(context, + receiveTimeType: controller.mailboxDashBoardController.emailReceiveTimeType.value), + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: filter.getTextStyle(controller.mailboxDashBoardController.listFilterQuickSearch), + ), + if (filter == QuickSearchFilter.last7Days) + ... [ + const SizedBox(width: 4), + SvgPicture.asset( + _imagePaths.icChevronDown, + width: 16, + height: 16, + fit: BoxFit.fill), + ] + ])), + ), + ); + }); + } + + List popupMenuEmailReceiveTimeType(BuildContext context, + EmailReceiveTimeType? receiveTimeSelected, Function(EmailReceiveTimeType?)? onCallBack) { + return EmailReceiveTimeType.values + .map((timeType) => PopupMenuItem( + padding: EdgeInsets.zero, + child: _receiveTimeTileAction(context, receiveTimeSelected, timeType, onCallBack))) + .toList(); + } + + Widget _receiveTimeTileAction( + BuildContext context, + EmailReceiveTimeType? receiveTimeSelected, + EmailReceiveTimeType receiveTimeType, + Function(EmailReceiveTimeType?)? onCallBack + ) { + return InkWell( + onTap: () => onCallBack?.call(receiveTimeType == receiveTimeSelected + ? null + : receiveTimeType), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), + child: SizedBox( + width: 320, + child: Row(children: [ + Expanded(child: Text( + receiveTimeType.getTitle(context), + style: const TextStyle(fontSize: 17, color: Colors.black, fontWeight: FontWeight.normal))), + if (receiveTimeType == receiveTimeSelected) + const SizedBox(width: 12), + if (receiveTimeType == receiveTimeSelected) + SvgPicture.asset(_imagePaths.icFilterSelected, width: 24, height: 24, fit: BoxFit.fill), + ]) + ), + ) + ); + } } \ No newline at end of file diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index d55f65148..fab2658c6 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-05-24T02:47:33.512669", + "@@last_modified": "2022-05-24T09:43:00.761124", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -1323,5 +1323,29 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "anyTime": "Any time", + "@anyTime": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "last30Days": "Last 30 days", + "@last30Days": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "last6Months": "Last 6 months", + "@last6Months": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "lastYears": "Last years", + "@lastYears": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index b3fdcdf10..336ae7964 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -1356,4 +1356,28 @@ class AppLocalizations { 'Showing results for:', name: 'showingResultsFor'); } + + String get anyTime { + return Intl.message( + 'Any time', + name: 'anyTime'); + } + + String get last30Days { + return Intl.message( + 'Last 30 days', + name: 'last30Days'); + } + + String get last6Months { + return Intl.message( + 'Last 6 months', + name: 'last6Months'); + } + + String get lastYears { + return Intl.message( + 'Last years', + name: 'lastYears'); + } } \ No newline at end of file