Use common FilterField & EmailReceiveTimeType objects for the entire app

Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
dab246
2025-07-11 01:31:07 +07:00
committed by Dat H. Pham
parent 84e3c526f7
commit da29ff01e3
17 changed files with 253 additions and 378 deletions
+104
View File
@@ -0,0 +1,104 @@
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
enum FilterField {
from,
to,
cc,
bcc,
replyTo,
subject,
hasKeyword,
notKeyword,
mailBox,
date,
sortBy,
hasAttachment,
deletionDate,
receptionDate,
recoverSubject,
recipients,
sender;
String getTitle(AppLocalizations appLocalizations) {
switch (this) {
case FilterField.from:
return appLocalizations.from_email_address_prefix;
case FilterField.to:
return appLocalizations.to_email_address_prefix;
case FilterField.subject:
case FilterField.recoverSubject:
return appLocalizations.subject;
case FilterField.hasKeyword:
return appLocalizations.hasTheWords;
case FilterField.notKeyword:
return appLocalizations.doesNotHave;
case FilterField.mailBox:
return appLocalizations.folder;
case FilterField.date:
return appLocalizations.date;
case FilterField.sortBy:
return appLocalizations.sortBy;
case FilterField.hasAttachment:
return appLocalizations.hasAttachment;
case FilterField.deletionDate:
return appLocalizations.deletionDate;
case FilterField.receptionDate:
return appLocalizations.receptionDate;
case FilterField.recipients:
return appLocalizations.headerRecipients;
case FilterField.sender:
return appLocalizations.sender;
default:
return '';
}
}
String getHintText(AppLocalizations appLocalizations) {
switch (this) {
case FilterField.from:
case FilterField.to:
return appLocalizations.nameOrEmailAddress;
case FilterField.subject:
return appLocalizations.enterASubject;
case FilterField.hasKeyword:
case FilterField.notKeyword:
return appLocalizations.enterSomeSuggestions;
case FilterField.mailBox:
return appLocalizations.allFolders;
case FilterField.date:
return appLocalizations.allTime;
case FilterField.sortBy:
return appLocalizations.mostRecent;
case FilterField.hasAttachment:
return appLocalizations.hasAttachment;
case FilterField.recoverSubject:
return appLocalizations.enterSubjectKeywords;
case FilterField.recipients:
case FilterField.sender:
return appLocalizations.addAnEmailAddress;
default:
return '';
}
}
List<EmailReceiveTimeType> getSupportedTimeTypes(
DateTime restorationHorizon,
) {
switch (this) {
case FilterField.deletionDate:
final supportedTypes = EmailReceiveTimeType
.valuesForRecoverDeletionDateField
.where((type) => restorationHorizon
.subtract(const Duration(seconds: 2)) // to allow "15 days" if restorationHorizon is exactly 15
.isBefore(type.toOldestUTCDate()!.value))
.toList();
return [...supportedTypes, EmailReceiveTimeType.customRange];
case FilterField.receptionDate:
return EmailReceiveTimeType.valuesForRecoverReceptionDateDateField;
default:
return [];
}
}
}
@@ -2,6 +2,7 @@
import 'package:flutter/cupertino.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/base/model/filter_filter.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
extension PrefixEmailAddressExtension on PrefixEmailAddress {
@@ -35,4 +36,19 @@ extension PrefixEmailAddressExtension on PrefixEmailAddress {
return email.from?.toList() ?? List.empty();
}
}
FilterField get filterField {
switch(this) {
case PrefixEmailAddress.from:
return FilterField.from;
case PrefixEmailAddress.to:
return FilterField.to;
case PrefixEmailAddress.cc:
return FilterField.cc;
case PrefixEmailAddress.bcc:
return FilterField.bcc;
case PrefixEmailAddress.replyTo:
return FilterField.replyTo;
}
}
}
@@ -1,6 +1,6 @@
import 'package:model/email/prefix_email_address.dart';
import 'package:model/mailbox/expand_mode.dart';
import 'package:tmail_ui_user/features/base/model/filter_filter.dart';
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/draggable_email_address.dart';
@@ -30,20 +30,20 @@ extension RemoveDraggableEmailAddressBetweenRecipientFieldsExtension on Composer
required ComposerController controller,
required DraggableEmailAddress draggableEmailAddress,
}) {
switch(draggableEmailAddress.prefix) {
case PrefixEmailAddress.to:
switch(draggableEmailAddress.filterField) {
case FilterField.to:
controller.listToEmailAddress.remove(draggableEmailAddress.emailAddress);
controller.toAddressExpandMode.value = ExpandMode.EXPAND;
break;
case PrefixEmailAddress.cc:
case FilterField.cc:
controller.listCcEmailAddress.remove(draggableEmailAddress.emailAddress);
controller.ccAddressExpandMode.value = ExpandMode.EXPAND;
break;
case PrefixEmailAddress.bcc:
case FilterField.bcc:
controller.listBccEmailAddress.remove(draggableEmailAddress.emailAddress);
controller.bccAddressExpandMode.value = ExpandMode.EXPAND;
break;
case PrefixEmailAddress.replyTo:
case FilterField.replyTo:
controller.listReplyToEmailAddress.remove(draggableEmailAddress.emailAddress);
controller.replyToAddressExpandMode.value = ExpandMode.EXPAND;
break;
@@ -1,18 +1,18 @@
import 'package:equatable/equatable.dart';
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:model/email/prefix_email_address.dart';
import 'package:tmail_ui_user/features/base/model/filter_filter.dart';
class DraggableEmailAddress with EquatableMixin {
final EmailAddress emailAddress;
final PrefixEmailAddress prefix;
final FilterField filterField;
final String? composerId;
DraggableEmailAddress({
required this.emailAddress,
required this.prefix,
required this.filterField,
this.composerId,
});
@override
List<Object?> get props => [emailAddress, prefix, composerId];
List<Object?> get props => [emailAddress, filterField, composerId];
}
@@ -594,7 +594,7 @@ class _RecipientComposerWidgetState extends State<RecipientComposerWidget> {
) {
log('_RecipientComposerWidgetState::_handleAcceptDraggableEmailAddressAction: $draggableEmailAddress');
if (draggableEmailAddress.composerId == widget.composerId) {
if (draggableEmailAddress.prefix != widget.prefix) {
if (draggableEmailAddress.filterField != widget.prefix.filterField) {
if (!_currentListEmailAddress.contains(draggableEmailAddress.emailAddress)) {
stateSetter(() {
_currentListEmailAddress.add(draggableEmailAddress.emailAddress);
@@ -11,6 +11,7 @@ import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:model/email/prefix_email_address.dart';
import 'package:model/extensions/email_address_extension.dart';
import 'package:tmail_ui_user/features/base/widget/card_with_smart_interaction_overlay_view.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/prefix_email_address_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/draggable_email_address.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/email_address_action_type.dart';
import 'package:tmail_ui_user/features/composer/presentation/styles/recipient_tag_item_widget_style.dart';
@@ -137,7 +138,7 @@ class RecipientTagItemWidget extends StatelessWidget {
tagWidget = Draggable<DraggableEmailAddress>(
data: DraggableEmailAddress(
emailAddress: currentEmailAddress,
prefix: prefix,
filterField: prefix.filterField,
composerId: composerId,
),
feedback: DraggableRecipientTagWidget(emailAddress: currentEmailAddress),
@@ -1,99 +0,0 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:flutter/material.dart';
import 'package:tmail_ui_user/features/email_recovery/presentation/model/email_recovery_time_type.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
enum EmailRecoveryField {
deletionDate,
receptionDate,
subject,
recipients,
sender;
String getTitle(BuildContext context) {
switch (this) {
case EmailRecoveryField.deletionDate:
return AppLocalizations.of(context).deletionDate;
case EmailRecoveryField.receptionDate:
return AppLocalizations.of(context).receptionDate;
case EmailRecoveryField.subject:
return AppLocalizations.of(context).subject;
case EmailRecoveryField.recipients:
return AppLocalizations.of(context).headerRecipients;
case EmailRecoveryField.sender:
return AppLocalizations.of(context).sender;
}
}
String getHintText(BuildContext context) {
switch (this) {
case EmailRecoveryField.deletionDate:
return AppLocalizations.of(context).last7Days;
case EmailRecoveryField.receptionDate:
return AppLocalizations.of(context).allTime;
case EmailRecoveryField.subject:
return AppLocalizations.of(context).enterSomeSuggestions;
case EmailRecoveryField.recipients:
return AppLocalizations.of(context).addRecipientButton;
case EmailRecoveryField.sender:
return AppLocalizations.of(context).addSender;
}
}
List<EmailRecoveryTimeType> getSupportedTimeTypes(DateTime restorationHorizon) {
switch (this) {
case EmailRecoveryField.deletionDate:
final supportedTypes = [
EmailRecoveryTimeType.last7Days,
EmailRecoveryTimeType.last15Days,
EmailRecoveryTimeType.last30Days,
EmailRecoveryTimeType.last6Months,
EmailRecoveryTimeType.last1Year,
].where((type) => restorationHorizon
.subtract(const Duration(seconds: 2)) // to allow "15 days" if restorationHorizon is exactly 15
.isBefore(type.toOldestUTCDate()!.value)).toList();
return [...supportedTypes, EmailRecoveryTimeType.customRange];
case EmailRecoveryField.receptionDate:
return [
EmailRecoveryTimeType.allTime,
EmailRecoveryTimeType.last7Days,
EmailRecoveryTimeType.last30Days,
EmailRecoveryTimeType.last6Months,
EmailRecoveryTimeType.lastYear,
EmailRecoveryTimeType.customRange,
];
default:
return [];
}
}
TextStyle getTitleTextStyle() {
return ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 14,
fontWeight: FontWeight.w400,
color: AppColor.colorContentEmail,
);
}
TextStyle getHintTextStyle() {
final Color color;
switch (this) {
case EmailRecoveryField.deletionDate:
case EmailRecoveryField.receptionDate:
color = Colors.black;
break;
case EmailRecoveryField.subject:
case EmailRecoveryField.recipients:
case EmailRecoveryField.sender:
color = AppColor.colorHintSearchBar;
break;
}
return ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 16,
fontWeight: FontWeight.w400,
color: color,
);
}
}
@@ -1,105 +0,0 @@
import 'package:flutter/material.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/datetime_extension.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:jmap_dart_client/jmap/core/utc_date.dart';
enum EmailRecoveryTimeType {
allTime,
last7Days,
last15Days,
last30Days,
last6Months,
last1Year,
lastYear,
customRange;
String getTitle(BuildContext context, {DateTime? startDate, DateTime? endDate}) {
switch(this) {
case EmailRecoveryTimeType.allTime:
return AppLocalizations.of(context).allTime;
case EmailRecoveryTimeType.last7Days:
return AppLocalizations.of(context).last7Days;
case EmailRecoveryTimeType.last15Days:
return AppLocalizations.of(context).last15Days;
case EmailRecoveryTimeType.last30Days:
return AppLocalizations.of(context).last30Days;
case EmailRecoveryTimeType.last6Months:
return AppLocalizations.of(context).last6Months;
case EmailRecoveryTimeType.last1Year:
return AppLocalizations.of(context).last1Year;
case EmailRecoveryTimeType.lastYear:
return AppLocalizations.of(context).lastYears;
case EmailRecoveryTimeType.customRange:
if (startDate != null && endDate != null) {
final startDateString = startDate.formatDate(pattern: 'yyyy-dd-MM');
final endDateString = endDate.formatDate(pattern: 'yyyy-dd-MM');
return AppLocalizations.of(context).dateRangeAdvancedSearchFilter(
startDateString,
endDateString
);
} else {
return AppLocalizations.of(context).customRange;
}
}
}
UTCDate? toOldestUTCDate() {
switch(this) {
case EmailRecoveryTimeType.last7Days:
final today = DateTime.now();
final last7Days = today.subtract(const Duration(days: 7));
return last7Days.toUTCDate();
case EmailRecoveryTimeType.last15Days:
final today = DateTime.now();
final last15Days = today.subtract(const Duration(days: 15));
return last15Days.toUTCDate();
case EmailRecoveryTimeType.last30Days:
final today = DateTime.now();
final last30Days = today.subtract(const Duration(days: 30));
return last30Days.toUTCDate();
case EmailRecoveryTimeType.last6Months:
final today = DateTime.now();
final last6months = DateTime(today.year, today.month - 6, today.day);
return last6months.toUTCDate();
case EmailRecoveryTimeType.last1Year:
case EmailRecoveryTimeType.lastYear:
final today = DateTime.now();
final lastYear = DateTime(today.year - 1, today.month, today.day);
return lastYear.toUTCDate();
default:
return null;
}
}
UTCDate? toLatestUTCDate() {
switch(this) {
case EmailRecoveryTimeType.last7Days:
case EmailRecoveryTimeType.last15Days:
case EmailRecoveryTimeType.last30Days:
case EmailRecoveryTimeType.last6Months:
case EmailRecoveryTimeType.last1Year:
case EmailRecoveryTimeType.lastYear:
final today = DateTime.now();
return today.toUTCDate();
default:
return null;
}
}
UTCDate? getAfterDate(UTCDate? startDate) {
if (startDate != null) {
return startDate;
} else {
return toOldestUTCDate();
}
}
UTCDate? getBeforeDate(UTCDate? endDate) {
if (endDate != null) {
return endDate;
} else {
return toLatestUTCDate();
}
}
}
@@ -18,7 +18,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/das
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/input_field_focus_manager.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' as search;
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/advanced_search_filter.dart';
import 'package:tmail_ui_user/features/base/model/filter_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';
@@ -359,14 +359,14 @@ class AdvancedFilterController extends BaseController {
}
}
void showFullEmailAddress(AdvancedSearchFilterField field) {
void showFullEmailAddress(FilterField field) {
FocusManager.instance.primaryFocus?.unfocus();
switch(field) {
case AdvancedSearchFilterField.from:
case FilterField.from:
fromAddressExpandMode.value = ExpandMode.EXPAND;
break;
case AdvancedSearchFilterField.to:
case FilterField.to:
toAddressExpandMode.value = ExpandMode.EXPAND;
break;
default:
@@ -375,18 +375,18 @@ class AdvancedFilterController extends BaseController {
}
void updateListEmailAddress(
AdvancedSearchFilterField field,
FilterField field,
List<EmailAddress> listEmailAddress,
) {
switch(field) {
case AdvancedSearchFilterField.from:
case FilterField.from:
listFromEmailAddress = List.from(listEmailAddress);
_updateMemorySearchFilter(
fromOption: option(
listFromEmailAddress.isNotEmpty,
listFromEmailAddress.asSetAddress()));
break;
case AdvancedSearchFilterField.to:
case FilterField.to:
listToEmailAddress = List.from(listEmailAddress);
_updateMemorySearchFilter(
toOption: option(
@@ -526,19 +526,19 @@ class AdvancedFilterController extends BaseController {
_updateMemorySearchFilter(hasAttachmentOption: Some(hasAttachment.value));
}
void onTextChanged(AdvancedSearchFilterField filterField, String value) {
void onTextChanged(FilterField filterField, String value) {
switch (filterField) {
case AdvancedSearchFilterField.subject:
case FilterField.subject:
final subjectOption = option(value.trim().isNotEmpty, value.trim());
_updateMemorySearchFilter(subjectOption: subjectOption);
break;
case AdvancedSearchFilterField.hasKeyword:
case FilterField.hasKeyword:
final textOption = option(
value.trim().isNotEmpty,
SearchQuery(value.trim()));
_updateMemorySearchFilter(textOption: textOption);
break;
case AdvancedSearchFilterField.notKeyword:
case FilterField.notKeyword:
final notKeywordsOption = option(
value.trim().isNotEmpty,
value.trim().split(',').map((value) => value.trim()).toSet());
@@ -551,8 +551,8 @@ class AdvancedFilterController extends BaseController {
void removeDraggableEmailAddress(DraggableEmailAddress draggableEmailAddress) {
log('AdvancedFilterController::removeDraggableEmailAddress:removeDraggableEmailAddress: $draggableEmailAddress');
switch(draggableEmailAddress.prefix) {
case PrefixEmailAddress.to:
switch(draggableEmailAddress.filterField) {
case FilterField.to:
listToEmailAddress.remove(draggableEmailAddress.emailAddress);
_updateMemorySearchFilter(
toOption: option(
@@ -563,7 +563,7 @@ class AdvancedFilterController extends BaseController {
toAddressExpandMode.value = ExpandMode.EXPAND;
toAddressExpandMode.refresh();
break;
case PrefixEmailAddress.from:
case FilterField.from:
listFromEmailAddress.remove(draggableEmailAddress.emailAddress);
_updateMemorySearchFilter(
fromOption: option(
@@ -818,7 +818,7 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView {
}
void _openPopupMenuDateFilter(BuildContext context, RelativeRect position) {
final popupMenuItems = EmailReceiveTimeType.values.map((timeType) {
final popupMenuItems = EmailReceiveTimeType.valuesForSearch.map((timeType) {
return PopupMenuItem(
padding: EdgeInsets.zero,
child: PopupMenuItemActionWidget(
@@ -1,85 +0,0 @@
import 'package:core/core.dart';
import 'package:flutter/cupertino.dart';
import 'package:model/email/prefix_email_address.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
enum AdvancedSearchFilterField {
from,
to,
subject,
hasKeyword,
notKeyword,
mailBox,
date,
sortBy,
hasAttachment;
String getTitle(BuildContext context) {
switch (this) {
case AdvancedSearchFilterField.from:
return AppLocalizations.of(context).from_email_address_prefix;
case AdvancedSearchFilterField.to:
return AppLocalizations.of(context).to_email_address_prefix;
case AdvancedSearchFilterField.subject:
return AppLocalizations.of(context).subject;
case AdvancedSearchFilterField.hasKeyword:
return AppLocalizations.of(context).hasTheWords;
case AdvancedSearchFilterField.notKeyword:
return AppLocalizations.of(context).doesNotHave;
case AdvancedSearchFilterField.mailBox:
return AppLocalizations.of(context).folder;
case AdvancedSearchFilterField.date:
return AppLocalizations.of(context).date;
case AdvancedSearchFilterField.sortBy:
return AppLocalizations.of(context).sortBy;
case AdvancedSearchFilterField.hasAttachment:
return AppLocalizations.of(context).hasAttachment;
}
}
String getHintText(BuildContext context) {
switch (this) {
case AdvancedSearchFilterField.from:
case AdvancedSearchFilterField.to:
return AppLocalizations.of(context).nameOrEmailAddress;
case AdvancedSearchFilterField.subject:
return AppLocalizations.of(context).enterASubject;
case AdvancedSearchFilterField.hasKeyword:
case AdvancedSearchFilterField.notKeyword:
return AppLocalizations.of(context).enterSomeSuggestions;
case AdvancedSearchFilterField.mailBox:
return AppLocalizations.of(context).allFolders;
case AdvancedSearchFilterField.date:
return AppLocalizations.of(context).allTime;
case AdvancedSearchFilterField.sortBy:
return AppLocalizations.of(context).mostRecent;
case AdvancedSearchFilterField.hasAttachment:
return AppLocalizations.of(context).hasAttachment;
}
}
TextStyle getTitleTextStyle() {
return ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 14,
fontWeight: FontWeight.w400,
color: AppColor.colorContentEmail);
}
PrefixEmailAddress getPrefixEmailAddress() {
switch (this) {
case AdvancedSearchFilterField.from:
return PrefixEmailAddress.from;
case AdvancedSearchFilterField.to:
return PrefixEmailAddress.to;
default:
return PrefixEmailAddress.cc;
}
}
TextStyle getHintTextStyle() {
return ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 16,
fontWeight: FontWeight.w400,
color: AppColor.colorHintSearchBar);
}
}
@@ -7,11 +7,39 @@ import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
enum EmailReceiveTimeType {
allTime,
last7Days,
last15Days,
last30Days,
last6Months,
last1Year,
lastYear,
customRange;
static List<EmailReceiveTimeType> get valuesForSearch => [
allTime,
last7Days,
last30Days,
last6Months,
lastYear,
customRange,
];
static List<EmailReceiveTimeType> get valuesForRecoverDeletionDateField => [
last7Days,
last15Days,
last30Days,
last6Months,
last1Year,
];
static List<EmailReceiveTimeType> get valuesForRecoverReceptionDateDateField => [
allTime,
last7Days,
last30Days,
last6Months,
lastYear,
customRange,
];
String getTitle(BuildContext context, {DateTime? startDate, DateTime? endDate}) {
return getTitleByAppLocalizations(
AppLocalizations.of(context),
@@ -32,10 +60,14 @@ enum EmailReceiveTimeType {
return appLocalizations.allTime;
case EmailReceiveTimeType.last7Days:
return appLocalizations.last7Days;
case EmailReceiveTimeType.last15Days:
return appLocalizations.last15Days;
case EmailReceiveTimeType.last30Days:
return appLocalizations.last30Days;
case EmailReceiveTimeType.last6Months:
return appLocalizations.last6Months;
case EmailReceiveTimeType.last1Year:
return appLocalizations.last1Year;
case EmailReceiveTimeType.lastYear:
return appLocalizations.lastYears;
case EmailReceiveTimeType.customRange:
@@ -58,6 +90,10 @@ enum EmailReceiveTimeType {
final today = DateTime.now();
final last7Days = today.subtract(const Duration(days: 7));
return last7Days.toUTCDate();
case EmailReceiveTimeType.last15Days:
final today = DateTime.now();
final last15Days = today.subtract(const Duration(days: 15));
return last15Days.toUTCDate();
case EmailReceiveTimeType.last30Days:
final today = DateTime.now();
final last30Days = today.subtract(const Duration(days: 30));
@@ -66,6 +102,7 @@ enum EmailReceiveTimeType {
final today = DateTime.now();
final last6months = DateTime(today.year, today.month - 6, today.day);
return last6months.toUTCDate();
case EmailReceiveTimeType.last1Year:
case EmailReceiveTimeType.lastYear:
final today = DateTime.now();
final lastYear = DateTime(today.year - 1, today.month, today.day);
@@ -78,8 +115,10 @@ enum EmailReceiveTimeType {
UTCDate? toLatestUTCDate() {
switch(this) {
case EmailReceiveTimeType.last7Days:
case EmailReceiveTimeType.last15Days:
case EmailReceiveTimeType.last30Days:
case EmailReceiveTimeType.last6Months:
case EmailReceiveTimeType.last1Year:
case EmailReceiveTimeType.lastYear:
return DateTime.now().toUTCDate();
default:
@@ -6,11 +6,11 @@ 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/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/model/search/advanced_search_filter.dart';
import 'package:tmail_ui_user/features/base/model/filter_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/styles/advanced_search_input_form_style.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/date_drop_down_button.dart';
import 'package:tmail_ui_user/features/base/widget/default_field/date_drop_down_button.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/label_advanced_search_field_widget.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/sort_by_drop_down_button.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/text_field_autocomplete_email_address_web.dart';
@@ -29,7 +29,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
child: Column(
children: [
Obx(() => TextFieldAutocompleteEmailAddressWeb(
field: AdvancedSearchFilterField.from,
field: FilterField.from,
listEmailAddress: controller.listFromEmailAddress,
expandMode: controller.fromAddressExpandMode.value,
minInputLengthAutocomplete: controller
@@ -47,7 +47,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
)),
const SizedBox(height: 12),
Obx(() => TextFieldAutocompleteEmailAddressWeb(
field: AdvancedSearchFilterField.to,
field: FilterField.to,
listEmailAddress: controller.listToEmailAddress,
expandMode: controller.toAddressExpandMode.value,
minInputLengthAutocomplete: controller
@@ -67,11 +67,11 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
_buildFilterField(
textEditingController: controller.subjectFilterInputController,
context: context,
advancedSearchFilterField: AdvancedSearchFilterField.subject,
filterField: FilterField.subject,
currentFocusNode: controller.focusManager.subjectFieldFocusNode,
nextFocusNode: controller.focusManager.hasKeywordFieldFocusNode,
onTextChange: (value) => controller.onTextChanged(
AdvancedSearchFilterField.subject,
FilterField.subject,
value
),
),
@@ -79,11 +79,11 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
_buildFilterField(
textEditingController: controller.hasKeyWordFilterInputController,
context: context,
advancedSearchFilterField: AdvancedSearchFilterField.hasKeyword,
filterField: FilterField.hasKeyword,
currentFocusNode: controller.focusManager.hasKeywordFieldFocusNode,
nextFocusNode: controller.focusManager.notKeywordFieldFocusNode,
onTextChange: (value) => controller.onTextChanged(
AdvancedSearchFilterField.hasKeyword,
FilterField.hasKeyword,
value
),
),
@@ -91,11 +91,11 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
_buildFilterField(
textEditingController: controller.notKeyWordFilterInputController,
context: context,
advancedSearchFilterField: AdvancedSearchFilterField.notKeyword,
filterField: FilterField.notKeyword,
currentFocusNode: controller.focusManager.notKeywordFieldFocusNode,
nextFocusNode: controller.focusManager.mailboxFieldFocusNode,
onTextChange: (value) => controller.onTextChanged(
AdvancedSearchFilterField.notKeyword,
FilterField.notKeyword,
value
),
),
@@ -103,7 +103,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
_buildFilterField(
textEditingController: controller.mailBoxFilterInputController,
context: context,
advancedSearchFilterField: AdvancedSearchFilterField.mailBox,
filterField: FilterField.mailBox,
isSelectFormList: true,
currentFocusNode: controller.focusManager.mailboxFieldFocusNode,
nextFocusNode: controller.focusManager.attachmentCheckboxFocusNode,
@@ -114,7 +114,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
Row(children: [
Expanded(child: _buildFilterField(
context: context,
advancedSearchFilterField: AdvancedSearchFilterField.date,
filterField: FilterField.date,
isSelectFormList: true,
onTap: () {
openContextMenuAction(
@@ -151,7 +151,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
const SizedBox(height: 12),
_buildFilterField(
context: context,
advancedSearchFilterField: AdvancedSearchFilterField.sortBy
filterField: FilterField.sortBy
),
const SizedBox(height: 24),
AdvancedSearchFilterFormBottomView(focusManager: controller.focusManager)
@@ -161,7 +161,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
}
List<Widget> _buildEmailReceiveTimeTypeActionTiles(BuildContext context) {
return EmailReceiveTimeType.values
return EmailReceiveTimeType.valuesForSearch
.map((receiveTime) => PopupMenuItem(
padding: EdgeInsets.zero,
child: PopupItemNoIconWidget(
@@ -176,7 +176,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
Widget _buildFilterField({
required BuildContext context,
required AdvancedSearchFilterField advancedSearchFilterField,
required FilterField filterField,
TextEditingController? textEditingController,
VoidCallback? onTap,
bool isSelectFormList = false,
@@ -189,7 +189,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
SizedBox(
width: _isVerticalArrange(context) ? null : 112,
child: LabelAdvancedSearchFieldWidget(
name: advancedSearchFilterField.getTitle(context),
name: filterField.getTitle(AppLocalizations.of(context)),
),
),
if (_isVerticalArrange(context))
@@ -204,20 +204,21 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
mouseCursor: mouseCursor,
currentFocusNode: currentFocusNode,
nextFocusNode: nextFocusNode,
advancedSearchFilterField: advancedSearchFilterField,
filterField: filterField,
textEditingController: textEditingController,
onTextChange: onTextChange,
)
else if (controller.responsiveUtils.landscapeTabletSupported(context))
if (advancedSearchFilterField == AdvancedSearchFilterField.date)
if (filterField == FilterField.date)
Obx(() => DateDropDownButton(
controller.imagePaths,
imagePaths: controller.imagePaths,
receiveTimeTypes: EmailReceiveTimeType.valuesForSearch,
startDate: controller.startDate.value,
endDate: controller.endDate.value,
receiveTimeSelected: controller.receiveTimeType.value,
receiveTimeTypeSelected: controller.receiveTimeType.value,
onReceiveTimeSelected: (receiveTime) => controller.updateReceiveDateSearchFilter(context, receiveTime),
))
else if (advancedSearchFilterField == AdvancedSearchFilterField.sortBy)
else if (filterField == FilterField.sortBy)
Obx(() => SortByDropDownButton(
imagePaths: controller.imagePaths,
sortOrderSelected: controller.sortOrderType.value,
@@ -231,7 +232,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
mouseCursor: mouseCursor,
currentFocusNode: currentFocusNode,
nextFocusNode: nextFocusNode,
advancedSearchFilterField: advancedSearchFilterField,
filterField: filterField,
textEditingController: textEditingController,
onTextChange: onTextChange,
)
@@ -244,7 +245,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
mouseCursor: mouseCursor,
currentFocusNode: currentFocusNode,
nextFocusNode: nextFocusNode,
advancedSearchFilterField: advancedSearchFilterField,
filterField: filterField,
textEditingController: textEditingController,
onTextChange: onTextChange,
),
@@ -269,7 +270,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
Widget _buildTextFieldFilterForWeb({
required BuildContext context,
required AdvancedSearchFilterField advancedSearchFilterField,
required FilterField filterField,
TextEditingController? textEditingController,
VoidCallback? onTap,
bool isSelectFormList = false,
@@ -278,16 +279,17 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
FocusNode? nextFocusNode,
ValueChanged<String>? onTextChange,
}) {
switch (advancedSearchFilterField) {
case AdvancedSearchFilterField.date:
switch (filterField) {
case FilterField.date:
return Obx(() => DateDropDownButton(
controller.imagePaths,
imagePaths: controller.imagePaths,
receiveTimeTypes: EmailReceiveTimeType.valuesForSearch,
startDate: controller.startDate.value,
endDate: controller.endDate.value,
receiveTimeSelected: controller.receiveTimeType.value,
receiveTimeTypeSelected: controller.receiveTimeType.value,
onReceiveTimeSelected: (receiveTime) => controller.updateReceiveDateSearchFilter(context, receiveTime),
));
case AdvancedSearchFilterField.sortBy:
case FilterField.sortBy:
return Obx(() => SortByDropDownButton(
imagePaths: controller.imagePaths,
sortOrderSelected: controller.sortOrderType.value,
@@ -301,7 +303,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
mouseCursor: mouseCursor,
currentFocusNode: currentFocusNode,
nextFocusNode: nextFocusNode,
advancedSearchFilterField: advancedSearchFilterField,
filterField: filterField,
textEditingController: textEditingController,
onTextChange: onTextChange,
);
@@ -310,7 +312,7 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
Widget _buildTextField({
required BuildContext context,
required AdvancedSearchFilterField advancedSearchFilterField,
required FilterField filterField,
TextEditingController? textEditingController,
VoidCallback? onTap,
bool isSelectFormList = false,
@@ -378,9 +380,9 @@ class AdvancedSearchInputForm extends GetWidget<AdvancedFilterController>
color: AppColor.primaryColor,
),
),
hintText: advancedSearchFilterField.getHintText(context),
hintText: filterField.getHintText(AppLocalizations.of(context)),
hintStyle: ThemeUtils.textStyleBodyBody3(
color: advancedSearchFilterField == AdvancedSearchFilterField.mailBox
color: filterField == FilterField.mailBox
? Colors.black
: AppColor.m3Tertiary,
),
@@ -13,26 +13,27 @@ import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:model/extensions/email_address_extension.dart';
import 'package:model/mailbox/expand_mode.dart';
import 'package:super_tag_editor/tag_editor.dart';
import 'package:tmail_ui_user/features/base/model/filter_filter.dart';
import 'package:tmail_ui_user/features/base/widget/default_field/default_autocomplete_tag_item_widget.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/draggable_email_address.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/suggestion_email_address.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/recipient_composer_widget.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/recipient_suggestion_item_widget.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/styles/advanced_search_input_form_style.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/text_field_autocomplete_email_address_web_style.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/autocomplete_tag_item_widget_web.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/label_advanced_search_field_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/utils/app_config.dart';
typedef OnSuggestionEmailAddress = Future<List<EmailAddress>> Function(String word, {int? limit});
typedef OnUpdateListEmailAddressAction = void Function(AdvancedSearchFilterField field, List<EmailAddress> newData);
typedef OnDeleteEmailAddressTypeAction = void Function(AdvancedSearchFilterField field);
typedef OnShowFullListEmailAddressAction = void Function(AdvancedSearchFilterField field);
typedef OnUpdateListEmailAddressAction = void Function(FilterField field, List<EmailAddress> newData);
typedef OnDeleteEmailAddressTypeAction = void Function(FilterField field);
typedef OnShowFullListEmailAddressAction = void Function(FilterField field);
typedef OnDeleteTagAction = void Function(EmailAddress emailAddress);
class TextFieldAutocompleteEmailAddressWeb extends StatefulWidget {
final AdvancedSearchFilterField field;
final FilterField field;
final List<EmailAddress> listEmailAddress;
final ExpandMode expandMode;
final FocusNode? focusNode;
@@ -106,7 +107,7 @@ class _TextFieldAutocompleteEmailAddressWebState extends State<TextFieldAutocomp
SizedBox(
width: TextFieldAutoCompleteEmailAddressWebStyles.fieldTitleWidth,
child: LabelAdvancedSearchFieldWidget(
name: widget.field.getTitle(context),
name: widget.field.getTitle(AppLocalizations.of(context)),
),
),
const SizedBox(width: TextFieldAutoCompleteEmailAddressWebStyles.space),
@@ -147,7 +148,7 @@ class _TextFieldAutocompleteEmailAddressWebState extends State<TextFieldAutocomp
),
fillColor: TextFieldAutoCompleteEmailAddressWebStyles.textInputFillColor,
border: TextFieldAutoCompleteEmailAddressWebStyles.textInputBorder,
hintText: widget.field.getHintText(context),
hintText: widget.field.getHintText(AppLocalizations.of(context)),
hintStyle: TextFieldAutoCompleteEmailAddressWebStyles.textInputHintStyle,
isDense: true,
contentPadding: _currentListEmailAddress.isNotEmpty
@@ -181,7 +182,7 @@ class _TextFieldAutocompleteEmailAddressWebState extends State<TextFieldAutocomp
tagBuilder: (context, index) {
final currentEmailAddress = _currentListEmailAddress.elementAt(index);
final isLatestEmail = currentEmailAddress == _currentListEmailAddress.last;
return AutoCompleteTagItemWidgetWeb(
return DefaultAutocompleteTagItemWidget(
field: widget.field,
currentEmailAddress: currentEmailAddress,
currentListEmailAddress: _currentListEmailAddress,
@@ -189,6 +190,7 @@ class _TextFieldAutocompleteEmailAddressWebState extends State<TextFieldAutocomp
isLatestEmail: isLatestEmail,
isCollapsed: _isCollapse,
isLatestTagFocused: _lastTagFocused,
iconClose: _imagePaths.icClose,
onDeleteTagAction: (emailAddress) => _handleDeleteTagAction.call(emailAddress, setState),
onShowFullAction: widget.onShowFullListEmailAddressAction,
);
@@ -389,7 +391,7 @@ class _TextFieldAutocompleteEmailAddressWebState extends State<TextFieldAutocomp
StateSetter stateSetter
) {
log('_TextFieldAutocompleteEmailAddressWebState::_handleAcceptDraggableEmailAddressAction:draggableEmailAddress = $draggableEmailAddress');
if (draggableEmailAddress.prefix != widget.field.getPrefixEmailAddress()) {
if (draggableEmailAddress.filterField != widget.field) {
if (!_currentListEmailAddress.contains(draggableEmailAddress.emailAddress)) {
stateSetter(() {
_currentListEmailAddress.add(draggableEmailAddress.emailAddress);
@@ -346,7 +346,7 @@ class SearchEmailView extends GetWidget<SearchEmailController>
}
void _openPopupMenuDateFilter(BuildContext context, RelativeRect position) {
final popupMenuItems = EmailReceiveTimeType.values.map((timeType) {
final popupMenuItems = EmailReceiveTimeType.valuesForSearch.map((timeType) {
return PopupMenuItem(
padding: EdgeInsets.zero,
child: PopupMenuItemActionWidget(
@@ -371,7 +371,7 @@ class SearchEmailView extends GetWidget<SearchEmailController>
}
void _openContextMenuDateFilter(BuildContext context) {
final contextMenuActions = EmailReceiveTimeType.values.map((timeType) {
final contextMenuActions = EmailReceiveTimeType.valuesForSearch.map((timeType) {
return ContextItemReceiveTimeTypeAction(
timeType,
controller.emailReceiveTimeType.value,
@@ -23,7 +23,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_re
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/base/model/filter_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';
@@ -225,10 +225,10 @@ void main() {
group('onTextChanged::test', () {
test(
'SHOULD update memory search filter for subject\n'
'WHEN onTextChanged called with AdvancedSearchFilterField is Subject',
'WHEN onTextChanged called with FilterField is Subject',
() {
// Arrange
const filterField = AdvancedSearchFilterField.subject;
const filterField = FilterField.subject;
const value = 'Subject';
// Act
@@ -242,10 +242,10 @@ void main() {
test(
'SHOULD update memory search filter for text\n'
'WHEN onTextChanged called with AdvancedSearchFilterField is hasKeyword',
'WHEN onTextChanged called with FilterField is hasKeyword',
() {
// Arrange
const filterField = AdvancedSearchFilterField.hasKeyword;
const filterField = FilterField.hasKeyword;
const value = 'keyword';
// Act
@@ -259,10 +259,10 @@ void main() {
test(
'SHOULD update memory search filter for notKeyword\n'
'WHEN onTextChanged called with AdvancedSearchFilterField is notKeyword',
'WHEN onTextChanged called with FilterField is notKeyword',
() {
// Arrange
const filterField = AdvancedSearchFilterField.notKeyword;
const filterField = FilterField.notKeyword;
const value = 'keyword1,keyword2';
// Act
@@ -276,10 +276,10 @@ void main() {
test(
'SHOULD update memory search filter for subject is null\n'
'WHEN onTextChanged called with AdvancedSearchFilterField is subject',
'WHEN onTextChanged called with FilterField is subject',
() {
// Arrange
const filterField = AdvancedSearchFilterField.subject;
const filterField = FilterField.subject;
const value = ' ';
// Act
@@ -293,10 +293,10 @@ void main() {
test(
'SHOULD update memory search filter for notKeyword is empty values\n'
'WHEN onTextChanged called with AdvancedSearchFilterField is notKeyword',
'WHEN onTextChanged called with FilterField is notKeyword',
() {
// Arrange
const filterField = AdvancedSearchFilterField.notKeyword;
const filterField = FilterField.notKeyword;
const value = ' ';
// Act
@@ -310,11 +310,11 @@ void main() {
test(
'SHOULD update memory search filter for subject and notKeyword is empty values\n'
'WHEN onTextChanged called with AdvancedSearchFilterField are subject and notKeyword',
'WHEN onTextChanged called with FilterField are subject and notKeyword',
() {
// Arrange
const filterFieldSubject = AdvancedSearchFilterField.subject;
const filterFieldNotKeyword = AdvancedSearchFilterField.notKeyword;
const filterFieldSubject = FilterField.subject;
const filterFieldNotKeyword = FilterField.notKeyword;
const validSubject = 'Subject';
const emptyNotKeyword = ' ';
@@ -71,7 +71,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller
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/controller/spam_report_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/advanced_search_filter.dart';
import 'package:tmail_ui_user/features/base/model/filter_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';
@@ -490,8 +490,8 @@ void main() {
when(context.mounted).thenReturn(true);
// expect query in advanced filter controller update as expected
advancedFilterController.updateListEmailAddress(AdvancedSearchFilterField.from, [fromEmailAddress]);
advancedFilterController.updateListEmailAddress(AdvancedSearchFilterField.to, [toEmailAddress]);
advancedFilterController.updateListEmailAddress(FilterField.from, [fromEmailAddress]);
advancedFilterController.updateListEmailAddress(FilterField.to, [toEmailAddress]);
advancedFilterController.subjectFilterInputController.text = emailSubject;
advancedFilterController.hasKeyWordFilterInputController.text = emailContainsWord;
advancedFilterController.notKeyWordFilterInputController.text = emailNotContainsWord;