From 243ce444cd71ed66ce2c75ea6f911691748b3a80 Mon Sep 17 00:00:00 2001 From: dab246 Date: Mon, 23 May 2022 20:59:37 +0700 Subject: [PATCH] TF-524 Implement quick search emails in selected mailbox by word --- .../state/quick_search_email_state.dart | 21 +++++++++ .../quick_search_email_interactor.dart | 45 +++++++++++++++++++ .../mailbox_dashboard_bindings.dart | 27 +++++++++++ .../mailbox_dashboard_controller.dart | 28 ++++++++++++ .../mailbox_dashboard_view_web.dart | 20 +++++++-- .../email_quick_search_item_tile_widget.dart | 2 +- .../domain/constants/thread_constants.dart | 12 +++++ .../presentation/thread_controller.dart | 8 +++- 8 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 lib/features/mailbox_dashboard/domain/state/quick_search_email_state.dart create mode 100644 lib/features/mailbox_dashboard/domain/usecases/quick_search_email_interactor.dart diff --git a/lib/features/mailbox_dashboard/domain/state/quick_search_email_state.dart b/lib/features/mailbox_dashboard/domain/state/quick_search_email_state.dart new file mode 100644 index 000000000..37e933b41 --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/state/quick_search_email_state.dart @@ -0,0 +1,21 @@ + +import 'package:core/core.dart'; +import 'package:model/email/presentation_email.dart'; + +class QuickSearchEmailSuccess extends UIState { + final List emailList; + + QuickSearchEmailSuccess(this.emailList); + + @override + List get props => [emailList]; +} + +class QuickSearchEmailFailure extends FeatureFailure { + final dynamic exception; + + QuickSearchEmailFailure(this.exception); + + @override + List get props => [exception]; +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/domain/usecases/quick_search_email_interactor.dart b/lib/features/mailbox_dashboard/domain/usecases/quick_search_email_interactor.dart new file mode 100644 index 000000000..ea1a170c3 --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/usecases/quick_search_email_interactor.dart @@ -0,0 +1,45 @@ + +import 'package:core/core.dart'; +import 'package:model/model.dart'; +import 'package:dartz/dartz.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/properties/properties.dart'; +import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; +import 'package:jmap_dart_client/jmap/core/sort/comparator.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/state/quick_search_email_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart'; + +class QuickSearchEmailInteractor { + + final ThreadRepository threadRepository; + + QuickSearchEmailInteractor(this.threadRepository); + + Future> execute( + AccountId accountId, + { + UnsignedInt? limit, + Set? sort, + Filter? filter, + Properties? properties, + } + ) async { + try { + final emailList = await threadRepository.searchEmails( + accountId, + limit: limit, + sort: sort, + filter: filter, + properties: properties); + + final presentationEmailList = emailList + .map((email) => email.toPresentationEmail()) + .toList(); + + return Right(QuickSearchEmailSuccess(presentationEmailList)); + } catch (e) { + return Left(QuickSearchEmailFailure(e)); + } + } +} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart index 3b301db7e..f5d103091 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_bindings.dart @@ -2,6 +2,7 @@ import 'package:core/core.dart'; import 'package:get/get.dart'; import 'package:tmail_ui_user/features/base/base_bindings.dart'; import 'package:tmail_ui_user/features/caching/recent_search_cache_client.dart'; +import 'package:tmail_ui_user/features/caching/state_cache_client.dart'; import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart'; import 'package:tmail_ui_user/features/email/data/datasource/html_datasource.dart'; import 'package:tmail_ui_user/features/email/data/datasource_impl/email_datasource_impl.dart'; @@ -14,6 +15,8 @@ import 'package:tmail_ui_user/features/email/domain/usecases/delete_email_perman import 'package:tmail_ui_user/features/email/domain/usecases/move_to_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/email/presentation/email_bindings.dart'; import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; +import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart'; +import 'package:tmail_ui_user/features/mailbox/data/datasource_impl/state_datasource_impl.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/mailbox_bindings.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/search_datasource.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource_impl/search_datasource_impl.dart'; @@ -21,9 +24,17 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/data/repository/search_ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/search_repository.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_all_recent_search_latest_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_user_profile_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/quick_search_email_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_email_drafts_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_recent_search_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart'; +import 'package:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart'; +import 'package:tmail_ui_user/features/thread/data/datasource_impl/local_thread_datasource_impl.dart'; +import 'package:tmail_ui_user/features/thread/data/datasource_impl/thread_datasource_impl.dart'; +import 'package:tmail_ui_user/features/thread/data/local/email_cache_manager.dart'; +import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart'; +import 'package:tmail_ui_user/features/thread/data/repository/thread_repository_impl.dart'; +import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart'; import 'package:tmail_ui_user/features/thread/presentation/thread_bindings.dart'; class MailboxDashBoardBindings extends BaseBindings { @@ -43,6 +54,7 @@ class MailboxDashBoardBindings extends BaseBindings { Get.find(), Get.find(), Get.find(), + Get.find(), )); } @@ -51,6 +63,8 @@ class MailboxDashBoardBindings extends BaseBindings { Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); + Get.lazyPut(() => Get.find()); + Get.lazyPut(() => Get.find()); } @override @@ -61,6 +75,9 @@ class MailboxDashBoardBindings extends BaseBindings { Get.find() )); Get.lazyPut(() => SearchDataSourceImpl(Get.find())); + Get.lazyPut(() => ThreadDataSourceImpl(Get.find())); + Get.lazyPut(() => LocalThreadDataSourceImpl(Get.find())); + Get.lazyPut(() => StateDataSourceImpl(Get.find())); } @override @@ -71,12 +88,14 @@ class MailboxDashBoardBindings extends BaseBindings { Get.lazyPut(() => DeleteEmailPermanentlyInteractor(Get.find())); Get.lazyPut(() => SaveRecentSearchInteractor(Get.find())); Get.lazyPut(() => GetAllRecentSearchLatestInteractor(Get.find())); + Get.lazyPut(() => QuickSearchEmailInteractor(Get.find())); } @override void bindingsRepository() { Get.lazyPut(() => Get.find()); Get.lazyPut(() => Get.find()); + Get.lazyPut(() => Get.find()); } @override @@ -86,5 +105,13 @@ class MailboxDashBoardBindings extends BaseBindings { Get.find() )); Get.lazyPut(() => SearchRepositoryImpl(Get.find())); + Get.lazyPut(() => ThreadRepositoryImpl( + { + DataSourceType.network: Get.find(), + DataSourceType.local: Get.find() + }, + Get.find(), + Get.find(), + )); } } \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart index cc09e47e2..7410dd774 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_controller.dart @@ -12,6 +12,10 @@ import 'package:jmap_dart_client/jmap/core/capability/mail_capability.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email_comparator.dart'; +import 'package:jmap_dart_client/jmap/core/sort/comparator.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/mailbox/mailbox.dart'; import 'package:model/model.dart'; import 'package:package_info_plus/package_info_plus.dart'; @@ -33,13 +37,16 @@ import 'package:tmail_ui_user/features/email/presentation/model/composer_argumen 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_user_profile_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/remove_email_drafts_state.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_all_recent_search_latest_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/get_user_profile_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/quick_search_email_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_email_drafts_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_recent_search_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart'; import 'package:tmail_ui_user/features/manage_account/presentation/model/manage_account_arguments.dart'; +import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart'; import 'package:tmail_ui_user/features/thread/domain/model/search_query.dart'; import 'package:tmail_ui_user/features/thread/domain/state/search_email_state.dart'; import 'package:tmail_ui_user/features/thread/presentation/model/search_state.dart'; @@ -64,6 +71,7 @@ class MailboxDashBoardController extends ReloadableController { final DeleteEmailPermanentlyInteractor _deleteEmailPermanentlyInteractor; final SaveRecentSearchInteractor _saveRecentSearchInteractor; final GetAllRecentSearchLatestInteractor _getAllRecentSearchLatestInteractor; + final QuickSearchEmailInteractor _quickSearchEmailInteractor; final scaffoldKey = GlobalKey(); final selectedMailbox = Rxn(); @@ -94,6 +102,7 @@ class MailboxDashBoardController extends ReloadableController { this._deleteEmailPermanentlyInteractor, this._saveRecentSearchInteractor, this._getAllRecentSearchLatestInteractor, + this._quickSearchEmailInteractor, ); @override @@ -415,6 +424,25 @@ class MailboxDashBoardController extends ReloadableController { : [])); } + Future> quickSearchEmailsAction(String pattern) async { + if (accountId.value != null && selectedMailbox.value != null) { + return await _quickSearchEmailInteractor.execute( + accountId.value!, + limit: UnsignedInt(5), + sort: {} + ..add(EmailComparator(EmailComparatorProperty.receivedAt) + ..setIsAscending(false)), + filter: EmailFilterCondition( + text: pattern, + inMailbox: selectedMailbox.value!.id), + properties: ThreadConstants.propertiesQuickSearch + ).then((result) => result.fold( + (failure) => [], + (success) => success is QuickSearchEmailSuccess ? success.emailList : [])); + } + return []; + } + void composeEmailAction() { if (kIsWeb) { if (dashBoardAction.value is! ComposeEmailAction) { diff --git a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart index 2c9866eb6..08e0e55a5 100644 --- a/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart +++ b/lib/features/mailbox_dashboard/presentation/mailbox_dashboard_view_web.dart @@ -4,6 +4,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:get/get.dart'; import 'package:model/model.dart'; +import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; import 'package:tmail_ui_user/features/base/mixin/network_connection_mixin.dart'; import 'package:tmail_ui_user/features/composer/presentation/composer_view_web.dart'; import 'package:tmail_ui_user/features/email/presentation/email_view.dart'; @@ -24,7 +25,7 @@ import 'package:tmail_ui_user/main/routes/app_routes.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; class MailboxDashBoardView extends GetWidget with NetworkConnectionMixin, - UserSettingPopupMenuMixin, FilterEmailPopupMenuMixin { + UserSettingPopupMenuMixin, FilterEmailPopupMenuMixin, AppLoaderMixin { final _responsiveUtils = Get.find(); final _imagePaths = Get.find(); @@ -425,6 +426,7 @@ class MailboxDashBoardView extends GetWidget with Ne if (keyword.trim().isNotEmpty) { controller.saveRecentSearch(RecentSearch(keyword, DateTime.now())); } + controller.searchEmail(context, keyword); }, decoration: InputDecoration( border: InputBorder.none, @@ -443,6 +445,7 @@ class MailboxDashBoardView extends GetWidget with Ne if (keyword.trim().isNotEmpty) { controller.saveRecentSearch(RecentSearch(keyword, DateTime.now())); } + controller.searchEmail(context, keyword); }), rightButton: buildIconWeb( icon: SvgPicture.asset(_imagePaths.icComposerClose, width: 18, height: 18, fit: BoxFit.fill), @@ -508,20 +511,31 @@ class MailboxDashBoardView extends GetWidget with Ne child: Text(AppLocalizations.of(context).recent, style: const TextStyle(fontSize: 13.0, color: AppColor.colorTextButtonHeaderThread, - fontWeight: FontWeight.w500))), + fontWeight: FontWeight.w500) + ) + ), + loadingBuilder: (context) => Padding( + padding: const EdgeInsets.only(bottom: 16), + child: loadingWidget, + ), getRecentCallback: (pattern) async { return controller.getAllRecentSearchAction(pattern); }, itemRecentBuilder: (context, recent) { return RecentSearchItemTileWidget(recent); }, + onRecentSelected: (recent) { + controller.searchInputController.text = recent.value; + controller.searchEmail(context, recent.value); + }, suggestionsCallback: (pattern) async { - return []; + return controller.quickSearchEmailsAction(pattern); }, itemBuilder: (context, email) { return EmailQuickSearchItemTileWidget(email, controller.selectedMailbox.value); }, onSuggestionSelected: (autoCompleteResult) async { + }), )), const SizedBox(width: 16), diff --git a/lib/features/mailbox_dashboard/presentation/widgets/email_quick_search_item_tile_widget.dart b/lib/features/mailbox_dashboard/presentation/widgets/email_quick_search_item_tile_widget.dart index 0fb8f94eb..169847107 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/email_quick_search_item_tile_widget.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/email_quick_search_item_tile_widget.dart @@ -23,7 +23,7 @@ class EmailQuickSearchItemTileWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/lib/features/thread/domain/constants/thread_constants.dart b/lib/features/thread/domain/constants/thread_constants.dart index 3cfc46910..bd5dee42e 100644 --- a/lib/features/thread/domain/constants/thread_constants.dart +++ b/lib/features/thread/domain/constants/thread_constants.dart @@ -24,4 +24,16 @@ class ThreadConstants { EmailProperty.keywords, EmailProperty.mailboxIds, }); + + static final propertiesQuickSearch = Properties({ + EmailProperty.id, + EmailProperty.subject, + EmailProperty.from, + EmailProperty.to, + EmailProperty.keywords, + EmailProperty.receivedAt, + EmailProperty.sentAt, + EmailProperty.preview, + EmailProperty.hasAttachment, + }); } \ No newline at end of file diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index 8fc02c3d5..7c8767bb2 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -396,7 +396,9 @@ class ThreadController extends BaseController { _accountId!, limit: limit, sort: _sortOrder, - filter: EmailFilterCondition(text: searchQuery!.value), + filter: EmailFilterCondition( + text: searchQuery!.value, + inMailbox: _currentMailboxId), properties: ThreadConstants.propertiesDefault, )); } @@ -844,7 +846,9 @@ class ThreadController extends BaseController { _accountId!, limit: ThreadConstants.defaultLimit, sort: _sortOrder, - filter: EmailFilterCondition(text: searchQuery!.value), + filter: EmailFilterCondition( + text: searchQuery!.value, + inMailbox: _currentMailboxId), properties: ThreadConstants.propertiesDefault, )); }