TF-3004 Fix open close advanced search looses data

This commit is contained in:
dab246
2024-10-09 11:14:47 +07:00
committed by Dat H. Pham
parent 6d0c7ed7f5
commit d77e1585b8
16 changed files with 681 additions and 274 deletions
@@ -6,7 +6,6 @@ import 'package:model/email/presentation_email.dart';
import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/base/action/ui_action.dart'; import 'package:tmail_ui_user/features/base/action/ui_action.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_receive_time_type.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart'; import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
@@ -56,15 +55,6 @@ class OpenEmailDetailedFromSuggestionQuickSearchAction extends DashBoardAction {
class StartSearchEmailAction extends DashBoardAction {} class StartSearchEmailAction extends DashBoardAction {}
class StartSearchEmailBySearchFilterAction extends DashBoardAction {
final QuickSearchFilter searchFilter;
StartSearchEmailBySearchFilterAction(this.searchFilter);
@override
List<Object?> get props => [searchFilter];
}
class EmptyTrashAction extends DashBoardAction {} class EmptyTrashAction extends DashBoardAction {}
class ClearSearchEmailAction extends DashBoardAction {} class ClearSearchEmailAction extends DashBoardAction {}
@@ -139,11 +129,11 @@ class ClearDateRangeToAdvancedSearch extends DashBoardAction {
List<Object?> get props => [receiveTime]; List<Object?> get props => [receiveTime];
} }
class SearchEmailByFromFieldsAction extends DashBoardAction { class QuickSearchEmailByFromAction extends DashBoardAction {
final EmailAddress emailAddress; final EmailAddress emailAddress;
SearchEmailByFromFieldsAction(this.emailAddress); QuickSearchEmailByFromAction(this.emailAddress);
@override @override
List<Object?> get props => [emailAddress]; List<Object?> get props => [emailAddress];
@@ -3,6 +3,7 @@ import 'package:core/core.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.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_address.dart';
import 'package:model/model.dart'; import 'package:model/model.dart';
import 'package:super_tag_editor/tag_editor.dart'; import 'package:super_tag_editor/tag_editor.dart';
@@ -20,7 +21,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/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_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/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/mailbox_dashboard/presentation/model/search/search_email_filter.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/datetime_extension.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/extensions/datetime_extension.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
@@ -32,10 +32,11 @@ import 'package:tmail_ui_user/main/routes/route_navigation.dart';
class AdvancedFilterController extends BaseController { class AdvancedFilterController extends BaseController {
GetAutoCompleteInteractor? _getAutoCompleteInteractor; GetAutoCompleteInteractor? _getAutoCompleteInteractor;
final dateFilterSelectedFormAdvancedSearch = EmailReceiveTimeType.allTime.obs; final receiveTimeType = EmailReceiveTimeType.allTime.obs;
final hasAttachment = false.obs; final hasAttachment = false.obs;
final startDate = Rxn<DateTime>(); final startDate = Rxn<DateTime>();
final endDate = Rxn<DateTime>(); final endDate = Rxn<DateTime>();
final sortOrderType = EmailSortOrderType.mostRecent.obs;
final GlobalKey<TagsEditorState> keyFromEmailTagEditor = GlobalKey<TagsEditorState>(); final GlobalKey<TagsEditorState> keyFromEmailTagEditor = GlobalKey<TagsEditorState>();
final GlobalKey<TagsEditorState> keyToEmailTagEditor = GlobalKey<TagsEditorState>(); final GlobalKey<TagsEditorState> keyToEmailTagEditor = GlobalKey<TagsEditorState>();
@@ -55,10 +56,8 @@ class AdvancedFilterController extends BaseController {
final search.SearchController searchController = Get.find<search.SearchController>(); final search.SearchController searchController = Get.find<search.SearchController>();
final MailboxDashBoardController _mailboxDashBoardController = Get.find<MailboxDashBoardController>(); final MailboxDashBoardController _mailboxDashBoardController = Get.find<MailboxDashBoardController>();
SearchEmailFilter get searchEmailFilter =>
searchController.searchEmailFilter.value;
final focusManager = InputFieldFocusManager.initial(); final focusManager = InputFieldFocusManager.initial();
SearchEmailFilter _memorySearchFilter = SearchEmailFilter.initial();
PresentationMailbox? _destinationMailboxSelected; PresentationMailbox? _destinationMailboxSelected;
@@ -80,87 +79,144 @@ class AdvancedFilterController extends BaseController {
} }
void clearSearchFilter() { void clearSearchFilter() {
_memorySearchFilter = SearchEmailFilter.initial();
_resetAllToOriginalValue(); _resetAllToOriginalValue();
_clearAllTextFieldInput(); _clearAllTextFieldInput();
_mailboxDashBoardController.handleClearAdvancedSearchFilterEmail(); _mailboxDashBoardController.handleClearAdvancedSearchFilterEmail();
} }
void _updateFilterEmailFromAdvancedSearchView() { @visibleForTesting
if (hasKeyWordFilterInputController.text.isNotEmpty) { void setDestinationMailboxSelected(PresentationMailbox? presentationMailbox) {
searchController.updateFilterEmail( _destinationMailboxSelected = presentationMailbox;
textOption: Some(SearchQuery(hasKeyWordFilterInputController.text))); }
searchController.searchInputController.text = hasKeyWordFilterInputController.text;
} else {
searchController.updateFilterEmail(
textOption: Some(SearchQuery.initial()));
}
if (notKeyWordFilterInputController.text.isNotEmpty) { @visibleForTesting
searchController.updateFilterEmail( SearchEmailFilter get memorySearchFilter => _memorySearchFilter;
notKeywordOption: Some(notKeyWordFilterInputController.text.split(',').toSet()));
} else {
searchController.updateFilterEmail(notKeywordOption: const None());
}
if (listFromEmailAddress.isNotEmpty) { @visibleForTesting
final listAddress = listFromEmailAddress.map((emailAddress) => emailAddress.emailAddress).toSet(); void setMemorySearchFilter(SearchEmailFilter searchFilter) {
searchController.updateFilterEmail(fromOption: Some(listAddress)); _memorySearchFilter = searchFilter;
} else { }
searchController.updateFilterEmail(fromOption: const None());
}
if (listToEmailAddress.isNotEmpty) {
final listAddress = listToEmailAddress.map((emailAddress) => emailAddress.emailAddress).toSet();
searchController.updateFilterEmail(toOption: Some(listAddress));
} else {
searchController.updateFilterEmail(toOption: const None());
}
searchController.updateFilterEmail( @visibleForTesting
mailboxOption: optionOf(_destinationMailboxSelected), MailboxDashBoardController get mailboxDashBoardController => _mailboxDashBoardController;
subjectOption: optionOf(subjectFilterInputController.text),
emailReceiveTimeTypeOption: optionOf(dateFilterSelectedFormAdvancedSearch.value), void _updateMemorySearchFilter({
hasAttachmentOption: optionOf(hasAttachment.value), Option<Set<String>>? fromOption,
startDateOption: optionOf(startDate.value?.toUTCDate()), Option<Set<String>>? toOption,
endDateOption: optionOf(endDate.value?.toUTCDate()) Option<SearchQuery>? textOption,
Option<String>? subjectOption,
Option<Set<String>>? notKeywordOption,
Option<PresentationMailbox>? mailboxOption,
Option<EmailReceiveTimeType>? emailReceiveTimeTypeOption,
Option<bool>? hasAttachmentOption,
Option<UTCDate>? beforeOption,
Option<UTCDate>? startDateOption,
Option<UTCDate>? endDateOption,
Option<int>? positionOption,
Option<EmailSortOrderType>? sortOrderTypeOption,
}) {
_memorySearchFilter = _memorySearchFilter.copyWith(
fromOption: fromOption,
toOption: toOption,
textOption: textOption,
subjectOption: subjectOption,
notKeywordOption: notKeywordOption,
mailboxOption: mailboxOption,
emailReceiveTimeTypeOption: emailReceiveTimeTypeOption,
hasAttachmentOption: hasAttachmentOption,
beforeOption: beforeOption,
startDateOption: startDateOption,
endDateOption: endDateOption,
positionOption: positionOption,
sortOrderTypeOption: sortOrderTypeOption);
}
void _synchronizeSearchFilter() {
final textOption = option(
hasKeyWordFilterInputController.text.trim().isNotEmpty,
SearchQuery(hasKeyWordFilterInputController.text.trim()));
final notKeywordsOption = option(
notKeyWordFilterInputController.text.trim().isNotEmpty,
notKeyWordFilterInputController.text.trim().split(',').map((value) => value.trim()).toSet());
final fromOption = option(
listFromEmailAddress.isNotEmpty,
listFromEmailAddress.asSetAddress());
final toOption = option(
listToEmailAddress.isNotEmpty,
listToEmailAddress.asSetAddress());
final sortOrderTypeOption = Some(sortOrderType.value);
final mailboxOption = optionOf(_destinationMailboxSelected);
final subjectOption = option(
subjectFilterInputController.text.trim().isNotEmpty,
subjectFilterInputController.text.trim());
final emailReceiveTimeTypeOption = Some(receiveTimeType.value);
final hasAttachmentOption = Some(hasAttachment.value);
final startDateOption = optionOf(startDate.value?.toUTCDate());
final endDateOption = optionOf(endDate.value?.toUTCDate());
_updateMemorySearchFilter(
textOption: textOption,
notKeywordOption: notKeywordsOption,
fromOption: fromOption,
toOption: toOption,
sortOrderTypeOption: sortOrderTypeOption,
mailboxOption: mailboxOption,
subjectOption: subjectOption,
emailReceiveTimeTypeOption: emailReceiveTimeTypeOption,
hasAttachmentOption: hasAttachmentOption,
startDateOption: startDateOption,
endDateOption: endDateOption
); );
searchController.synchronizeSearchFilter(_memorySearchFilter);
} }
void selectedMailBox(BuildContext context) async { void selectedMailBox(BuildContext context) async {
final accountId = _mailboxDashBoardController.accountId.value; final accountId = _mailboxDashBoardController.accountId.value;
final session = _mailboxDashBoardController.sessionCurrent; final session = _mailboxDashBoardController.sessionCurrent;
if (accountId != null && session != null) {
final arguments = DestinationPickerArguments(
accountId,
MailboxActions.select,
session,
mailboxIdSelected: _destinationMailboxSelected?.id
);
final destinationMailbox = PlatformInfo.isWeb if (session == null || accountId == null) return;
? await DialogRouter.pushGeneralDialog(routeName: AppRoutes.destinationPicker, arguments: arguments)
: await push(AppRoutes.destinationPicker, arguments: arguments);
if (destinationMailbox is PresentationMailbox) { final arguments = DestinationPickerArguments(
_destinationMailboxSelected = destinationMailbox; accountId,
String? mailboxName; MailboxActions.select,
if (context.mounted) { session,
mailboxName = _destinationMailboxSelected?.getDisplayName(context); mailboxIdSelected: _destinationMailboxSelected?.id
} else { );
mailboxName = _destinationMailboxSelected?.name?.name;
} final destinationMailbox = PlatformInfo.isWeb
mailBoxFilterInputController.text = StringConvert.writeNullToEmpty(mailboxName); ? await DialogRouter.pushGeneralDialog(
} routeName: AppRoutes.destinationPicker,
} arguments: arguments)
: await push(AppRoutes.destinationPicker, arguments: arguments);
if (destinationMailbox is! PresentationMailbox) return;
_destinationMailboxSelected = destinationMailbox;
final mailboxName = context.mounted
? _destinationMailboxSelected?.getDisplayName(context)
: _destinationMailboxSelected?.name?.name;
mailBoxFilterInputController.text = StringConvert.writeNullToEmpty(mailboxName);
_updateMemorySearchFilter(mailboxOption: optionOf(_destinationMailboxSelected));
} }
void applyAdvancedSearchFilter() { void applyAdvancedSearchFilter() {
_updateFilterEmailFromAdvancedSearchView(); _synchronizeSearchFilter();
if (isAdvancedSearchHasApplied) { if (searchController.searchEmailFilter.value.isApplied) {
searchController.activateAdvancedSearch(); searchController.activateAdvancedSearch();
} else { } else {
searchController.deactivateAdvancedSearch(); searchController.deactivateAdvancedSearch();
}
if (!isAdvancedSearchHasApplied) {
searchController.updateFilterEmail(beforeOption: const None()); searchController.updateFilterEmail(beforeOption: const None());
} }
searchController.isAdvancedSearchViewOpen.value = false; searchController.isAdvancedSearchViewOpen.value = false;
@@ -183,43 +239,48 @@ class AdvancedFilterController extends BaseController {
)) ?? []; )) ?? [];
} }
bool get isAdvancedSearchHasApplied { void initSearchFilterField(BuildContext? context) {
return searchEmailFilter.from.isNotEmpty || subjectFilterInputController.text = StringConvert.writeNullToEmpty(
searchEmailFilter.to.isNotEmpty || _memorySearchFilter.subject);
subjectFilterInputController.text.isNotEmpty ||
hasKeyWordFilterInputController.text.isNotEmpty ||
notKeyWordFilterInputController.text.isNotEmpty ||
searchEmailFilter.emailReceiveTimeType != EmailReceiveTimeType.allTime ||
searchEmailFilter.mailbox != PresentationMailbox.unifiedMailbox ||
hasAttachment.isTrue;
}
void initSearchFilterField() { hasKeyWordFilterInputController.text = StringConvert.writeNullToEmpty(
subjectFilterInputController.text = StringConvert.writeNullToEmpty(searchEmailFilter.subject); _memorySearchFilter.text?.value);
hasKeyWordFilterInputController.text = StringConvert.writeNullToEmpty(searchEmailFilter.text?.value);
notKeyWordFilterInputController.text = StringConvert.writeNullToEmpty(searchEmailFilter.notKeyword.firstOrNull); notKeyWordFilterInputController.text = StringConvert.writeNullToEmpty(
dateFilterSelectedFormAdvancedSearch.value = searchEmailFilter.emailReceiveTimeType; _memorySearchFilter.notKeyword.join(','));
_destinationMailboxSelected = searchEmailFilter.mailbox;
if (currentContext != null) { receiveTimeType.value = _memorySearchFilter.emailReceiveTimeType;
if (searchEmailFilter.mailbox == null) {
mailBoxFilterInputController.text = AppLocalizations.of(currentContext!).allFolders; sortOrderType.value = _memorySearchFilter.sortOrderType;
} else {
mailBoxFilterInputController.text = StringConvert.writeNullToEmpty( _destinationMailboxSelected = _memorySearchFilter.mailbox;
searchEmailFilter.mailbox?.getDisplayName(currentContext!)
); hasAttachment.value = _memorySearchFilter.hasAttachment;
}
} if (_memorySearchFilter.from.isEmpty) {
hasAttachment.value = searchEmailFilter.hasAttachment;
if (searchEmailFilter.from.isEmpty) {
listFromEmailAddress.clear(); listFromEmailAddress.clear();
} else { } else {
final listEmailAddress = _memorySearchFilter.from
.map((email) => EmailAddress(null, email));
listFromEmailAddress = List.from(listEmailAddress);
fromAddressExpandMode.value = ExpandMode.COLLAPSE; fromAddressExpandMode.value = ExpandMode.COLLAPSE;
} }
if (searchEmailFilter.to.isEmpty) {
if (_memorySearchFilter.to.isEmpty) {
listToEmailAddress.clear(); listToEmailAddress.clear();
} else { } else {
final listEmailAddress = _memorySearchFilter.to
.map((email) => EmailAddress(null, email));
listToEmailAddress = List.from(listEmailAddress);
toAddressExpandMode.value = ExpandMode.COLLAPSE; toAddressExpandMode.value = ExpandMode.COLLAPSE;
} }
if (context != null) {
mailBoxFilterInputController.text = _memorySearchFilter.mailbox == null
? AppLocalizations.of(context).allFolders
: StringConvert.writeNullToEmpty(
_memorySearchFilter.mailbox?.getDisplayName(context));
}
} }
void selectDateRange(BuildContext context) { void selectDateRange(BuildContext context) {
@@ -239,7 +300,13 @@ class AdvancedFilterController extends BaseController {
void _updateDateRangeTime(EmailReceiveTimeType receiveTime, {DateTime? newStartDate, DateTime? newEndDate}) { void _updateDateRangeTime(EmailReceiveTimeType receiveTime, {DateTime? newStartDate, DateTime? newEndDate}) {
startDate.value = newStartDate; startDate.value = newStartDate;
endDate.value = newEndDate; endDate.value = newEndDate;
dateFilterSelectedFormAdvancedSearch.value = receiveTime; receiveTimeType.value = receiveTime;
_updateMemorySearchFilter(
emailReceiveTimeTypeOption: Some(receiveTimeType.value),
startDateOption: optionOf(startDate.value?.toUTCDate()),
endDateOption: optionOf(endDate.value?.toUTCDate())
);
} }
void updateReceiveDateSearchFilter(BuildContext context, EmailReceiveTimeType receiveTime) { void updateReceiveDateSearchFilter(BuildContext context, EmailReceiveTimeType receiveTime) {
@@ -314,9 +381,17 @@ class AdvancedFilterController extends BaseController {
switch(field) { switch(field) {
case AdvancedSearchFilterField.from: case AdvancedSearchFilterField.from:
listFromEmailAddress = List.from(listEmailAddress); listFromEmailAddress = List.from(listEmailAddress);
_updateMemorySearchFilter(
fromOption: option(
listFromEmailAddress.isNotEmpty,
listFromEmailAddress.asSetAddress()));
break; break;
case AdvancedSearchFilterField.to: case AdvancedSearchFilterField.to:
listToEmailAddress = List.from(listEmailAddress); listToEmailAddress = List.from(listEmailAddress);
_updateMemorySearchFilter(
toOption: option(
listToEmailAddress.isNotEmpty,
listToEmailAddress.asSetAddress()));
break; break;
default: default:
break; break;
@@ -339,6 +414,7 @@ class AdvancedFilterController extends BaseController {
if (!_isDuplicatedEmailAddress(inputEmail, listFromEmailAddress)) { if (!_isDuplicatedEmailAddress(inputEmail, listFromEmailAddress)) {
final emailAddress = EmailAddress(null, inputEmail); final emailAddress = EmailAddress(null, inputEmail);
listFromEmailAddress.add(emailAddress); listFromEmailAddress.add(emailAddress);
_updateMemorySearchFilter(fromOption: Some(listFromEmailAddress.asSetAddress()));
keyFromEmailTagEditor.currentState?.resetTextField(); keyFromEmailTagEditor.currentState?.resetTextField();
Future.delayed(const Duration(milliseconds: 300), () { Future.delayed(const Duration(milliseconds: 300), () {
keyFromEmailTagEditor.currentState?.closeSuggestionBox(); keyFromEmailTagEditor.currentState?.closeSuggestionBox();
@@ -353,8 +429,8 @@ class AdvancedFilterController extends BaseController {
} }
if (!_isDuplicatedEmailAddress(inputEmail, listToEmailAddress)) { if (!_isDuplicatedEmailAddress(inputEmail, listToEmailAddress)) {
final emailAddress = EmailAddress(null, inputEmail); listToEmailAddress.add(EmailAddress(null, inputEmail));
listToEmailAddress.add(emailAddress); _updateMemorySearchFilter(toOption: Some(listToEmailAddress.asSetAddress()));
keyToEmailTagEditor.currentState?.resetTextField(); keyToEmailTagEditor.currentState?.resetTextField();
Future.delayed(const Duration(milliseconds: 300), () { Future.delayed(const Duration(milliseconds: 300), () {
keyToEmailTagEditor.currentState?.closeSuggestionBox(); keyToEmailTagEditor.currentState?.closeSuggestionBox();
@@ -364,12 +440,15 @@ class AdvancedFilterController extends BaseController {
void updateSortOrder(EmailSortOrderType? sortOrder) { void updateSortOrder(EmailSortOrderType? sortOrder) {
if (sortOrder != null) { if (sortOrder != null) {
searchController.sortOrderFiltered.value = sortOrder; sortOrderType.value = sortOrder;
_updateMemorySearchFilter(sortOrderTypeOption: Some(sortOrder));
} }
} }
void _resetAllToOriginalValue() { void _resetAllToOriginalValue() {
_updateDateRangeTime(EmailReceiveTimeType.allTime); startDate.value = null;
endDate.value = null;
receiveTimeType.value = EmailReceiveTimeType.allTime;
hasAttachment.value = false; hasAttachment.value = false;
listFromEmailAddress.clear(); listFromEmailAddress.clear();
listToEmailAddress.clear(); listToEmailAddress.clear();
@@ -399,23 +478,12 @@ class AdvancedFilterController extends BaseController {
); );
} else if (action is ClearDateRangeToAdvancedSearch) { } else if (action is ClearDateRangeToAdvancedSearch) {
_updateDateRangeTime(action.receiveTime); _updateDateRangeTime(action.receiveTime);
} else if (action is StartSearchEmailBySearchFilterAction) { } else if (action is StartSearchEmailAction) {
_handleSearchEmailBySearchFilterAction(action.searchFilter); _handleStartSearchEmailAction();
} else if (action is SearchEmailByFromFieldsAction) { } else if (action is QuickSearchEmailByFromAction) {
searchController.clearSearchFilter(); _handleQuickSearchEmailByFromAction(action.emailAddress);
_resetAllToOriginalValue();
_clearAllTextFieldInput();
searchController.searchInputController.clear();
searchController.deactivateAdvancedSearch();
searchController.isAdvancedSearchViewOpen.value = false;
listFromEmailAddress = List.from({action.emailAddress});
final listAddress = listFromEmailAddress.map((emailAddress) => emailAddress.emailAddress).toSet();
searchController.updateFilterEmail(fromOption: Some(listAddress));
_mailboxDashBoardController.dispatchAction(StartSearchEmailAction());
} else if (action is OpenAdvancedSearchViewAction) { } else if (action is OpenAdvancedSearchViewAction) {
initSearchFilterField(); initSearchFilterField(currentContext);
} else if (action is ClearSearchFilterAppliedAction) { } else if (action is ClearSearchFilterAppliedAction) {
clearSearchFilter(); clearSearchFilter();
} }
@@ -440,24 +508,7 @@ class AdvancedFilterController extends BaseController {
void _handleClearAllFieldOfAdvancedSearch() { void _handleClearAllFieldOfAdvancedSearch() {
_resetAllToOriginalValue(); _resetAllToOriginalValue();
_clearAllTextFieldInput(); _clearAllTextFieldInput();
} _memorySearchFilter = SearchEmailFilter.initial();
void _updateFromField() {
if (searchEmailFilter.from.isNotEmpty) {
listFromEmailAddress = List.from(
searchEmailFilter.from.map((address) => EmailAddress(null, address)));
} else {
listFromEmailAddress.clear();
}
}
void _updateToField() {
if (searchEmailFilter.to.isNotEmpty) {
listToEmailAddress = List.from(
searchEmailFilter.to.map((address) => EmailAddress(null, address)));
} else {
listToEmailAddress.clear();
}
} }
void onSearchAction() { void onSearchAction() {
@@ -465,13 +516,33 @@ class AdvancedFilterController extends BaseController {
applyAdvancedSearchFilter(); applyAdvancedSearchFilter();
} }
void _handleSearchEmailBySearchFilterAction(QuickSearchFilter searchFilter) { void _handleStartSearchEmailAction() {
switch(searchFilter) { _memorySearchFilter = searchController.searchEmailFilter.value;
case QuickSearchFilter.from: initSearchFilterField(currentContext);
_updateFromField(); }
void onHasAttachmentCheckboxChanged(bool? isChecked) {
hasAttachment.value = isChecked ?? false;
_updateMemorySearchFilter(hasAttachmentOption: Some(hasAttachment.value));
}
void onTextChanged(AdvancedSearchFilterField filterField, String value) {
switch (filterField) {
case AdvancedSearchFilterField.subject:
final subjectOption = option(value.trim().isNotEmpty, value.trim());
_updateMemorySearchFilter(subjectOption: subjectOption);
break; break;
case QuickSearchFilter.to: case AdvancedSearchFilterField.hasKeyword:
_updateToField(); final textOption = option(
value.trim().isNotEmpty,
SearchQuery(value.trim()));
_updateMemorySearchFilter(textOption: textOption);
break;
case AdvancedSearchFilterField.notKeyword:
final notKeywordsOption = option(
value.trim().isNotEmpty,
value.trim().split(',').map((value) => value.trim()).toSet());
_updateMemorySearchFilter(notKeywordOption: notKeywordsOption);
break; break;
default: default:
break; break;
@@ -496,6 +567,20 @@ class AdvancedFilterController extends BaseController {
} }
} }
void _handleQuickSearchEmailByFromAction(EmailAddress emailAddress) {
searchController.clearSearchFilter();
_memorySearchFilter = SearchEmailFilter.initial();
_resetAllToOriginalValue();
_clearAllTextFieldInput();
searchController.searchInputController.clear();
searchController.deactivateAdvancedSearch();
searchController.isAdvancedSearchViewOpen.value = false;
listFromEmailAddress = List.from({emailAddress});
searchController.updateFilterEmail(fromOption: Some(listFromEmailAddress.asSetAddress()));
_updateMemorySearchFilter(fromOption: Some(listFromEmailAddress.asSetAddress()));
_mailboxDashBoardController.dispatchAction(StartSearchEmailAction());
}
@override @override
void onClose() { void onClose() {
_removeFocusListener(); _removeFocusListener();
@@ -507,6 +592,8 @@ class AdvancedFilterController extends BaseController {
toEmailAddressController.dispose(); toEmailAddressController.dispose();
fromEmailAddressController.dispose(); fromEmailAddressController.dispose();
_unregisterWorkerListener(); _unregisterWorkerListener();
_resetAllToOriginalValue();
_memorySearchFilter = SearchEmailFilter.initial();
super.onClose(); super.onClose();
} }
} }
@@ -669,7 +669,6 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo
void handleAdvancedSearchEmail() { void handleAdvancedSearchEmail() {
log('MailboxDashBoardController::handleAdvancedSearchEmail:'); log('MailboxDashBoardController::handleAdvancedSearchEmail:');
clearFilterMessageOption();
if (_searchInsideEmailDetailedViewIsActive()) { if (_searchInsideEmailDetailedViewIsActive()) {
_closeEmailDetailedView(); _closeEmailDetailedView();
} }
@@ -711,11 +710,7 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo
FocusManager.instance.primaryFocus?.unfocus(); FocusManager.instance.primaryFocus?.unfocus();
if (isMailAddress) { dispatchAction(StartSearchEmailAction());
dispatchAction(StartSearchEmailBySearchFilterAction(QuickSearchFilter.from));
} else {
dispatchAction(StartSearchEmailAction());
}
} }
bool _searchInsideEmailDetailedViewIsActive() { bool _searchInsideEmailDetailedViewIsActive() {
@@ -1715,7 +1710,7 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo
.map((emailAddress) => emailAddress.emailAddress) .map((emailAddress) => emailAddress.emailAddress)
.toSet(); .toSet();
searchController.updateFilterEmail(fromOption: Some(listMailAddress)); searchController.updateFilterEmail(fromOption: Some(listMailAddress));
dispatchAction(StartSearchEmailBySearchFilterAction(QuickSearchFilter.from)); dispatchAction(StartSearchEmailAction());
} }
} }
@@ -1738,7 +1733,7 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo
.map((emailAddress) => emailAddress.emailAddress) .map((emailAddress) => emailAddress.emailAddress)
.toSet(); .toSet();
searchController.updateFilterEmail(toOption: Some(listMailAddress)); searchController.updateFilterEmail(toOption: Some(listMailAddress));
dispatchAction(StartSearchEmailBySearchFilterAction(QuickSearchFilter.to)); dispatchAction(StartSearchEmailAction());
} }
} }
@@ -1805,7 +1800,7 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo
void selectSortOrderQuickSearchFilter(EmailSortOrderType sortOrder) { void selectSortOrderQuickSearchFilter(EmailSortOrderType sortOrder) {
log('MailboxDashBoardController::selectSortOrderQuickSearchFilter():sortOrder: $sortOrder'); log('MailboxDashBoardController::selectSortOrderQuickSearchFilter():sortOrder: $sortOrder');
popBack(); popBack();
searchController.sortOrderFiltered.value = sortOrder; searchController.updateFilterEmail(sortOrderTypeOption: Some(sortOrder));
dispatchAction(StartSearchEmailAction()); dispatchAction(StartSearchEmailAction());
} }
@@ -1820,18 +1815,19 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo
} }
void _deleteSortOrderSearchFilter() { void _deleteSortOrderSearchFilter() {
searchController.sortOrderFiltered.value = EmailSortOrderType.mostRecent; searchController.updateFilterEmail(
sortOrderTypeOption: const Some(EmailSortOrderType.mostRecent));
dispatchAction(StartSearchEmailAction()); dispatchAction(StartSearchEmailAction());
} }
void _deleteFromSearchFilter() { void _deleteFromSearchFilter() {
searchController.updateFilterEmail(fromOption: const None()); searchController.updateFilterEmail(fromOption: const None());
dispatchAction(StartSearchEmailBySearchFilterAction(QuickSearchFilter.from)); dispatchAction(StartSearchEmailAction());
} }
void _deleteToSearchFilter() { void _deleteToSearchFilter() {
searchController.updateFilterEmail(toOption: const None()); searchController.updateFilterEmail(toOption: const None());
dispatchAction(StartSearchEmailBySearchFilterAction(QuickSearchFilter.to)); dispatchAction(StartSearchEmailAction());
} }
void _deleteHasAttachmentSearchFilter() { void _deleteHasAttachmentSearchFilter() {
@@ -2514,7 +2510,7 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo
return listEmailAddress; return listEmailAddress;
} }
void searchEmailByFromFields(EmailAddress emailAddress) { void quickSearchEmailByFrom(EmailAddress emailAddress) {
FocusManager.instance.primaryFocus?.unfocus(); FocusManager.instance.primaryFocus?.unfocus();
clearFilterMessageOption(); clearFilterMessageOption();
searchController.clearFilterSuggestion(); searchController.clearFilterSuggestion();
@@ -2522,7 +2518,7 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo
_closeEmailDetailedView(); _closeEmailDetailedView();
} }
_unSelectedMailbox(); _unSelectedMailbox();
dispatchAction(SearchEmailByFromFieldsAction(emailAddress)); dispatchAction(QuickSearchEmailByFromAction(emailAddress));
} }
void unsubscribeMail(EmailId emailId) { void unsubscribeMail(EmailId emailId) {
@@ -2843,7 +2839,7 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo
} }
bool get isSearchFilterHasApplied { bool get isSearchFilterHasApplied {
return searchController.isSearchFilterHasApplied || return searchController.searchEmailFilter.value.isApplied ||
filterMessageOption.value != FilterMessageOption.all; filterMessageOption.value != FilterMessageOption.all;
} }
@@ -47,7 +47,6 @@ class SearchController extends BaseController with DateRangePickerMixin {
final listFilterOnSuggestionForm = RxList<QuickSearchFilter>(); final listFilterOnSuggestionForm = RxList<QuickSearchFilter>();
final simpleSearchIsActivated = RxBool(false); final simpleSearchIsActivated = RxBool(false);
final advancedSearchIsActivated = RxBool(false); final advancedSearchIsActivated = RxBool(false);
final sortOrderFiltered = EmailSortOrderType.mostRecent.obs;
SearchQuery? get searchQuery => searchEmailFilter.value.text; SearchQuery? get searchQuery => searchEmailFilter.value.text;
@@ -71,6 +70,10 @@ class SearchController extends BaseController with DateRangePickerMixin {
searchEmailFilter.value = SearchEmailFilter.initial(); searchEmailFilter.value = SearchEmailFilter.initial();
} }
void synchronizeSearchFilter(SearchEmailFilter searchFilter) {
searchEmailFilter.value = searchFilter;
}
void addQuickSearchFilterToSuggestionSearchView(QuickSearchFilter searchFilter) { void addQuickSearchFilterToSuggestionSearchView(QuickSearchFilter searchFilter) {
if (!listFilterOnSuggestionForm.contains(searchFilter)) { if (!listFilterOnSuggestionForm.contains(searchFilter)) {
listFilterOnSuggestionForm.add(searchFilter); listFilterOnSuggestionForm.add(searchFilter);
@@ -164,6 +167,7 @@ class SearchController extends BaseController with DateRangePickerMixin {
Option<SearchQuery>? textOption, Option<SearchQuery>? textOption,
Option<String>? subjectOption, Option<String>? subjectOption,
Option<Set<String>>? notKeywordOption, Option<Set<String>>? notKeywordOption,
Option<Set<String>>? hasKeywordOption,
Option<PresentationMailbox>? mailboxOption, Option<PresentationMailbox>? mailboxOption,
Option<EmailReceiveTimeType>? emailReceiveTimeTypeOption, Option<EmailReceiveTimeType>? emailReceiveTimeTypeOption,
Option<bool>? hasAttachmentOption, Option<bool>? hasAttachmentOption,
@@ -171,6 +175,7 @@ class SearchController extends BaseController with DateRangePickerMixin {
Option<UTCDate>? startDateOption, Option<UTCDate>? startDateOption,
Option<UTCDate>? endDateOption, Option<UTCDate>? endDateOption,
Option<int>? positionOption, Option<int>? positionOption,
Option<EmailSortOrderType>? sortOrderTypeOption,
}) { }) {
searchEmailFilter.value = searchEmailFilter.value.copyWith( searchEmailFilter.value = searchEmailFilter.value.copyWith(
fromOption: fromOption, fromOption: fromOption,
@@ -178,6 +183,7 @@ class SearchController extends BaseController with DateRangePickerMixin {
textOption: textOption, textOption: textOption,
subjectOption: subjectOption, subjectOption: subjectOption,
notKeywordOption: notKeywordOption, notKeywordOption: notKeywordOption,
hasKeywordOption: hasKeywordOption,
mailboxOption: mailboxOption, mailboxOption: mailboxOption,
emailReceiveTimeTypeOption: emailReceiveTimeTypeOption, emailReceiveTimeTypeOption: emailReceiveTimeTypeOption,
hasAttachmentOption: hasAttachmentOption, hasAttachmentOption: hasAttachmentOption,
@@ -185,6 +191,7 @@ class SearchController extends BaseController with DateRangePickerMixin {
startDateOption: startDateOption, startDateOption: startDateOption,
endDateOption: endDateOption, endDateOption: endDateOption,
positionOption: positionOption, positionOption: positionOption,
sortOrderTypeOption: sortOrderTypeOption,
); );
searchEmailFilter.refresh(); searchEmailFilter.refresh();
} }
@@ -201,20 +208,13 @@ class SearchController extends BaseController with DateRangePickerMixin {
Set<String> get listAddressOfFromFiltered => searchEmailFilter.value.from; Set<String> get listAddressOfFromFiltered => searchEmailFilter.value.from;
EmailSortOrderType get sortOrderFiltered => searchEmailFilter.value.sortOrderType;
bool isSearchActive() => bool isSearchActive() =>
searchState.value.searchStatus == SearchStatus.ACTIVE; searchState.value.searchStatus == SearchStatus.ACTIVE;
bool get isSearchEmailRunning => simpleSearchIsActivated.isTrue || advancedSearchIsActivated.isTrue; bool get isSearchEmailRunning => simpleSearchIsActivated.isTrue || advancedSearchIsActivated.isTrue;
bool get isSearchFilterHasApplied {
return searchEmailFilter.value.from.isNotEmpty ||
searchEmailFilter.value.to.isNotEmpty ||
searchEmailFilter.value.emailReceiveTimeType != EmailReceiveTimeType.allTime ||
(searchEmailFilter.value.mailbox != PresentationMailbox.unifiedMailbox && searchEmailFilter.value.mailbox != null) ||
searchEmailFilter.value.hasAttachment == true ||
sortOrderFiltered.value != EmailSortOrderType.mostRecent;
}
void enableSearch() { void enableSearch() {
searchState.value = searchState.value.enableSearchState(); searchState.value = searchState.value.enableSearchState();
} }
@@ -278,13 +278,8 @@ class SearchController extends BaseController with DateRangePickerMixin {
searchFocus.unfocus(); searchFocus.unfocus();
} }
void clearSortOrder() {
sortOrderFiltered.value = EmailSortOrderType.mostRecent;
}
void clearAllFilterSearch() { void clearAllFilterSearch() {
_clearAllTextInputSimpleSearch(); _clearAllTextInputSimpleSearch();
clearSortOrder();
clearFilterSuggestion(); clearFilterSuggestion();
clearSearchFilter(); clearSearchFilter();
deactivateAdvancedSearch(); deactivateAdvancedSearch();
@@ -296,7 +291,6 @@ class SearchController extends BaseController with DateRangePickerMixin {
deactivateSimpleSearch(); deactivateSimpleSearch();
hideSimpleSearchFormView(); hideSimpleSearchFormView();
clearSortOrder();
clearSearchFilter(); clearSearchFilter();
deactivateAdvancedSearch(); deactivateAdvancedSearch();
hideAdvancedSearchFormView(); hideAdvancedSearchFormView();
@@ -533,7 +533,7 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView {
) { ) {
return Obx(() { return Obx(() {
final searchEmailFilter = controller.searchController.searchEmailFilter.value; final searchEmailFilter = controller.searchController.searchEmailFilter.value;
final sortOrderType = controller.searchController.sortOrderFiltered.value; final sortOrderType = controller.searchController.sortOrderFiltered;
final listAddressOfFrom = controller.searchController.listAddressOfFromFiltered; final listAddressOfFrom = controller.searchController.listAddressOfFromFiltered;
final userName = controller.sessionCurrent?.username; final userName = controller.sessionCurrent?.username;
final startDate = controller.searchController.startDateFiltered; final startDate = controller.searchController.startDateFiltered;
@@ -668,7 +668,7 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView {
position, position,
popupMenuEmailSortOrderType( popupMenuEmailSortOrderType(
context, context,
controller.searchController.sortOrderFiltered.value, controller.searchController.sortOrderFiltered,
onCallBack: controller.selectSortOrderQuickSearchFilter onCallBack: controller.selectSortOrderQuickSearchFilter
) )
); );
@@ -28,30 +28,33 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin {
final UTCDate? startDate; final UTCDate? startDate;
final UTCDate? endDate; final UTCDate? endDate;
final int? position; final int? position;
final EmailSortOrderType sortOrderType;
factory SearchEmailFilter.initial() => SearchEmailFilter(); factory SearchEmailFilter.initial() => SearchEmailFilter();
SearchEmailFilter({ SearchEmailFilter({
Set<String>? from,
Set<String>? to,
EmailReceiveTimeType? emailReceiveTimeType,
bool? hasAttachment,
this.text, this.text,
this.subject, this.subject,
Set<String>? notKeyword,
Set<String>? hasKeyword,
this.mailbox, this.mailbox,
this.before, this.before,
this.startDate, this.startDate,
this.endDate, this.endDate,
this.position, this.position,
Set<String>? from,
Set<String>? to,
EmailReceiveTimeType? emailReceiveTimeType,
bool? hasAttachment,
Set<String>? notKeyword,
Set<String>? hasKeyword,
EmailSortOrderType? sortOrderType,
}) : from = from ?? <String>{}, }) : from = from ?? <String>{},
to = to ?? <String>{}, to = to ?? <String>{},
notKeyword = notKeyword ?? <String>{}, notKeyword = notKeyword ?? <String>{},
hasKeyword = hasKeyword ?? <String>{}, hasKeyword = hasKeyword ?? <String>{},
hasAttachment = hasAttachment ?? false, hasAttachment = hasAttachment ?? false,
emailReceiveTimeType = emailReceiveTimeType =
emailReceiveTimeType ?? EmailReceiveTimeType.allTime; emailReceiveTimeType ?? EmailReceiveTimeType.allTime,
sortOrderType = sortOrderType ?? EmailSortOrderType.mostRecent;
SearchEmailFilter copyWith({ SearchEmailFilter copyWith({
Option<Set<String>>? fromOption, Option<Set<String>>? fromOption,
@@ -67,6 +70,7 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin {
Option<UTCDate>? startDateOption, Option<UTCDate>? startDateOption,
Option<UTCDate>? endDateOption, Option<UTCDate>? endDateOption,
Option<int>? positionOption, Option<int>? positionOption,
Option<EmailSortOrderType>? sortOrderTypeOption,
}) { }) {
return SearchEmailFilter( return SearchEmailFilter(
from: getOptionParam(fromOption, from), from: getOptionParam(fromOption, from),
@@ -82,24 +86,24 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin {
startDate: getOptionParam(startDateOption, startDate), startDate: getOptionParam(startDateOption, startDate),
endDate: getOptionParam(endDateOption, endDate), endDate: getOptionParam(endDateOption, endDate),
position: getOptionParam(positionOption, position), position: getOptionParam(positionOption, position),
sortOrderType: getOptionParam(sortOrderTypeOption, sortOrderType),
); );
} }
Filter? mappingToEmailFilterCondition({ Filter? mappingToEmailFilterCondition({
required EmailSortOrderType sortOrderType,
EmailFilterCondition? moreFilterCondition EmailFilterCondition? moreFilterCondition
}) { }) {
final emailEmailFilterConditionShared = EmailFilterCondition( final emailEmailFilterConditionShared = EmailFilterCondition(
text: text?.value.trim().isNotEmpty == true text: text?.value.trim().isNotEmpty == true
? text?.value ? text?.value.trim()
: null, : null,
inMailbox: mailbox?.mailboxId, inMailbox: mailbox?.mailboxId,
after: sortOrderType.isScrollByPosition() after: sortOrderType.isScrollByPosition()
? null ? null
: emailReceiveTimeType.getAfterDate(startDate), : emailReceiveTimeType.getAfterDate(startDate),
hasAttachment: hasAttachment == false ? null : hasAttachment, hasAttachment: !hasAttachment ? null : hasAttachment,
subject: subject?.trim().isNotEmpty == true subject: subject?.trim().isNotEmpty == true
? subject ? subject?.trim()
: null, : null,
before: sortOrderType.isScrollByPosition() before: sortOrderType.isScrollByPosition()
? null ? null
@@ -145,6 +149,17 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin {
} }
} }
bool get isApplied => from.isNotEmpty ||
to.isNotEmpty ||
text?.value.trim().isNotEmpty == true ||
subject?.trim().isNotEmpty == true ||
hasKeyword.isNotEmpty == true ||
notKeyword.isNotEmpty == true ||
emailReceiveTimeType != EmailReceiveTimeType.allTime ||
sortOrderType != EmailSortOrderType.mostRecent ||
(mailbox != null && mailbox?.id != PresentationMailbox.unifiedMailbox.id) ||
hasAttachment;
@override @override
List<Object?> get props => [ List<Object?> get props => [
from, from,
@@ -160,5 +175,6 @@ class SearchEmailFilter with EquatableMixin, OptionParamMixin {
startDate, startDate,
endDate, endDate,
position, position,
sortOrderType
]; ];
} }
@@ -12,11 +12,11 @@ import 'package:tmail_ui_user/main/routes/route_navigation.dart';
class AdvancedSearchFilterFormBottomView extends GetWidget<AdvancedFilterController> { class AdvancedSearchFilterFormBottomView extends GetWidget<AdvancedFilterController> {
final InputFieldFocusManager? focusManager; final InputFieldFocusManager focusManager;
const AdvancedSearchFilterFormBottomView({ const AdvancedSearchFilterFormBottomView({
Key? key, Key? key,
this.focusManager, required this.focusManager,
}) : super(key: key); }) : super(key: key);
@override @override
@@ -30,8 +30,8 @@ class AdvancedSearchFilterFormBottomView extends GetWidget<AdvancedFilterControl
transform: Matrix4.translationValues(-8.0, 0.0, 0.0), transform: Matrix4.translationValues(-8.0, 0.0, 0.0),
child: _buildCheckboxHasAttachment( child: _buildCheckboxHasAttachment(
context, context,
currentFocusNode: focusManager?.attachmentCheckboxFocusNode, currentFocusNode: focusManager.attachmentCheckboxFocusNode,
nextFocusNode: focusManager?.searchButtonFocusNode), nextFocusNode: focusManager.searchButtonFocusNode),
), ),
_buildListButton(context, controller.responsiveUtils), _buildListButton(context, controller.responsiveUtils),
], ],
@@ -67,8 +67,8 @@ class AdvancedSearchFilterFormBottomView extends GetWidget<AdvancedFilterControl
text: AppLocalizations.of(context).search, text: AppLocalizations.of(context).search,
context: context, context: context,
responsiveUtils: responsiveUtils, responsiveUtils: responsiveUtils,
currentFocusNode: focusManager?.searchButtonFocusNode, currentFocusNode: focusManager.searchButtonFocusNode,
nextFocusNode: focusManager?.fromFieldFocusNode nextFocusNode: focusManager.fromFieldFocusNode
), ),
), ),
]); ]);
@@ -99,8 +99,8 @@ class AdvancedSearchFilterFormBottomView extends GetWidget<AdvancedFilterControl
context: context, context: context,
responsiveUtils: responsiveUtils, responsiveUtils: responsiveUtils,
minWidth: 144, minWidth: 144,
currentFocusNode: focusManager?.searchButtonFocusNode, currentFocusNode: focusManager.searchButtonFocusNode,
nextFocusNode: focusManager?.fromFieldFocusNode nextFocusNode: focusManager.fromFieldFocusNode
), ),
]); ]);
} }
@@ -128,7 +128,7 @@ class AdvancedSearchFilterFormBottomView extends GetWidget<AdvancedFilterControl
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
value: controller.hasAttachment.value, value: controller.hasAttachment.value,
activeColor: AppColor.primaryColor, activeColor: AppColor.primaryColor,
onChanged: (value) => controller.hasAttachment.value = value ?? false, onChanged: controller.onHasAttachmentCheckboxChanged,
), ),
), ),
); );
@@ -60,14 +60,22 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
context: context, context: context,
advancedSearchFilterField: AdvancedSearchFilterField.subject, advancedSearchFilterField: AdvancedSearchFilterField.subject,
currentFocusNode: controller.focusManager.subjectFieldFocusNode, currentFocusNode: controller.focusManager.subjectFieldFocusNode,
nextFocusNode: controller.focusManager.hasKeywordFieldFocusNode nextFocusNode: controller.focusManager.hasKeywordFieldFocusNode,
onTextChange: (value) => controller.onTextChanged(
AdvancedSearchFilterField.subject,
value
),
), ),
_buildFilterField( _buildFilterField(
textEditingController: controller.hasKeyWordFilterInputController, textEditingController: controller.hasKeyWordFilterInputController,
context: context, context: context,
advancedSearchFilterField: AdvancedSearchFilterField.hasKeyword, advancedSearchFilterField: AdvancedSearchFilterField.hasKeyword,
currentFocusNode: controller.focusManager.hasKeywordFieldFocusNode, currentFocusNode: controller.focusManager.hasKeywordFieldFocusNode,
nextFocusNode: controller.focusManager.notKeywordFieldFocusNode nextFocusNode: controller.focusManager.notKeywordFieldFocusNode,
onTextChange: (value) => controller.onTextChanged(
AdvancedSearchFilterField.hasKeyword,
value
),
), ),
_buildFilterField( _buildFilterField(
textEditingController: controller.notKeyWordFilterInputController, textEditingController: controller.notKeyWordFilterInputController,
@@ -75,6 +83,10 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
advancedSearchFilterField: AdvancedSearchFilterField.notKeyword, advancedSearchFilterField: AdvancedSearchFilterField.notKeyword,
currentFocusNode: controller.focusManager.notKeywordFieldFocusNode, currentFocusNode: controller.focusManager.notKeywordFieldFocusNode,
nextFocusNode: controller.focusManager.mailboxFieldFocusNode, nextFocusNode: controller.focusManager.mailboxFieldFocusNode,
onTextChange: (value) => controller.onTextChanged(
AdvancedSearchFilterField.notKeyword,
value
),
), ),
_buildFilterField( _buildFilterField(
textEditingController: controller.mailBoxFilterInputController, textEditingController: controller.mailBoxFilterInputController,
@@ -127,7 +139,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
receiveTime.getTitle(context), receiveTime.getTitle(context),
svgIconSelected: controller.imagePaths.icFilterSelected, svgIconSelected: controller.imagePaths.icFilterSelected,
maxWidth: 320, maxWidth: 320,
isSelected: controller.dateFilterSelectedFormAdvancedSearch.value == receiveTime, isSelected: controller.receiveTimeType.value == receiveTime,
onCallbackAction: () => controller.updateReceiveDateSearchFilter(context, receiveTime), onCallbackAction: () => controller.updateReceiveDateSearchFilter(context, receiveTime),
))) )))
.toList(); .toList();
@@ -142,6 +154,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
MouseCursor? mouseCursor, MouseCursor? mouseCursor,
FocusNode? currentFocusNode, FocusNode? currentFocusNode,
FocusNode? nextFocusNode, FocusNode? nextFocusNode,
ValueChanged<String>? onTextChange,
}) { }) {
final child = [ final child = [
SizedBox( SizedBox(
@@ -166,6 +179,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
nextFocusNode: nextFocusNode, nextFocusNode: nextFocusNode,
advancedSearchFilterField: advancedSearchFilterField, advancedSearchFilterField: advancedSearchFilterField,
textEditingController: textEditingController, textEditingController: textEditingController,
onTextChange: onTextChange,
) )
else if (controller.responsiveUtils.landscapeTabletSupported(context)) else if (controller.responsiveUtils.landscapeTabletSupported(context))
if (advancedSearchFilterField == AdvancedSearchFilterField.date) if (advancedSearchFilterField == AdvancedSearchFilterField.date)
@@ -173,13 +187,13 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
controller.imagePaths, controller.imagePaths,
startDate: controller.startDate.value, startDate: controller.startDate.value,
endDate: controller.endDate.value, endDate: controller.endDate.value,
receiveTimeSelected: controller.dateFilterSelectedFormAdvancedSearch.value, receiveTimeSelected: controller.receiveTimeType.value,
onReceiveTimeSelected: (receiveTime) => controller.updateReceiveDateSearchFilter(context, receiveTime), onReceiveTimeSelected: (receiveTime) => controller.updateReceiveDateSearchFilter(context, receiveTime),
)) ))
else if (advancedSearchFilterField == AdvancedSearchFilterField.sortBy) else if (advancedSearchFilterField == AdvancedSearchFilterField.sortBy)
Obx(() => SortByDropDownButton( Obx(() => SortByDropDownButton(
imagePaths: controller.imagePaths, imagePaths: controller.imagePaths,
sortOrderSelected: controller.searchController.sortOrderFiltered.value, sortOrderSelected: controller.sortOrderType.value,
onSortOrderSelected: controller.updateSortOrder, onSortOrderSelected: controller.updateSortOrder,
)) ))
else else
@@ -192,6 +206,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
nextFocusNode: nextFocusNode, nextFocusNode: nextFocusNode,
advancedSearchFilterField: advancedSearchFilterField, advancedSearchFilterField: advancedSearchFilterField,
textEditingController: textEditingController, textEditingController: textEditingController,
onTextChange: onTextChange,
) )
else else
Expanded( Expanded(
@@ -204,6 +219,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
nextFocusNode: nextFocusNode, nextFocusNode: nextFocusNode,
advancedSearchFilterField: advancedSearchFilterField, advancedSearchFilterField: advancedSearchFilterField,
textEditingController: textEditingController, textEditingController: textEditingController,
onTextChange: onTextChange,
), ),
) )
]; ];
@@ -229,6 +245,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
MouseCursor? mouseCursor, MouseCursor? mouseCursor,
FocusNode? currentFocusNode, FocusNode? currentFocusNode,
FocusNode? nextFocusNode, FocusNode? nextFocusNode,
ValueChanged<String>? onTextChange,
}) { }) {
switch (advancedSearchFilterField) { switch (advancedSearchFilterField) {
case AdvancedSearchFilterField.date: case AdvancedSearchFilterField.date:
@@ -236,13 +253,13 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
controller.imagePaths, controller.imagePaths,
startDate: controller.startDate.value, startDate: controller.startDate.value,
endDate: controller.endDate.value, endDate: controller.endDate.value,
receiveTimeSelected: controller.dateFilterSelectedFormAdvancedSearch.value, receiveTimeSelected: controller.receiveTimeType.value,
onReceiveTimeSelected: (receiveTime) => controller.updateReceiveDateSearchFilter(context, receiveTime), onReceiveTimeSelected: (receiveTime) => controller.updateReceiveDateSearchFilter(context, receiveTime),
)); ));
case AdvancedSearchFilterField.sortBy: case AdvancedSearchFilterField.sortBy:
return Obx(() => SortByDropDownButton( return Obx(() => SortByDropDownButton(
imagePaths: controller.imagePaths, imagePaths: controller.imagePaths,
sortOrderSelected: controller.searchController.sortOrderFiltered.value, sortOrderSelected: controller.sortOrderType.value,
onSortOrderSelected: controller.updateSortOrder, onSortOrderSelected: controller.updateSortOrder,
)); ));
default: default:
@@ -255,6 +272,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
nextFocusNode: nextFocusNode, nextFocusNode: nextFocusNode,
advancedSearchFilterField: advancedSearchFilterField, advancedSearchFilterField: advancedSearchFilterField,
textEditingController: textEditingController, textEditingController: textEditingController,
onTextChange: onTextChange,
); );
} }
} }
@@ -268,6 +286,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
MouseCursor? mouseCursor, MouseCursor? mouseCursor,
FocusNode? currentFocusNode, FocusNode? currentFocusNode,
FocusNode? nextFocusNode, FocusNode? nextFocusNode,
ValueChanged<String>? onTextChange,
}) { }) {
return KeyboardListener( return KeyboardListener(
focusNode: currentFocusNode ?? FocusNode(), focusNode: currentFocusNode ?? FocusNode(),
@@ -293,6 +312,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
popBack(); popBack();
} }
}, },
onTextChange: onTextChange,
decoration: InputDecoration( decoration: InputDecoration(
filled: true, filled: true,
fillColor: isSelectFormList ? AppColor.colorItemSelected : Colors.white, fillColor: isSelectFormList ? AppColor.colorItemSelected : Colors.white,
@@ -39,7 +39,7 @@ class IconOpenAdvancedSearchWidget extends StatelessWidget {
height: 16), height: 16),
onTap: () { onTap: () {
log('IconOpenAdvancedSearchWidget::build(): clicked'); log('IconOpenAdvancedSearchWidget::build(): clicked');
advancedFilterController.initSearchFilterField(); advancedFilterController.initSearchFilterField(context);
searchController.openAdvanceSearch(); searchController.openAdvanceSearch();
}), }),
), ),
@@ -137,7 +137,6 @@ class SearchInputFormWidget extends StatelessWidget with AppLoaderMixin {
} }
if (queryString.isNotEmpty || _searchController.listFilterOnSuggestionForm.isNotEmpty) { if (queryString.isNotEmpty || _searchController.listFilterOnSuggestionForm.isNotEmpty) {
_searchController.clearSortOrder();
_searchController.clearSearchFilter(); _searchController.clearSearchFilter();
_searchController.applyFilterSuggestionToSearchFilter(_dashBoardController.sessionCurrent?.username); _searchController.applyFilterSuggestionToSearchFilter(_dashBoardController.sessionCurrent?.username);
_dashBoardController.searchEmailByQueryString(queryString); _dashBoardController.searchEmailByQueryString(queryString);
@@ -158,7 +157,6 @@ class SearchInputFormWidget extends StatelessWidget with AppLoaderMixin {
_searchController.searchInputController.text = recent.value; _searchController.searchInputController.text = recent.value;
_searchController.searchFocus.unfocus(); _searchController.searchFocus.unfocus();
_searchController.enableSearch(); _searchController.enableSearch();
_searchController.clearSortOrder();
_searchController.clearSearchFilter(); _searchController.clearSearchFilter();
_searchController.applyFilterSuggestionToSearchFilter(_dashBoardController.sessionCurrent?.username); _searchController.applyFilterSuggestionToSearchFilter(_dashBoardController.sessionCurrent?.username);
_dashBoardController.searchEmailByQueryString(recent.value); _dashBoardController.searchEmailByQueryString(recent.value);
@@ -257,6 +255,6 @@ class SearchInputFormWidget extends StatelessWidget with AppLoaderMixin {
_searchController.searchInputController.clear(); _searchController.searchInputController.clear();
_searchController.searchFocus.unfocus(); _searchController.searchFocus.unfocus();
_searchController.enableSearch(); _searchController.enableSearch();
_dashBoardController.searchEmailByFromFields(emailAddress); _dashBoardController.quickSearchEmailByFrom(emailAddress);
} }
} }
@@ -218,9 +218,9 @@ class SearchEmailController extends BaseController
suggestionSearchViewState.value = Right(LoadingState()); suggestionSearchViewState.value = Right(LoadingState());
currentSearchText.value = value; currentSearchText.value = value;
_updateSimpleSearchFilter( _updateSimpleSearchFilter(
textOption: value.isNotEmpty ? Some(SearchQuery(value)) : const None(), textOption: option(value.isNotEmpty, SearchQuery(value)),
beforeOption: const None(), beforeOption: const None(),
positionOption: emailSortOrderType.value.isScrollByPosition() ? const Some(0) : const None() positionOption: option(searchEmailFilter.value.sortOrderType.isScrollByPosition(), 0)
); );
if (value.isNotEmpty && session != null && accountId != null) { if (value.isNotEmpty && session != null && accountId != null) {
final tupleListSuggestion = await Future.wait([ final tupleListSuggestion = await Future.wait([
@@ -297,15 +297,15 @@ class SearchEmailController extends BaseController
: ThreadConstants.defaultLimit; : ThreadConstants.defaultLimit;
_updateSimpleSearchFilter( _updateSimpleSearchFilter(
beforeOption: const None(), beforeOption: const None(),
positionOption: emailSortOrderType.value.isScrollByPosition() ? const Some(0) : const None() positionOption: option(searchEmailFilter.value.sortOrderType.isScrollByPosition(), 0)
); );
consumeState(_refreshChangesSearchEmailInteractor.execute( consumeState(_refreshChangesSearchEmailInteractor.execute(
session!, session!,
accountId!, accountId!,
limit: limit, limit: limit,
position: searchEmailFilter.value.position, position: searchEmailFilter.value.position,
sort: emailSortOrderType.value.getSortOrder().toNullable(), sort: searchEmailFilter.value.sortOrderType.getSortOrder().toNullable(),
filter: searchEmailFilter.value.mappingToEmailFilterCondition(sortOrderType: emailSortOrderType.value), filter: searchEmailFilter.value.mappingToEmailFilterCondition(),
properties: EmailUtils.getPropertiesForEmailGetMethod(session!, accountId!), properties: EmailUtils.getPropertiesForEmailGetMethod(session!, accountId!),
)); ));
} }
@@ -347,8 +347,8 @@ class SearchEmailController extends BaseController
session, session,
accountId, accountId,
limit: UnsignedInt(5), limit: UnsignedInt(5),
sort: emailSortOrderType.value.getSortOrder().toNullable(), sort: searchEmailFilter.value.sortOrderType.getSortOrder().toNullable(),
filter: searchEmailFilter.value.mappingToEmailFilterCondition(sortOrderType: emailSortOrderType.value), filter: searchEmailFilter.value.mappingToEmailFilterCondition(),
properties: ThreadConstants.propertiesQuickSearch) properties: ThreadConstants.propertiesQuickSearch)
.then((result) => result.fold( .then((result) => result.fold(
(failure) => <PresentationEmail>[], (failure) => <PresentationEmail>[],
@@ -389,9 +389,7 @@ class SearchEmailController extends BaseController
} }
_updateSimpleSearchFilter( _updateSimpleSearchFilter(
positionOption: emailSortOrderType.value.isScrollByPosition() positionOption: option(searchEmailFilter.value.sortOrderType.isScrollByPosition(), 0),
? const Some(0)
: const None(),
beforeOption: const None(), beforeOption: const None(),
); );
@@ -400,8 +398,8 @@ class SearchEmailController extends BaseController
accountId!, accountId!,
limit: ThreadConstants.defaultLimit, limit: ThreadConstants.defaultLimit,
position: searchEmailFilter.value.position, position: searchEmailFilter.value.position,
sort: emailSortOrderType.value.getSortOrder().toNullable(), sort: searchEmailFilter.value.sortOrderType.getSortOrder().toNullable(),
filter: searchEmailFilter.value.mappingToEmailFilterCondition(sortOrderType: emailSortOrderType.value), filter: searchEmailFilter.value.mappingToEmailFilterCondition(),
properties: EmailUtils.getPropertiesForEmailGetMethod(session!, accountId!), properties: EmailUtils.getPropertiesForEmailGetMethod(session!, accountId!),
)); ));
} }
@@ -440,12 +438,12 @@ class SearchEmailController extends BaseController
if (canSearchMore && session != null && accountId != null) { if (canSearchMore && session != null && accountId != null) {
final lastEmail = listResultSearch.last; final lastEmail = listResultSearch.last;
if (emailSortOrderType.value.isScrollByPosition()) { if (searchEmailFilter.value.sortOrderType.isScrollByPosition()) {
_updateSimpleSearchFilter( _updateSimpleSearchFilter(
positionOption: Some(listResultSearch.length), positionOption: Some(listResultSearch.length),
beforeOption: const None() beforeOption: const None()
); );
} else if (emailSortOrderType.value == EmailSortOrderType.oldest) { } else if (searchEmailFilter.value.sortOrderType == EmailSortOrderType.oldest) {
_updateSimpleSearchFilter(startDateOption: optionOf(lastEmail.receivedAt)); _updateSimpleSearchFilter(startDateOption: optionOf(lastEmail.receivedAt));
} else { } else {
_updateSimpleSearchFilter(beforeOption: optionOf(lastEmail.receivedAt)); _updateSimpleSearchFilter(beforeOption: optionOf(lastEmail.receivedAt));
@@ -455,9 +453,9 @@ class SearchEmailController extends BaseController
session!, session!,
accountId!, accountId!,
limit: ThreadConstants.defaultLimit, limit: ThreadConstants.defaultLimit,
sort: emailSortOrderType.value.getSortOrder().toNullable(), sort: searchEmailFilter.value.sortOrderType.getSortOrder().toNullable(),
position: searchEmailFilter.value.position, position: searchEmailFilter.value.position,
filter: searchEmailFilter.value.mappingToEmailFilterCondition(sortOrderType: emailSortOrderType.value), filter: searchEmailFilter.value.mappingToEmailFilterCondition(),
properties: EmailUtils.getPropertiesForEmailGetMethod(session!, accountId!), properties: EmailUtils.getPropertiesForEmailGetMethod(session!, accountId!),
lastEmailId: lastEmail.id lastEmailId: lastEmail.id
)); ));
@@ -576,9 +574,6 @@ class SearchEmailController extends BaseController
onCallbackAction: (newStartDate, newEndDate) { onCallbackAction: (newStartDate, newEndDate) {
_updateSimpleSearchFilter( _updateSimpleSearchFilter(
emailReceiveTimeTypeOption: Some(emailReceiveTimeType), emailReceiveTimeTypeOption: Some(emailReceiveTimeType),
textOption: searchQuery == null
? Some(SearchQuery.initial())
: optionOf(searchEmailFilter.value.text),
startDateOption: optionOf(newStartDate?.toUTCDate()), startDateOption: optionOf(newStartDate?.toUTCDate()),
endDateOption: optionOf(newEndDate?.toUTCDate()), endDateOption: optionOf(newEndDate?.toUTCDate()),
); );
@@ -590,9 +585,6 @@ class SearchEmailController extends BaseController
} else { } else {
_updateSimpleSearchFilter( _updateSimpleSearchFilter(
emailReceiveTimeTypeOption: Some(emailReceiveTimeType), emailReceiveTimeTypeOption: Some(emailReceiveTimeType),
textOption: searchQuery == null
? Some(SearchQuery.initial())
: optionOf(searchEmailFilter.value.text),
startDateOption: const None(), startDateOption: const None(),
endDateOption: const None(), endDateOption: const None(),
); );
@@ -605,6 +597,7 @@ class SearchEmailController extends BaseController
void selectSortOrderQuickSearchFilter(BuildContext context, EmailSortOrderType sortOrderType) { void selectSortOrderQuickSearchFilter(BuildContext context, EmailSortOrderType sortOrderType) {
popBack(); popBack();
emailSortOrderType.value = sortOrderType; emailSortOrderType.value = sortOrderType;
_updateSimpleSearchFilter(sortOrderTypeOption: Some(sortOrderType));
_searchEmailAction(context); _searchEmailAction(context);
} }
@@ -702,6 +695,7 @@ class SearchEmailController extends BaseController
Option<UTCDate>? endDateOption, Option<UTCDate>? endDateOption,
Option<int>? positionOption, Option<int>? positionOption,
Option<Set<String>>? hasKeywordOption, Option<Set<String>>? hasKeywordOption,
Option<EmailSortOrderType>? sortOrderTypeOption,
}) { }) {
searchEmailFilter.value = searchEmailFilter.value.copyWith( searchEmailFilter.value = searchEmailFilter.value.copyWith(
fromOption: fromOption, fromOption: fromOption,
@@ -715,6 +709,7 @@ class SearchEmailController extends BaseController
endDateOption: endDateOption, endDateOption: endDateOption,
positionOption: positionOption, positionOption: positionOption,
hasKeywordOption: hasKeywordOption, hasKeywordOption: hasKeywordOption,
sortOrderTypeOption: sortOrderTypeOption,
); );
searchEmailFilter.refresh(); searchEmailFilter.refresh();
} }
@@ -947,9 +942,6 @@ class SearchEmailController extends BaseController
void _deleteDateTimeSearchFilter(BuildContext context) { void _deleteDateTimeSearchFilter(BuildContext context) {
_updateSimpleSearchFilter( _updateSimpleSearchFilter(
emailReceiveTimeTypeOption: const Some(EmailReceiveTimeType.allTime), emailReceiveTimeTypeOption: const Some(EmailReceiveTimeType.allTime),
textOption: searchQuery == null
? Some(SearchQuery.initial())
: optionOf(searchEmailFilter.value.text),
startDateOption: const None(), startDateOption: const None(),
endDateOption: const None(), endDateOption: const None(),
); );
@@ -959,6 +951,7 @@ class SearchEmailController extends BaseController
void _deleteSortOrderSearchFilter(BuildContext context) { void _deleteSortOrderSearchFilter(BuildContext context) {
emailSortOrderType.value = EmailSortOrderType.mostRecent; emailSortOrderType.value = EmailSortOrderType.mostRecent;
_updateSimpleSearchFilter(sortOrderTypeOption: const Some(EmailSortOrderType.mostRecent));
_searchEmailAction(context); _searchEmailAction(context);
} }
@@ -987,16 +980,6 @@ class SearchEmailController extends BaseController
_searchEmailAction(context); _searchEmailAction(context);
} }
bool get isSearchFilterHasApplied {
return searchEmailFilter.value.from.isNotEmpty ||
searchEmailFilter.value.to.isNotEmpty ||
searchEmailFilter.value.emailReceiveTimeType != EmailReceiveTimeType.allTime ||
(searchEmailFilter.value.mailbox != PresentationMailbox.unifiedMailbox && searchEmailFilter.value.mailbox != null) ||
searchEmailFilter.value.hasAttachment == true ||
searchEmailFilter.value.hasKeyword.contains(KeyWordIdentifier.emailFlagged.value) ||
emailSortOrderType.value != EmailSortOrderType.mostRecent;
}
void clearAllSearchFilterApplied(BuildContext context) { void clearAllSearchFilterApplied(BuildContext context) {
textInputSearchController.clear(); textInputSearchController.clear();
currentSearchText.value = ''; currentSearchText.value = '';
@@ -217,7 +217,7 @@ class SearchEmailView extends GetWidget<SearchEmailController>
), ),
), ),
Obx(() { Obx(() {
if (controller.isSearchFilterHasApplied) { if (controller.searchEmailFilter.value.isApplied) {
return TMailButtonWidget.fromText( return TMailButtonWidget.fromText(
text: AppLocalizations.of(context).clearFilter, text: AppLocalizations.of(context).clearFilter,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
@@ -273,12 +273,6 @@ class ThreadController extends BaseController with EmailActionController {
} }
}); });
ever(searchController.isAdvancedSearchViewOpen, (hasOpen) {
if (hasOpen == true) {
mailboxDashBoardController.filterMessageOption.value = FilterMessageOption.all;
}
});
ever(mailboxDashBoardController.dashBoardAction, (action) { ever(mailboxDashBoardController.dashBoardAction, (action) {
if (action is SelectionAllEmailAction) { if (action is SelectionAllEmailAction) {
setSelectAllEmailAction(); setSelectAllEmailAction();
@@ -302,8 +296,7 @@ class ThreadController extends BaseController with EmailActionController {
); );
mailboxDashBoardController.clearDashBoardAction(); mailboxDashBoardController.clearDashBoardAction();
} else if (action is StartSearchEmailAction } else if (action is StartSearchEmailAction
|| action is ClearAdvancedSearchFilterEmailAction || action is ClearAdvancedSearchFilterEmailAction) {
|| action is StartSearchEmailBySearchFilterAction) {
cancelSelectEmail(); cancelSelectEmail();
_replaceBrowserHistory(); _replaceBrowserHistory();
_searchEmail(); _searchEmail();
@@ -514,7 +507,7 @@ class ThreadController extends BaseController with EmailActionController {
_session!, _session!,
_accountId!, _accountId!,
limit: ThreadConstants.defaultLimit, limit: ThreadConstants.defaultLimit,
sort: searchController.sortOrderFiltered.value.getSortOrder().toNullable(), sort: EmailSortOrderType.mostRecent.getSortOrder().toNullable(),
emailFilter: EmailFilter( emailFilter: EmailFilter(
filter: _getFilterCondition(mailboxIdSelected: selectedMailboxId), filter: _getFilterCondition(mailboxIdSelected: selectedMailboxId),
filterOption: mailboxDashBoardController.filterMessageOption.value, filterOption: mailboxDashBoardController.filterMessageOption.value,
@@ -594,7 +587,7 @@ class ThreadController extends BaseController with EmailActionController {
_session!, _session!,
_accountId!, _accountId!,
newEmailState, newEmailState,
sort: searchController.sortOrderFiltered.value.getSortOrder().toNullable(), sort: EmailSortOrderType.mostRecent.getSortOrder().toNullable(),
propertiesCreated: EmailUtils.getPropertiesForEmailGetMethod(_session!, _accountId!), propertiesCreated: EmailUtils.getPropertiesForEmailGetMethod(_session!, _accountId!),
propertiesUpdated: ThreadConstants.propertiesUpdatedDefault, propertiesUpdated: ThreadConstants.propertiesUpdatedDefault,
emailFilter: EmailFilter( emailFilter: EmailFilter(
@@ -619,8 +612,7 @@ class ThreadController extends BaseController with EmailActionController {
_session!, _session!,
_accountId!, _accountId!,
limit: ThreadConstants.defaultLimit, limit: ThreadConstants.defaultLimit,
position: _searchEmailFilter.position, sort: EmailSortOrderType.mostRecent.getSortOrder().toNullable(),
sort: searchController.sortOrderFiltered.value.getSortOrder().toNullable(),
filterOption: mailboxDashBoardController.filterMessageOption.value, filterOption: mailboxDashBoardController.filterMessageOption.value,
filter: _getFilterCondition(oldestEmail: oldestEmail, mailboxIdSelected: selectedMailboxId), filter: _getFilterCondition(oldestEmail: oldestEmail, mailboxIdSelected: selectedMailboxId),
properties: EmailUtils.getPropertiesForEmailGetMethod(_session!, _accountId!), properties: EmailUtils.getPropertiesForEmailGetMethod(_session!, _accountId!),
@@ -819,9 +811,7 @@ class ThreadController extends BaseController with EmailActionController {
canSearchMore = false; canSearchMore = false;
searchController.updateFilterEmail( searchController.updateFilterEmail(
positionOption: searchController.sortOrderFiltered.value.isScrollByPosition() positionOption: option(_searchEmailFilter.sortOrderType.isScrollByPosition(), 0),
? const Some(0)
: const None(),
beforeOption: const None()); beforeOption: const None());
searchController.activateSimpleSearch(); searchController.activateSimpleSearch();
@@ -831,9 +821,8 @@ class ThreadController extends BaseController with EmailActionController {
_accountId!, _accountId!,
limit: limit ?? ThreadConstants.defaultLimit, limit: limit ?? ThreadConstants.defaultLimit,
position: _searchEmailFilter.position, position: _searchEmailFilter.position,
sort: searchController.sortOrderFiltered.value.getSortOrder().toNullable(), sort: _searchEmailFilter.sortOrderType.getSortOrder().toNullable(),
filter: _searchEmailFilter.mappingToEmailFilterCondition( filter: _searchEmailFilter.mappingToEmailFilterCondition(
sortOrderType: searchController.sortOrderFiltered.value,
moreFilterCondition: _getFilterCondition() moreFilterCondition: _getFilterCondition()
), ),
properties: EmailUtils.getPropertiesForEmailGetMethod(_session!, _accountId!), properties: EmailUtils.getPropertiesForEmailGetMethod(_session!, _accountId!),
@@ -891,14 +880,14 @@ class ThreadController extends BaseController with EmailActionController {
? mailboxDashBoardController.emailsInCurrentMailbox.last ? mailboxDashBoardController.emailsInCurrentMailbox.last
: null; : null;
if (searchController.sortOrderFiltered.value.isScrollByPosition()) { if (_searchEmailFilter.sortOrderType.isScrollByPosition()) {
final nextPosition = mailboxDashBoardController.emailsInCurrentMailbox.length; final nextPosition = mailboxDashBoardController.emailsInCurrentMailbox.length;
log('ThreadController::_searchMoreEmails:nextPosition: $nextPosition'); log('ThreadController::_searchMoreEmails:nextPosition: $nextPosition');
searchController.updateFilterEmail( searchController.updateFilterEmail(
positionOption: Some(nextPosition), positionOption: Some(nextPosition),
beforeOption: const None() beforeOption: const None()
); );
} else if (searchController.sortOrderFiltered.value == EmailSortOrderType.oldest) { } else if (_searchEmailFilter.sortOrderType == EmailSortOrderType.oldest) {
searchController.updateFilterEmail(startDateOption: optionOf(lastEmail?.receivedAt)); searchController.updateFilterEmail(startDateOption: optionOf(lastEmail?.receivedAt));
} else { } else {
searchController.updateFilterEmail(beforeOption: optionOf(lastEmail?.receivedAt)); searchController.updateFilterEmail(beforeOption: optionOf(lastEmail?.receivedAt));
@@ -908,10 +897,9 @@ class ThreadController extends BaseController with EmailActionController {
_session!, _session!,
_accountId!, _accountId!,
limit: ThreadConstants.defaultLimit, limit: ThreadConstants.defaultLimit,
sort: searchController.sortOrderFiltered.value.getSortOrder().toNullable(), sort: _searchEmailFilter.sortOrderType.getSortOrder().toNullable(),
position: _searchEmailFilter.position, position: _searchEmailFilter.position,
filter: _searchEmailFilter.mappingToEmailFilterCondition( filter: _searchEmailFilter.mappingToEmailFilterCondition(
sortOrderType: searchController.sortOrderFiltered.value,
moreFilterCondition: _getFilterCondition() moreFilterCondition: _getFilterCondition()
), ),
properties: EmailUtils.getPropertiesForEmailGetMethod(_session!, _accountId!), properties: EmailUtils.getPropertiesForEmailGetMethod(_session!, _accountId!),
@@ -2,7 +2,7 @@ import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:model/mailbox/expand_mode.dart'; import 'package:model/mailbox/expand_mode.dart';
import 'package:model/extensions/email_address_extension.dart'; import 'package:model/extensions/email_address_extension.dart';
extension ListEmailAddressExtension on Set<EmailAddress>? { extension SetEmailAddressExtension on Set<EmailAddress>? {
List<String>? getListAddress() => this?.map((emailAddress) => emailAddress.emailAddress).toList(); List<String>? getListAddress() => this?.map((emailAddress) => emailAddress.emailAddress).toList();
@@ -35,3 +35,7 @@ extension ListEmailAddressExtension on Set<EmailAddress>? {
: List.empty(); : List.empty();
} }
} }
extension ListEmailAddressExtension on List<EmailAddress> {
Set<String> asSetAddress() => map((emailAddress) => emailAddress.emailAddress).toSet();
}
@@ -0,0 +1,330 @@
import 'package:core/data/network/config/dynamic_url_interceptors.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/app_toast.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/utils/application_manager.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/caching/caching_manager.dart';
import 'package:tmail_ui_user/features/composer/domain/usecases/get_autocomplete_interactor.dart';
import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_all_recent_search_latest_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/quick_search_email_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_recent_search_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart';
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/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/log_out_oidc_interactor.dart';
import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
import 'package:tmail_ui_user/main/bindings/network/binding_tag.dart';
import 'package:tmail_ui_user/main/utils/toast_manager.dart';
import 'package:uuid/uuid.dart';
import 'advanced_filter_controller_test.mocks.dart';
mockControllerCallback() => InternalFinalCallback<void>(callback: () {});
const fallbackGenerators = {
#onStart: mockControllerCallback,
#onDelete: mockControllerCallback,
};
@GenerateNiceMocks([
// Base controller mock specs
MockSpec<CachingManager>(),
MockSpec<LanguageCacheManager>(),
MockSpec<AuthorizationInterceptors>(),
MockSpec<DynamicUrlInterceptors>(),
MockSpec<DeleteCredentialInteractor>(),
MockSpec<LogoutOidcInteractor>(),
MockSpec<DeleteAuthorityOidcInteractor>(),
MockSpec<AppToast>(),
MockSpec<ImagePaths>(),
MockSpec<ResponsiveUtils>(),
MockSpec<Uuid>(),
MockSpec<ApplicationManager>(),
MockSpec<ToastManager>(),
// Advanced filter controller mock specs
MockSpec<MailboxDashBoardController>(fallbackGenerators: fallbackGenerators),
MockSpec<GetAutoCompleteInteractor>(),
MockSpec<BuildContext>(),
// Search controller mock specs
MockSpec<QuickSearchEmailInteractor>(),
MockSpec<SaveRecentSearchInteractor>(),
MockSpec<GetAllRecentSearchLatestInteractor>(),
])
void main() {
// Declaration advanced filter controller
late AdvancedFilterController advancedFilterController;
late MockMailboxDashBoardController mockMailboxDashBoardController;
late MockGetAutoCompleteInteractor mockGetAutoCompleteInteractor;
late MockBuildContext mockBuildContext;
// Declaration search controller
late SearchController searchController;
late MockQuickSearchEmailInteractor mockQuickSearchEmailInteractor;
late MockSaveRecentSearchInteractor mockSaveRecentSearchInteractor;
late MockGetAllRecentSearchLatestInteractor mockGetAllRecentSearchLatestInteractor;
// Declaration base controller
late MockCachingManager mockCachingManager;
late MockLanguageCacheManager mockLanguageCacheManager;
late MockAuthorizationInterceptors mockAuthorizationInterceptors;
late MockDynamicUrlInterceptors mockDynamicUrlInterceptors;
late MockDeleteCredentialInteractor mockDeleteCredentialInteractor;
late MockLogoutOidcInteractor mockLogoutOidcInteractor;
late MockDeleteAuthorityOidcInteractor mockDeleteAuthorityOidcInteractor;
late MockAppToast mockAppToast;
late MockImagePaths mockImagePaths;
late MockResponsiveUtils mockResponsiveUtils;
late MockUuid mockUuid;
late MockApplicationManager mockApplicationManager;
late MockToastManager mockToastManager;
setUpAll(() {
Get.testMode = true;
// Mock base controller
mockCachingManager = MockCachingManager();
mockLanguageCacheManager = MockLanguageCacheManager();
mockAuthorizationInterceptors = MockAuthorizationInterceptors();
mockDynamicUrlInterceptors = MockDynamicUrlInterceptors();
mockDeleteCredentialInteractor = MockDeleteCredentialInteractor();
mockLogoutOidcInteractor = MockLogoutOidcInteractor();
mockDeleteAuthorityOidcInteractor = MockDeleteAuthorityOidcInteractor();
mockAppToast = MockAppToast();
mockImagePaths = MockImagePaths();
mockResponsiveUtils = MockResponsiveUtils();
mockUuid = MockUuid();
mockApplicationManager = MockApplicationManager();
mockToastManager = MockToastManager();
Get.put<CachingManager>(mockCachingManager);
Get.put<LanguageCacheManager>(mockLanguageCacheManager);
Get.put<AuthorizationInterceptors>(mockAuthorizationInterceptors);
Get.put<AuthorizationInterceptors>(
mockAuthorizationInterceptors,
tag: BindingTag.isolateTag,
);
Get.put<DynamicUrlInterceptors>(mockDynamicUrlInterceptors);
Get.put<DeleteCredentialInteractor>(mockDeleteCredentialInteractor);
Get.put<LogoutOidcInteractor>(mockLogoutOidcInteractor);
Get.put<DeleteAuthorityOidcInteractor>(mockDeleteAuthorityOidcInteractor);
Get.put<AppToast>(mockAppToast);
Get.put<ImagePaths>(mockImagePaths);
Get.put<ResponsiveUtils>(mockResponsiveUtils);
Get.put<Uuid>(mockUuid);
Get.put<ApplicationManager>(mockApplicationManager);
Get.put<ToastManager>(mockToastManager);
// Mock search controller
mockQuickSearchEmailInteractor = MockQuickSearchEmailInteractor();
mockSaveRecentSearchInteractor = MockSaveRecentSearchInteractor();
mockGetAllRecentSearchLatestInteractor = MockGetAllRecentSearchLatestInteractor();
searchController = SearchController(
mockQuickSearchEmailInteractor,
mockSaveRecentSearchInteractor,
mockGetAllRecentSearchLatestInteractor);
Get.put<SearchController>(searchController);
// Mock advanced filter controller
mockMailboxDashBoardController = MockMailboxDashBoardController();
mockGetAutoCompleteInteractor = MockGetAutoCompleteInteractor();
mockBuildContext = MockBuildContext();
Get.put<MailboxDashBoardController>(mockMailboxDashBoardController);
Get.put<GetAutoCompleteInteractor>(mockGetAutoCompleteInteractor);
advancedFilterController = AdvancedFilterController();
});
group('AdvancedFilterController::test', () {
group('applyAdvancedSearchFilter::test', () {
test('SHOULD make sure memory search filter and search filter should be the same after applying', () async {
// Arrange
advancedFilterController.hasKeyWordFilterInputController.text = 'Hello';
advancedFilterController.notKeyWordFilterInputController.text = 'dab';
advancedFilterController.listFromEmailAddress = [EmailAddress(null, 'user1@example.com')];
advancedFilterController.listToEmailAddress = [EmailAddress(null, 'user2@example.com')];
advancedFilterController.sortOrderType.value = EmailSortOrderType.oldest;
advancedFilterController.setDestinationMailboxSelected(PresentationMailbox.unifiedMailbox);
advancedFilterController.subjectFilterInputController.text = 'Subject';
advancedFilterController.receiveTimeType.value = EmailReceiveTimeType.last7Days;
advancedFilterController.hasAttachment.value = true;
// Act
advancedFilterController.applyAdvancedSearchFilter();
await untilCalled(mockMailboxDashBoardController.handleAdvancedSearchEmail());
final memorySearchFilter = advancedFilterController.memorySearchFilter;
final searchFilter = searchController.searchEmailFilter.value;
// Assert
verify(mockMailboxDashBoardController.handleAdvancedSearchEmail()).called(1);
expect(memorySearchFilter, equals(searchFilter));
});
});
group('initSearchFilterField::test', () {
test(
'SHOULD make sure the values of the variables in the controller are the same as the values of the MemorySearchFilter\n'
'WHEN initSearchFilterField is called',
() async {
// Arrange
final memorySearchFilter = SearchEmailFilter(
text: SearchQuery('hello'),
subject: 'subject',
notKeyword: {'hello', 'nice'},
emailReceiveTimeType: EmailReceiveTimeType.last7Days,
sortOrderType: EmailSortOrderType.oldest,
mailbox: PresentationMailbox(
MailboxId(Id('mailbox1')),
name: MailboxName('mailbox1')
),
hasAttachment: true,
from: {'user1@example.com'},
to: {'user2@example.com'},
);
advancedFilterController.setMemorySearchFilter(memorySearchFilter);
// Act
advancedFilterController.initSearchFilterField(mockBuildContext);
// Assert
expect(advancedFilterController.subjectFilterInputController.text, equals('subject'));
expect(advancedFilterController.hasKeyWordFilterInputController.text, equals('hello'));
expect(advancedFilterController.notKeyWordFilterInputController.text, equals('hello,nice'));
expect(advancedFilterController.receiveTimeType.value, equals(EmailReceiveTimeType.last7Days));
expect(advancedFilterController.sortOrderType.value, equals(EmailSortOrderType.oldest));
expect(advancedFilterController.mailBoxFilterInputController.text, equals('mailbox1'));
expect(advancedFilterController.hasAttachment.value, equals(true));
expect(advancedFilterController.listFromEmailAddress, equals([EmailAddress(null, 'user1@example.com')]));
expect(advancedFilterController.listToEmailAddress, equals([EmailAddress(null, 'user2@example.com')]));
});
});
group('onTextChanged::test', () {
test(
'SHOULD update memory search filter for subject\n'
'WHEN onTextChanged called with AdvancedSearchFilterField is Subject',
() {
// Arrange
const filterField = AdvancedSearchFilterField.subject;
const value = 'Subject';
// Act
advancedFilterController.onTextChanged(filterField, value);
// Assert
expect(
advancedFilterController.memorySearchFilter.subject,
'Subject');
});
test(
'SHOULD update memory search filter for text\n'
'WHEN onTextChanged called with AdvancedSearchFilterField is hasKeyword',
() {
// Arrange
const filterField = AdvancedSearchFilterField.hasKeyword;
const value = 'keyword';
// Act
advancedFilterController.onTextChanged(filterField, value);
// Assert
expect(
advancedFilterController.memorySearchFilter.text,
SearchQuery('keyword'));
});
test(
'SHOULD update memory search filter for notKeyword\n'
'WHEN onTextChanged called with AdvancedSearchFilterField is notKeyword',
() {
// Arrange
const filterField = AdvancedSearchFilterField.notKeyword;
const value = 'keyword1,keyword2';
// Act
advancedFilterController.onTextChanged(filterField, value);
// Assert
expect(
advancedFilterController.memorySearchFilter.notKeyword,
{'keyword1','keyword2'});
});
test(
'SHOULD update memory search filter for subject is null\n'
'WHEN onTextChanged called with AdvancedSearchFilterField is subject',
() {
// Arrange
const filterField = AdvancedSearchFilterField.subject;
const value = ' ';
// Act
advancedFilterController.onTextChanged(filterField, value);
// Assert
expect(
advancedFilterController.memorySearchFilter.subject,
isNull);
});
test(
'SHOULD update memory search filter for notKeyword is empty values\n'
'WHEN onTextChanged called with AdvancedSearchFilterField is notKeyword',
() {
// Arrange
const filterField = AdvancedSearchFilterField.notKeyword;
const value = ' ';
// Act
advancedFilterController.onTextChanged(filterField, value);
// Assert
expect(
advancedFilterController.memorySearchFilter.notKeyword,
<String>{});
});
test(
'SHOULD update memory search filter for subject and notKeyword is empty values\n'
'WHEN onTextChanged called with AdvancedSearchFilterField are subject and notKeyword',
() {
// Arrange
const filterFieldSubject = AdvancedSearchFilterField.subject;
const filterFieldNotKeyword = AdvancedSearchFilterField.notKeyword;
const validSubject = 'Subject';
const emptyNotKeyword = ' ';
// Act
advancedFilterController.onTextChanged(filterFieldSubject, validSubject);
advancedFilterController.onTextChanged(filterFieldNotKeyword, emptyNotKeyword);
// Assert
expect(
advancedFilterController.memorySearchFilter.subject,
'Subject');
expect(
advancedFilterController.memorySearchFilter.notKeyword,
<String>{});
});
});
});
}
@@ -400,7 +400,7 @@ void main() {
// expect sort in search controller update as expected // expect sort in search controller update as expected
mailboxDashboardController.selectSortOrderQuickSearchFilter( mailboxDashboardController.selectSortOrderQuickSearchFilter(
EmailSortOrderType.oldest); EmailSortOrderType.oldest);
expect(searchController.sortOrderFiltered.value, EmailSortOrderType.oldest); expect(searchController.sortOrderFiltered, EmailSortOrderType.oldest);
// expect filter in search controller update as expected // expect filter in search controller update as expected
mailboxDashboardController.selectHasAttachmentSearchFilter(); mailboxDashboardController.selectHasAttachmentSearchFilter();
@@ -418,7 +418,7 @@ void main() {
emailFilter: anyNamed('emailFilter'), emailFilter: anyNamed('emailFilter'),
propertiesCreated: anyNamed('propertiesCreated'), propertiesCreated: anyNamed('propertiesCreated'),
propertiesUpdated: anyNamed('propertiesUpdated'))); propertiesUpdated: anyNamed('propertiesUpdated')));
expect(searchController.sortOrderFiltered.value, EmailSortOrderType.mostRecent); expect(searchController.sortOrderFiltered, EmailSortOrderType.mostRecent);
expect(searchController.searchEmailFilter.value, SearchEmailFilter.initial()); expect(searchController.searchEmailFilter.value, SearchEmailFilter.initial());
verify(getEmailsInMailboxInteractor.execute( verify(getEmailsInMailboxInteractor.execute(
testSession, testAccountId, testSession, testAccountId,
@@ -459,6 +459,7 @@ void main() {
from: {fromEmailAddress.email!}, from: {fromEmailAddress.email!},
to: {toEmailAddress.email!}, to: {toEmailAddress.email!},
subject: emailSubject, subject: emailSubject,
sortOrderType: EmailSortOrderType.relevance,
text: SearchQuery(emailContainsWord), text: SearchQuery(emailContainsWord),
notKeyword: {emailNotContainsWord}, notKeyword: {emailNotContainsWord},
emailReceiveTimeType: EmailReceiveTimeType.last30Days, emailReceiveTimeType: EmailReceiveTimeType.last30Days,
@@ -474,7 +475,7 @@ void main() {
emailFilter: anyNamed('emailFilter'), emailFilter: anyNamed('emailFilter'),
propertiesCreated: anyNamed('propertiesCreated'), propertiesCreated: anyNamed('propertiesCreated'),
propertiesUpdated: anyNamed('propertiesUpdated'))); propertiesUpdated: anyNamed('propertiesUpdated')));
expect(searchController.sortOrderFiltered.value, EmailSortOrderType.mostRecent); expect(searchController.sortOrderFiltered, EmailSortOrderType.mostRecent);
expect(searchController.searchEmailFilter.value, SearchEmailFilter.initial()); expect(searchController.searchEmailFilter.value, SearchEmailFilter.initial());
verify(getEmailsInMailboxInteractor.execute( verify(getEmailsInMailboxInteractor.execute(
testSession, testAccountId, testSession, testAccountId,