From 17eeefbb39828286d9b099ce2ad76efdc474a3d3 Mon Sep 17 00:00:00 2001 From: dab246 Date: Tue, 26 Oct 2021 13:59:25 +0700 Subject: [PATCH] TF-138 Implement load more for search email --- .../domain/state/search_more_email_state.dart | 30 +++++++++ .../search_more_email_interactor.dart | 50 ++++++++++++++ .../thread/presentation/thread_bindings.dart | 3 + .../presentation/thread_controller.dart | 62 +++++++++++++----- .../thread/presentation/thread_view.dart | 65 ++++++++++++++----- 5 files changed, 176 insertions(+), 34 deletions(-) create mode 100644 lib/features/thread/domain/state/search_more_email_state.dart create mode 100644 lib/features/thread/domain/usecases/search_more_email_interactor.dart diff --git a/lib/features/thread/domain/state/search_more_email_state.dart b/lib/features/thread/domain/state/search_more_email_state.dart new file mode 100644 index 000000000..2157cfa56 --- /dev/null +++ b/lib/features/thread/domain/state/search_more_email_state.dart @@ -0,0 +1,30 @@ + +import 'package:core/core.dart'; +import 'package:model/email/presentation_email.dart'; + +class SearchingMoreState extends UIState { + + SearchingMoreState(); + + @override + List get props => []; +} + + +class SearchMoreEmailSuccess extends UIState { + final List emailList; + + SearchMoreEmailSuccess(this.emailList); + + @override + List get props => [emailList]; +} + +class SearchMoreEmailFailure extends FeatureFailure { + final exception; + + SearchMoreEmailFailure(this.exception); + + @override + List get props => [exception]; +} \ No newline at end of file diff --git a/lib/features/thread/domain/usecases/search_more_email_interactor.dart b/lib/features/thread/domain/usecases/search_more_email_interactor.dart new file mode 100644 index 000000000..06ea63da0 --- /dev/null +++ b/lib/features/thread/domain/usecases/search_more_email_interactor.dart @@ -0,0 +1,50 @@ + +import 'package:core/core.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.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/thread/domain/repository/thread_repository.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/search_more_email_state.dart'; + +class SearchMoreEmailInteractor { + + final ThreadRepository threadRepository; + + SearchMoreEmailInteractor(this.threadRepository); + + Stream> execute( + AccountId accountId, + { + UnsignedInt? limit, + Set? sort, + Filter? filter, + Properties? properties, + EmailId? lastEmailId + } + ) async* { + try { + yield Right(SearchingMoreState()); + + final emailList = await threadRepository.searchEmails( + accountId, + limit: limit, + sort: sort, + filter: filter, + properties: properties); + + final presentationEmailList = emailList + .where((email) => email.id != lastEmailId) + .map((email) => email.toPresentationEmail()) + .toList(); + + yield Right(SearchMoreEmailSuccess(presentationEmailList)); + } catch (e) { + yield Left(SearchMoreEmailFailure(e)); + } + } +} \ No newline at end of file diff --git a/lib/features/thread/presentation/thread_bindings.dart b/lib/features/thread/presentation/thread_bindings.dart index ca8169f03..73cd9d3a8 100644 --- a/lib/features/thread/presentation/thread_bindings.dart +++ b/lib/features/thread/presentation/thread_bindings.dart @@ -30,6 +30,7 @@ import 'package:tmail_ui_user/features/thread/domain/usecases/move_multiple_emai import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_star_multiple_email_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/search_email_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/search_more_email_interactor.dart'; import 'package:tmail_ui_user/features/thread/presentation/thread_controller.dart'; class ThreadBindings extends Bindings { @@ -69,6 +70,7 @@ class ThreadBindings extends Bindings { Get.lazyPut(() => RefreshChangesEmailsInMailboxInteractor(Get.find())); Get.lazyPut(() => LoadMoreEmailsInMailboxInteractor(Get.find())); Get.lazyPut(() => SearchEmailInteractor(Get.find())); + Get.lazyPut(() => SearchMoreEmailInteractor(Get.find())); Get.put(ThreadController( Get.find(), Get.find(), @@ -81,6 +83,7 @@ class ThreadBindings extends Bindings { Get.find(), Get.find(), Get.find(), + Get.find(), )); } } \ 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 5550e94a3..93a4496ae 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -28,6 +28,7 @@ import 'package:tmail_ui_user/features/thread/domain/state/mark_as_multiple_emai import 'package:tmail_ui_user/features/thread/domain/state/move_multiple_email_to_mailbox_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/mark_as_star_multiple_email_state.dart'; import 'package:tmail_ui_user/features/thread/domain/state/search_email_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/search_more_email_state.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/get_emails_in_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/load_more_emails_in_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_multiple_email_read_interactor.dart'; @@ -35,6 +36,7 @@ import 'package:tmail_ui_user/features/thread/domain/usecases/move_multiple_emai import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_star_multiple_email_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/search_email_interactor.dart'; +import 'package:tmail_ui_user/features/thread/domain/usecases/search_more_email_interactor.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/app_routes.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; @@ -54,12 +56,14 @@ class ThreadController extends BaseController { final RefreshChangesEmailsInMailboxInteractor _refreshChangesEmailsInMailboxInteractor; final LoadMoreEmailsInMailboxInteractor _loadMoreEmailsInMailboxInteractor; final SearchEmailInteractor _searchEmailInteractor; + final SearchMoreEmailInteractor _searchMoreEmailInteractor; final emailList = [].obs; final emailListSearch = [].obs; final currentSelectMode = SelectMode.INACTIVE.obs; bool canLoadMore = true; + bool canSearchMore = true; MailboxId? _currentMailboxId; jmap.State? _currentEmailState; @@ -86,6 +90,7 @@ class ThreadController extends BaseController { this._refreshChangesEmailsInMailboxInteractor, this._loadMoreEmailsInMailboxInteractor, this._searchEmailInteractor, + this._searchMoreEmailInteractor, ); @override @@ -135,9 +140,7 @@ class ThreadController extends BaseController { super.onData(newState); newState.fold( (failure) { - if (failure is LoadMoreEmailsFailure) { - canLoadMore = false; - } else if (failure is SearchEmailFailure) { + if (failure is SearchEmailFailure) { emailListSearch.clear(); } }, @@ -148,6 +151,8 @@ class ThreadController extends BaseController { _loadMoreEmailsSuccess(success); } else if (success is SearchEmailSuccess) { _searchEmailsSuccess(success); + } else if (success is SearchMoreEmailSuccess) { + _searchMoreEmailsSuccess(success); } } ); @@ -236,20 +241,16 @@ class ThreadController extends BaseController { } void loadMoreEmails() { - if (isSearchActive()) { - return; - } else { - if (canLoadMore && _accountId != null) { - consumeState(_loadMoreEmailsInMailboxInteractor.execute( - _accountId!, - limit: ThreadConstants.defaultLimit, - sort: _sortOrder, - filter: EmailFilterCondition( - inMailbox: mailboxDashBoardController.selectedMailbox.value?.id, - before: emailList.last.receivedAt), - properties: ThreadConstants.propertiesDefault, - )); - } + if (canLoadMore && _accountId != null) { + consumeState(_loadMoreEmailsInMailboxInteractor.execute( + _accountId!, + limit: ThreadConstants.defaultLimit, + sort: _sortOrder, + filter: EmailFilterCondition( + inMailbox: mailboxDashBoardController.selectedMailbox.value?.id, + before: emailList.last.receivedAt), + properties: ThreadConstants.propertiesDefault, + )); } } @@ -547,6 +548,33 @@ class ThreadController extends BaseController { emailListSearch.value = resultEmailSearchList; } + void searchMoreEmails() { + if (canSearchMore && _accountId != null) { + consumeState(_searchMoreEmailInteractor.execute( + _accountId!, + limit: ThreadConstants.defaultLimit, + sort: _sortOrder, + filter: EmailFilterCondition( + text: searchQuery!.value, + before: emailListSearch.last.receivedAt), + properties: ThreadConstants.propertiesDefault, + lastEmailId: emailListSearch.last.id + )); + } + } + + void _searchMoreEmailsSuccess(SearchMoreEmailSuccess success) { + if (success.emailList.isNotEmpty) { + final resultEmailSearchList = success.emailList + .map((email) => email.toSearchPresentationEmail(mailboxDashBoardController.mapMailbox)) + .where((email) => !emailListSearch.contains(email)) + .toList(); + emailListSearch.addAll(resultEmailSearchList); + } else { + canSearchMore = false; + } + } + bool canComposeEmail() => mailboxDashBoardController.sessionCurrent != null && mailboxDashBoardController.userProfile.value != null && mailboxDashBoardController.mapDefaultMailboxId.containsKey(PresentationMailbox.roleOutbox); diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index 1e3812589..0dc73e1a3 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -6,6 +6,7 @@ import 'package:get/get.dart'; import 'package:model/email/presentation_email.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/thread/domain/state/search_email_state.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/search_more_email_state.dart'; import 'package:tmail_ui_user/features/thread/presentation/thread_controller.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/app_bar_thread_select_mode_active_builder.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart'; @@ -214,27 +215,53 @@ class ThreadView extends GetWidget { Widget _buildLoadingView() { return Obx(() => controller.viewState.value.fold( (failure) => SizedBox.shrink(), - (success) => success is LoadingState || success is SearchingState - ? Center(child: Padding( - padding: EdgeInsets.only(top: 16, bottom: 16), - child: SizedBox( - width: 24, - height: 24, - child: CircularProgressIndicator(color: AppColor.primaryColor)))) - : SizedBox.shrink())); + (success) { + if (controller.isSearchActive()) { + return success is SearchingState + ? Center(child: Padding( + padding: EdgeInsets.only(top: 16, bottom: 16), + child: SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator(color: AppColor.primaryColor)))) + : SizedBox.shrink(); + } else { + return success is LoadingState + ? Center(child: Padding( + padding: EdgeInsets.only(top: 16, bottom: 16), + child: SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator(color: AppColor.primaryColor)))) + : SizedBox.shrink(); + } + })); } Widget _buildLoadingViewLoadMore() { return Obx(() => controller.viewState.value.fold( (failure) => SizedBox.shrink(), - (success) => success is LoadingMoreState - ? Center(child: Padding( - padding: EdgeInsets.only(top: 16, bottom: 16), - child: SizedBox( - width: 24, - height: 24, - child: CircularProgressIndicator(color: AppColor.primaryColor)))) - : SizedBox.shrink())); + (success) { + if (controller.isSearchActive()) { + return success is SearchingMoreState + ? Center(child: Padding( + padding: EdgeInsets.only(top: 16, bottom: 16), + child: SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator(color: AppColor.primaryColor)))) + : SizedBox.shrink(); + } else { + return success is LoadingMoreState + ? Center(child: Padding( + padding: EdgeInsets.only(top: 16, bottom: 16), + child: SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator(color: AppColor.primaryColor)))) + : SizedBox.shrink(); + } + })); } Widget _buildListEmail(BuildContext context) { @@ -288,7 +315,11 @@ class ThreadView extends GetWidget { onNotification: (ScrollNotification scrollInfo) { if (scrollInfo is ScrollEndNotification && scrollInfo.metrics.pixels == scrollInfo.metrics.maxScrollExtent) { - controller.loadMoreEmails(); + if (controller.isSearchActive()) { + controller.searchMoreEmails(); + } else { + controller.loadMoreEmails(); + } } return false; },