TF-1477 Improve UX for advanced and quick search

This commit is contained in:
dab246
2023-03-03 14:20:48 +07:00
committed by Dat Vu
parent a0b51ae22d
commit b3ec90b507
20 changed files with 764 additions and 503 deletions
@@ -0,0 +1,93 @@
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/app_toast.dart';
import 'package:flutter/material.dart';
import 'package:flutter_date_range_picker/multiple_view_date_range_picker.dart';
import 'package:get/get.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
mixin DateRangePickerMixin {
void showMultipleViewDateRangePicker(
BuildContext context,
DateTime? initStartDate,
DateTime? initEndDate,
{Function(DateTime? startDate, DateTime? endDate)? onCallbackAction}
) {
showGeneralDialog(
context: context,
barrierDismissible: true,
barrierLabel: '',
barrierColor: Colors.black54,
pageBuilder: (context, _, __) {
return Dialog(
elevation: 0,
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(18.0)),
child: PointerInterceptor(child: MultipleViewDateRangePicker(
confirmText: AppLocalizations.of(context).setDate,
cancelText: AppLocalizations.of(context).cancel,
last7daysTitle: AppLocalizations.of(context).last7Days,
last30daysTitle: AppLocalizations.of(context).last30Days,
last6monthsTitle: AppLocalizations.of(context).last6Months,
lastYearTitle: AppLocalizations.of(context).lastYears,
startDate: initStartDate,
endDate: initEndDate,
setDateActionCallback: ({startDate, endDate}) =>
_handleSelectDateRangeResult(
context,
startDate,
endDate,
onCallbackAction: onCallbackAction
)
))
);
}
);
}
void _handleSelectDateRangeResult(
BuildContext context,
DateTime? startDate,
DateTime? endDate,
{Function(DateTime? startDate, DateTime? endDate)? onCallbackAction}
) {
final _appToast = Get.find<AppToast>();
final _imagePaths = Get.find<ImagePaths>();
if (startDate == null) {
_appToast.showToastWithIcon(
context,
textColor: Colors.black,
message: AppLocalizations.of(context).toastMessageErrorWhenSelectStartDateIsEmpty,
icon: _imagePaths.icNotConnection
);
return;
}
if (endDate == null) {
_appToast.showToastWithIcon(
context,
textColor: Colors.black,
message: AppLocalizations.of(context).toastMessageErrorWhenSelectEndDateIsEmpty,
icon: _imagePaths.icNotConnection
);
return;
}
if (endDate.isBefore(startDate)) {
_appToast.showToastWithIcon(
context,
textColor: Colors.black,
message: AppLocalizations.of(context).toastMessageErrorWhenSelectDateIsInValid,
icon: _imagePaths.icNotConnection
);
return;
}
popBack();
onCallbackAction?.call(startDate, endDate);
}
}
@@ -28,7 +28,7 @@ class DropDownButtonWidget<T> extends StatelessWidget {
final double radiusButton; final double radiusButton;
final double opacity; final double opacity;
final Widget? iconArrowDown; final Widget? iconArrowDown;
final Color? colorButton; final Color colorButton;
final String tooltip; final String tooltip;
final double? dropdownWidth; final double? dropdownWidth;
final double? dropdownMaxHeight; final double? dropdownMaxHeight;
@@ -36,7 +36,7 @@ class DropDownButtonWidget<T> extends StatelessWidget {
const DropDownButtonWidget({ const DropDownButtonWidget({
Key? key, Key? key,
required this.items, required this.items,
required this.itemSelected, this.itemSelected,
this.onChanged, this.onChanged,
this.onMenuStateChange, this.onMenuStateChange,
this.supportHint = false, this.supportHint = false,
@@ -112,7 +112,7 @@ class DropDownButtonWidget<T> extends StatelessWidget {
color: AppColor.colorInputBorderCreateMailbox, color: AppColor.colorInputBorderCreateMailbox,
width: 1, width: 1,
), ),
color: colorButton ?? AppColor.colorInputBackgroundCreateMailbox, color: colorButton,
), ),
padding: const EdgeInsets.only(left: 12, right: 10), padding: const EdgeInsets.only(left: 12, right: 10),
child: Row(children: [ child: Row(children: [
@@ -139,7 +139,7 @@ class DropDownButtonWidget<T> extends StatelessWidget {
color: AppColor.colorInputBorderCreateMailbox, color: AppColor.colorInputBorderCreateMailbox,
width: 1, width: 1,
), ),
color: colorButton ?? AppColor.colorInputBackgroundCreateMailbox, color: colorButton,
), ),
itemHeight: heightItem, itemHeight: heightItem,
buttonHeight: heightItem, buttonHeight: heightItem,
@@ -0,0 +1,63 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
class PopupItemNoIconWidget extends StatelessWidget {
final String _nameAction;
final String? svgIconSelected;
final bool isSelected;
final double? maxWidth;
final TextStyle? styleName;
final EdgeInsets? padding;
final VoidCallback? onCallbackAction;
const PopupItemNoIconWidget(
this._nameAction,
{
Key? key,
this.isSelected = false,
this.svgIconSelected,
this.maxWidth,
this.styleName,
this.padding,
this.onCallbackAction
}
) : super(key: key);
@override
Widget build(BuildContext context) {
return PointerInterceptor(
child: InkWell(
onTap: onCallbackAction,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: SizedBox(
width: maxWidth,
child: Row(children: [
Expanded(child: Text(
_nameAction,
style: const TextStyle(
fontSize: 17,
color: Colors.black,
fontWeight: FontWeight.normal
)
)),
if (isSelected && svgIconSelected != null)
...[
const SizedBox(width: 12),
SvgPicture.asset(
svgIconSelected!,
width: 24,
height: 24,
fit: BoxFit.fill
),
]
]),
),
)
)
);
}
}
@@ -4,6 +4,7 @@ import 'package:model/email/email_action_type.dart';
import 'package:model/email/presentation_email.dart'; 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/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/main/routes/navigation_router.dart'; import 'package:tmail_ui_user/main/routes/navigation_router.dart';
@@ -135,3 +136,24 @@ class OpenMailboxAction extends DashBoardAction {
@override @override
List<Object?> get props => [context, presentationMailbox]; List<Object?> get props => [context, presentationMailbox];
} }
class SelectDateRangeToAdvancedSearch extends DashBoardAction {
final DateTime? startDate;
final DateTime? endDate;
SelectDateRangeToAdvancedSearch(this.startDate, this.endDate);
@override
List<Object?> get props => [startDate, endDate];
}
class ClearDateRangeToAdvancedSearch extends DashBoardAction {
final EmailReceiveTimeType receiveTime;
ClearDateRangeToAdvancedSearch(this.receiveTime);
@override
List<Object?> get props => [receiveTime];
}
@@ -2,14 +2,12 @@ import 'package:collection/collection.dart';
import 'package:core/core.dart'; 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:flutter_date_range_picker/multiple_view_date_range_picker.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/account_id.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/session/session.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:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/composer/domain/model/contact_suggestion_source.dart'; import 'package:tmail_ui_user/features/composer/domain/model/contact_suggestion_source.dart';
import 'package:tmail_ui_user/features/composer/domain/state/get_autocomplete_state.dart'; import 'package:tmail_ui_user/features/composer/domain/state/get_autocomplete_state.dart';
@@ -37,28 +35,24 @@ class AdvancedFilterController extends BaseController {
final hasAttachment = false.obs; final hasAttachment = false.obs;
final lastTextForm = ''.obs; final lastTextForm = ''.obs;
final lastTextTo = ''.obs; final lastTextTo = ''.obs;
final startDate = Rxn<DateTime>();
final endDate = Rxn<DateTime>();
TextEditingController subjectFilterInputController = TextEditingController(); TextEditingController subjectFilterInputController = TextEditingController();
TextEditingController hasKeyWordFilterInputController = TextEditingController(); TextEditingController hasKeyWordFilterInputController = TextEditingController();
TextEditingController notKeyWordFilterInputController = TextEditingController(); TextEditingController notKeyWordFilterInputController = TextEditingController();
TextEditingController dateFilterInputController = TextEditingController();
TextEditingController mailBoxFilterInputController = TextEditingController(); TextEditingController mailBoxFilterInputController = TextEditingController();
ContactSuggestionSource _contactSuggestionSource = ContactSuggestionSource.tMailContact; ContactSuggestionSource _contactSuggestionSource = ContactSuggestionSource.tMailContact;
final SearchController searchController = Get.find<SearchController>(); final SearchController searchController = Get.find<SearchController>();
final MailboxDashBoardController _mailboxDashBoardController = Get.find<MailboxDashBoardController>(); final MailboxDashBoardController _mailboxDashBoardController = Get.find<MailboxDashBoardController>();
final _appToast = Get.find<AppToast>();
final _imagePaths = Get.find<ImagePaths>();
SearchEmailFilter get searchEmailFilter => SearchEmailFilter get searchEmailFilter =>
searchController.searchEmailFilter.value; searchController.searchEmailFilter.value;
final focusManager = InputFieldFocusManager.initial(); final focusManager = InputFieldFocusManager.initial();
DateTime? _startDate, _endDate; PresentationMailbox? _destinationMailboxSelected;
DateTime? get startDate => _startDate;
DateTime? get endDate => _endDate;
late Worker _dashboardActionWorker; late Worker _dashboardActionWorker;
@@ -83,17 +77,15 @@ class AdvancedFilterController extends BaseController {
void cleanSearchFilter(BuildContext context) { void cleanSearchFilter(BuildContext context) {
searchController.clearSearchFilter(); searchController.clearSearchFilter();
dateFilterSelectedFormAdvancedSearch.value = EmailReceiveTimeType.allTime; _updateDateRangeTime(EmailReceiveTimeType.allTime);
clearDateRangeOfFilter();
subjectFilterInputController.text = ''; subjectFilterInputController.text = '';
hasKeyWordFilterInputController.text = ''; hasKeyWordFilterInputController.text = '';
notKeyWordFilterInputController.text = ''; notKeyWordFilterInputController.text = '';
dateFilterInputController.text = '';
hasAttachment.value = false; hasAttachment.value = false;
_destinationMailboxSelected = null;
searchController.deactivateAdvancedSearch(); searchController.deactivateAdvancedSearch();
searchController.isAdvancedSearchViewOpen.toggle(); searchController.isAdvancedSearchViewOpen.toggle();
_mailboxDashBoardController.searchEmail( _mailboxDashBoardController.searchEmail(context, StringConvert.writeNullToEmpty(searchEmailFilter.text?.value));
context, StringConvert.writeNullToEmpty(searchEmailFilter.text?.value));
} }
void _updateFilterEmailFromAdvancedSearchView() { void _updateFilterEmailFromAdvancedSearchView() {
@@ -127,11 +119,12 @@ class AdvancedFilterController extends BaseController {
} }
searchController.updateFilterEmail( searchController.updateFilterEmail(
mailbox: _destinationMailboxSelected,
subjectOption: optionOf(subjectFilterInputController.text), subjectOption: optionOf(subjectFilterInputController.text),
emailReceiveTimeType: dateFilterSelectedFormAdvancedSearch.value, emailReceiveTimeType: dateFilterSelectedFormAdvancedSearch.value,
hasAttachment: hasAttachment.value, hasAttachment: hasAttachment.value,
endDate: _endDate.toUTCDate(), startDateOption: optionOf(startDate.value?.toUTCDate()),
startDate: _startDate.toUTCDate() endDateOption: optionOf(endDate.value?.toUTCDate())
); );
} }
@@ -150,9 +143,9 @@ class AdvancedFilterController extends BaseController {
context: context, context: context,
arguments: arguments, arguments: arguments,
onSelectedMailbox: (destinationMailbox) { onSelectedMailbox: (destinationMailbox) {
searchController.updateFilterEmail(mailbox: destinationMailbox); _destinationMailboxSelected = destinationMailbox;
mailBoxFilterInputController.text = final mailboxName = destinationMailbox.name?.name;
StringConvert.writeNullToEmpty(destinationMailbox.name?.name); mailBoxFilterInputController.text = StringConvert.writeNullToEmpty(mailboxName);
}); });
} else { } else {
final destinationMailbox = await push( final destinationMailbox = await push(
@@ -160,9 +153,9 @@ class AdvancedFilterController extends BaseController {
arguments: arguments); arguments: arguments);
if (destinationMailbox is PresentationMailbox) { if (destinationMailbox is PresentationMailbox) {
searchController.updateFilterEmail(mailbox: destinationMailbox); _destinationMailboxSelected = destinationMailbox;
mailBoxFilterInputController.text = final mailboxName = destinationMailbox.name?.name;
StringConvert.writeNullToEmpty(destinationMailbox.name?.name); mailBoxFilterInputController.text = StringConvert.writeNullToEmpty(mailboxName);
} }
} }
} }
@@ -176,8 +169,7 @@ class AdvancedFilterController extends BaseController {
searchController.deactivateAdvancedSearch(); searchController.deactivateAdvancedSearch();
} }
if (!isAdvancedSearchHasApplied) { if (!isAdvancedSearchHasApplied) {
final newSearchEmailFilter = searchController.searchEmailFilter.value.clearBeforeDate(); searchController.updateFilterEmail(beforeOption: const None());
searchController.searchEmailFilter.value = newSearchEmailFilter;
} }
searchController.isAdvancedSearchViewOpen.toggle(); searchController.isAdvancedSearchViewOpen.toggle();
_mailboxDashBoardController.searchEmail( _mailboxDashBoardController.searchEmail(
@@ -259,111 +251,55 @@ class AdvancedFilterController extends BaseController {
} }
void initSearchFilterField(BuildContext context) { void initSearchFilterField(BuildContext context) {
searchController.updateFilterEmail( subjectFilterInputController.text = StringConvert.writeNullToEmpty(searchEmailFilter.subject);
mailbox: PresentationMailbox.unifiedMailbox); hasKeyWordFilterInputController.text = StringConvert.writeNullToEmpty(searchEmailFilter.text?.value);
subjectFilterInputController.text = notKeyWordFilterInputController.text = StringConvert.writeNullToEmpty(searchEmailFilter.notKeyword.firstOrNull);
StringConvert.writeNullToEmpty(searchEmailFilter.subject); dateFilterSelectedFormAdvancedSearch.value = searchEmailFilter.emailReceiveTimeType;
hasKeyWordFilterInputController.text = StringConvert.writeNullToEmpty( _destinationMailboxSelected = searchEmailFilter.mailbox;
searchEmailFilter.text?.value); if (searchEmailFilter.mailbox == null) {
notKeyWordFilterInputController.text = StringConvert.writeNullToEmpty( mailBoxFilterInputController.text = AppLocalizations.of(context).allMailboxes;
searchEmailFilter.notKeyword.firstOrNull); } else {
dateFilterInputController.text = StringConvert.writeNullToEmpty( mailBoxFilterInputController.text = StringConvert.writeNullToEmpty(searchEmailFilter.mailbox?.name?.name);
searchEmailFilter.emailReceiveTimeType.getTitle( }
context,
startDate: _startDate,
endDate: _endDate));
mailBoxFilterInputController.text =
StringConvert.writeNullToEmpty(searchEmailFilter.mailbox?.name?.name);
dateFilterSelectedFormAdvancedSearch.value =
searchEmailFilter.emailReceiveTimeType;
hasAttachment.value = searchEmailFilter.hasAttachment; hasAttachment.value = searchEmailFilter.hasAttachment;
} }
void selectDateRange(BuildContext context) { void selectDateRange(BuildContext context) {
showGeneralDialog( searchController.showMultipleViewDateRangePicker(
context: context, context,
barrierDismissible: true, startDate.value,
barrierLabel: '', endDate.value,
barrierColor: Colors.black54, onCallbackAction: (startDate, endDate) =>
pageBuilder: (context, animation, secondaryAnimation) { _updateDateRangeTime(
return Dialog( EmailReceiveTimeType.customRange,
elevation: 0, newStartDate: startDate,
backgroundColor: Colors.white, newEndDate: endDate
shape: RoundedRectangleBorder( )
borderRadius: BorderRadius.circular(18.0)),
child: PointerInterceptor(
child: MultipleViewDateRangePicker(
confirmText: AppLocalizations.of(context).setDate,
cancelText: AppLocalizations.of(context).cancel,
last7daysTitle: AppLocalizations.of(context).last7Days,
last30daysTitle: AppLocalizations.of(context).last30Days,
last6monthsTitle: AppLocalizations.of(context).last6Months,
lastYearTitle: AppLocalizations.of(context).lastYears,
startDate: _startDate,
endDate: _endDate,
setDateActionCallback: ({startDate, endDate}) {
_handleSelectDateRangeResult(context, startDate, endDate);
},
),
)
);
}
); );
} }
void _handleSelectDateRangeResult( void _updateDateRangeTime(EmailReceiveTimeType receiveTime, {DateTime? newStartDate, DateTime? newEndDate}) {
BuildContext context, startDate.value = newStartDate;
DateTime? startDate, endDate.value = newEndDate;
DateTime? endDate dateFilterSelectedFormAdvancedSearch.value = receiveTime;
) {
log('AdvancedFilterController::_handleSelectDateRangeResult(): startDate: $startDate');
log('AdvancedFilterController::_handleSelectDateRangeResult(): endDate: $endDate');
if (startDate == null) {
_appToast.showToastWithIcon(
context,
textColor: Colors.black,
message: AppLocalizations.of(context).toastMessageErrorWhenSelectStartDateIsEmpty,
icon: _imagePaths.icNotConnection);
return;
}
if (endDate == null) {
_appToast.showToastWithIcon(
context,
textColor: Colors.black,
message: AppLocalizations.of(context).toastMessageErrorWhenSelectEndDateIsEmpty,
icon: _imagePaths.icNotConnection);
return;
}
if (endDate.isBefore(startDate)) {
_appToast.showToastWithIcon(
context,
textColor: Colors.black,
message: AppLocalizations.of(context).toastMessageErrorWhenSelectDateIsInValid,
icon: _imagePaths.icNotConnection);
return;
}
_startDate = startDate;
_endDate = endDate;
dateFilterSelectedFormAdvancedSearch.value = EmailReceiveTimeType.customRange;
dateFilterInputController.text = EmailReceiveTimeType.customRange.getTitle(
context,
startDate: startDate,
endDate: endDate);
dateFilterSelectedFormAdvancedSearch.refresh();
popBack();
} }
void clearDateRangeOfFilter() { void updateReceiveDateSearchFilter(BuildContext context, EmailReceiveTimeType receiveTime) {
_startDate = null; if (receiveTime == EmailReceiveTimeType.customRange) {
_endDate = null; searchController.showMultipleViewDateRangePicker(
context,
searchController.searchEmailFilter.value = startDate.value,
searchController.searchEmailFilter.value.withDateRange( endDate.value,
startDate: _startDate.toUTCDate(), onCallbackAction: (startDate, endDate) =>
endDate: _endDate.toUTCDate()); _updateDateRangeTime(
EmailReceiveTimeType.customRange,
newStartDate: startDate,
newEndDate: endDate
)
);
} else {
_updateDateRangeTime(receiveTime);
}
} }
void _resetAllToOriginalValue() { void _resetAllToOriginalValue() {
@@ -371,15 +307,14 @@ class AdvancedFilterController extends BaseController {
hasAttachment.value = false; hasAttachment.value = false;
lastTextForm.value = ''; lastTextForm.value = '';
lastTextTo.value = ''; lastTextTo.value = '';
_startDate = null; startDate.value = null;
_endDate = null; endDate.value = null;
} }
void _clearAllTextFieldInput() { void _clearAllTextFieldInput() {
subjectFilterInputController.clear(); subjectFilterInputController.clear();
hasKeyWordFilterInputController.clear(); hasKeyWordFilterInputController.clear();
notKeyWordFilterInputController.clear(); notKeyWordFilterInputController.clear();
dateFilterInputController.clear();
mailBoxFilterInputController.clear(); mailBoxFilterInputController.clear();
} }
@@ -389,6 +324,14 @@ class AdvancedFilterController extends BaseController {
(action) { (action) {
if (action is ClearAllFieldOfAdvancedSearchAction) { if (action is ClearAllFieldOfAdvancedSearchAction) {
_handleClearAllFieldOfAdvancedSearch(); _handleClearAllFieldOfAdvancedSearch();
} else if (action is SelectDateRangeToAdvancedSearch) {
_updateDateRangeTime(
EmailReceiveTimeType.customRange,
newStartDate: action.startDate,
newEndDate: action.endDate
);
} else if (action is ClearDateRangeToAdvancedSearch) {
_updateDateRangeTime(action.receiveTime);
} }
} }
); );
@@ -410,7 +353,6 @@ class AdvancedFilterController extends BaseController {
hasKeyWordFilterInputController.dispose(); hasKeyWordFilterInputController.dispose();
notKeyWordFilterInputController.dispose(); notKeyWordFilterInputController.dispose();
mailBoxFilterInputController.dispose(); mailBoxFilterInputController.dispose();
dateFilterInputController.dispose();
_unregisterWorkerListener(); _unregisterWorkerListener();
super.onClose(); super.onClose();
} }
@@ -73,6 +73,7 @@ import 'package:tmail_ui_user/features/manage_account/domain/state/update_vacati
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_vacation_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_vacation_interactor.dart';
import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/log_out_oidc_interactor.dart';
import 'package:tmail_ui_user/features/manage_account/domain/usecases/update_vacation_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/update_vacation_interactor.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/datetime_extension.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/vacation_response_extension.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/extensions/vacation_response_extension.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/account_menu_item.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/model/account_menu_item.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/model/manage_account_arguments.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/model/manage_account_arguments.dart';
@@ -535,9 +536,6 @@ class MailboxDashBoardController extends ReloadableController {
if (_searchInsideEmailDetailedViewIsActive(context)) { if (_searchInsideEmailDetailedViewIsActive(context)) {
_closeEmailDetailedView(); _closeEmailDetailedView();
} }
if (value.isEmpty){
searchController.setEmailReceiveTimeType(null);
}
_unSelectedMailbox(); _unSelectedMailbox();
} }
@@ -1281,23 +1279,13 @@ class MailboxDashBoardController extends ReloadableController {
} }
} }
void selectQuickSearchFilter({ void selectQuickSearchFilter(QuickSearchFilter filter) {
required QuickSearchFilter quickSearchFilter, return searchController.selectQuickSearchFilter(filter, userProfile.value!);
bool fromSuggestionBox = false, }
}) => searchController.selectQuickSearchFilter(
quickSearchFilter: quickSearchFilter,
userProfile: userProfile.value!,
fromSuggestionBox: fromSuggestionBox,
);
bool checkQuickSearchFilterSelected({ void addFilterToSuggestionForm(QuickSearchFilter filter) {
required QuickSearchFilter quickSearchFilter, searchController.addFilterToSuggestionForm(filter);
bool fromSuggestionBox = false, }
}) => searchController.checkQuickSearchFilterSelected(
quickSearchFilter: quickSearchFilter,
userProfile: userProfile.value!,
fromSuggestionBox: fromSuggestionBox,
);
Future<List<PresentationEmail>> quickSearchEmails() => searchController.quickSearchEmails(accountId: accountId.value!); Future<List<PresentationEmail>> quickSearchEmails() => searchController.quickSearchEmails(accountId: accountId.value!);
@@ -1353,26 +1341,44 @@ class MailboxDashBoardController extends ReloadableController {
void selectQuickSearchFilterAction(QuickSearchFilter filter) { void selectQuickSearchFilterAction(QuickSearchFilter filter) {
log('MailboxDashBoardController::selectQuickSearchFilterAction(): filter: $filter'); log('MailboxDashBoardController::selectQuickSearchFilterAction(): filter: $filter');
selectQuickSearchFilter(quickSearchFilter: filter); selectQuickSearchFilter(filter);
dispatchAction(StartSearchEmailAction()); dispatchAction(StartSearchEmailAction());
} }
void selectReceiveTimeQuickSearchFilter(EmailReceiveTimeType? emailReceiveTimeType) { void selectReceiveTimeQuickSearchFilter(BuildContext context, EmailReceiveTimeType receiveTime) {
log('MailboxDashBoardController::selectReceiveTimeQuickSearchFilter():receiveTime: $receiveTime');
popBack(); popBack();
if (emailReceiveTimeType != null) { if (receiveTime == EmailReceiveTimeType.customRange) {
searchController.updateFilterEmail(emailReceiveTimeType: emailReceiveTimeType); searchController.showMultipleViewDateRangePicker(
context,
searchController.startDateFiltered,
searchController.endDateFiltered,
onCallbackAction: (startDate, endDate) {
dispatchAction(SelectDateRangeToAdvancedSearch(startDate, endDate));
searchController.updateFilterEmail(
emailReceiveTimeType: receiveTime,
startDateOption: optionOf(startDate?.toUTCDate()),
endDateOption: optionOf(startDate?.toUTCDate()),
beforeOption: const None()
);
dispatchAction(StartSearchEmailAction());
}
);
} else { } else {
searchController.updateFilterEmail(emailReceiveTimeType: EmailReceiveTimeType.allTime); dispatchAction(ClearDateRangeToAdvancedSearch(receiveTime));
searchController.updateFilterEmail(
emailReceiveTimeType: receiveTime,
startDateOption: const None(),
endDateOption: const None(),
beforeOption: const None()
);
dispatchAction(StartSearchEmailAction());
} }
searchController.setEmailReceiveTimeType(emailReceiveTimeType);
searchController.updateFilterEmail();
if (searchController.searchQuery == null){
searchController.updateFilterEmail(text: SearchQuery.initial());
}
dispatchAction(StartSearchEmailAction());
} }
bool get isMailboxTrash => selectedMailbox.value?.isTrash == true; bool get isMailboxTrash => selectedMailbox.value?.isTrash == true;
void emptyTrashAction(BuildContext context) { void emptyTrashAction(BuildContext context) {
@@ -1,17 +1,26 @@
import 'dart:async'; import 'dart:async';
import 'package:core/core.dart'; import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/utils/app_logger.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/account_id.dart'; import 'package:jmap_dart_client/jmap/account_id.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/sort/comparator.dart';
import 'package:jmap_dart_client/jmap/core/unsigned_int.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/core/utc_date.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_comparator.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_comparator.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_comparator_property.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_comparator_property.dart';
import 'package:model/model.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart';
import 'package:model/email/presentation_email.dart';
import 'package:model/extensions/email_filter_condition_extension.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:model/user/user_profile.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/base/mixin/date_range_picker_mixin.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/model/recent_search.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'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/get_all_recent_search_latest_state.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/quick_search_email_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/quick_search_email_state.dart';
@@ -27,7 +36,7 @@ import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/search_state.dart'; import 'package:tmail_ui_user/features/thread/presentation/model/search_state.dart';
import 'package:tmail_ui_user/features/thread/presentation/model/search_status.dart'; import 'package:tmail_ui_user/features/thread/presentation/model/search_status.dart';
class SearchController extends BaseController { class SearchController extends BaseController with DateRangePickerMixin {
final QuickSearchEmailInteractor _quickSearchEmailInteractor; final QuickSearchEmailInteractor _quickSearchEmailInteractor;
final SaveRecentSearchInteractor _saveRecentSearchInteractor; final SaveRecentSearchInteractor _saveRecentSearchInteractor;
final GetAllRecentSearchLatestInteractor _getAllRecentSearchLatestInteractor; final GetAllRecentSearchLatestInteractor _getAllRecentSearchLatestInteractor;
@@ -38,7 +47,7 @@ class SearchController extends BaseController {
final searchEmailFilter = SearchEmailFilter.initial().obs; final searchEmailFilter = SearchEmailFilter.initial().obs;
final searchState = SearchState.initial().obs; final searchState = SearchState.initial().obs;
final isAdvancedSearchViewOpen = false.obs; final isAdvancedSearchViewOpen = false.obs;
final emailReceiveTimeType = Rxn<EmailReceiveTimeType>(); final listFilterOnSuggestionForm = RxList<QuickSearchFilter>();
final simpleSearchIsActivated = RxBool(false); final simpleSearchIsActivated = RxBool(false);
final advancedSearchIsActivated = RxBool(false); final advancedSearchIsActivated = RxBool(false);
final autoFocus = RxBool(true); final autoFocus = RxBool(true);
@@ -67,53 +76,78 @@ class SearchController extends BaseController {
searchEmailFilter.value = SearchEmailFilter.initial(); searchEmailFilter.value = SearchEmailFilter.initial();
} }
void selectQuickSearchFilter({ void selectQuickSearchFilter(QuickSearchFilter quickSearchFilter, UserProfile userProfile) {
required QuickSearchFilter quickSearchFilter, final isFilterSelected = quickSearchFilter.isSelected(searchEmailFilter.value, userProfile);
required UserProfile userProfile,
bool fromSuggestionBox = false,
}) {
final quickSearchFilterSelected = checkQuickSearchFilterSelected(
userProfile: userProfile,
quickSearchFilter: quickSearchFilter,
fromSuggestionBox: fromSuggestionBox,
);
switch (quickSearchFilter) { switch (quickSearchFilter) {
case QuickSearchFilter.hasAttachment: case QuickSearchFilter.hasAttachment:
updateFilterEmail(hasAttachment: !quickSearchFilterSelected); updateFilterEmail(hasAttachment: !isFilterSelected);
return; return;
case QuickSearchFilter.last7Days: case QuickSearchFilter.last7Days:
if (quickSearchFilterSelected) { updateFilterEmail(emailReceiveTimeType: EmailReceiveTimeType.allTime);
setEmailReceiveTimeType(null);
updateFilterEmail(emailReceiveTimeType: EmailReceiveTimeType.allTime);
} else {
setEmailReceiveTimeType(EmailReceiveTimeType.last7Days);
updateFilterEmail(emailReceiveTimeType: EmailReceiveTimeType.last7Days);
}
return; return;
case QuickSearchFilter.fromMe: case QuickSearchFilter.fromMe:
quickSearchFilterSelected isFilterSelected
? searchEmailFilter.value.from.removeWhere((e) => e == userProfile.email) ? searchEmailFilter.value.from.removeWhere((e) => e == userProfile.email)
: searchEmailFilter.value.from.add(userProfile.email); : searchEmailFilter.value.from.add(userProfile.email);
updateFilterEmail(from: searchEmailFilter.value.from); updateFilterEmail(from: searchEmailFilter.value.from);
return; return;
} }
} }
Future<List<PresentationEmail>> quickSearchEmails({required AccountId accountId}) async { void addFilterToSuggestionForm(QuickSearchFilter filter) {
return await _quickSearchEmailInteractor if (listFilterOnSuggestionForm.contains(filter)) {
.execute(accountId, listFilterOnSuggestionForm.remove(filter);
limit: UnsignedInt(5), } else {
sort: <Comparator>{}..add( listFilterOnSuggestionForm.add(filter);
EmailComparator(EmailComparatorProperty.receivedAt) }
..setIsAscending(false)), }
filter: searchEmailFilter.value.mappingToEmailFilterCondition(),
properties: ThreadConstants.propertiesQuickSearch) Future<List<PresentationEmail>> quickSearchEmails({
.then((result) => result.fold( required AccountId accountId,
(failure) => <PresentationEmail>[], UserProfile? userProfile
(success) => success is QuickSearchEmailSuccess }) async {
? success.emailList return await _quickSearchEmailInteractor.execute(
: <PresentationEmail>[])); accountId,
limit: UnsignedInt(5),
sort: <Comparator>{}..add(
EmailComparator(EmailComparatorProperty.receivedAt)
..setIsAscending(false)),
filter: _mappingToFilterOnSuggestionForm(userProfile),
properties: ThreadConstants.propertiesQuickSearch
).then((result) => result.fold(
(failure) => <PresentationEmail>[],
(success) => success is QuickSearchEmailSuccess
? success.emailList
: <PresentationEmail>[]
));
}
Filter? _mappingToFilterOnSuggestionForm(UserProfile? userProfile) {
final searchText = searchEmailFilter.value.text?.value.trim();
final filterCondition = EmailFilterCondition(
text: searchText?.isNotEmpty == true ? searchText : null,
after: listFilterOnSuggestionForm.contains(QuickSearchFilter.last7Days)
? EmailReceiveTimeType.last7Days.toOldestUTCDate()
: null,
before: listFilterOnSuggestionForm.contains(QuickSearchFilter.last7Days)
? EmailReceiveTimeType.last7Days.toLatestUTCDate()
: null,
hasAttachment: listFilterOnSuggestionForm.contains(QuickSearchFilter.hasAttachment)
? true
: null
);
final listEmailCondition = {
if (filterCondition.hasCondition) filterCondition,
if (listFilterOnSuggestionForm.contains(QuickSearchFilter.fromMe) && userProfile != null)
LogicFilterOperator(Operator.AND, {EmailFilterCondition(from: userProfile.email)})
};
return listEmailCondition.isNotEmpty
? LogicFilterOperator(Operator.AND, listEmailCondition)
: null;
} }
void updateFilterEmail({ void updateFilterEmail({
@@ -125,9 +159,9 @@ class SearchController extends BaseController {
PresentationMailbox? mailbox, PresentationMailbox? mailbox,
EmailReceiveTimeType? emailReceiveTimeType, EmailReceiveTimeType? emailReceiveTimeType,
bool? hasAttachment, bool? hasAttachment,
UTCDate? before, Option<UTCDate>? beforeOption,
UTCDate? startDate, Option<UTCDate>? startDateOption,
UTCDate? endDate, Option<UTCDate>? endDateOption
}) { }) {
searchEmailFilter.value = searchEmailFilter.value.copyWith( searchEmailFilter.value = searchEmailFilter.value.copyWith(
from: from, from: from,
@@ -138,9 +172,9 @@ class SearchController extends BaseController {
mailbox: mailbox, mailbox: mailbox,
emailReceiveTimeType: emailReceiveTimeType, emailReceiveTimeType: emailReceiveTimeType,
hasAttachment: hasAttachment, hasAttachment: hasAttachment,
before: before, beforeOption: beforeOption,
startDate: startDate, startDateOption: startDateOption,
endDate: endDate, endDateOption: endDateOption,
); );
searchEmailFilter.refresh(); searchEmailFilter.refresh();
} }
@@ -159,6 +193,12 @@ class SearchController extends BaseController {
}); });
} }
EmailReceiveTimeType get receiveTimeFiltered => searchEmailFilter.value.emailReceiveTimeType;
DateTime? get startDateFiltered => searchEmailFilter.value.startDate?.value.toLocal();
DateTime? get endDateFiltered => searchEmailFilter.value.endDate?.value.toLocal();
bool isSearchActive() => bool isSearchActive() =>
searchState.value.searchStatus == SearchStatus.ACTIVE; searchState.value.searchStatus == SearchStatus.ACTIVE;
@@ -188,21 +228,15 @@ class SearchController extends BaseController {
searchInputController.text = value; searchInputController.text = value;
} }
bool checkQuickSearchFilterSelected({ bool checkQuickSearchFilterSelected(QuickSearchFilter quickSearchFilter, UserProfile userProfile) {
required QuickSearchFilter quickSearchFilter,
required UserProfile userProfile,
bool fromSuggestionBox = false,
}) {
switch (quickSearchFilter) { switch (quickSearchFilter) {
case QuickSearchFilter.hasAttachment: case QuickSearchFilter.hasAttachment:
return searchEmailFilter.value.hasAttachment == true; return searchEmailFilter.value.hasAttachment == true;
case QuickSearchFilter.last7Days: case QuickSearchFilter.last7Days:
if (emailReceiveTimeType.value != null) { return true;
return true;
}
return searchEmailFilter.value.emailReceiveTimeType == EmailReceiveTimeType.last7Days;
case QuickSearchFilter.fromMe: case QuickSearchFilter.fromMe:
return searchEmailFilter.value.from.contains( userProfile.email) && searchEmailFilter.value.from.length == 1; return searchEmailFilter.value.from.contains(userProfile.email) &&
searchEmailFilter.value.from.length == 1;
} }
} }
@@ -220,11 +254,7 @@ class SearchController extends BaseController {
: <RecentSearch>[])); : <RecentSearch>[]));
} }
void setEmailReceiveTimeType(EmailReceiveTimeType? receiveTimeType) { void showAdvancedFilterView(BuildContext context) async {
emailReceiveTimeType.value = receiveTimeType;
}
showAdvancedFilterView(BuildContext context) async {
selectOpenAdvanceSearch(); selectOpenAdvanceSearch();
if (_responsiveUtils.isMobile(context)) { if (_responsiveUtils.isMobile(context)) {
await showAdvancedSearchFilterBottomSheet(context); await showAdvancedSearchFilterBottomSheet(context);
@@ -259,7 +289,6 @@ class SearchController extends BaseController {
void _clearAllTextInputSimpleSearch() { void _clearAllTextInputSimpleSearch() {
searchInputController.clear(); searchInputController.clear();
searchFocus.unfocus(); searchFocus.unfocus();
emailReceiveTimeType.value = null;
} }
void disableAllSearchEmail() { void disableAllSearchEmail() {
@@ -4,6 +4,7 @@ import 'package:flutter_portal/flutter_portal.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:tmail_ui_user/features/base/widget/popup_item_no_icon_widget.dart';
import 'package:tmail_ui_user/features/composer/presentation/composer_view_web.dart'; import 'package:tmail_ui_user/features/composer/presentation/composer_view_web.dart';
import 'package:tmail_ui_user/features/email/presentation/email_view.dart'; import 'package:tmail_ui_user/features/email/presentation/email_view.dart';
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
@@ -541,8 +542,9 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView {
QuickSearchFilter filter QuickSearchFilter filter
) { ) {
return Obx(() { return Obx(() {
final quickSearchFilterSelected = controller.checkQuickSearchFilterSelected( final isFilterSelected = filter.isSelected(
quickSearchFilter: filter, controller.searchController.searchEmailFilter.value,
controller.userProfile.value
); );
return Padding( return Padding(
@@ -563,35 +565,40 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView {
screenSize.width - offset.dx, screenSize.width - offset.dx,
screenSize.height - offset.dy, screenSize.height - offset.dy,
); );
controller.openPopupMenuAction(context, position, controller.openPopupMenuAction(
popupMenuEmailReceiveTimeType(context, context,
controller.searchController.emailReceiveTimeType.value, position,
(receiveTime) => controller.selectReceiveTimeQuickSearchFilter(receiveTime))); popupMenuEmailReceiveTimeType(
context,
controller.searchController.receiveTimeFiltered,
onCallBack: (receiveTime) => controller.selectReceiveTimeQuickSearchFilter(context, receiveTime)
)
);
} }
}, },
borderRadius: const BorderRadius.all(Radius.circular(10)), borderRadius: const BorderRadius.all(Radius.circular(10)),
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
color: filter.getBackgroundColor(quickSearchFilterSelected: quickSearchFilterSelected)), color: filter.getBackgroundColor(isFilterSelected: isFilterSelected)),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
SvgPicture.asset( SvgPicture.asset(
filter.getIcon( filter.getIcon(imagePaths, isFilterSelected: isFilterSelected),
imagePaths,
quickSearchFilterSelected: quickSearchFilterSelected),
width: 16, width: 16,
height: 16, height: 16,
fit: BoxFit.fill), fit: BoxFit.fill),
const SizedBox(width: 4), const SizedBox(width: 4),
Text( Text(
filter.getTitle( filter.getTitle(
context, context,
receiveTimeType: controller.searchController.emailReceiveTimeType.value), receiveTimeType: controller.searchController.receiveTimeFiltered,
startDate: controller.searchController.startDateFiltered,
endDate: controller.searchController.endDateFiltered
),
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: filter.getTextStyle( style: filter.getTextStyle(isFilterSelected: isFilterSelected),
quickSearchFilterSelected: quickSearchFilterSelected),
), ),
if (filter == QuickSearchFilter.last7Days) if (filter == QuickSearchFilter.last7Days)
... [ ... [
@@ -609,55 +616,21 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView {
} }
List<PopupMenuEntry> popupMenuEmailReceiveTimeType( List<PopupMenuEntry> popupMenuEmailReceiveTimeType(
BuildContext context, BuildContext context,
EmailReceiveTimeType? receiveTimeSelected, EmailReceiveTimeType? receiveTimeSelected,
Function(EmailReceiveTimeType?)? onCallBack {Function(EmailReceiveTimeType)? onCallBack}
) { ) {
return EmailReceiveTimeType.values return EmailReceiveTimeType.values
.map((timeType) => PopupMenuItem( .map((receiveTime) => PopupMenuItem(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
child: _receiveTimeTileAction( child: PopupItemNoIconWidget(
context, receiveTime.getTitle(context),
receiveTimeSelected, svgIconSelected: imagePaths.icFilterSelected,
timeType, maxWidth: 320,
onCallBack))) isSelected: receiveTimeSelected == receiveTime,
.toList(); onCallbackAction: () => onCallBack?.call(receiveTime),
} )))
.toList();
Widget _receiveTimeTileAction(
BuildContext context,
EmailReceiveTimeType? receiveTimeSelected,
EmailReceiveTimeType receiveTimeType,
Function(EmailReceiveTimeType?)? onCallBack
) {
return InkWell(
onTap: () => onCallBack?.call(receiveTimeType == receiveTimeSelected
? null
: receiveTimeType),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: SizedBox(
width: 320,
child: Row(children: [
Expanded(child: Text(
receiveTimeType.getTitle(context),
style: const TextStyle(
fontSize: 17,
color: Colors.black,
fontWeight: FontWeight.normal))),
if (receiveTimeType == receiveTimeSelected)
...[
const SizedBox(width: 12),
SvgPicture.asset(
imagePaths.icFilterSelected,
width: 24,
height: 24,
fit: BoxFit.fill),
]
])
),
)
);
} }
bool supportEmptyTrash(BuildContext context) { bool supportEmptyTrash(BuildContext context) {
@@ -43,7 +43,7 @@ enum AdvancedSearchFilterField {
case AdvancedSearchFilterField.notKeyword: case AdvancedSearchFilterField.notKeyword:
return AppLocalizations.of(context).enterSearchTerm; return AppLocalizations.of(context).enterSearchTerm;
case AdvancedSearchFilterField.mailBox: case AdvancedSearchFilterField.mailBox:
return AppLocalizations.of(context).allMails; return AppLocalizations.of(context).allMailboxes;
case AdvancedSearchFilterField.date: case AdvancedSearchFilterField.date:
return AppLocalizations.of(context).allTime; return AppLocalizations.of(context).allTime;
case AdvancedSearchFilterField.hasAttachment: case AdvancedSearchFilterField.hasAttachment:
@@ -37,10 +37,8 @@ enum EmailReceiveTimeType {
} }
} }
UTCDate? toUTCDate() { UTCDate? toOldestUTCDate() {
switch(this) { switch(this) {
case EmailReceiveTimeType.allTime:
return null;
case EmailReceiveTimeType.last7Days: case EmailReceiveTimeType.last7Days:
final today = DateTime.now(); final today = DateTime.now();
final last7Days = today.subtract(const Duration(days: 7)); final last7Days = today.subtract(const Duration(days: 7));
@@ -57,8 +55,49 @@ enum EmailReceiveTimeType {
final today = DateTime.now(); final today = DateTime.now();
final lastYear = DateTime(today.year - 1, today.month, today.day); final lastYear = DateTime(today.year - 1, today.month, today.day);
return lastYear.toUTCDate(); return lastYear.toUTCDate();
case EmailReceiveTimeType.customRange: default:
return null; return null;
} }
} }
UTCDate? toLatestUTCDate() {
switch(this) {
case EmailReceiveTimeType.last7Days:
case EmailReceiveTimeType.last30Days:
case EmailReceiveTimeType.last6Months:
case EmailReceiveTimeType.lastYear:
return DateTime.now().toUTCDate();
default:
return null;
}
}
UTCDate? getAfterDate(UTCDate? startDate) {
if (startDate != null) {
return startDate;
} else {
return toOldestUTCDate();
}
}
UTCDate? getBeforeDate(UTCDate? endDate, UTCDate? loadMoreDate) {
if (endDate != null) {
if (loadMoreDate != null && loadMoreDate.value.isBefore(endDate.value)) {
return loadMoreDate;
} else {
return endDate;
}
} else {
final latestDate = toLatestUTCDate();
if (latestDate != null) {
if (loadMoreDate != null && loadMoreDate.value.isBefore(latestDate.value)) {
return loadMoreDate;
} else {
return latestDate;
}
} else {
return loadMoreDate;
}
}
}
} }
@@ -1,7 +1,10 @@
import 'package:core/core.dart'; import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:model/user/user_profile.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/search_email_filter.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
enum QuickSearchFilter { enum QuickSearchFilter {
@@ -9,22 +12,36 @@ enum QuickSearchFilter {
last7Days, last7Days,
fromMe; fromMe;
String getTitle(BuildContext context, {EmailReceiveTimeType? receiveTimeType}) { String getName(BuildContext context) {
switch(this) { switch(this) {
case QuickSearchFilter.hasAttachment: case QuickSearchFilter.hasAttachment:
return AppLocalizations.of(context).hasAttachment; return AppLocalizations.of(context).hasAttachment;
case QuickSearchFilter.last7Days: case QuickSearchFilter.last7Days:
if (receiveTimeType != null) {
return receiveTimeType.getTitle(context);
}
return AppLocalizations.of(context).last7Days; return AppLocalizations.of(context).last7Days;
case QuickSearchFilter.fromMe: case QuickSearchFilter.fromMe:
return AppLocalizations.of(context).fromMe; return AppLocalizations.of(context).fromMe;
} }
} }
String getIcon(ImagePaths imagePaths, {required bool quickSearchFilterSelected}) { String getTitle(
if (quickSearchFilterSelected) { BuildContext context, {
EmailReceiveTimeType? receiveTimeType,
DateTime? startDate,
DateTime? endDate,
}) {
switch(this) {
case QuickSearchFilter.hasAttachment:
return AppLocalizations.of(context).hasAttachment;
case QuickSearchFilter.last7Days:
return receiveTimeType?.getTitle(context, startDate: startDate, endDate: endDate)
?? AppLocalizations.of(context).allTime;
case QuickSearchFilter.fromMe:
return AppLocalizations.of(context).fromMe;
}
}
String getIcon(ImagePaths imagePaths, {required bool isFilterSelected}) {
if (isFilterSelected) {
return imagePaths.icSelectedSB; return imagePaths.icSelectedSB;
} else { } else {
switch(this) { switch(this) {
@@ -38,16 +55,16 @@ enum QuickSearchFilter {
} }
} }
Color getBackgroundColor({required bool quickSearchFilterSelected}) { Color getBackgroundColor({required bool isFilterSelected}) {
if (quickSearchFilterSelected) { if (isFilterSelected) {
return AppColor.colorItemEmailSelectedDesktop; return AppColor.colorItemEmailSelectedDesktop;
} else { } else {
return AppColor.colorButtonHeaderThread; return AppColor.colorButtonHeaderThread;
} }
} }
TextStyle getTextStyle({required bool quickSearchFilterSelected}) { TextStyle getTextStyle({required bool isFilterSelected}) {
if (quickSearchFilterSelected) { if (isFilterSelected) {
return const TextStyle( return const TextStyle(
fontSize: 13, fontSize: 13,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
@@ -59,4 +76,19 @@ enum QuickSearchFilter {
color: AppColor.colorTextButtonHeaderThread); color: AppColor.colorTextButtonHeaderThread);
} }
} }
bool isApplied(List<QuickSearchFilter> listFilter) => listFilter.contains(this);
bool isSelected(SearchEmailFilter filter, UserProfile? userProfile) {
switch (this) {
case QuickSearchFilter.hasAttachment:
return filter.hasAttachment == true;
case QuickSearchFilter.last7Days:
return true;
case QuickSearchFilter.fromMe:
return userProfile != null &&
filter.from.contains(userProfile.email) &&
filter.from.length == 1;
}
}
} }
@@ -5,7 +5,8 @@ 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/filter/operator/logic_filter_operator.dart';
import 'package:jmap_dart_client/jmap/core/utc_date.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:jmap_dart_client/jmap/mail/email/email_filter_condition.dart';
import 'package:model/model.dart'; import 'package:model/extensions/email_filter_condition_extension.dart';
import 'package:model/mailbox/presentation_mailbox.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/thread/domain/model/search_query.dart'; import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart';
@@ -52,9 +53,9 @@ class SearchEmailFilter with EquatableMixin {
PresentationMailbox? mailbox, PresentationMailbox? mailbox,
EmailReceiveTimeType? emailReceiveTimeType, EmailReceiveTimeType? emailReceiveTimeType,
bool? hasAttachment, bool? hasAttachment,
UTCDate? before, Option<UTCDate>? beforeOption,
UTCDate? startDate, Option<UTCDate>? startDateOption,
UTCDate? endDate, Option<UTCDate>? endDateOption,
}) { }) {
return SearchEmailFilter( return SearchEmailFilter(
from: from ?? this.from, from: from ?? this.from,
@@ -65,9 +66,9 @@ class SearchEmailFilter with EquatableMixin {
mailbox: mailbox ?? this.mailbox, mailbox: mailbox ?? this.mailbox,
emailReceiveTimeType: emailReceiveTimeType ?? this.emailReceiveTimeType, emailReceiveTimeType: emailReceiveTimeType ?? this.emailReceiveTimeType,
hasAttachment: hasAttachment ?? this.hasAttachment, hasAttachment: hasAttachment ?? this.hasAttachment,
before: before ?? this.before, before: _getOptionParam(beforeOption, before),
startDate: startDate ?? this.startDate, startDate: _getOptionParam(startDateOption, startDate),
endDate: endDate ?? this.endDate, endDate: _getOptionParam(endDateOption, endDate),
); );
} }
@@ -84,17 +85,13 @@ class SearchEmailFilter with EquatableMixin {
text: text?.value.trim().isNotEmpty == true text: text?.value.trim().isNotEmpty == true
? text?.value ? text?.value
: null, : null,
inMailbox: mailbox == PresentationMailbox.unifiedMailbox inMailbox: mailbox?.mailboxId,
? null after: emailReceiveTimeType.getAfterDate(startDate),
: mailbox?.id,
after: emailReceiveTimeType == EmailReceiveTimeType.customRange
? startDate
: emailReceiveTimeType.toUTCDate(),
hasAttachment: hasAttachment == false ? null : hasAttachment, hasAttachment: hasAttachment == false ? null : hasAttachment,
subject: subject, subject: subject?.trim().isNotEmpty == true
before: emailReceiveTimeType == EmailReceiveTimeType.customRange ? subject
? endDate : null,
: before, before: emailReceiveTimeType.getBeforeDate(endDate, before)
); );
final listEmailCondition = { final listEmailCondition = {
@@ -136,38 +133,3 @@ class SearchEmailFilter with EquatableMixin {
endDate endDate
]; ];
} }
extension SearchEmailFilterExtension on SearchEmailFilter {
SearchEmailFilter clearBeforeDate() {
return SearchEmailFilter(
from: from,
to: to,
text: text,
subject: subject,
notKeyword: notKeyword,
mailbox: mailbox,
emailReceiveTimeType: emailReceiveTimeType,
hasAttachment: hasAttachment,
before: null,
startDate: startDate,
endDate: endDate,
);
}
SearchEmailFilter withDateRange({UTCDate? startDate, UTCDate? endDate}) {
return SearchEmailFilter(
from: from,
to: to,
text: text,
subject: subject,
notKeyword: notKeyword,
mailbox: mailbox,
emailReceiveTimeType: emailReceiveTimeType,
hasAttachment: hasAttachment,
before: before,
startDate: startDate,
endDate: endDate,
);
}
}
@@ -4,11 +4,12 @@ import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/mixin/popup_context_menu_action_mixin.dart'; import 'package:tmail_ui_user/features/base/mixin/popup_context_menu_action_mixin.dart';
import 'package:tmail_ui_user/features/base/widget/popup_item_no_icon_widget.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/advanced_filter_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/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/widgets/advanced_search/advanced_search_filter_form_bottom_view.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_filter_form_bottom_view.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/drop_down_button_filter_widget.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/date_drop_down_button.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/text_field_auto_complete_email_adress.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/text_field_auto_complete_email_adress.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
@@ -73,7 +74,6 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
onTap: () => controller.selectedMailBox(context)), onTap: () => controller.selectedMailBox(context)),
Row(children: [ Row(children: [
Expanded(child: _buildFilterField( Expanded(child: _buildFilterField(
textEditingController: controller.dateFilterInputController,
context: context, context: context,
advancedSearchFilterField: AdvancedSearchFilterField.date, advancedSearchFilterField: AdvancedSearchFilterField.date,
isSelectFormList: true, isSelectFormList: true,
@@ -102,52 +102,22 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
List<Widget> _buildEmailReceiveTimeTypeActionTiles(BuildContext context) { List<Widget> _buildEmailReceiveTimeTypeActionTiles(BuildContext context) {
return EmailReceiveTimeType.values return EmailReceiveTimeType.values
.map( .map((receiveTime) => PopupMenuItem(
(e) => Material( padding: EdgeInsets.zero,
child: PopupMenuItem( child: PopupItemNoIconWidget(
child: Row(children: [ receiveTime.getTitle(context),
const SizedBox(width: 12), svgIconSelected: _imagePaths.icFilterSelected,
Expanded( maxWidth: 320,
child: Text( isSelected: controller.dateFilterSelectedFormAdvancedSearch.value == receiveTime,
e.getTitle( onCallbackAction: () => controller.updateReceiveDateSearchFilter(context, receiveTime),
context, )))
startDate: controller.startDate, .toList();
endDate: controller.endDate),
style: const TextStyle(
fontSize: 15,
color: Colors.black,
fontWeight: FontWeight.w500))),
if (e == controller.dateFilterSelectedFormAdvancedSearch.value)
...[
const SizedBox(width: 12),
SvgPicture.asset(
_imagePaths.icFilterSelected,
width: 16,
height: 16,
fit: BoxFit.fill,
),
]
]),
onTap: () {
if (e != EmailReceiveTimeType.customRange) {
controller.clearDateRangeOfFilter();
}
controller.dateFilterSelectedFormAdvancedSearch.value = e;
controller.dateFilterInputController.text = e.getTitle(
context,
startDate: controller.startDate,
endDate: controller.endDate);
},
),
),
)
.toList();
} }
Widget _buildFilterField({ Widget _buildFilterField({
required BuildContext context, required BuildContext context,
required AdvancedSearchFilterField advancedSearchFilterField, required AdvancedSearchFilterField advancedSearchFilterField,
required TextEditingController textEditingController, TextEditingController? textEditingController,
VoidCallback? onTap, VoidCallback? onTap,
bool isSelectFormList = false, bool isSelectFormList = false,
MouseCursor? mouseCursor, MouseCursor? mouseCursor,
@@ -180,7 +150,13 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
) )
else if (_responsiveUtils.landscapeTabletSupported(context)) else if (_responsiveUtils.landscapeTabletSupported(context))
if (advancedSearchFilterField == AdvancedSearchFilterField.date) if (advancedSearchFilterField == AdvancedSearchFilterField.date)
const DateDropDownButton() Obx(() => DateDropDownButton(
_imagePaths,
startDate: controller.startDate.value,
endDate: controller.endDate.value,
receiveTimeSelected: controller.dateFilterSelectedFormAdvancedSearch.value,
onReceiveTimeSelected: (receiveTime) => controller.updateReceiveDateSearchFilter(context, receiveTime),
))
else else
_buildTextField( _buildTextField(
isSelectFormList: isSelectFormList, isSelectFormList: isSelectFormList,
@@ -222,7 +198,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
Widget _buildTextFieldFilterForWeb({ Widget _buildTextFieldFilterForWeb({
required BuildContext context, required BuildContext context,
required AdvancedSearchFilterField advancedSearchFilterField, required AdvancedSearchFilterField advancedSearchFilterField,
required TextEditingController textEditingController, TextEditingController? textEditingController,
VoidCallback? onTap, VoidCallback? onTap,
bool isSelectFormList = false, bool isSelectFormList = false,
MouseCursor? mouseCursor, MouseCursor? mouseCursor,
@@ -231,7 +207,13 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
}) { }) {
switch (advancedSearchFilterField) { switch (advancedSearchFilterField) {
case AdvancedSearchFilterField.date: case AdvancedSearchFilterField.date:
return const DateDropDownButton(); return Obx(() => DateDropDownButton(
_imagePaths,
startDate: controller.startDate.value,
endDate: controller.endDate.value,
receiveTimeSelected: controller.dateFilterSelectedFormAdvancedSearch.value,
onReceiveTimeSelected: (receiveTime) => controller.updateReceiveDateSearchFilter(context, receiveTime),
));
default: default:
return _buildTextField( return _buildTextField(
isSelectFormList: isSelectFormList, isSelectFormList: isSelectFormList,
@@ -357,7 +339,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
Widget _buildTextField({ Widget _buildTextField({
required BuildContext context, required BuildContext context,
required AdvancedSearchFilterField advancedSearchFilterField, required AdvancedSearchFilterField advancedSearchFilterField,
required TextEditingController textEditingController, TextEditingController? textEditingController,
VoidCallback? onTap, VoidCallback? onTap,
bool isSelectFormList = false, bool isSelectFormList = false,
MouseCursor? mouseCursor, MouseCursor? mouseCursor,
@@ -0,0 +1,130 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/style_utils.dart';
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart';
class DateDropDownButton extends StatelessWidget {
final ImagePaths imagePaths;
final DateTime? startDate;
final DateTime? endDate;
final EmailReceiveTimeType? receiveTimeSelected;
final Function(EmailReceiveTimeType)? onReceiveTimeSelected;
const DateDropDownButton(
this.imagePaths, {
Key? key,
this.startDate,
this.endDate,
this.receiveTimeSelected,
this.onReceiveTimeSelected,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return DropdownButtonHideUnderline(
child: PointerInterceptor(
child: DropdownButton2<EmailReceiveTimeType>(
isExpanded: true,
items: EmailReceiveTimeType.values
.map((item) => _buildItemMenu(context, item))
.toList(),
value: receiveTimeSelected,
customButton: Container(
height: 44,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: AppColor.colorInputBorderCreateMailbox,
width: 1,
),
color: AppColor.colorInputBackgroundCreateMailbox
),
padding: const EdgeInsets.only(left: 12, right: 10),
child: Row(children: [
Expanded(child: Text(
receiveTimeSelected?.getTitle(context, startDate: startDate, endDate: endDate) ?? '',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black
),
maxLines: 1,
softWrap: CommonTextStyle.defaultSoftWrap,
overflow: CommonTextStyle.defaultTextOverFlow,
)),
SvgPicture.asset(imagePaths.icDropDown)
]),
),
onChanged: (value) {
if (value != null) {
onReceiveTimeSelected?.call(value);
}
},
icon: SvgPicture.asset(imagePaths.icDropDown),
buttonPadding: const EdgeInsets.symmetric(horizontal: 12),
buttonDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: AppColor.colorInputBorderCreateMailbox,
width: 1,
),
color: AppColor.colorInputBackgroundCreateMailbox,
),
itemHeight: 44,
buttonHeight: 44,
selectedItemHighlightColor: Colors.white,
itemPadding: const EdgeInsets.symmetric(horizontal: 12),
dropdownMaxHeight: 200,
dropdownDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
),
offset: const Offset(0.0, -8.0),
dropdownElevation: 4,
scrollbarRadius: const Radius.circular(40),
scrollbarThickness: 6
),
),
);
}
DropdownMenuItem<EmailReceiveTimeType> _buildItemMenu(
BuildContext context,
EmailReceiveTimeType receiveTime
) {
return DropdownMenuItem<EmailReceiveTimeType>(
value: receiveTime,
child: PointerInterceptor(
child: Container(
color: Colors.transparent,
height: 44,
child: Row(children: [
Expanded(child: Text(
receiveTime.getTitle(context),
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black
),
maxLines: 1,
softWrap: CommonTextStyle.defaultSoftWrap,
overflow: CommonTextStyle.defaultTextOverFlow,
)),
if (receiveTime == receiveTimeSelected)
SvgPicture.asset(
imagePaths.icChecked,
width: 20,
height: 20,
fit: BoxFit.fill
)
]),
),
),
);
}
}
@@ -1,6 +1,7 @@
import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/views/button/icon_button_web.dart'; import 'package:core/presentation/views/button/icon_button_web.dart';
import 'package:core/utils/app_logger.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@@ -34,9 +35,8 @@ class IconOpenAdvancedSearchWidget extends StatelessWidget {
width: 16, width: 16,
height: 16), height: 16),
onTap: () { onTap: () {
if(searchController.isAdvancedSearchViewOpen.isFalse && searchController.advancedSearchIsActivated.isFalse){ log('IconOpenAdvancedSearchWidget::build(): clicked');
advancedFilterController.initSearchFilterField(context); advancedFilterController.initSearchFilterField(context);
}
searchController.showAdvancedFilterView(_parentContext); searchController.showAdvancedFilterView(_parentContext);
}), }),
), ),
@@ -128,10 +128,7 @@ class SearchInputFormWidget extends StatelessWidget with AppLoaderMixin {
}, },
buttonActionCallback: (filterAction) { buttonActionCallback: (filterAction) {
if (filterAction is QuickSearchFilter) { if (filterAction is QuickSearchFilter) {
dashBoardController.selectQuickSearchFilter( dashBoardController.addFilterToSuggestionForm(filterAction);
quickSearchFilter: filterAction,
fromSuggestionBox: true,
);
} }
}, },
listActionPadding: const EdgeInsets.only( listActionPadding: const EdgeInsets.only(
@@ -210,43 +207,31 @@ class SearchInputFormWidget extends StatelessWidget with AppLoaderMixin {
} }
Widget buildListButtonForQuickSearchForm(BuildContext context, QuickSearchFilter filter) { Widget buildListButtonForQuickSearchForm(BuildContext context, QuickSearchFilter filter) {
final controller = dashBoardController.searchController;
return Obx(() { return Obx(() {
final isFilterSelected = dashBoardController.checkQuickSearchFilterSelected( final isFilterSelected = filter.isApplied(dashBoardController.searchController.listFilterOnSuggestionForm);
quickSearchFilter: filter);
return Chip( return Chip(
labelPadding: const EdgeInsets.only( labelPadding: const EdgeInsets.only(
top: 2, top: 2,
bottom: 2, bottom: 2,
right: 10), right: 10),
label: Text( label: Text(
filter.getTitle( filter.getName(context),
context,
receiveTimeType: controller.emailReceiveTimeType.value),
maxLines: 1, maxLines: 1,
overflow: CommonTextStyle.defaultTextOverFlow, overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap, softWrap: CommonTextStyle.defaultSoftWrap,
style: filter.getTextStyle( style: filter.getTextStyle(isFilterSelected: isFilterSelected),
quickSearchFilterSelected: isFilterSelected),
), ),
avatar: SvgPicture.asset( avatar: SvgPicture.asset(
filter.getIcon( filter.getIcon(imagePaths, isFilterSelected: isFilterSelected),
imagePaths, width: 16,
quickSearchFilterSelected: isFilterSelected), height: 16,
width: 16, fit: BoxFit.fill),
height: 16, labelStyle: filter.getTextStyle(isFilterSelected: isFilterSelected),
fit: BoxFit.fill), backgroundColor: filter.getBackgroundColor(isFilterSelected: isFilterSelected),
labelStyle: filter.getTextStyle(
quickSearchFilterSelected: isFilterSelected),
backgroundColor: filter.getBackgroundColor(
quickSearchFilterSelected: isFilterSelected),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
side: BorderSide( side: BorderSide(color: filter.getBackgroundColor(isFilterSelected: isFilterSelected)),
color: filter.getBackgroundColor(
quickSearchFilterSelected: isFilterSelected)),
), ),
); );
}); });
@@ -151,6 +151,7 @@ class ThreadController extends BaseController with EmailActionController {
newState.fold( newState.fold(
(failure) { (failure) {
if (failure is SearchEmailFailure) { if (failure is SearchEmailFailure) {
canSearchMore = false;
mailboxDashBoardController.emailsInCurrentMailbox.clear(); mailboxDashBoardController.emailsInCurrentMailbox.clear();
} else if (failure is SearchMoreEmailFailure || failure is LoadMoreEmailsFailure) { } else if (failure is SearchMoreEmailFailure || failure is LoadMoreEmailsFailure) {
_isLoadingMore = false; _isLoadingMore = false;
@@ -197,7 +198,7 @@ class ThreadController extends BaseController with EmailActionController {
if (_currentMailboxId != mailbox.id) { if (_currentMailboxId != mailbox.id) {
_currentMailboxId = mailbox.id; _currentMailboxId = mailbox.id;
_resetToOriginalValue(); _resetToOriginalValue();
_getAllEmail(); _getAllEmailAction();
} }
} else if (mailbox == null) { // disable current mailbox when search active } else if (mailbox == null) { // disable current mailbox when search active
_currentMailboxId = null; _currentMailboxId = null;
@@ -262,6 +263,12 @@ class ThreadController extends BaseController with EmailActionController {
_navigationRouter = action.navigationRouter; _navigationRouter = action.navigationRouter;
_activateSearchFromRouter(); _activateSearchFromRouter();
mailboxDashBoardController.clearDashBoardAction(); mailboxDashBoardController.clearDashBoardAction();
} else if (action is SelectDateRangeToAdvancedSearch || action is ClearDateRangeToAdvancedSearch) {
if (listEmailController.hasClients) {
listEmailController.jumpTo(0);
}
canSearchMore = true;
mailboxDashBoardController.emailsInCurrentMailbox.clear();
} }
}); });
@@ -335,18 +342,12 @@ class ThreadController extends BaseController with EmailActionController {
logError('ThreadController::_handleErrorGetAllOrRefreshChangesEmail():Error: $error'); logError('ThreadController::_handleErrorGetAllOrRefreshChangesEmail():Error: $error');
if (error is CannotCalculateChangesMethodResponseException) { if (error is CannotCalculateChangesMethodResponseException) {
await _cachingManager.clearEmailCache(); await _cachingManager.clearEmailCache();
_getAllEmail(); _getAllEmailAction();
} else { } else {
super.onError(error); super.onError(error);
} }
} }
void _getAllEmail() {
if (_accountId != null) {
_getAllEmailAction(_accountId!, mailboxId: _currentMailboxId);
}
}
void _resetToOriginalValue() { void _resetToOriginalValue() {
dispatchState(Right(LoadingState())); dispatchState(Right(LoadingState()));
mailboxDashBoardController.emailsInCurrentMailbox.clear(); mailboxDashBoardController.emailsInCurrentMailbox.clear();
@@ -389,48 +390,47 @@ class ThreadController extends BaseController with EmailActionController {
} }
} }
void _getAllEmailAction(AccountId accountId, {MailboxId? mailboxId}) { void _getAllEmailAction() {
consumeState(_getEmailsInMailboxInteractor.execute( if (_accountId != null) {
accountId, consumeState(_getEmailsInMailboxInteractor.execute(
limit: ThreadConstants.defaultLimit, _accountId!,
sort: _sortOrder, limit: ThreadConstants.defaultLimit,
emailFilter: EmailFilter( sort: _sortOrder,
filter: _getFilterCondition(), emailFilter: EmailFilter(
filterOption: mailboxDashBoardController.filterMessageOption.value, filter: _getFilterCondition(mailboxIdSelected: _currentMailboxId),
mailboxId: mailboxId ?? _currentMailboxId), filterOption: mailboxDashBoardController.filterMessageOption.value,
propertiesCreated: ThreadConstants.propertiesDefault, mailboxId: _currentMailboxId
propertiesUpdated: ThreadConstants.propertiesUpdatedDefault, ),
)); propertiesCreated: ThreadConstants.propertiesDefault,
propertiesUpdated: ThreadConstants.propertiesUpdatedDefault,
));
}
} }
EmailFilterCondition _getFilterCondition({bool isLoadMore = false}) { EmailFilterCondition _getFilterCondition({PresentationEmail? oldestEmail, MailboxId? mailboxIdSelected}) {
final lastEmail = mailboxDashBoardController.emailsInCurrentMailbox.isNotEmpty
? mailboxDashBoardController.emailsInCurrentMailbox.last
: null;
final mailboxIdSelected = mailboxDashBoardController.selectedMailbox.value?.id;
switch(mailboxDashBoardController.filterMessageOption.value) { switch(mailboxDashBoardController.filterMessageOption.value) {
case FilterMessageOption.all: case FilterMessageOption.all:
return EmailFilterCondition( return EmailFilterCondition(
inMailbox: mailboxIdSelected, inMailbox: mailboxIdSelected,
before: isLoadMore ? lastEmail?.receivedAt : null before: oldestEmail?.receivedAt
); );
case FilterMessageOption.unread: case FilterMessageOption.unread:
return EmailFilterCondition( return EmailFilterCondition(
inMailbox: mailboxIdSelected, inMailbox: mailboxIdSelected,
notKeyword: KeyWordIdentifier.emailSeen.value, notKeyword: KeyWordIdentifier.emailSeen.value,
before: isLoadMore ? lastEmail?.receivedAt : null before: oldestEmail?.receivedAt
); );
case FilterMessageOption.attachments: case FilterMessageOption.attachments:
return EmailFilterCondition( return EmailFilterCondition(
inMailbox: mailboxIdSelected, inMailbox: mailboxIdSelected,
hasAttachment: true, hasAttachment: true,
before: isLoadMore ? lastEmail?.receivedAt : null before: oldestEmail?.receivedAt
); );
case FilterMessageOption.starred: case FilterMessageOption.starred:
return EmailFilterCondition( return EmailFilterCondition(
inMailbox: mailboxIdSelected, inMailbox: mailboxIdSelected,
hasKeyword: KeyWordIdentifier.emailFlagged.value, hasKeyword: KeyWordIdentifier.emailFlagged.value,
before: isLoadMore ? lastEmail?.receivedAt : null before: oldestEmail?.receivedAt
); );
} }
} }
@@ -441,10 +441,9 @@ class ThreadController extends BaseController with EmailActionController {
cancelSelectEmail(); cancelSelectEmail();
if (searchController.isSearchEmailRunning) { if (searchController.isSearchEmailRunning) {
searchController.searchEmailFilter.value = _searchEmailFilter.clearBeforeDate();
_searchEmail(limit: limitEmailFetched); _searchEmail(limit: limitEmailFetched);
} else { } else {
_getAllEmail(); _getAllEmailAction();
} }
} }
@@ -459,22 +458,22 @@ class ThreadController extends BaseController with EmailActionController {
void _refreshEmailChanges({jmap.State? currentEmailState}) { void _refreshEmailChanges({jmap.State? currentEmailState}) {
log('ThreadController::_refreshEmailChanges(): currentEmailState: $currentEmailState'); log('ThreadController::_refreshEmailChanges(): currentEmailState: $currentEmailState');
if (searchController.isSearchEmailRunning) { if (searchController.isSearchEmailRunning) {
searchController.searchEmailFilter.value = _searchEmailFilter.clearBeforeDate();
_searchEmail(limit: limitEmailFetched); _searchEmail(limit: limitEmailFetched);
} else { } else {
final newEmailState = currentEmailState ?? _currentEmailState; final newEmailState = currentEmailState ?? _currentEmailState;
log('ThreadController::_refreshEmailChanges(): newEmailState: $newEmailState'); log('ThreadController::_refreshEmailChanges(): newEmailState: $newEmailState');
if (_accountId != null && newEmailState != null) { if (_accountId != null && newEmailState != null) {
consumeState(_refreshChangesEmailsInMailboxInteractor.execute( consumeState(_refreshChangesEmailsInMailboxInteractor.execute(
_accountId!, _accountId!,
newEmailState, newEmailState,
sort: _sortOrder, sort: _sortOrder,
propertiesCreated: ThreadConstants.propertiesDefault, propertiesCreated: ThreadConstants.propertiesDefault,
propertiesUpdated: ThreadConstants.propertiesUpdatedDefault, propertiesUpdated: ThreadConstants.propertiesUpdatedDefault,
emailFilter: EmailFilter( emailFilter: EmailFilter(
filter: _getFilterCondition(), filter: _getFilterCondition(mailboxIdSelected: _currentMailboxId),
filterOption: mailboxDashBoardController.filterMessageOption.value, filterOption: mailboxDashBoardController.filterMessageOption.value,
mailboxId: _currentMailboxId), mailboxId: _currentMailboxId
)
)); ));
} }
} }
@@ -483,15 +482,19 @@ class ThreadController extends BaseController with EmailActionController {
void loadMoreEmails() { void loadMoreEmails() {
log('ThreadController::loadMoreEmails()'); log('ThreadController::loadMoreEmails()');
if (canLoadMore && _accountId != null) { if (canLoadMore && _accountId != null) {
final oldestEmail = mailboxDashBoardController.emailsInCurrentMailbox.isNotEmpty
? mailboxDashBoardController.emailsInCurrentMailbox.last
: null;
consumeState(_loadMoreEmailsInMailboxInteractor.execute( consumeState(_loadMoreEmailsInMailboxInteractor.execute(
GetEmailRequest( GetEmailRequest(
_accountId!, _accountId!,
limit: ThreadConstants.defaultLimit, limit: ThreadConstants.defaultLimit,
sort: _sortOrder, sort: _sortOrder,
filterOption: mailboxDashBoardController.filterMessageOption.value, filterOption: mailboxDashBoardController.filterMessageOption.value,
filter: _getFilterCondition(isLoadMore: true), filter: _getFilterCondition(oldestEmail: oldestEmail),
properties: ThreadConstants.propertiesDefault, properties: ThreadConstants.propertiesDefault,
lastEmailId: mailboxDashBoardController.emailsInCurrentMailbox.last.id) lastEmailId: oldestEmail?.id
)
)); ));
} }
} }
@@ -649,7 +652,7 @@ class ThreadController extends BaseController with EmailActionController {
icon: newFilterOption.getIconToast(_imagePaths)); icon: newFilterOption.getIconToast(_imagePaths));
if (searchController.isSearchEmailRunning) { if (searchController.isSearchEmailRunning) {
_searchEmail(filterCondition: _getFilterCondition()); _searchEmail();
} else { } else {
refreshAllEmail(); refreshAllEmail();
} }
@@ -664,21 +667,22 @@ class ThreadController extends BaseController with EmailActionController {
searchController.clearTextSearch(); searchController.clearTextSearch();
} }
void _searchEmail({UnsignedInt? limit, EmailFilterCondition? filterCondition}) { void _searchEmail({UnsignedInt? limit}) {
if (_accountId != null) { if (_accountId != null) {
searchController.activateSimpleSearch(); if (listEmailController.hasClients) {
listEmailController.jumpTo(0);
}
mailboxDashBoardController.emailsInCurrentMailbox.clear();
canSearchMore = true;
searchController.updateFilterEmail(beforeOption: const None());
filterCondition = EmailFilterCondition( searchController.activateSimpleSearch();
notKeyword: filterCondition?.notKeyword,
hasKeyword: filterCondition?.hasKeyword,
hasAttachment: filterCondition?.hasAttachment,
);
consumeState(_searchEmailInteractor.execute( consumeState(_searchEmailInteractor.execute(
_accountId!, _accountId!,
limit: limit ?? ThreadConstants.defaultLimit, limit: limit ?? ThreadConstants.defaultLimit,
sort: _sortOrder, sort: _sortOrder,
filter: _searchEmailFilter.mappingToEmailFilterCondition(moreFilterCondition: filterCondition), filter: _searchEmailFilter.mappingToEmailFilterCondition(moreFilterCondition: _getFilterCondition()),
properties: ThreadConstants.propertiesDefault, properties: ThreadConstants.propertiesDefault,
)); ));
} }
@@ -697,6 +701,7 @@ class ThreadController extends BaseController with EmailActionController {
} }
void _searchEmailsSuccess(SearchEmailSuccess success) { void _searchEmailsSuccess(SearchEmailSuccess success) {
canSearchMore = true;
final resultEmailSearchList = success.emailList final resultEmailSearchList = success.emailList
.map((email) => email.toSearchPresentationEmail(mailboxDashBoardController.mapMailboxById)) .map((email) => email.toSearchPresentationEmail(mailboxDashBoardController.mapMailboxById))
.toList(); .toList();
@@ -716,21 +721,24 @@ class ThreadController extends BaseController with EmailActionController {
void searchMoreEmails() { void searchMoreEmails() {
if (canSearchMore && _accountId != null) { if (canSearchMore && _accountId != null) {
final lastEmail = mailboxDashBoardController.emailsInCurrentMailbox.last; final oldestEmail = mailboxDashBoardController.emailsInCurrentMailbox.isNotEmpty
searchController.updateFilterEmail(before: lastEmail.receivedAt); ? mailboxDashBoardController.emailsInCurrentMailbox.last
: null;
searchController.updateFilterEmail(beforeOption: optionOf(oldestEmail?.receivedAt));
consumeState(_searchMoreEmailInteractor.execute( consumeState(_searchMoreEmailInteractor.execute(
_accountId!, _accountId!,
limit: ThreadConstants.defaultLimit, limit: ThreadConstants.defaultLimit,
sort: _sortOrder, sort: _sortOrder,
filter: searchController.searchEmailFilter.value.mappingToEmailFilterCondition(), filter: searchController.searchEmailFilter.value.mappingToEmailFilterCondition(moreFilterCondition: _getFilterCondition()),
properties: ThreadConstants.propertiesDefault, properties: ThreadConstants.propertiesDefault,
lastEmailId: lastEmail.id lastEmailId: oldestEmail?.id
)); ));
} }
} }
void _searchMoreEmailsSuccess(SearchMoreEmailSuccess success) { void _searchMoreEmailsSuccess(SearchMoreEmailSuccess success) {
if (success.emailList.isNotEmpty) { if (success.emailList.isNotEmpty) {
canSearchMore = true;
final resultEmailSearchList = success.emailList final resultEmailSearchList = success.emailList
.map((email) => email.toSearchPresentationEmail(mailboxDashBoardController.mapMailboxById)) .map((email) => email.toSearchPresentationEmail(mailboxDashBoardController.mapMailboxById))
.where((email) => !mailboxDashBoardController.emailsInCurrentMailbox.contains(email)) .where((email) => !mailboxDashBoardController.emailsInCurrentMailbox.contains(email))
+1 -7
View File
@@ -1,5 +1,5 @@
{ {
"@@last_modified": "2023-02-24T11:57:26.810965", "@@last_modified": "2023-03-02T21:08:14.879842",
"initializing_data": "Initializing data...", "initializing_data": "Initializing data...",
"@initializing_data": { "@initializing_data": {
"type": "text", "type": "text",
@@ -1488,12 +1488,6 @@
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
}, },
"allMails": "All mails",
"@allMails": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"allTime": "All time", "allTime": "All time",
"@allTime": { "@allTime": {
"type": "text", "type": "text",
@@ -1515,13 +1515,6 @@ class AppLocalizations {
); );
} }
String get allMails {
return Intl.message(
'All mails',
name: 'allMails',
);
}
String get allTime { String get allTime {
return Intl.message( return Intl.message(
'All time', 'All time',
@@ -107,6 +107,14 @@ class PresentationMailbox with EquatableMixin {
bool get allowedToDisplay => isSubscribedMailbox || isDefault; bool get allowedToDisplay => isSubscribedMailbox || isDefault;
MailboxId? get mailboxId {
if (id == unifiedMailbox.id) {
return null;
} else {
return id;
}
}
@override @override
List<Object?> get props => [ List<Object?> get props => [
id, id,