diff --git a/assets/images/ic_chevron_down_outline.svg b/assets/images/ic_chevron_down_outline.svg new file mode 100644 index 000000000..832fade05 --- /dev/null +++ b/assets/images/ic_chevron_down_outline.svg @@ -0,0 +1,3 @@ + + + diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart index 8a3931e56..8bc01332f 100644 --- a/core/lib/presentation/resources/image_paths.dart +++ b/core/lib/presentation/resources/image_paths.dart @@ -160,6 +160,7 @@ class ImagePaths { String get icClock => _getImagePath('ic_clock.svg'); String get icCalendar => _getImagePath('ic_calendar.svg'); String get icAddEmailForward => _getImagePath('ic_add_email_forwards.svg'); + String get icChevronDownOutline => _getImagePath('ic_chevron_down_outline.svg'); String _getImagePath(String imageName) { return AssetsPaths.images + imageName; diff --git a/lib/features/search/presentation/model/simple_search_filter.dart b/lib/features/search/presentation/model/simple_search_filter.dart index ebe2c03db..9b82b7655 100644 --- a/lib/features/search/presentation/model/simple_search_filter.dart +++ b/lib/features/search/presentation/model/simple_search_filter.dart @@ -1,4 +1,5 @@ import 'package:equatable/equatable.dart'; +import 'package:flutter/material.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'; @@ -7,9 +8,11 @@ import 'package:jmap_dart_client/jmap/mail/email/email_filter_condition.dart'; import 'package:model/model.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/main/localizations/app_localizations.dart'; class SimpleSearchFilter with EquatableMixin { final Set from; + final Set to; final SearchQuery? text; final PresentationMailbox? mailbox; final EmailReceiveTimeType emailReceiveTimeType; @@ -18,17 +21,20 @@ class SimpleSearchFilter with EquatableMixin { SimpleSearchFilter({ Set? from, + Set? to, EmailReceiveTimeType? emailReceiveTimeType, bool? hasAttachment, this.text, this.before, this.mailbox, }) : from = from ?? {}, + to = to ?? {}, hasAttachment = hasAttachment ?? false, emailReceiveTimeType = emailReceiveTimeType ?? EmailReceiveTimeType.allTime; SimpleSearchFilter copyWith({ Set? from, + Set? to, SearchQuery? text, PresentationMailbox? mailbox, EmailReceiveTimeType? emailReceiveTimeType, @@ -37,6 +43,7 @@ class SimpleSearchFilter with EquatableMixin { }) { return SimpleSearchFilter( from: from ?? this.from, + to: to ?? this.to, text: text ?? this.text, mailbox: mailbox ?? this.mailbox, emailReceiveTimeType: emailReceiveTimeType ?? this.emailReceiveTimeType, @@ -63,6 +70,10 @@ class SimpleSearchFilter with EquatableMixin { LogicFilterOperator( Operator.AND, from.map((e) => EmailFilterCondition(from: e)).toSet()), + if (to.isNotEmpty) + LogicFilterOperator( + Operator.AND, + to.map((e) => EmailFilterCondition(to: e)).toSet()), }; return listEmailCondition.isNotEmpty @@ -71,7 +82,7 @@ class SimpleSearchFilter with EquatableMixin { } @override - List get props => [from, text, mailbox, emailReceiveTimeType, hasAttachment, before]; + List get props => [from, to, text, mailbox, emailReceiveTimeType, hasAttachment, before]; } extension SearchEmailFilterExtension on SimpleSearchFilter { @@ -79,6 +90,7 @@ extension SearchEmailFilterExtension on SimpleSearchFilter { SimpleSearchFilter toSimpleSearchFilterByBefore({UTCDate? newBefore}) { return SimpleSearchFilter( from: from, + to: to, text: text, mailbox: mailbox, emailReceiveTimeType: emailReceiveTimeType, @@ -100,5 +112,53 @@ extension SearchEmailFilterExtension on SimpleSearchFilter { bool get searchFilterByMailboxApplied => mailbox != null; + bool get searchFilterByFromApplied => from.isNotEmpty; + + bool get searchFilterByToApplied => to.isNotEmpty; + + bool searchFilterByContactApplied(PrefixEmailAddress prefixEmailAddress) { + switch(prefixEmailAddress) { + case PrefixEmailAddress.from: + return searchFilterByFromApplied; + case PrefixEmailAddress.to: + return searchFilterByToApplied; + default: + return false; + } + } + + Set getContactApplied(PrefixEmailAddress prefixEmailAddress) { + switch(prefixEmailAddress) { + case PrefixEmailAddress.from: + return from; + case PrefixEmailAddress.to: + return to; + default: + return {}; + } + } + + String getNameContactApplied(PrefixEmailAddress prefixEmailAddress) { + switch(prefixEmailAddress) { + case PrefixEmailAddress.from: + return from.first; + case PrefixEmailAddress.to: + return to.first; + default: + return ''; + } + } + + String getNameContactDefault(BuildContext context, PrefixEmailAddress prefixEmailAddress) { + switch(prefixEmailAddress) { + case PrefixEmailAddress.from: + return AppLocalizations.of(context).from_email_address_prefix; + case PrefixEmailAddress.to: + return AppLocalizations.of(context).to_email_address_prefix; + default: + return ''; + } + } + String get mailboxName => mailbox?.name?.name ?? ''; } \ No newline at end of file diff --git a/lib/features/search/presentation/search_email_controller.dart b/lib/features/search/presentation/search_email_controller.dart index d4481ff33..4a6886412 100644 --- a/lib/features/search/presentation/search_email_controller.dart +++ b/lib/features/search/presentation/search_email_controller.dart @@ -441,6 +441,13 @@ class SearchEmailController extends BaseController } } + void selectContactForSearchFilter( + BuildContext context, + PrefixEmailAddress prefixEmailAddress + ) async { + + } + void _updateSimpleSearchFilter({ Set? from, SearchQuery? text, @@ -546,9 +553,7 @@ class SearchEmailController extends BaseController void selectEmail(BuildContext context, PresentationEmail presentationEmailSelected) { listResultSearch.value = listResultSearch - .map((email) => email.id == presentationEmailSelected.id - ? email.toggleSelect() - : email) + .map((email) => email.id == presentationEmailSelected.id ? email.toggleSelect() : email) .toList(); if (listResultSearch.isAllSelectionInActive) { diff --git a/lib/features/search/presentation/search_email_view.dart b/lib/features/search/presentation/search_email_view.dart index d3c35c07e..26c594c24 100644 --- a/lib/features/search/presentation/search_email_view.dart +++ b/lib/features/search/presentation/search_email_view.dart @@ -6,7 +6,6 @@ import 'package:core/presentation/utils/style_utils.dart'; import 'package:core/presentation/views/background/background_widget_builder.dart'; import 'package:core/presentation/views/button/icon_button_web.dart'; import 'package:core/presentation/views/text/text_field_builder.dart'; -import 'package:core/utils/app_logger.dart'; import 'package:core/utils/build_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -163,8 +162,9 @@ class SearchEmailView extends GetWidget child: ListView( scrollDirection: Axis.horizontal, children: [ + _buildSearchFilterByContactButton(context, PrefixEmailAddress.from), + _buildSearchFilterByContactButton(context, PrefixEmailAddress.to), ...[ - QuickSearchFilter.fromMe, QuickSearchFilter.hasAttachment, QuickSearchFilter.last7Days ].map((filter) => _buildQuickSearchFilterButton(context, filter)), @@ -177,7 +177,6 @@ class SearchEmailView extends GetWidget Widget _buildQuickSearchFilterButton(BuildContext context, QuickSearchFilter filter) { return Obx(() { final filterSelected = controller.checkQuickSearchFilterSelected(filter); - log('SearchEmailView::_buildQuickSearchFilterButton(): filterSelected: $filterSelected'); return Padding( padding: const EdgeInsets.only(right: 8), child: InkWell( @@ -237,9 +236,10 @@ class SearchEmailView extends GetWidget ... [ const SizedBox(width: 4), SvgPicture.asset( - _imagePaths.icChevronDown, - width: 16, - height: 16, + _imagePaths.icChevronDownOutline, + color: filterSelected + ? AppColor.primaryColor + : AppColor.colorDefaultRichTextButton, fit: BoxFit.fill), ] ]) @@ -253,7 +253,7 @@ class SearchEmailView extends GetWidget BuildContext context, EmailReceiveTimeType? receiveTimeSelected, Function(EmailReceiveTimeType?)? onCallBack - ) { + ) { return EmailReceiveTimeType.values .map((timeType) => PopupMenuItem( padding: EdgeInsets.zero, @@ -553,10 +553,65 @@ class SearchEmailView extends GetWidget ), const SizedBox(width: 4), SvgPicture.asset( - _imagePaths.icChevronDown, + _imagePaths.icChevronDownOutline, + color: filterSelected + ? AppColor.primaryColor + : AppColor.colorDefaultRichTextButton, + fit: BoxFit.fill), + ]) + ), + ), + ); + }); + } + + Widget _buildSearchFilterByContactButton(BuildContext context, PrefixEmailAddress prefixEmailAddress) { + return Obx(() { + final filterSelected = controller.simpleSearchFilter.value + .searchFilterByContactApplied(prefixEmailAddress); + return Padding( + padding: const EdgeInsets.only(right: 8), + child: InkWell( + onTap: () => controller.selectContactForSearchFilter(context, prefixEmailAddress), + borderRadius: const BorderRadius.all(Radius.circular(10)), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: filterSelected + ? AppColor.colorItemEmailSelectedDesktop + : AppColor.colorButtonHeaderThread), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + SvgPicture.asset( + filterSelected ? _imagePaths.icSelectedSB : _imagePaths.icUserSB, width: 16, height: 16, fit: BoxFit.fill), + const SizedBox(width: 4), + Text( + filterSelected + ? controller.simpleSearchFilter.value.getNameContactApplied(prefixEmailAddress) + : controller.simpleSearchFilter.value.getNameContactDefault(context, prefixEmailAddress), + maxLines: 1, + overflow: CommonTextStyle.defaultTextOverFlow, + softWrap: CommonTextStyle.defaultSoftWrap, + style: filterSelected + ? const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500, + color: AppColor.colorTextButton) + : const TextStyle( + fontSize: 13, + fontWeight: FontWeight.normal, + color: AppColor.colorTextButtonHeaderThread) + ), + const SizedBox(width: 4), + SvgPicture.asset( + _imagePaths.icChevronDownOutline, + color: filterSelected + ? AppColor.primaryColor + : AppColor.colorDefaultRichTextButton, + fit: BoxFit.fill), ]) ), ),