diff --git a/docs/adr/0052-logic-sort-order-in-search.md b/docs/adr/0052-logic-sort-order-in-search.md new file mode 100644 index 0000000000..d90b87dfcc --- /dev/null +++ b/docs/adr/0052-logic-sort-order-in-search.md @@ -0,0 +1,216 @@ +# 52. Logic Sort Order in search + +Date: 2024-10-07 + +## Status + +Accepted + +## Context + +- Using `sort` by `Subject`, `Sender` or `Relevance` together with `filter` by `conditions` with `before` or `after` date results in incorrect `Email/query` response results. +- ISSUE: https://github.com/linagora/tmail-flutter/pull/2360 + +```json +{ + "using": [ + "urn:ietf:params:jmap:core", + "urn:ietf:params:jmap:mail", + "urn:apache:james:params:jmap:mail:shares" + ], + "methodCalls": [ + [ + "Email/query", + { + "accountId": "3cd281b49cxxxxxxxxexx", + "filter": { + "operator": "AND", + "conditions": [ + { + "before": "2023-11-10T16:46:32.000Z", + "text": "firstname99" + } + ] + }, + "sort": [ + { + "isAscending": true, + "property": "subject" + } + ], + "limit": 20 + }, + "c0" + ], + [ + "Email/get", + { + "accountId": "3cd281b49cxxxxxxxxexx", + "#ids": { + "resultOf": "c0", + "name": "Email/query", + "path": "/ids/*" + }, + "properties": [ + "id", + "subject", + "from", + "to", + "cc", + "bcc", + "keywords", + "size", + "receivedAt", + "sentAt", + "preview", + "hasAttachment", + "replyTo", + "mailboxIds", + "header:X-MEETING-UID:asText" + ] + }, + "c1" + ] + ] +} +``` + +## Decision + +Brief the logic flows when click `Sort Order` in search: + +- To sort by `Subject`, `Sender` or `Relevance` we will have to use the `position` property and ignore `before` and `after` in `conditions` of `filter` + +```json +{ + "using": [ + "urn:ietf:params:jmap:core", + "urn:ietf:params:jmap:mail", + "urn:apache:james:params:jmap:mail:shares" + ], + "methodCalls": [ + [ + "Email/query", + { + "accountId": "3cd281b49cxxxxxxxxexx", + "filter": { + "operator": "AND", + "conditions": [ + { + "text": "firstname99" + } + ] + }, + "sort": [ + { + "isAscending": true, + "property": "subject" + } + ], + "limit": 20, + "position": 20 + }, + "c0" + ], + [ + "Email/get", + { + "accountId": "3cd281b49cxxxxxxxxexx", + "#ids": { + "resultOf": "c0", + "name": "Email/query", + "path": "/ids/*" + }, + "properties": [ + "id", + "subject", + "from", + "to", + "cc", + "bcc", + "keywords", + "size", + "receivedAt", + "sentAt", + "preview", + "hasAttachment", + "replyTo", + "mailboxIds", + "header:X-MEETING-UID:asText" + ] + }, + "c1" + ] + ] +} +``` + +- As for `sort` by `Most Recent` and `Oldest` we need to use `before` and `after` in `conditions` of `filter` + +```json +{ + "using": [ + "urn:ietf:params:jmap:core", + "urn:ietf:params:jmap:mail", + "urn:apache:james:params:jmap:mail:shares" + ], + "methodCalls": [ + [ + "Email/query", + { + "accountId": "3cd281b49cxxxxxxxxexx", + "filter": { + "operator": "AND", + "conditions": [ + { + "before": "2023-11-10T16:46:32.000Z", + "text": "firstname99" + } + ] + }, + "sort": [ + { + "isAscending": true, + "property": "receivedAt" + } + ], + "limit": 20 + }, + "c0" + ], + [ + "Email/get", + { + "accountId": "3cd281b49cxxxxxxxxexx", + "#ids": { + "resultOf": "c0", + "name": "Email/query", + "path": "/ids/*" + }, + "properties": [ + "id", + "subject", + "from", + "to", + "cc", + "bcc", + "keywords", + "size", + "receivedAt", + "sentAt", + "preview", + "hasAttachment", + "replyTo", + "mailboxIds", + "header:X-MEETING-UID:asText" + ] + }, + "c1" + ] + ] +} +``` + +## Consequences + +- Any changes to the `Sort Order` logic in search must be updated in this ADR. diff --git a/lib/features/mailbox_dashboard/presentation/action/dashboard_action.dart b/lib/features/mailbox_dashboard/presentation/action/dashboard_action.dart index 787fa6bd6f..b1686a61f4 100644 --- a/lib/features/mailbox_dashboard/presentation/action/dashboard_action.dart +++ b/lib/features/mailbox_dashboard/presentation/action/dashboard_action.dart @@ -54,13 +54,15 @@ class OpenEmailDetailedFromSuggestionQuickSearchAction extends DashBoardAction { List get props => [presentationEmail]; } -class StartSearchEmailAction extends DashBoardAction { - final QuickSearchFilter? filter; +class StartSearchEmailAction extends DashBoardAction {} - StartSearchEmailAction({this.filter}); +class StartSearchEmailBySearchFilterAction extends DashBoardAction { + final QuickSearchFilter searchFilter; + + StartSearchEmailBySearchFilterAction(this.searchFilter); @override - List get props => [filter]; + List get props => [searchFilter]; } class EmptyTrashAction extends DashBoardAction {} @@ -153,4 +155,6 @@ class CancelSelectionSearchEmailAction extends DashBoardAction {} class OpenAdvancedSearchViewAction extends DashBoardAction {} -class ClearSearchFilterAppliedAction extends DashBoardAction {} \ No newline at end of file +class ClearSearchFilterAppliedAction extends DashBoardAction {} + +class ClearAdvancedSearchFilterEmailAction extends DashBoardAction {} \ No newline at end of file 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 da3a463b42..66c4c830c9 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart @@ -79,14 +79,9 @@ class AdvancedFilterController extends BaseController { } void clearSearchFilter() { - searchController.clearSearchFilter(); _resetAllToOriginalValue(); _clearAllTextFieldInput(); - searchController.searchInputController.clear(); - searchController.clearSortOrder(); - searchController.deactivateAdvancedSearch(); - searchController.isAdvancedSearchViewOpen.value = false; - _mailboxDashBoardController.searchEmail(); + _mailboxDashBoardController.handleClearAdvancedSearchFilterEmail(); } void _updateFilterEmailFromAdvancedSearchView() { @@ -119,8 +114,6 @@ class AdvancedFilterController extends BaseController { searchController.updateFilterEmail(toOption: const None()); } - searchController.updateFilterEmail(sortOrderOption: searchController.sortOrderFiltered.value.getSortOrder()); - searchController.updateFilterEmail( mailboxOption: optionOf(_destinationMailboxSelected), subjectOption: optionOf(subjectFilterInputController.text), @@ -170,7 +163,7 @@ class AdvancedFilterController extends BaseController { searchController.updateFilterEmail(beforeOption: const None()); } searchController.isAdvancedSearchViewOpen.value = false; - _mailboxDashBoardController.searchEmail(); + _mailboxDashBoardController.handleAdvancedSearchEmail(); } Future> getAutoCompleteSuggestion(String word) async { @@ -405,17 +398,8 @@ class AdvancedFilterController extends BaseController { ); } else if (action is ClearDateRangeToAdvancedSearch) { _updateDateRangeTime(action.receiveTime); - } else if (action is StartSearchEmailAction) { - switch(action.filter) { - case QuickSearchFilter.from: - _updateFromField(); - break; - case QuickSearchFilter.to: - _updateToField(); - break; - default: - break; - } + } else if (action is StartSearchEmailBySearchFilterAction) { + _handleSearchEmailBySearchFilterAction(action.searchFilter); } else if (action is SearchEmailByFromFieldsAction) { searchController.clearSearchFilter(); _resetAllToOriginalValue(); @@ -480,6 +464,19 @@ class AdvancedFilterController extends BaseController { applyAdvancedSearchFilter(); } + void _handleSearchEmailBySearchFilterAction(QuickSearchFilter searchFilter) { + switch(searchFilter) { + case QuickSearchFilter.from: + _updateFromField(); + break; + case QuickSearchFilter.to: + _updateToField(); + break; + default: + break; + } + } + @override void onClose() { _removeFocusListener(); 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 eadf7f6f6f..5f88cd3b0c 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -667,20 +667,55 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo bool isSelectionEnabled() => currentSelectMode.value == SelectMode.ACTIVE; - void searchEmail({String? queryString}) { - log('MailboxDashBoardController::searchEmail():'); + void handleAdvancedSearchEmail() { + log('MailboxDashBoardController::handleAdvancedSearchEmail:'); clearFilterMessageOption(); - searchController.clearFilterSuggestion(); - if (queryString?.isNotEmpty == true) { - searchController.updateFilterEmail(textOption: Some(SearchQuery(queryString!))); + if (_searchInsideEmailDetailedViewIsActive()) { + _closeEmailDetailedView(); } - searchController.updateFilterEmail(sortOrderOption: searchController.sortOrderFiltered.value.getSortOrder()); + _unSelectedMailbox(); + searchController.clearFilterSuggestion(); + FocusManager.instance.primaryFocus?.unfocus(); dispatchAction(StartSearchEmailAction()); + } + + void handleClearAdvancedSearchFilterEmail() { + log('MailboxDashBoardController::handleClearAdvancedSearchFilterEmail:'); + clearFilterMessageOption(); + if (_searchInsideEmailDetailedViewIsActive()) { + _closeEmailDetailedView(); + } + _unSelectedMailbox(); + searchController.clearAllFilterSearch(); FocusManager.instance.primaryFocus?.unfocus(); + dispatchAction(ClearAdvancedSearchFilterEmailAction()); + } + + void searchEmailByQueryString(String queryString) { + final isMailAddress = EmailUtils.isEmailAddressValid(queryString); + log('MailboxDashBoardController::searchEmailByQueryString():QueryString = $queryString | isMailAddress = $isMailAddress'); + clearFilterMessageOption(); if (_searchInsideEmailDetailedViewIsActive()) { _closeEmailDetailedView(); } _unSelectedMailbox(); + searchController.clearFilterSuggestion(); + + searchController.updateFilterEmail( + textOption: !isMailAddress + ? Some(SearchQuery(queryString)) + : null, + fromOption: isMailAddress + ? Some({queryString}) + : null); + + FocusManager.instance.primaryFocus?.unfocus(); + + if (isMailAddress) { + dispatchAction(StartSearchEmailBySearchFilterAction(QuickSearchFilter.from)); + } else { + dispatchAction(StartSearchEmailAction()); + } } bool _searchInsideEmailDetailedViewIsActive() { @@ -1675,7 +1710,7 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo if (newContact is EmailAddress) { searchController.updateFilterEmail(fromOption: Some({newContact.emailAddress})); - dispatchAction(StartSearchEmailAction(filter: QuickSearchFilter.from)); + dispatchAction(StartSearchEmailBySearchFilterAction(QuickSearchFilter.from)); } } @@ -1693,7 +1728,7 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo if (newContact is EmailAddress) { searchController.updateFilterEmail(toOption: Some({newContact.emailAddress})); - dispatchAction(StartSearchEmailAction(filter: QuickSearchFilter.to)); + dispatchAction(StartSearchEmailBySearchFilterAction(QuickSearchFilter.to)); } } @@ -1721,11 +1756,7 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo searchController.updateFilterEmail( mailboxOption: destinationMailbox.id == PresentationMailbox.unifiedMailbox.id ? const None() - : Some(destinationMailbox), - beforeOption: const None(), - positionOption: searchController.sortOrderFiltered.value.isScrollByPosition() - ? const Some(0) - : const None() + : Some(destinationMailbox) ); dispatchAction(StartSearchEmailAction()); @@ -1745,8 +1776,7 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo searchController.updateFilterEmail( emailReceiveTimeTypeOption: Some(receiveTime), startDateOption: optionOf(startDate?.toUTCDate()), - endDateOption: optionOf(endDate?.toUTCDate()), - beforeOption: const None() + endDateOption: optionOf(endDate?.toUTCDate()) ); dispatchAction(StartSearchEmailAction()); } @@ -1756,8 +1786,7 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo searchController.updateFilterEmail( emailReceiveTimeTypeOption: Some(receiveTime), startDateOption: const None(), - endDateOption: const None(), - beforeOption: const None() + endDateOption: const None() ); dispatchAction(StartSearchEmailAction()); } @@ -1767,7 +1796,6 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo log('MailboxDashBoardController::selectSortOrderQuickSearchFilter():sortOrder: $sortOrder'); popBack(); searchController.sortOrderFiltered.value = sortOrder; - searchController.updateFilterEmail(sortOrderOption: sortOrder.getSortOrder()); dispatchAction(StartSearchEmailAction()); } @@ -1776,27 +1804,24 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo searchController.updateFilterEmail( emailReceiveTimeTypeOption: const Some(EmailReceiveTimeType.allTime), startDateOption: const None(), - endDateOption: const None(), - beforeOption: const None() + endDateOption: const None() ); dispatchAction(StartSearchEmailAction()); } void _deleteSortOrderSearchFilter() { searchController.sortOrderFiltered.value = EmailSortOrderType.mostRecent; - searchController.updateFilterEmail( - sortOrderOption: EmailSortOrderType.mostRecent.getSortOrder()); dispatchAction(StartSearchEmailAction()); } void _deleteFromSearchFilter() { searchController.updateFilterEmail(fromOption: const None()); - dispatchAction(StartSearchEmailAction(filter: QuickSearchFilter.from)); + dispatchAction(StartSearchEmailBySearchFilterAction(QuickSearchFilter.from)); } void _deleteToSearchFilter() { searchController.updateFilterEmail(toOption: const None()); - dispatchAction(StartSearchEmailAction(filter: QuickSearchFilter.to)); + dispatchAction(StartSearchEmailBySearchFilterAction(QuickSearchFilter.to)); } void _deleteHasAttachmentSearchFilter() { @@ -1805,13 +1830,7 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo } void _deleteFolderSearchFilter() { - searchController.updateFilterEmail( - mailboxOption: const None(), - beforeOption: const None(), - positionOption: searchController.sortOrderFiltered.value.isScrollByPosition() - ? const Some(0) - : const None() - ); + searchController.updateFilterEmail(mailboxOption: const None()); dispatchAction(StartSearchEmailAction()); } diff --git a/lib/features/mailbox_dashboard/presentation/controller/search_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/search_controller.dart index 5f74904265..2cccca6449 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/search_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/search_controller.dart @@ -170,7 +170,6 @@ class SearchController extends BaseController with DateRangePickerMixin { Option? beforeOption, Option? startDateOption, Option? endDateOption, - Option>? sortOrderOption, Option? positionOption, }) { searchEmailFilter.value = searchEmailFilter.value.copyWith( @@ -185,7 +184,6 @@ class SearchController extends BaseController with DateRangePickerMixin { beforeOption: beforeOption, startDateOption: startDateOption, endDateOption: endDateOption, - sortOrderOption: sortOrderOption, positionOption: positionOption, ); searchEmailFilter.refresh(); @@ -284,6 +282,15 @@ class SearchController extends BaseController with DateRangePickerMixin { sortOrderFiltered.value = EmailSortOrderType.mostRecent; } + void clearAllFilterSearch() { + _clearAllTextInputSimpleSearch(); + clearSortOrder(); + clearFilterSuggestion(); + clearSearchFilter(); + deactivateAdvancedSearch(); + hideAdvancedSearchFormView(); + } + void disableAllSearchEmail() { _clearAllTextInputSimpleSearch(); deactivateSimpleSearch(); 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 526430daa6..19b428a718 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 @@ -4,7 +4,6 @@ import 'package:equatable/equatable.dart'; import 'package:jmap_dart_client/jmap/core/filter/filter.dart'; import 'package:jmap_dart_client/jmap/core/filter/filter_operator.dart'; import 'package:jmap_dart_client/jmap/core/filter/operator/logic_filter_operator.dart'; -import 'package:jmap_dart_client/jmap/core/sort/comparator.dart'; import 'package:jmap_dart_client/jmap/core/utc_date.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart'; import 'package:model/email/prefix_email_address.dart'; @@ -28,12 +27,9 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin { final UTCDate? before; final UTCDate? startDate; final UTCDate? endDate; - final Set? sortOrder; final int? position; - factory SearchEmailFilter.initial() => SearchEmailFilter( - sortOrder: EmailSortOrderType.mostRecent.getSortOrder().toNullable() - ); + factory SearchEmailFilter.initial() => SearchEmailFilter(); SearchEmailFilter({ Set? from, @@ -48,7 +44,6 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin { this.before, this.startDate, this.endDate, - this.sortOrder, this.position, }) : from = from ?? {}, to = to ?? {}, @@ -71,7 +66,6 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin { Option? beforeOption, Option? startDateOption, Option? endDateOption, - Option>? sortOrderOption, Option? positionOption, }) { return SearchEmailFilter( @@ -87,7 +81,6 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin { before: getOptionParam(beforeOption, before), startDate: getOptionParam(startDateOption, startDate), endDate: getOptionParam(endDateOption, endDate), - sortOrder: getOptionParam(sortOrderOption, sortOrder), position: getOptionParam(positionOption, position), ); } @@ -166,7 +159,6 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin { before, startDate, endDate, - sortOrder, 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 6f1d11b892..848711df81 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 @@ -4,6 +4,7 @@ import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/utils/responsive_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/app_logger.dart'; import 'package:core/utils/direction_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_portal/flutter_portal.dart'; @@ -126,17 +127,20 @@ class SearchInputFormWidget extends StatelessWidget with AppLoaderMixin { }); } - void _invokeSearchEmailAction(String query) { + void _invokeSearchEmailAction(String queryString) { + log('SearchInputFormWidget::_invokeSearchEmailAction:QueryString = $queryString'); _searchController.searchFocus.unfocus(); _searchController.enableSearch(); - if (query.isNotEmpty) { - _searchController.saveRecentSearch(RecentSearch.now(query)); + if (queryString.isNotEmpty) { + _searchController.saveRecentSearch(RecentSearch.now(queryString)); } - if (query.isNotEmpty || _searchController.listFilterOnSuggestionForm.isNotEmpty) { + if (queryString.isNotEmpty || _searchController.listFilterOnSuggestionForm.isNotEmpty) { + _searchController.clearSortOrder(); + _searchController.clearSearchFilter(); _searchController.applyFilterSuggestionToSearchFilter(_dashBoardController.sessionCurrent?.username); - _dashBoardController.searchEmail(queryString: query); + _dashBoardController.searchEmailByQueryString(queryString); } else { _dashBoardController.clearSearchEmail(); } @@ -154,9 +158,10 @@ class SearchInputFormWidget extends StatelessWidget with AppLoaderMixin { _searchController.searchInputController.text = recent.value; _searchController.searchFocus.unfocus(); _searchController.enableSearch(); - + _searchController.clearSortOrder(); + _searchController.clearSearchFilter(); _searchController.applyFilterSuggestionToSearchFilter(_dashBoardController.sessionCurrent?.username); - _dashBoardController.searchEmail(queryString: recent.value); + _dashBoardController.searchEmailByQueryString(recent.value); } Widget _buildShowAllResultButton(BuildContext context, String keyword) { diff --git a/lib/features/search/email/presentation/search_email_controller.dart b/lib/features/search/email/presentation/search_email_controller.dart index dd566c167c..54eb981e59 100644 --- a/lib/features/search/email/presentation/search_email_controller.dart +++ b/lib/features/search/email/presentation/search_email_controller.dart @@ -10,7 +10,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; -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/utc_date.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; @@ -39,6 +38,7 @@ import 'package:tmail_ui_user/features/email/domain/state/move_to_mailbox_state. import 'package:tmail_ui_user/features/email/domain/state/store_event_attendance_status_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/unsubscribe_email_state.dart'; import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart'; +import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/recent_search.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_all_recent_search_latest_state.dart'; @@ -363,46 +363,45 @@ class SearchEmailController extends BaseController KeyboardUtils.hideKeyboard(context); textInputSearchFocus.unfocus(); - if (session != null && accountId != null) { - resultSearchViewState.value = Right(SearchingState()); - canSearchMore = true; - searchIsRunning.value = true; - cancelSelectionMode(); - if (PlatformInfo.isWeb) { - RouteUtils.replaceBrowserHistory( - title: 'SearchEmail', - url: RouteUtils.createUrlWebLocationBar( - AppRoutes.dashboard, - router: NavigationRouter( - searchQuery: searchQuery, - dashboardType: DashboardType.search - ) + if (session == null || accountId == null) { + consumeState(Stream.value(Left(SearchEmailFailure(NotFoundSessionException())))); + return; + } + + resultSearchViewState.value = Right(SearchingState()); + canSearchMore = true; + searchMoreState = SearchMoreState.idle; + searchIsRunning.value = true; + cancelSelectionMode(); + if (PlatformInfo.isWeb) { + RouteUtils.replaceBrowserHistory( + title: 'SearchEmail', + url: RouteUtils.createUrlWebLocationBar( + AppRoutes.dashboard, + router: NavigationRouter( + searchQuery: searchQuery, + dashboardType: DashboardType.search ) - ); - } + ) + ); + } - if (emailSortOrderType.value.isScrollByPosition()) { - _updateSimpleSearchFilter( - positionOption: const Some(0), - beforeOption: const None(), - ); - } else { - _updateSimpleSearchFilter( - positionOption: const None(), - beforeOption: const None(), - ); - } + _updateSimpleSearchFilter( + positionOption: emailSortOrderType.value.isScrollByPosition() + ? const Some(0) + : const None(), + beforeOption: const None(), + ); - consumeState(_searchEmailInteractor.execute( - session!, - accountId!, - limit: ThreadConstants.defaultLimit, - position: searchEmailFilter.value.position, - sort: emailSortOrderType.value.getSortOrder().toNullable(), - filter: searchEmailFilter.value.mappingToEmailFilterCondition(sortOrderType: emailSortOrderType.value), - properties: EmailUtils.getPropertiesForEmailGetMethod(session!, accountId!), - )); - } + consumeState(_searchEmailInteractor.execute( + session!, + accountId!, + limit: ThreadConstants.defaultLimit, + position: searchEmailFilter.value.position, + sort: emailSortOrderType.value.getSortOrder().toNullable(), + filter: searchEmailFilter.value.mappingToEmailFilterCondition(sortOrderType: emailSortOrderType.value), + properties: EmailUtils.getPropertiesForEmailGetMethod(session!, accountId!), + )); } void _searchEmailsSuccess(SearchEmailSuccess success) { @@ -485,24 +484,18 @@ class SearchEmailController extends BaseController searchMoreState = SearchMoreState.completed; } - void showAllResultSearchAction(BuildContext context, String query) { - setTextInputSearchForm(query); - _updateSimpleSearchFilter( - textOption: Some(SearchQuery(query)), - beforeOption: const None(), - positionOption: emailSortOrderType.value.isScrollByPosition() ? const Some(0) : const None() - ); - _searchEmailAction(context); + void showAllResultSearchAction(BuildContext context, String queryString) { + setTextInputSearchForm(queryString); + _searchEmailByQueryString( + context: context, + queryString: queryString); } void searchEmailByRecentAction(BuildContext context, RecentSearch recentSearch) { setTextInputSearchForm(recentSearch.value); - _updateSimpleSearchFilter( - textOption: Some(SearchQuery(recentSearch.value)), - beforeOption: const None(), - positionOption: emailSortOrderType.value.isScrollByPosition() ? const Some(0) : const None() - ); - _searchEmailAction(context); + _searchEmailByQueryString( + context: context, + queryString: recentSearch.value); } void searchEmailByEmailAddressAction( @@ -513,30 +506,34 @@ class SearchEmailController extends BaseController currentSearchText.value = ''; _updateSimpleSearchFilter( textOption: const None(), - fromOption: Some({emailAddress.emailAddress}), - beforeOption: const None(), - positionOption: emailSortOrderType.value.isScrollByPosition() ? const Some(0) : const None() - ); + fromOption: Some({emailAddress.emailAddress})); _searchEmailAction(context); } - void submitSearchAction(BuildContext context, String query) { - _updateSimpleSearchFilter( - textOption: Some(SearchQuery(query)), - beforeOption: const None(), - positionOption: emailSortOrderType.value.isScrollByPosition() ? const Some(0) : const None() - ); - _searchEmailAction(context); + void _searchEmailByQueryString({ + required BuildContext context, + required String queryString + }) { + log('SearchEmailController::_searchEmailByQueryString:QueryString = $queryString'); + resultSearchViewState.value = Right(SearchingState()); + listRecentSearch.clear(); + listSuggestionSearch.clear(); + listResultSearch.clear(); + emailReceiveTimeType.value = EmailReceiveTimeType.allTime; + emailSortOrderType.value = EmailSortOrderType.mostRecent; + searchEmailFilter.value = SearchEmailFilter.initial(); + searchIsRunning.value = false; + final isMailAddress = EmailUtils.isEmailAddressValid(queryString); + if (isMailAddress) { + searchEmailByEmailAddressAction(context, EmailAddress(null, queryString)); + } else { + _updateSimpleSearchFilter(textOption: Some(SearchQuery(queryString))); + _searchEmailAction(context); + } } void selectHasAttachmentSearchFilter(BuildContext context) { - _updateSimpleSearchFilter( - hasAttachmentOption: const Some(true), - beforeOption: const None(), - positionOption: emailSortOrderType.value.isScrollByPosition() - ? const Some(0) - : const None() - ); + _updateSimpleSearchFilter(hasAttachmentOption: const Some(true)); _searchEmailAction(context); } @@ -545,13 +542,7 @@ class SearchEmailController extends BaseController if (!listKeyword.contains(KeyWordIdentifier.emailFlagged.value)) { listKeyword.add(KeyWordIdentifier.emailFlagged.value); } - _updateSimpleSearchFilter( - hasKeywordOption: Some(listKeyword), - beforeOption: const None(), - positionOption: emailSortOrderType.value.isScrollByPosition() - ? const Some(0) - : const None() - ); + _updateSimpleSearchFilter(hasKeywordOption: Some(listKeyword)); _searchEmailAction(context); } @@ -586,10 +577,8 @@ class SearchEmailController extends BaseController textOption: searchQuery == null ? Some(SearchQuery.initial()) : optionOf(searchEmailFilter.value.text), - beforeOption: const None(), startDateOption: optionOf(newStartDate?.toUTCDate()), endDateOption: optionOf(newEndDate?.toUTCDate()), - positionOption: emailSortOrderType.value.isScrollByPosition() ? const Some(0) : const None() ); _setEmailReceiveTimeType(emailReceiveTimeType); @@ -602,10 +591,8 @@ class SearchEmailController extends BaseController textOption: searchQuery == null ? Some(SearchQuery.initial()) : optionOf(searchEmailFilter.value.text), - beforeOption: const None(), startDateOption: const None(), endDateOption: const None(), - positionOption: emailSortOrderType.value.isScrollByPosition() ? const Some(0) : const None() ); _setEmailReceiveTimeType(emailReceiveTimeType); @@ -615,11 +602,6 @@ class SearchEmailController extends BaseController void selectSortOrderQuickSearchFilter(BuildContext context, EmailSortOrderType sortOrderType) { popBack(); - _updateSimpleSearchFilter( - sortOrderOption: sortOrderType.getSortOrder(), - beforeOption: const None(), - positionOption: emailSortOrderType.value.isScrollByPosition() ? const Some(0) : const None() - ); emailSortOrderType.value = sortOrderType; _searchEmailAction(context); } @@ -640,11 +622,7 @@ class SearchEmailController extends BaseController _updateSimpleSearchFilter( mailboxOption: destinationMailbox.id == PresentationMailbox.unifiedMailbox.id ? const None() - : Some(destinationMailbox), - beforeOption: const None(), - positionOption: emailSortOrderType.value.isScrollByPosition() - ? const Some(0) - : const None() + : Some(destinationMailbox) ); if (context.mounted) { @@ -714,18 +692,10 @@ class SearchEmailController extends BaseController switch(prefixEmailAddress) { case PrefixEmailAddress.from: - _updateSimpleSearchFilter( - fromOption: Some(searchEmailFilter.value.from), - beforeOption: const None(), - positionOption: emailSortOrderType.value.isScrollByPosition() ? const Some(0) : const None() - ); + _updateSimpleSearchFilter(fromOption: Some(searchEmailFilter.value.from)); break; case PrefixEmailAddress.to: - _updateSimpleSearchFilter( - toOption: Some(searchEmailFilter.value.to), - beforeOption: const None(), - positionOption: emailSortOrderType.value.isScrollByPosition() ? const Some(0) : const None() - ); + _updateSimpleSearchFilter(toOption: Some(searchEmailFilter.value.to)); break; default: break; @@ -744,7 +714,6 @@ class SearchEmailController extends BaseController Option? beforeOption, Option? startDateOption, Option? endDateOption, - Option>? sortOrderOption, Option? positionOption, Option>? hasKeywordOption, }) { @@ -758,7 +727,6 @@ class SearchEmailController extends BaseController beforeOption: beforeOption, startDateOption: startDateOption, endDateOption: endDateOption, - sortOrderOption: sortOrderOption, positionOption: positionOption, hasKeywordOption: hasKeywordOption, ); @@ -770,10 +738,11 @@ class SearchEmailController extends BaseController } void onTextSearchSubmitted(BuildContext context, String text) { - if (text.trim().isNotEmpty) { - saveRecentSearch(RecentSearch.now(text.trim())); + final queryString = text.trim(); + if (queryString.isNotEmpty) { + saveRecentSearch(RecentSearch.now(queryString)); } - submitSearchAction(context, text); + _searchEmailByQueryString(context: context, queryString: queryString); } void setTextInputSearchForm(String value) { @@ -995,81 +964,40 @@ class SearchEmailController extends BaseController textOption: searchQuery == null ? Some(SearchQuery.initial()) : optionOf(searchEmailFilter.value.text), - beforeOption: const None(), startDateOption: const None(), endDateOption: const None(), - positionOption: emailSortOrderType.value.isScrollByPosition() - ? const Some(0) - : const None() ); _setEmailReceiveTimeType(EmailReceiveTimeType.allTime); _searchEmailAction(context); } void _deleteSortOrderSearchFilter(BuildContext context) { - _updateSimpleSearchFilter( - sortOrderOption: EmailSortOrderType.mostRecent.getSortOrder(), - beforeOption: const None(), - positionOption: emailSortOrderType.value.isScrollByPosition() - ? const Some(0) - : const None() - ); emailSortOrderType.value = EmailSortOrderType.mostRecent; _searchEmailAction(context); } void _deleteFromSearchFilter(BuildContext context) { - _updateSimpleSearchFilter( - fromOption: const None(), - beforeOption: const None(), - positionOption: emailSortOrderType.value.isScrollByPosition() - ? const Some(0) - : const None() - ); + _updateSimpleSearchFilter(fromOption: const None()); _searchEmailAction(context); } void _deleteToSearchFilter(BuildContext context) { - _updateSimpleSearchFilter( - toOption: const None(), - beforeOption: const None(), - positionOption: emailSortOrderType.value.isScrollByPosition() - ? const Some(0) - : const None() - ); + _updateSimpleSearchFilter(toOption: const None()); _searchEmailAction(context); } void _deleteHasAttachmentSearchFilter(BuildContext context) { - _updateSimpleSearchFilter( - hasAttachmentOption: const None(), - beforeOption: const None(), - positionOption: emailSortOrderType.value.isScrollByPosition() - ? const Some(0) - : const None() - ); + _updateSimpleSearchFilter(hasAttachmentOption: const None()); _searchEmailAction(context); } void _deleteFolderSearchFilter(BuildContext context) { - _updateSimpleSearchFilter( - mailboxOption: const None(), - beforeOption: const None(), - positionOption: emailSortOrderType.value.isScrollByPosition() - ? const Some(0) - : const None() - ); + _updateSimpleSearchFilter(mailboxOption: const None()); _searchEmailAction(context); } void _deleteStarredSearchFilter(BuildContext context) { - _updateSimpleSearchFilter( - hasKeywordOption: const None(), - beforeOption: const None(), - positionOption: emailSortOrderType.value.isScrollByPosition() - ? const Some(0) - : const None() - ); + _updateSimpleSearchFilter(hasKeywordOption: const None()); _searchEmailAction(context); } diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index f3bb28c51b..5eff361d45 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -301,7 +301,9 @@ class ThreadController extends BaseController with EmailActionController { mailboxContain: mailboxContain, ); mailboxDashBoardController.clearDashBoardAction(); - } else if (action is StartSearchEmailAction) { + } else if (action is StartSearchEmailAction + || action is ClearAdvancedSearchFilterEmailAction + || action is StartSearchEmailBySearchFilterAction) { cancelSelectEmail(); _replaceBrowserHistory(); _searchEmail(); @@ -816,17 +818,11 @@ class ThreadController extends BaseController with EmailActionController { mailboxDashBoardController.emailsInCurrentMailbox.clear(); canSearchMore = false; - if (searchController.sortOrderFiltered.value.isScrollByPosition()) { - searchController.updateFilterEmail( - positionOption: const Some(0), - beforeOption: const None(), - ); - } else { - searchController.updateFilterEmail( - positionOption: const None(), - beforeOption: const None(), - ); - } + searchController.updateFilterEmail( + positionOption: searchController.sortOrderFiltered.value.isScrollByPosition() + ? const Some(0) + : const None(), + beforeOption: const None()); searchController.activateSimpleSearch(); 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 469ff10efd..e36ff127d0 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 @@ -394,7 +394,7 @@ void main() { when(context.owner).thenReturn(BuildOwner(focusManager: FocusManager())); // expect query in search controller update as expected - mailboxDashboardController.searchEmail(queryString: queryString); + mailboxDashboardController.searchEmailByQueryString(queryString); expect(searchController.searchEmailFilter.value.text, SearchQuery(queryString)); // expect sort in search controller update as expected @@ -463,7 +463,6 @@ void main() { text: SearchQuery(emailContainsWord), notKeyword: {emailNotContainsWord}, emailReceiveTimeType: EmailReceiveTimeType.last30Days, - sortOrder: EmailSortOrderType.relevance.getSortOrder().toNullable(), position: 0)); // expect mailbox dashboard controller calls GetEmailsInMailboxInteractor @@ -500,8 +499,7 @@ void main() { when(context.owner).thenReturn(BuildOwner(focusManager: FocusManager())); // act - mailboxDashboardController.searchEmail(queryString: queryString); - mailboxDashboardController.selectSortOrderQuickSearchFilter(context, EmailSortOrderType.oldest); + mailboxDashboardController.searchEmailByQueryString(queryString); mailboxDashboardController.selectHasAttachmentSearchFilter(); mailboxDashboardController.selectReceiveTimeQuickSearchFilter(context, EmailReceiveTimeType.last30Days); @@ -521,7 +519,6 @@ void main() { text: SearchQuery(queryString), emailReceiveTimeType: EmailReceiveTimeType.last30Days, hasAttachment: true, - sortOrder: EmailSortOrderType.oldest.getSortOrder().toNullable() ) ); });