TF-3002 [WEB] Apply new design for SearchFilterButton

This commit is contained in:
dab246
2024-09-16 13:20:43 +07:00
committed by Dat H. Pham
parent 5cb6541ef6
commit 34c9c0cd0e
14 changed files with 345 additions and 230 deletions
+3
View File
@@ -0,0 +1,3 @@
<svg width="11" height="10" viewBox="0 0 11 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.0221 0.263604C10.3735 0.615076 10.3735 1.18492 10.0221 1.5364L6.55864 5L10.0221 8.46362C10.3442 8.7858 10.3711 9.29148 10.1026 9.64424L10.0221 9.73641C9.67058 10.0879 9.10073 10.0879 8.74926 9.73641L5.28564 6.273L1.82204 9.73641C1.47057 10.0879 0.90072 10.0879 0.549248 9.73641C0.197777 9.38494 0.197777 8.81509 0.549248 8.46362L4.01264 5L0.549248 1.5364C0.227066 1.21421 0.200217 0.708534 0.468703 0.355769L0.549248 0.263604C0.90072 -0.087868 1.47057 -0.087868 1.82204 0.263604L5.28564 3.727L8.74926 0.263604C9.10073 -0.087868 9.67058 -0.087868 10.0221 0.263604Z" fill="#818C99"/>
</svg>

After

Width:  |  Height:  |  Size: 698 B

@@ -225,6 +225,8 @@ extension AppColor on Color {
static const loginViewShadowColor = Color(0x3DBCBCBC); static const loginViewShadowColor = Color(0x3DBCBCBC);
static const colorEmailTileCheckboxUnhover = Color(0xFFAEB7C2); static const colorEmailTileCheckboxUnhover = Color(0xFFAEB7C2);
static const colorEmailTileHoverWeb = Color(0xFFDFEEFF); static const colorEmailTileHoverWeb = Color(0xFFDFEEFF);
static const colorSearchFilterButton = Color(0xFFECEEF1);
static const colorSuggestionSearchFilterButton = Color(0xFFEBEDF0);
static const mapGradientColor = [ static const mapGradientColor = [
[Color(0xFF21D4FD), Color(0xFFB721FF)], [Color(0xFF21D4FD), Color(0xFFB721FF)],
@@ -218,6 +218,7 @@ class ImagePaths {
String get icCheckboxSelected => _getImagePath('ic_checkbox_selected.svg'); String get icCheckboxSelected => _getImagePath('ic_checkbox_selected.svg');
String get icGoodSignature => _getImagePath('ic_good_signature.svg'); String get icGoodSignature => _getImagePath('ic_good_signature.svg');
String get icBadSignature => _getImagePath('ic_bad_signature.svg'); String get icBadSignature => _getImagePath('ic_bad_signature.svg');
String get icDeleteSelection => _getImagePath('ic_delete_selection.svg');
String _getImagePath(String imageName) { String _getImagePath(String imageName) {
return AssetsPaths.images + imageName; return AssetsPaths.images + imageName;
@@ -1286,13 +1286,16 @@ class _SuggestionsListState<T, P, R> extends State<_SuggestionsList<T, P, R>>
if (widget.buttonActionCallback != null) { if (widget.buttonActionCallback != null) {
return Padding( return Padding(
padding: const EdgeInsetsDirectional.only(end: 8, bottom: 8), padding: const EdgeInsetsDirectional.only(end: 8, bottom: 8),
child: InkWell( child: Material(
borderRadius: const BorderRadius.all(Radius.circular(10)), type: MaterialType.transparency,
onTap: () { child: InkWell(
widget.buttonActionCallback?.call(action); borderRadius: const BorderRadius.all(Radius.circular(10)),
invalidateSuggestions(); onTap: () {
}, widget.buttonActionCallback?.call(action);
child: widget.actionButtonBuilder!(context, action), invalidateSuggestions();
},
child: widget.actionButtonBuilder!(context, action),
),
), ),
); );
} else { } else {
@@ -397,7 +397,7 @@ class AdvancedFilterController extends BaseController {
); );
} else if (action is ClearDateRangeToAdvancedSearch) { } else if (action is ClearDateRangeToAdvancedSearch) {
_updateDateRangeTime(action.receiveTime); _updateDateRangeTime(action.receiveTime);
} else if (action is StartSearchEmailAction && action.filter == QuickSearchFilter.fromMe) { } else if (action is StartSearchEmailAction && action.filter == QuickSearchFilter.from) {
_updateFromField(); _updateFromField();
} else if (action is SearchEmailByFromFieldsAction) { } else if (action is SearchEmailByFromFieldsAction) {
searchController.clearSearchFilter(); searchController.clearSearchFilter();
@@ -1575,17 +1575,6 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo
_getAllIdentities(); _getAllIdentities();
} }
void selectQuickSearchFilter(QuickSearchFilter filter) {
return searchController.selectQuickSearchFilter(filter);
}
void selectQuickSearchFilterFrom(EmailAddress fromEmailFilter) {
return searchController.selectQuickSearchFilter(
QuickSearchFilter.fromMe,
fromEmailFilter: fromEmailFilter
);
}
void addFilterToSuggestionForm(QuickSearchFilter filter) { void addFilterToSuggestionForm(QuickSearchFilter filter) {
searchController.addFilterToSuggestionForm(filter); searchController.addFilterToSuggestionForm(filter);
} }
@@ -1667,24 +1656,26 @@ class MailboxDashBoardController extends ReloadableController with UserSettingPo
} }
} }
void selectQuickSearchFilterAction(QuickSearchFilter filter) async { void selectHasAttachmentSearchFilter() {
log('MailboxDashBoardController::selectQuickSearchFilterAction(): filter: $filter'); searchController.updateFilterEmail(hasAttachment: true);
if (filter == QuickSearchFilter.fromMe) { dispatchAction(StartSearchEmailAction());
if (accountId.value == null || sessionCurrent == null) { }
logError('MailboxDashBoardController::selectQuickSearchFilterAction(): accountId or sessionCurrent is null');
}
final listContactSelected = searchController.searchEmailFilter.value.from;
final arguments = ContactArguments(accountId.value!, sessionCurrent!, listContactSelected);
final newContact = await DialogRouter.pushGeneralDialog(routeName: AppRoutes.contact, arguments: arguments); Future<void> selectFromSearchFilter() async {
if (accountId.value == null || sessionCurrent == null) return;
if (newContact is EmailAddress) { final contactArgument = ContactArguments(
selectQuickSearchFilterFrom(newContact); accountId.value!,
dispatchAction(StartSearchEmailAction(filter: filter)); sessionCurrent!,
} searchController.searchEmailFilter.value.from);
} else {
selectQuickSearchFilter(filter); final newContact = await DialogRouter.pushGeneralDialog(
dispatchAction(StartSearchEmailAction(filter: filter)); routeName: AppRoutes.contact,
arguments: contactArgument);
if (newContact is EmailAddress) {
searchController.updateFilterEmail(fromOption: Some({newContact.emailAddress}));
dispatchAction(StartSearchEmailAction(filter: QuickSearchFilter.from));
} }
} }
@@ -11,7 +11,6 @@ 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/user_name.dart'; import 'package:jmap_dart_client/jmap/core/user_name.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_address.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:jmap_dart_client/jmap/mail/email/email_filter_condition.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart';
@@ -71,32 +70,6 @@ class SearchController extends BaseController with DateRangePickerMixin {
searchEmailFilter.value = SearchEmailFilter.initial(); searchEmailFilter.value = SearchEmailFilter.initial();
} }
void selectQuickSearchFilter(
QuickSearchFilter quickSearchFilter,
{EmailAddress? fromEmailFilter}
) {
final isFilterSelected = quickSearchFilter.isSelected(searchEmailFilter.value);
switch (quickSearchFilter) {
case QuickSearchFilter.hasAttachment:
updateFilterEmail(hasAttachment: !isFilterSelected);
return;
case QuickSearchFilter.last7Days:
updateFilterEmail(emailReceiveTimeType: EmailReceiveTimeType.allTime);
return;
case QuickSearchFilter.fromMe:
if (fromEmailFilter != null) {
final newListEmailAddress = (fromEmailFilter.email != null && fromEmailFilter.email!.isNotEmpty)
? <String>{fromEmailFilter.email!}
: <String>{};
updateFilterEmail(fromOption: Some(newListEmailAddress));
}
return;
case QuickSearchFilter.sortBy:
return;
}
}
void addFilterToSuggestionForm(QuickSearchFilter filter) { void addFilterToSuggestionForm(QuickSearchFilter filter) {
if (listFilterOnSuggestionForm.contains(filter)) { if (listFilterOnSuggestionForm.contains(filter)) {
listFilterOnSuggestionForm.remove(filter); listFilterOnSuggestionForm.remove(filter);
@@ -21,6 +21,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/sear
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/download/download_task_item_widget.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/download/download_task_item_widget.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/navigation_bar/navigation_bar_widget.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/navigation_bar/navigation_bar_widget.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/recover_deleted_message_loading_banner_widget.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/recover_deleted_message_loading_banner_widget.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/search_filters/search_filter_button.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/search_input_form_widget.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/search_input_form_widget.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/top_bar_thread_selection.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/top_bar_thread_selection.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';
@@ -36,7 +37,6 @@ import 'package:tmail_ui_user/features/thread/presentation/widgets/banner_delete
import 'package:tmail_ui_user/features/thread/presentation/widgets/banner_empty_trash_widget.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/banner_empty_trash_widget.dart';
import 'package:tmail_ui_user/features/thread/presentation/widgets/spam_banner/spam_report_banner_web_widget.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/spam_banner/spam_report_banner_web_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/utils/app_utils.dart';
class MailboxDashBoardView extends BaseMailboxDashBoardView { class MailboxDashBoardView extends BaseMailboxDashBoardView {
@@ -460,24 +460,23 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView {
}); });
} }
bool supportListButtonQuickSearchFilter(BuildContext context) {
return controller.searchController.isSearchEmailRunning
&& controller.dashboardRoute.value == DashboardRoutes.thread;
}
Widget _buildListButtonQuickSearchFilter(BuildContext context) { Widget _buildListButtonQuickSearchFilter(BuildContext context) {
return Obx(() { return Obx(() {
if (supportListButtonQuickSearchFilter(context)) { if (controller.searchController.isSearchEmailRunning &&
controller.dashboardRoute.value == DashboardRoutes.thread) {
return Padding( return Padding(
padding: const EdgeInsetsDirectional.only(end: 16, top: 8), padding: const EdgeInsetsDirectional.only(end: 16, top: 8),
child: Row( child: Row(
children: [ children: [
...QuickSearchFilter.values _buildQuickSearchFilterButton(context, QuickSearchFilter.from),
.where((filter) => filter != QuickSearchFilter.sortBy) const SizedBox(width: 8),
.map((filter) => _buildQuickSearchFilterButton(context, filter)), _buildQuickSearchFilterButton(context, QuickSearchFilter.dateTime),
const Spacer(), const SizedBox(width: 8),
_buildQuickSearchFilterButton(context, QuickSearchFilter.sortBy) _buildQuickSearchFilterButton(context, QuickSearchFilter.hasAttachment),
] const Spacer(),
_buildQuickSearchFilterButton(context, QuickSearchFilter.sortBy),
const SizedBox(width: 8),
]
), ),
); );
} else { } else {
@@ -488,87 +487,64 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView {
Widget _buildQuickSearchFilterButton( Widget _buildQuickSearchFilterButton(
BuildContext context, BuildContext context,
QuickSearchFilter filter QuickSearchFilter searchFilter
) { ) {
return Obx(() { return Obx(() {
final isFilterSelected = filter.isSelected( final searchEmailFilter = controller.searchController.searchEmailFilter.value;
controller.searchController.searchEmailFilter.value, final sortOrderType = controller.searchController.sortOrderFiltered.value;
); final listAddressOfFrom = controller.searchController.searchEmailFilter.value.from;
final userName = controller.sessionCurrent?.username;
final startDate = controller.searchController.startDateFiltered;
final endDate = controller.searchController.endDateFiltered;
final receiveTimeType = controller.searchController.receiveTimeFiltered;
return Padding( final isSelected = searchFilter.isSelected(
padding: EdgeInsets.only( context,
right: AppUtils.isDirectionRTL(context) ? 0 : 8, searchEmailFilter,
left: AppUtils.isDirectionRTL(context) ? 8 : 0, sortOrderType);
),
child: InkWell(
onTap: () {
if (!filter.isTapOpenPopupMenu()) {
controller.selectQuickSearchFilterAction(filter);
}
},
onTapDown: (detail) {
final screenSize = MediaQuery.of(context).size;
final offset = detail.globalPosition;
final position = RelativeRect.fromLTRB(
offset.dx,
offset.dy,
screenSize.width - offset.dx,
screenSize.height - offset.dy,
);
switch(filter) { return SearchFilterButton(
case QuickSearchFilter.last7Days: searchFilter: searchFilter,
_openPopupMenuDateFilter(context, position); imagePaths: controller.imagePaths,
break; isSelected: isSelected,
case QuickSearchFilter.sortBy: receiveTimeType: receiveTimeType,
_openPopupMenuSortFilter(context, position); startDate: startDate,
break; endDate: endDate,
default: sortOrderType: sortOrderType,
break; listAddressOfFrom: listAddressOfFrom,
} userName: userName,
}, onSelectSearchFilterAction: _onSelectSearchFilterAction,
borderRadius: const BorderRadius.all(Radius.circular(10)),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: filter.getBackgroundColor(isFilterSelected: isFilterSelected)),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
SvgPicture.asset(
filter.getIcon(controller.imagePaths, isFilterSelected: isFilterSelected),
width: 16,
height: 16,
fit: BoxFit.fill),
const SizedBox(width: 4),
Text(
filter == QuickSearchFilter.fromMe
? _getQuickSearchFilterFromTitle(context)
: filter.getTitle(
context,
receiveTimeType: controller.searchController.receiveTimeFiltered,
startDate: controller.searchController.startDateFiltered,
endDate: controller.searchController.endDateFiltered,
sortOrderType: controller.searchController.sortOrderFiltered.value,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: filter.getTextStyle(isFilterSelected: isFilterSelected),
),
if (filter == QuickSearchFilter.last7Days || filter == QuickSearchFilter.fromMe)
... [
const SizedBox(width: 4),
SvgPicture.asset(
controller.imagePaths.icChevronDown,
width: 16,
height: 16,
fit: BoxFit.fill),
]
])),
),
); );
}); });
} }
void _onSelectSearchFilterAction(
BuildContext context,
QuickSearchFilter searchFilter,
{RelativeRect? buttonPosition}
) async {
switch(searchFilter) {
case QuickSearchFilter.dateTime:
if (buttonPosition != null) {
_openPopupMenuDateFilter(context, buttonPosition);
}
break;
case QuickSearchFilter.sortBy:
if (buttonPosition != null) {
_openPopupMenuSortFilter(context, buttonPosition);
}
break;
case QuickSearchFilter.from:
controller.selectFromSearchFilter();
break;
case QuickSearchFilter.hasAttachment:
controller.selectHasAttachmentSearchFilter();
break;
default:
break;
}
}
List<PopupMenuEntry> popupMenuEmailReceiveTimeType( List<PopupMenuEntry> popupMenuEmailReceiveTimeType(
BuildContext context, BuildContext context,
EmailReceiveTimeType? receiveTimeSelected, EmailReceiveTimeType? receiveTimeSelected,
@@ -666,18 +642,4 @@ class MailboxDashBoardView extends BaseMailboxDashBoardView {
), ),
); );
} }
String _getQuickSearchFilterFromTitle(BuildContext context) {
final searchEmailFilterFromFiled = controller.searchController.searchEmailFilter.value.from;
if (searchEmailFilterFromFiled.length == 1) {
if (searchEmailFilterFromFiled.first == controller.sessionCurrent?.username.value &&
controller.sessionCurrent?.username.value.isNotEmpty == true) {
return QuickSearchFilter.fromMe.getTitle(context);
} else {
return '${AppLocalizations.of(context).from_email_address_prefix} ${searchEmailFilterFromFiled.first}';
}
} else {
return AppLocalizations.of(context).from_email_address_prefix;
}
}
} }
@@ -2,6 +2,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:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:jmap_dart_client/jmap/core/user_name.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart';
@@ -11,7 +12,9 @@ enum QuickSearchFilter {
hasAttachment, hasAttachment,
last7Days, last7Days,
fromMe, fromMe,
sortBy; sortBy,
dateTime,
from;
String getName(BuildContext context) { String getName(BuildContext context) {
switch(this) { switch(this) {
@@ -23,6 +26,12 @@ enum QuickSearchFilter {
return AppLocalizations.of(context).fromMe; return AppLocalizations.of(context).fromMe;
case QuickSearchFilter.sortBy: case QuickSearchFilter.sortBy:
return AppLocalizations.of(context).sortBy; return AppLocalizations.of(context).sortBy;
case QuickSearchFilter.dateTime:
return AppLocalizations.of(context).allTime;
case QuickSearchFilter.from:
return AppLocalizations.of(context).from_email_address_prefix;
default:
return '';
} }
} }
@@ -32,74 +41,109 @@ enum QuickSearchFilter {
EmailSortOrderType? sortOrderType, EmailSortOrderType? sortOrderType,
DateTime? startDate, DateTime? startDate,
DateTime? endDate, DateTime? endDate,
Set<String>? listAddressOfFrom,
UserName? userName
}) { }) {
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:
return receiveTimeType?.getTitle(context, startDate: startDate, endDate: endDate) return AppLocalizations.of(context).last7Days;
?? AppLocalizations.of(context).allTime;
case QuickSearchFilter.fromMe: case QuickSearchFilter.fromMe:
return AppLocalizations.of(context).fromMe; return AppLocalizations.of(context).fromMe;
case QuickSearchFilter.sortBy: case QuickSearchFilter.sortBy:
return sortOrderType?.getTitle(context) return sortOrderType?.getTitle(context)
?? AppLocalizations.of(context).mostRecent; ?? AppLocalizations.of(context).mostRecent;
case QuickSearchFilter.dateTime:
return receiveTimeType?.getTitle(
context,
startDate: startDate,
endDate: endDate
) ?? AppLocalizations.of(context).allTime;
case QuickSearchFilter.from:
if (listAddressOfFrom?.length != 1) {
return AppLocalizations.of(context).from_email_address_prefix;
}
if (userName?.value.isNotEmpty == true && listAddressOfFrom?.first == userName?.value) {
return AppLocalizations.of(context).fromMe;
} else {
return '${AppLocalizations.of(context).from_email_address_prefix} ${listAddressOfFrom?.first}';
}
default:
return '';
} }
} }
String getIcon(ImagePaths imagePaths, {required bool isFilterSelected}) { String getIcon(ImagePaths imagePaths, {bool isSelected = false}) {
if (isFilterSelected) { switch(this) {
return imagePaths.icSelectedSB; case QuickSearchFilter.hasAttachment:
} else { return isSelected ? imagePaths.icSelectedSB : imagePaths.icAttachmentSB;
switch(this) { case QuickSearchFilter.last7Days:
case QuickSearchFilter.hasAttachment: return isSelected ? imagePaths.icSelectedSB : imagePaths.icCalendarSB;
return imagePaths.icAttachmentSB; case QuickSearchFilter.fromMe:
case QuickSearchFilter.last7Days: return isSelected ? imagePaths.icSelectedSB : imagePaths.icUserSB;
return imagePaths.icCalendarSB; case QuickSearchFilter.sortBy:
case QuickSearchFilter.fromMe: return imagePaths.icFilterSB;
return imagePaths.icUserSB; case QuickSearchFilter.dateTime:
case QuickSearchFilter.sortBy: return imagePaths.icCalendarSB;
return imagePaths.icFilterSB; case QuickSearchFilter.from:
} return imagePaths.icUserSB;
} }
} }
Color getBackgroundColor({required bool isFilterSelected}) { Color getBackgroundColor({bool isFilterSelected = false}) {
if (isFilterSelected) { if (isFilterSelected) {
return AppColor.colorItemEmailSelectedDesktop; return AppColor.primaryColor.withOpacity(0.06);
} else { } else {
return AppColor.colorButtonHeaderThread; return AppColor.colorSearchFilterButton;
} }
} }
TextStyle getTextStyle({required bool isFilterSelected}) { Color getSuggestionBackgroundColor({bool isFilterSelected = false}) {
if (isFilterSelected) { if (isFilterSelected) {
return const TextStyle( return AppColor.primaryColor.withOpacity(0.06);
fontSize: 13,
fontWeight: FontWeight.w500,
color: AppColor.colorTextButton);
} else { } else {
return const TextStyle( return AppColor.colorSuggestionSearchFilterButton.withOpacity(0.6);
fontSize: 13, }
fontWeight: FontWeight.normal, }
color: AppColor.colorTextButtonHeaderThread);
Color getIconColor({bool isSelected = false}) {
if (isSelected) {
return AppColor.primaryColor;
} else {
return AppColor.colorTextBody;
} }
} }
bool isApplied(List<QuickSearchFilter> listFilter) => listFilter.contains(this); bool isApplied(List<QuickSearchFilter> listFilter) => listFilter.contains(this);
bool isSelected(SearchEmailFilter filter) { bool isSelected(
BuildContext context,
SearchEmailFilter searchFilter,
EmailSortOrderType sortOrderType
) {
switch (this) { switch (this) {
case QuickSearchFilter.hasAttachment: case QuickSearchFilter.hasAttachment:
return filter.hasAttachment == true; return searchFilter.hasAttachment;
case QuickSearchFilter.last7Days: case QuickSearchFilter.last7Days:
return true; return true;
case QuickSearchFilter.fromMe: case QuickSearchFilter.fromMe:
return filter.from.length == 1; return searchFilter.from.length == 1;
case QuickSearchFilter.sortBy: case QuickSearchFilter.sortBy:
return true; return sortOrderType != EmailSortOrderType.mostRecent;
case QuickSearchFilter.dateTime:
return searchFilter.emailReceiveTimeType != EmailReceiveTimeType.allTime;
case QuickSearchFilter.from:
return searchFilter.from.isNotEmpty;
} }
} }
bool isTapOpenPopupMenu() => this == QuickSearchFilter.last7Days || this == QuickSearchFilter.sortBy; bool isOnTapWithPositionActionSupported() =>
this == QuickSearchFilter.dateTime || this == QuickSearchFilter.sortBy;
bool isArrowDownIconSupported() =>
this == QuickSearchFilter.dateTime ||
this == QuickSearchFilter.from ||
this == QuickSearchFilter.sortBy;
} }
@@ -0,0 +1,17 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:flutter/material.dart';
class SearchFilterButtonStyle {
static const double iconSize = 16;
static const double deleteIconSize = 10;
static const double spaceSize = 4;
static const EdgeInsetsGeometry buttonPadding = EdgeInsets.symmetric(horizontal: 12, vertical: 8);
static const EdgeInsetsGeometry elementPadding = EdgeInsetsDirectional.only(start: 8);
static const BorderRadius borderRadius = BorderRadius.all(Radius.circular(10));
static const TextStyle titleStyle = TextStyle(
fontSize: 13,
fontWeight: FontWeight.normal,
color: AppColor.colorTextButtonHeaderThread);
}
@@ -0,0 +1,133 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/views/button/tmail_button_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:jmap_dart_client/jmap/core/user_name.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/quick_search_filter.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/search_filter_button_style.dart';
typedef OnSelectSearchFilterAction = Function(
BuildContext context,
QuickSearchFilter searchFilter,
{RelativeRect? buttonPosition});
class SearchFilterButton extends StatelessWidget {
final QuickSearchFilter searchFilter;
final bool isSelected;
final ImagePaths imagePaths;
final EmailReceiveTimeType? receiveTimeType;
final EmailSortOrderType? sortOrderType;
final DateTime? startDate;
final DateTime? endDate;
final Set<String>? listAddressOfFrom;
final UserName? userName;
final Color? backgroundColor;
final OnSelectSearchFilterAction? onSelectSearchFilterAction;
const SearchFilterButton({
super.key,
required this.searchFilter,
required this.imagePaths,
this.isSelected = false,
this.receiveTimeType,
this.sortOrderType,
this.startDate,
this.endDate,
this.listAddressOfFrom,
this.userName,
this.backgroundColor,
this.onSelectSearchFilterAction,
});
@override
Widget build(BuildContext context) {
final childItem = Container(
decoration: BoxDecoration(
borderRadius: SearchFilterButtonStyle.borderRadius,
color: backgroundColor ?? searchFilter.getBackgroundColor(isFilterSelected: isSelected)),
padding: SearchFilterButtonStyle.buttonPadding,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset(
searchFilter.getIcon(imagePaths, isSelected: isSelected),
width: SearchFilterButtonStyle.iconSize,
height: SearchFilterButtonStyle.iconSize,
colorFilter: searchFilter.getIconColor(isSelected: isSelected).asFilter(),
fit: BoxFit.fill),
const SizedBox(width: SearchFilterButtonStyle.spaceSize),
Text(
searchFilter.getTitle(
context,
receiveTimeType: receiveTimeType,
startDate: startDate,
endDate: startDate,
sortOrderType: sortOrderType,
listAddressOfFrom: listAddressOfFrom,
userName: userName),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: SearchFilterButtonStyle.titleStyle,
),
if (searchFilter.isArrowDownIconSupported())
Padding(
padding: SearchFilterButtonStyle.elementPadding,
child: SvgPicture.asset(
imagePaths.icDropDown,
width: SearchFilterButtonStyle.iconSize,
height: SearchFilterButtonStyle.iconSize,
colorFilter: AppColor.colorTextBody.asFilter(),
fit: BoxFit.fill),
),
if (isSelected)
TMailButtonWidget.fromIcon(
icon: imagePaths.icDeleteSelection,
iconSize: SearchFilterButtonStyle.deleteIconSize,
iconColor: AppColor.colorTextBody,
padding: EdgeInsets.zero,
backgroundColor: Colors.transparent,
margin: SearchFilterButtonStyle.elementPadding,
),
]
)
);
if (onSelectSearchFilterAction != null) {
return Material(
type: MaterialType.transparency,
child: InkWell(
onTap: searchFilter.isOnTapWithPositionActionSupported()
? null
: () => _onTapAction(context),
onTapDown: searchFilter.isOnTapWithPositionActionSupported()
? (details) => _onTapDownAction(context, details)
: null,
borderRadius: SearchFilterButtonStyle.borderRadius,
child: childItem),
);
} else {
return childItem;
}
}
void _onTapAction(BuildContext context) {
onSelectSearchFilterAction?.call(context, searchFilter);
}
void _onTapDownAction(BuildContext context, TapDownDetails details) {
final screenSize = MediaQuery.sizeOf(context);
final offset = details.globalPosition;
final position = RelativeRect.fromLTRB(
offset.dx,
offset.dy,
screenSize.width - offset.dx,
screenSize.height - offset.dy);
onSelectSearchFilterAction?.call(context, searchFilter, buttonPosition: position);
}
}
@@ -1,7 +1,6 @@
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/utils/style_utils.dart';
import 'package:core/presentation/views/button/icon_button_web.dart'; import 'package:core/presentation/views/button/icon_button_web.dart';
import 'package:core/presentation/views/quick_search/quick_search_input_form.dart'; import 'package:core/presentation/views/quick_search/quick_search_input_form.dart';
import 'package:core/utils/direction_utils.dart'; import 'package:core/utils/direction_utils.dart';
@@ -23,6 +22,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/ad
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/quick_search/contact_quick_search_item.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/quick_search/contact_quick_search_item.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/quick_search/email_quick_search_item_tile_widget.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/quick_search/email_quick_search_item_tile_widget.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/quick_search/recent_search_item_tile_widget.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/quick_search/recent_search_item_tile_widget.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/search_filters/search_filter_button.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class SearchInputFormWidget extends StatelessWidget with AppLoaderMixin { class SearchInputFormWidget extends StatelessWidget with AppLoaderMixin {
@@ -65,9 +65,13 @@ class SearchInputFormWidget extends StatelessWidget with AppLoaderMixin {
borderRadius: BorderRadius.all(Radius.circular(16)), borderRadius: BorderRadius.all(Radius.circular(16)),
), ),
debounceDuration: const Duration(milliseconds: 300), debounceDuration: const Duration(milliseconds: 300),
listActionButton: QuickSearchFilter.values, listActionButton: const [
QuickSearchFilter.hasAttachment,
QuickSearchFilter.last7Days,
QuickSearchFilter.fromMe,
],
actionButtonBuilder: (context, filterAction) { actionButtonBuilder: (context, filterAction) {
if (filterAction is QuickSearchFilter && filterAction != QuickSearchFilter.sortBy) { if (filterAction is QuickSearchFilter) {
return buildListButtonForQuickSearchForm(context, filterAction); return buildListButtonForQuickSearchForm(context, filterAction);
} else { } else {
return const SizedBox.shrink(); return const SizedBox.shrink();
@@ -217,34 +221,15 @@ class SearchInputFormWidget extends StatelessWidget with AppLoaderMixin {
); );
} }
Widget buildListButtonForQuickSearchForm(BuildContext context, QuickSearchFilter filter) { Widget buildListButtonForQuickSearchForm(BuildContext context, QuickSearchFilter searchFilter) {
return Obx(() { return Obx(() {
final isFilterSelected = filter.isApplied(_searchController.listFilterOnSuggestionForm); final isSelected = searchFilter.isApplied(_searchController.listFilterOnSuggestionForm);
return Chip( return SearchFilterButton(
labelPadding: const EdgeInsetsDirectional.only( searchFilter: searchFilter,
top: 2, imagePaths: _imagePaths,
bottom: 2, isSelected: isSelected,
end: 10, backgroundColor: searchFilter.getSuggestionBackgroundColor(isFilterSelected: isSelected),
),
label: Text(
filter.getName(context),
maxLines: 1,
overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap,
style: filter.getTextStyle(isFilterSelected: isFilterSelected),
),
avatar: SvgPicture.asset(
filter.getIcon(_imagePaths, isFilterSelected: isFilterSelected),
width: 16,
height: 16,
fit: BoxFit.fill),
labelStyle: filter.getTextStyle(isFilterSelected: isFilterSelected),
backgroundColor: filter.getBackgroundColor(isFilterSelected: isFilterSelected),
shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.all(Radius.circular(10)),
side: BorderSide(color: filter.getBackgroundColor(isFilterSelected: isFilterSelected)),
),
); );
}); });
} }
@@ -257,7 +257,7 @@ class SearchEmailView extends GetWidget<SearchEmailController>
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(controller.imagePaths, isFilterSelected: filterSelected), filter.getIcon(controller.imagePaths, isSelected: filterSelected),
width: 16, width: 16,
height: 16, height: 16,
fit: BoxFit.fill), fit: BoxFit.fill),
@@ -273,7 +273,10 @@ class SearchEmailView extends GetWidget<SearchEmailController>
maxLines: 1, maxLines: 1,
overflow: CommonTextStyle.defaultTextOverFlow, overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap, softWrap: CommonTextStyle.defaultSoftWrap,
style: filter.getTextStyle(isFilterSelected: filterSelected), style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.normal,
color: AppColor.colorTextButtonHeaderThread),
), ),
if (filter == QuickSearchFilter.last7Days || filter == QuickSearchFilter.sortBy) if (filter == QuickSearchFilter.last7Days || filter == QuickSearchFilter.sortBy)
... [ ... [
@@ -64,7 +64,6 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/advanced_search_filter.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/advanced_search_filter.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_sort_order_type.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/quick_search_filter.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/search_email_filter.dart';
import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart'; import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart';
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart';
@@ -405,9 +404,8 @@ void main() {
expect(searchController.sortOrderFiltered.value, EmailSortOrderType.oldest); expect(searchController.sortOrderFiltered.value, EmailSortOrderType.oldest);
// expect filter in search controller update as expected // expect filter in search controller update as expected
mailboxDashboardController.selectQuickSearchFilterAction(QuickSearchFilter.hasAttachment); mailboxDashboardController.selectHasAttachmentSearchFilter();
expect(searchController.searchEmailFilter.value.hasAttachment, true); expect(searchController.searchEmailFilter.value.hasAttachment, true);
mailboxDashboardController.selectQuickSearchFilterAction(QuickSearchFilter.last7Days);
mailboxDashboardController.selectReceiveTimeQuickSearchFilter(context, EmailReceiveTimeType.last30Days); mailboxDashboardController.selectReceiveTimeQuickSearchFilter(context, EmailReceiveTimeType.last30Days);
expect(searchController.searchEmailFilter.value.emailReceiveTimeType, EmailReceiveTimeType.last30Days); expect(searchController.searchEmailFilter.value.emailReceiveTimeType, EmailReceiveTimeType.last30Days);