diff --git a/lib/features/thread/data/datasource/thread_datasource.dart b/lib/features/thread/data/datasource/thread_datasource.dart index 053396e85..bba7bf94e 100644 --- a/lib/features/thread/data/datasource/thread_datasource.dart +++ b/lib/features/thread/data/datasource/thread_datasource.dart @@ -6,6 +6,7 @@ import 'package:jmap_dart_client/jmap/core/state.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/mailbox/mailbox.dart'; +import 'package:model/model.dart'; import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart'; import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart'; @@ -29,7 +30,7 @@ abstract class ThreadDataSource { } ); - Future> getAllEmailCache({MailboxId? inMailboxId, Set? sort}); + Future> getAllEmailCache({MailboxId? inMailboxId, Set? sort, FilterMessageOption? filterOption}); Future update({List? updated, List? created, List? destroyed}); } \ No newline at end of file diff --git a/lib/features/thread/data/datasource_impl/local_thread_datasource_impl.dart b/lib/features/thread/data/datasource_impl/local_thread_datasource_impl.dart index 0799fc8bb..8d1f65828 100644 --- a/lib/features/thread/data/datasource_impl/local_thread_datasource_impl.dart +++ b/lib/features/thread/data/datasource_impl/local_thread_datasource_impl.dart @@ -6,6 +6,7 @@ import 'package:jmap_dart_client/jmap/core/state.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/mailbox/mailbox.dart'; +import 'package:model/model.dart'; import 'package:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart'; import 'package:tmail_ui_user/features/thread/data/local/email_cache_manager.dart'; import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart'; @@ -43,9 +44,9 @@ class LocalThreadDataSourceImpl extends ThreadDataSource { } @override - Future> getAllEmailCache({MailboxId? inMailboxId, Set? sort}) { + Future> getAllEmailCache({MailboxId? inMailboxId, Set? sort, FilterMessageOption? filterOption}) { return Future.sync(() async { - return await _emailCacheManager.getAllEmail(inMailboxId: inMailboxId, sort: sort); + return await _emailCacheManager.getAllEmail(inMailboxId: inMailboxId, sort: sort, filterOption: filterOption ?? FilterMessageOption.all); }).catchError((error) { throw error; }); diff --git a/lib/features/thread/data/datasource_impl/thread_datasource_impl.dart b/lib/features/thread/data/datasource_impl/thread_datasource_impl.dart index 0c37f90f9..82d262a8a 100644 --- a/lib/features/thread/data/datasource_impl/thread_datasource_impl.dart +++ b/lib/features/thread/data/datasource_impl/thread_datasource_impl.dart @@ -6,6 +6,7 @@ import 'package:jmap_dart_client/jmap/core/state.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/mailbox/mailbox.dart'; +import 'package:model/model.dart'; import 'package:tmail_ui_user/features/thread/data/datasource/thread_datasource.dart'; import 'package:tmail_ui_user/features/thread/data/model/email_change_response.dart'; import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart'; @@ -60,7 +61,7 @@ class ThreadDataSourceImpl extends ThreadDataSource { } @override - Future> getAllEmailCache({MailboxId? inMailboxId, Set? sort, UnsignedInt? limit}) { + Future> getAllEmailCache({MailboxId? inMailboxId, Set? sort, FilterMessageOption? filterOption}) { throw UnimplementedError(); } diff --git a/lib/features/thread/data/local/email_cache_manager.dart b/lib/features/thread/data/local/email_cache_manager.dart index 29a112c9a..98595f16d 100644 --- a/lib/features/thread/data/local/email_cache_manager.dart +++ b/lib/features/thread/data/local/email_cache_manager.dart @@ -16,11 +16,18 @@ class EmailCacheManager { EmailCacheManager(this._emailCacheClient); - Future> getAllEmail({MailboxId? inMailboxId, Set? sort}) async { + Future> getAllEmail({ + MailboxId? inMailboxId, + Set? sort, + FilterMessageOption filterOption = FilterMessageOption.all + }) async { final emailCacheList = inMailboxId != null ? await _emailCacheClient.getListEmailCacheByMailboxId(inMailboxId) : await _emailCacheClient.getAll(); - final emailList = emailCacheList.map((emailCache) => emailCache.toEmail()).toList(); + final emailList = emailCacheList + .map((emailCache) => emailCache.toEmail()) + .where((email) => filterOption.filterEmail(email)) + .toList(); if (sort != null) { sort.forEach((comparator) { emailList.sortBy(comparator); diff --git a/lib/features/thread/data/repository/thread_repository_impl.dart b/lib/features/thread/data/repository/thread_repository_impl.dart index f226da758..c6e92d9be 100644 --- a/lib/features/thread/data/repository/thread_repository_impl.dart +++ b/lib/features/thread/data/repository/thread_repository_impl.dart @@ -36,7 +36,10 @@ class ThreadRepositoryImpl extends ThreadRepository { } ) async* { final localEmailResponse = await Future.wait([ - mapDataSource[DataSourceType.local]!.getAllEmailCache(inMailboxId: emailFilter?.mailboxId, sort: sort), + mapDataSource[DataSourceType.local]!.getAllEmailCache( + inMailboxId: emailFilter?.mailboxId, + sort: sort, + filterOption: emailFilter?.filterOption), stateDataSource.getState(StateType.email) ]).then((List response) { return EmailsResponse(emailList: response.first, state: response.last); @@ -110,7 +113,10 @@ class ThreadRepositoryImpl extends ThreadRepository { } final newEmailResponse = await Future.wait([ - mapDataSource[DataSourceType.local]!.getAllEmailCache(inMailboxId: emailFilter?.mailboxId, sort: sort), + mapDataSource[DataSourceType.local]!.getAllEmailCache( + inMailboxId: emailFilter?.mailboxId, + sort: sort, + filterOption: emailFilter?.filterOption), stateDataSource.getState(StateType.email) ]).then((List response) { return EmailsResponse(emailList: response.first, state: response.last); @@ -167,12 +173,14 @@ class ThreadRepositoryImpl extends ThreadRepository { Set? sort, Properties? propertiesCreated, Properties? propertiesUpdated, - MailboxId? inMailboxId + MailboxId? inMailboxId, + FilterMessageOption? filterOption, } ) async* { final localEmailList = await mapDataSource[DataSourceType.local]!.getAllEmailCache( inMailboxId: inMailboxId, - sort: sort); + sort: sort, + filterOption: filterOption); EmailChangeResponse? emailChangeResponse; bool hasMoreChanges = true; @@ -212,7 +220,7 @@ class ThreadRepositoryImpl extends ThreadRepository { } final newEmailResponse = await Future.wait([ - mapDataSource[DataSourceType.local]!.getAllEmailCache(inMailboxId: inMailboxId, sort: sort), + mapDataSource[DataSourceType.local]!.getAllEmailCache(inMailboxId: inMailboxId, sort: sort, filterOption: filterOption), stateDataSource.getState(StateType.email) ]).then((List response) { return EmailsResponse(emailList: response.first, state: response.last); @@ -270,4 +278,40 @@ class ThreadRepositoryImpl extends ThreadRepository { return emailResponse.emailList ?? List.empty(); } + + @override + Stream refreshAll( + AccountId accountId, + { + UnsignedInt? limit, + Set? sort, + EmailFilter? emailFilter, + Properties? propertiesCreated, + Properties? propertiesUpdated, + } + ) async* { + EmailsResponse? networkEmailResponse = await mapDataSource[DataSourceType.network]!.getAllEmail( + accountId, + limit: limit, + sort: sort, + filter: emailFilter?.filter, + properties: propertiesCreated); + + await _updateEmailCache(newCreated: networkEmailResponse.emailList); + if (networkEmailResponse.state != null) { + await _updateState(networkEmailResponse.state!); + } + + final newEmailResponse = await Future.wait([ + mapDataSource[DataSourceType.local]!.getAllEmailCache( + inMailboxId: emailFilter?.mailboxId, + sort: sort, + filterOption: emailFilter?.filterOption), + stateDataSource.getState(StateType.email) + ]).then((List response) { + return EmailsResponse(emailList: response.first, state: response.last); + }); + + yield newEmailResponse; + } } \ No newline at end of file diff --git a/lib/features/thread/domain/repository/thread_repository.dart b/lib/features/thread/domain/repository/thread_repository.dart index 79b0d8c72..f46e8224e 100644 --- a/lib/features/thread/domain/repository/thread_repository.dart +++ b/lib/features/thread/domain/repository/thread_repository.dart @@ -21,6 +21,17 @@ abstract class ThreadRepository { } ); + Stream refreshAll( + AccountId accountId, + { + UnsignedInt? limit, + Set? sort, + EmailFilter? emailFilter, + Properties? propertiesCreated, + Properties? propertiesUpdated, + } + ); + Stream refreshChanges( AccountId accountId, jmap.State currentState, @@ -28,7 +39,8 @@ abstract class ThreadRepository { Set? sort, Properties? propertiesCreated, Properties? propertiesUpdated, - MailboxId? inMailboxId + MailboxId? inMailboxId, + FilterMessageOption? filterOption, } ); diff --git a/lib/features/thread/domain/usecases/refresh_all_emails_in_mailbox_interactor.dart b/lib/features/thread/domain/usecases/refresh_all_emails_in_mailbox_interactor.dart new file mode 100644 index 000000000..93b6a5e66 --- /dev/null +++ b/lib/features/thread/domain/usecases/refresh_all_emails_in_mailbox_interactor.dart @@ -0,0 +1,52 @@ +import 'package:core/core.dart'; +import 'package:dartz/dartz.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; +import 'package:jmap_dart_client/jmap/core/sort/comparator.dart'; +import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; +import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart'; +import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/get_all_email_state.dart'; +import 'package:model/model.dart'; + +class RefreshAllEmailsInMailboxInteractor { + final ThreadRepository threadRepository; + + RefreshAllEmailsInMailboxInteractor(this.threadRepository); + + Stream> execute( + AccountId accountId, + { + UnsignedInt? limit, + Set? sort, + EmailFilter? emailFilter, + Properties? propertiesCreated, + Properties? propertiesUpdated, + } + ) async* { + try { + yield Right(LoadingState()); + + yield* threadRepository + .refreshAll( + accountId, + limit: limit, + sort: sort, + emailFilter: emailFilter, + propertiesCreated: propertiesCreated, + propertiesUpdated: propertiesUpdated) + .map(_toGetEmailState); + } catch (e) { + yield Left(GetAllEmailFailure(e)); + } + } + + Either _toGetEmailState(EmailsResponse emailResponse) { + final presentationEmailList = emailResponse.emailList + ?.map((email) => email.toPresentationEmail()).toList() ?? List.empty(); + + return Right(GetAllEmailSuccess( + emailList: presentationEmailList, + currentEmailState: emailResponse.state)); + } +} \ No newline at end of file diff --git a/lib/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor.dart b/lib/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor.dart index bfe40ad3d..a236c6c48 100644 --- a/lib/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor.dart +++ b/lib/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor.dart @@ -22,7 +22,8 @@ class RefreshChangesEmailsInMailboxInteractor { Set? sort, Properties? propertiesCreated, Properties? propertiesUpdated, - MailboxId? inMailboxId + MailboxId? inMailboxId, + FilterMessageOption? filterOption, } ) async* { yield Right(RefreshingState()); @@ -35,7 +36,8 @@ class RefreshChangesEmailsInMailboxInteractor { sort: sort, propertiesCreated: propertiesCreated, propertiesUpdated: propertiesUpdated, - inMailboxId: inMailboxId) + inMailboxId: inMailboxId, + filterOption: filterOption) .map(_toGetEmailState); } catch (e) { yield Left(GetAllEmailFailure(e)); diff --git a/lib/features/thread/presentation/model/filter_message_option.dart b/lib/features/thread/presentation/extensions/filter_message_option_extension.dart similarity index 77% rename from lib/features/thread/presentation/model/filter_message_option.dart rename to lib/features/thread/presentation/extensions/filter_message_option_extension.dart index 44e2ab09b..6f5eb77f6 100644 --- a/lib/features/thread/presentation/model/filter_message_option.dart +++ b/lib/features/thread/presentation/extensions/filter_message_option_extension.dart @@ -1,31 +1,11 @@ import 'package:core/core.dart'; import 'package:flutter/cupertino.dart'; -import 'package:model/email/presentation_email.dart'; +import 'package:model/model.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -enum FilterMessageOption { - all, - unread, - attachments, - starred, -} - extension FilterMessageOptionExtension on FilterMessageOption { - bool filterEmail(PresentationEmail email) { - switch(this) { - case FilterMessageOption.all: - return true; - case FilterMessageOption.unread: - return email.isUnReadEmail(); - case FilterMessageOption.attachments: - return email.withAttachments; - case FilterMessageOption.starred: - return email.isFlaggedEmail(); - } - } - String getIconToast(ImagePaths imagePaths) { switch(this) { case FilterMessageOption.all: diff --git a/lib/features/thread/presentation/thread_bindings.dart b/lib/features/thread/presentation/thread_bindings.dart index 212999d4c..18cc9d230 100644 --- a/lib/features/thread/presentation/thread_bindings.dart +++ b/lib/features/thread/presentation/thread_bindings.dart @@ -28,6 +28,7 @@ import 'package:tmail_ui_user/features/thread/domain/usecases/load_more_emails_i import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_multiple_email_read_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/move_multiple_email_to_mailbox_interactor.dart'; 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_all_emails_in_mailbox_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'; @@ -46,6 +47,7 @@ class ThreadBindings extends BaseBindings { Get.put(ThreadController( Get.find(), Get.find(), + Get.find(), Get.find(), Get.find(), Get.find(), @@ -83,6 +85,7 @@ class ThreadBindings extends BaseBindings { @override void bindingsInteractor() { Get.lazyPut(() => GetEmailsInMailboxInteractor(Get.find())); + Get.lazyPut(() => RefreshAllEmailsInMailboxInteractor(Get.find())); Get.lazyPut(() => MarkAsEmailReadInteractor(Get.find())); Get.lazyPut(() => MarkAsMultipleEmailReadInteractor(Get.find())); Get.lazyPut(() => MoveToMailboxInteractor(Get.find())); diff --git a/lib/features/thread/presentation/thread_controller.dart b/lib/features/thread/presentation/thread_controller.dart index c4996e903..3fac920ad 100644 --- a/lib/features/thread/presentation/thread_controller.dart +++ b/lib/features/thread/presentation/thread_controller.dart @@ -11,6 +11,7 @@ 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/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/keyword_identifier.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; @@ -41,10 +42,11 @@ import 'package:tmail_ui_user/features/thread/domain/usecases/load_more_emails_i import 'package:tmail_ui_user/features/thread/domain/usecases/mark_as_multiple_email_read_interactor.dart'; import 'package:tmail_ui_user/features/thread/domain/usecases/move_multiple_email_to_mailbox_interactor.dart'; 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_all_emails_in_mailbox_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/model/filter_message_option.dart'; +import 'package:tmail_ui_user/features/thread/presentation/extensions/filter_message_option_extension.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,6 +56,7 @@ class ThreadController extends BaseController { final mailboxDashBoardController = Get.find(); final GetEmailsInMailboxInteractor _getEmailsInMailboxInteractor; + final RefreshAllEmailsInMailboxInteractor _refreshAllEmailsInMailboxInteractor; final MarkAsMultipleEmailReadInteractor _markAsMultipleEmailReadInteractor; final AppToast _appToast; final ResponsiveUtils responsiveUtils; @@ -69,7 +72,6 @@ class ThreadController extends BaseController { final emailList = [].obs; final emailListSearch = [].obs; - final emailListFiltered = [].obs; final currentSelectMode = SelectMode.INACTIVE.obs; final filterMessageOption = FilterMessageOption.all.obs; @@ -82,9 +84,6 @@ class ThreadController extends BaseController { SearchQuery? get searchQuery => mailboxDashBoardController.searchQuery; - EmailFilterCondition? get _filterCondition => EmailFilterCondition( - inMailbox: mailboxDashBoardController.selectedMailbox.value?.id); - Set? get _sortOrder => Set() ..add(EmailComparator(EmailComparatorProperty.receivedAt) ..setIsAscending(false)); @@ -94,6 +93,7 @@ class ThreadController extends BaseController { ThreadController( this.responsiveUtils, this._getEmailsInMailboxInteractor, + this._refreshAllEmailsInMailboxInteractor, this.listEmailController, this._markAsMultipleEmailReadInteractor, this._appToast, @@ -220,7 +220,6 @@ class ThreadController extends BaseController { void _resetToOriginalValue() { dispatchState(Right(LoadingState())); emailList.clear(); - emailListFiltered.clear(); canLoadMore = true; disableSearch(); cancelSelectEmail(); @@ -232,12 +231,6 @@ class ThreadController extends BaseController { .map((email) => email.asAvatarGradientColor(random)) .toList(); emailList.value = listEmailHaveAvatarGradientColor; - if (isFilterMessagesEnabled) { - final emailsFiltered = listEmailHaveAvatarGradientColor - .where((email) => filterMessageOption.value.filterEmail(email)) - .toList(); - emailListFiltered.addAll(emailsFiltered); - } } void _getAllEmailAction(AccountId accountId, {MailboxId? mailboxId}) { @@ -246,16 +239,60 @@ class ThreadController extends BaseController { limit: ThreadConstants.defaultLimit, sort: _sortOrder, emailFilter: EmailFilter( - filter: _filterCondition, + filter: _getFilterCondition(), + filterOption: filterMessageOption.value, mailboxId: mailboxId ?? _currentMailboxId), propertiesCreated: ThreadConstants.propertiesDefault, propertiesUpdated: ThreadConstants.propertiesUpdatedDefault, )); } + EmailFilterCondition _getFilterCondition({bool isLoadMore = false}) { + switch(filterMessageOption.value) { + case FilterMessageOption.all: + return EmailFilterCondition( + inMailbox: mailboxDashBoardController.selectedMailbox.value?.id, + before: isLoadMore ? emailList.last.receivedAt : null + ); + case FilterMessageOption.unread: + return EmailFilterCondition( + inMailbox: mailboxDashBoardController.selectedMailbox.value?.id, + notKeyword: KeyWordIdentifier.emailSeen.value, + before: isLoadMore ? emailList.last.receivedAt : null + ); + case FilterMessageOption.attachments: + return EmailFilterCondition( + inMailbox: mailboxDashBoardController.selectedMailbox.value?.id, + hasAttachment: true, + before: isLoadMore ? emailList.last.receivedAt : null + ); + case FilterMessageOption.starred: + return EmailFilterCondition( + inMailbox: mailboxDashBoardController.selectedMailbox.value?.id, + hasKeyword: KeyWordIdentifier.emailFlagged.value, + before: isLoadMore ? emailList.last.receivedAt : null + ); + } + } + void refreshAllEmail() { dispatchState(Right(LoadingState())); - _getAllEmail(); + canLoadMore = true; + cancelSelectEmail(); + + if (_accountId != null) { + consumeState(_refreshAllEmailsInMailboxInteractor.execute( + _accountId!, + limit: ThreadConstants.defaultLimit, + sort: _sortOrder, + emailFilter: EmailFilter( + filter: _getFilterCondition(), + filterOption: filterMessageOption.value, + mailboxId: _currentMailboxId), + propertiesCreated: ThreadConstants.propertiesDefault, + propertiesUpdated: ThreadConstants.propertiesUpdatedDefault, + )); + } } void _refreshEmailChanges() { @@ -279,6 +316,7 @@ class ThreadController extends BaseController { propertiesCreated: ThreadConstants.propertiesDefault, propertiesUpdated: ThreadConstants.propertiesUpdatedDefault, inMailboxId: _currentMailboxId, + filterOption: filterMessageOption.value )); } } @@ -290,9 +328,7 @@ class ThreadController extends BaseController { _accountId!, limit: ThreadConstants.defaultLimit, sort: _sortOrder, - filter: EmailFilterCondition( - inMailbox: mailboxDashBoardController.selectedMailbox.value?.id, - before: emailList.last.receivedAt), + filter: _getFilterCondition(isLoadMore: true), properties: ThreadConstants.propertiesDefault, lastEmailId: emailList.last.id )); @@ -305,13 +341,6 @@ class ThreadController extends BaseController { .map((email) => email.asAvatarGradientColor(random)) .toList(); emailList.addAll(listEmailHaveAvatarGradientColor); - - if (isFilterMessagesEnabled) { - final emailsFilteredMore = listEmailHaveAvatarGradientColor - .where((email) => filterMessageOption.value.filterEmail(email)) - .toList(); - emailListFiltered.addAll(emailsFilteredMore); - } } else { canLoadMore = false; } @@ -336,15 +365,9 @@ class ThreadController extends BaseController { .map((email) => email.id == presentationEmailSelected.id ? email.toggleSelect() : email) .toList(); } else { - if (isFilterMessagesEnabled) { - emailListFiltered.value = emailListFiltered - .map((email) => email.id == presentationEmailSelected.id ? email.toggleSelect() : email) - .toList(); - } else { - emailList.value = emailList - .map((email) => email.id == presentationEmailSelected.id ? email.toggleSelect() : email) - .toList(); - } + emailList.value = emailList + .map((email) => email.id == presentationEmailSelected.id ? email.toggleSelect() : email) + .toList(); } if (_isUnSelectedAll()) { @@ -364,11 +387,7 @@ class ThreadController extends BaseController { if (isSearchActive()) { return emailListSearch.where((email) => email.selectMode == SelectMode.ACTIVE).toList(); } else { - if (isFilterMessagesEnabled) { - return emailListFiltered.where((email) => email.selectMode == SelectMode.ACTIVE).toList(); - } else { - return emailList.where((email) => email.selectMode == SelectMode.ACTIVE).toList(); - } + return emailList.where((email) => email.selectMode == SelectMode.ACTIVE).toList(); } } @@ -376,11 +395,7 @@ class ThreadController extends BaseController { if (isSearchActive()) { return emailListSearch.every((email) => email.selectMode == SelectMode.INACTIVE); } else { - if (isFilterMessagesEnabled) { - return emailListFiltered.every((email) => email.selectMode == SelectMode.INACTIVE); - } else { - return emailList.every((email) => email.selectMode == SelectMode.INACTIVE); - } + return emailList.every((email) => email.selectMode == SelectMode.INACTIVE); } } @@ -392,11 +407,7 @@ class ThreadController extends BaseController { if (isSearchActive()) { emailListSearch.value = emailListSearch.map((email) => email.toSelectedEmail(selectMode: SelectMode.INACTIVE)).toList(); } else { - if (isFilterMessagesEnabled) { - emailListFiltered.value = emailListFiltered.map((email) => email.toSelectedEmail(selectMode: SelectMode.INACTIVE)).toList(); - } else { - emailList.value = emailList.map((email) => email.toSelectedEmail(selectMode: SelectMode.INACTIVE)).toList(); - } + emailList.value = emailList.map((email) => email.toSelectedEmail(selectMode: SelectMode.INACTIVE)).toList(); } currentSelectMode.value = SelectMode.INACTIVE; } @@ -465,22 +476,18 @@ class ThreadController extends BaseController { popBack(); } - bool get isFilterMessagesEnabled => filterMessageOption != FilterMessageOption.all; - void filterMessagesAction(BuildContext context, FilterMessageOption filterOption) { popBack(); final newFilterOption = filterMessageOption.value == filterOption ? FilterMessageOption.all : filterOption; - - final emailsFiltered = emailList.where((email) => newFilterOption.filterEmail(email)).toList(); - emailListFiltered.value = emailsFiltered; - filterMessageOption.value = newFilterOption; _appToast.showToastWithIcon( Get.overlayContext!, message: newFilterOption.getMessageToast(context), icon: newFilterOption.getIconToast(_imagePaths)); + + refreshAllEmail(); } void moveSelectedMultipleEmailToMailboxAction(List listEmail) async { diff --git a/lib/features/thread/presentation/thread_view.dart b/lib/features/thread/presentation/thread_view.dart index 5f4053c94..42ba55a0e 100644 --- a/lib/features/thread/presentation/thread_view.dart +++ b/lib/features/thread/presentation/thread_view.dart @@ -6,7 +6,6 @@ import 'package:get/get.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/model/filter_message_option.dart'; import 'package:tmail_ui_user/features/thread/presentation/thread_controller.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart'; import 'package:tmail_ui_user/features/thread/presentation/widgets/bottom_bar_thread_selection_widget.dart'; @@ -317,9 +316,7 @@ class ThreadView extends GetWidget { return _buildResultSearchEmails(context, controller.emailListSearch); } } else { - return _buildResultListEmail( - context, - controller.isFilterMessagesEnabled ? controller.emailListFiltered : controller.emailList); + return _buildResultListEmail(context, controller.emailList); } }) ); diff --git a/lib/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart b/lib/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart index 33bfb8a33..df76a1434 100644 --- a/lib/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart +++ b/lib/features/thread/presentation/widgets/app_bar_thread_widget_builder.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:model/model.dart'; -import 'package:tmail_ui_user/features/thread/presentation/model/filter_message_option.dart'; +import 'package:tmail_ui_user/features/thread/presentation/extensions/filter_message_option_extension.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; typedef OnFilterEmailAction = void Function(FilterMessageOption, RelativeRect? position); diff --git a/lib/features/thread/presentation/widgets/filter_message_cupertino_action_sheet_action_builder.dart b/lib/features/thread/presentation/widgets/filter_message_cupertino_action_sheet_action_builder.dart index e296e36e8..66550a0ba 100644 --- a/lib/features/thread/presentation/widgets/filter_message_cupertino_action_sheet_action_builder.dart +++ b/lib/features/thread/presentation/widgets/filter_message_cupertino_action_sheet_action_builder.dart @@ -3,7 +3,7 @@ import 'package:core/core.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:tmail_ui_user/features/thread/presentation/model/filter_message_option.dart'; +import 'package:model/model.dart'; class FilterMessageCupertinoActionSheetActionBuilder extends CupertinoActionSheetActionBuilder { diff --git a/model/lib/email/email_filter.dart b/model/lib/email/email_filter.dart index 857b2485d..e225c576c 100644 --- a/model/lib/email/email_filter.dart +++ b/model/lib/email/email_filter.dart @@ -2,14 +2,16 @@ import 'package:equatable/equatable.dart'; import 'package:jmap_dart_client/jmap/core/filter/filter.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:model/email/filter_message_option.dart'; class EmailFilter with EquatableMixin { final Filter? filter; + final FilterMessageOption? filterOption; final MailboxId? mailboxId; - EmailFilter({this.filter, this.mailboxId}); + EmailFilter({this.filter, this.filterOption, this.mailboxId}); @override - List get props => [filter, mailboxId]; + List get props => [filter, filterOption, mailboxId]; } \ No newline at end of file diff --git a/model/lib/email/filter_message_option.dart b/model/lib/email/filter_message_option.dart new file mode 100644 index 000000000..dfbfe4c65 --- /dev/null +++ b/model/lib/email/filter_message_option.dart @@ -0,0 +1,40 @@ + +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:model/email/presentation_email.dart'; +import 'package:model/extensions/email_extension.dart'; + +enum FilterMessageOption { + all, + unread, + attachments, + starred, +} + +extension FilterMessageOptionExtension on FilterMessageOption { + + bool filterPresentationEmail(PresentationEmail email) { + switch(this) { + case FilterMessageOption.all: + return true; + case FilterMessageOption.unread: + return email.isUnReadEmail(); + case FilterMessageOption.attachments: + return email.withAttachments; + case FilterMessageOption.starred: + return email.isFlaggedEmail(); + } + } + + bool filterEmail(Email email) { + switch(this) { + case FilterMessageOption.all: + return true; + case FilterMessageOption.unread: + return email.isUnReadEmail(); + case FilterMessageOption.attachments: + return email.withAttachments; + case FilterMessageOption.starred: + return email.isFlaggedEmail(); + } + } +} \ No newline at end of file diff --git a/model/lib/extensions/email_extension.dart b/model/lib/extensions/email_extension.dart index f6a52eb30..bb2d48067 100644 --- a/model/lib/extensions/email_extension.dart +++ b/model/lib/extensions/email_extension.dart @@ -16,6 +16,8 @@ extension EmailExtension on Email { bool isFlaggedEmail() => keywords?.containsKey(KeyWordIdentifier.emailFlagged) == true; + bool get withAttachments => hasAttachment == true; + Set getRecipientEmailAddressList() { final listEmailAddress = Set(); final listToAddress = to.getListAddress() ?? []; diff --git a/model/lib/model.dart b/model/lib/model.dart index 53b52546d..4f150db12 100644 --- a/model/lib/model.dart +++ b/model/lib/model.dart @@ -30,6 +30,7 @@ export 'email/mark_star_action.dart'; export 'email/email_property.dart'; export 'email/email_filter.dart'; export 'email/email_content_type.dart'; +export 'email/filter_message_option.dart'; // Extensions export 'extensions/email_address_extension.dart'; diff --git a/test/features/mailbox/domain/usecases/get_all_mailbox_interactor_test.mocks.dart b/test/features/mailbox/domain/usecases/get_all_mailbox_interactor_test.mocks.dart index 880603997..2cc0a69d8 100644 --- a/test/features/mailbox/domain/usecases/get_all_mailbox_interactor_test.mocks.dart +++ b/test/features/mailbox/domain/usecases/get_all_mailbox_interactor_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.0.10 from annotations +// Mocks generated by Mockito 5.0.17 from annotations // in tmail_ui_user/test/features/mailbox/domain/usecases/get_all_mailbox_interactor_test.dart. // Do not manually edit this file. @@ -7,17 +7,25 @@ import 'dart:async' as _i3; import 'package:jmap_dart_client/jmap/account_id.dart' as _i5; import 'package:jmap_dart_client/jmap/core/properties/properties.dart' as _i6; import 'package:jmap_dart_client/jmap/core/state.dart' as _i7; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart' as _i8; import 'package:mockito/mockito.dart' as _i1; +import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart' + as _i9; import 'package:tmail_ui_user/features/mailbox/domain/model/mailbox_response.dart' as _i4; +import 'package:tmail_ui_user/features/mailbox/domain/model/rename_mailbox_request.dart' + as _i10; import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart' as _i2; // ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types /// A class which mocks [MailboxRepository]. /// @@ -42,4 +50,23 @@ class MockMailboxRepository extends _i1.Mock implements _i2.MailboxRepository { Invocation.method(#refresh, [accountId, currentState]), returnValue: Stream<_i4.MailboxResponse>.empty()) as _i3.Stream<_i4.MailboxResponse>); + @override + _i3.Future<_i8.Mailbox?> createNewMailbox(_i5.AccountId? accountId, + _i9.CreateNewMailboxRequest? newMailboxRequest) => + (super.noSuchMethod( + Invocation.method(#createNewMailbox, [accountId, newMailboxRequest]), + returnValue: + Future<_i8.Mailbox?>.value()) as _i3.Future<_i8.Mailbox?>); + @override + _i3.Future deleteMultipleMailbox( + _i5.AccountId? accountId, List<_i8.MailboxId>? mailboxIds) => + (super.noSuchMethod( + Invocation.method(#deleteMultipleMailbox, [accountId, mailboxIds]), + returnValue: Future.value(false)) as _i3.Future); + @override + _i3.Future renameMailbox( + _i5.AccountId? accountId, _i10.RenameMailboxRequest? request) => + (super.noSuchMethod( + Invocation.method(#renameMailbox, [accountId, request]), + returnValue: Future.value(false)) as _i3.Future); } diff --git a/test/features/mailbox/domain/usecases/refresh_all_mailbox_interactor_test.mocks.dart b/test/features/mailbox/domain/usecases/refresh_all_mailbox_interactor_test.mocks.dart index 09d4b6256..0021d5495 100644 --- a/test/features/mailbox/domain/usecases/refresh_all_mailbox_interactor_test.mocks.dart +++ b/test/features/mailbox/domain/usecases/refresh_all_mailbox_interactor_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.0.10 from annotations +// Mocks generated by Mockito 5.0.17 from annotations // in tmail_ui_user/test/features/mailbox/domain/usecases/refresh_all_mailbox_interactor_test.dart. // Do not manually edit this file. @@ -7,17 +7,25 @@ import 'dart:async' as _i3; import 'package:jmap_dart_client/jmap/account_id.dart' as _i5; import 'package:jmap_dart_client/jmap/core/properties/properties.dart' as _i6; import 'package:jmap_dart_client/jmap/core/state.dart' as _i7; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart' as _i8; import 'package:mockito/mockito.dart' as _i1; +import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart' + as _i9; import 'package:tmail_ui_user/features/mailbox/domain/model/mailbox_response.dart' as _i4; +import 'package:tmail_ui_user/features/mailbox/domain/model/rename_mailbox_request.dart' + as _i10; import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart' as _i2; // ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types /// A class which mocks [MailboxRepository]. /// @@ -42,4 +50,23 @@ class MockMailboxRepository extends _i1.Mock implements _i2.MailboxRepository { Invocation.method(#refresh, [accountId, currentState]), returnValue: Stream<_i4.MailboxResponse>.empty()) as _i3.Stream<_i4.MailboxResponse>); + @override + _i3.Future<_i8.Mailbox?> createNewMailbox(_i5.AccountId? accountId, + _i9.CreateNewMailboxRequest? newMailboxRequest) => + (super.noSuchMethod( + Invocation.method(#createNewMailbox, [accountId, newMailboxRequest]), + returnValue: + Future<_i8.Mailbox?>.value()) as _i3.Future<_i8.Mailbox?>); + @override + _i3.Future deleteMultipleMailbox( + _i5.AccountId? accountId, List<_i8.MailboxId>? mailboxIds) => + (super.noSuchMethod( + Invocation.method(#deleteMultipleMailbox, [accountId, mailboxIds]), + returnValue: Future.value(false)) as _i3.Future); + @override + _i3.Future renameMailbox( + _i5.AccountId? accountId, _i10.RenameMailboxRequest? request) => + (super.noSuchMethod( + Invocation.method(#renameMailbox, [accountId, request]), + returnValue: Future.value(false)) as _i3.Future); } diff --git a/test/features/thread/domain/usecases/get_emails_in_mailbox_interactor_test.mocks.dart b/test/features/thread/domain/usecases/get_emails_in_mailbox_interactor_test.mocks.dart index 540fa99b5..8c053db23 100644 --- a/test/features/thread/domain/usecases/get_emails_in_mailbox_interactor_test.mocks.dart +++ b/test/features/thread/domain/usecases/get_emails_in_mailbox_interactor_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.0.10 from annotations +// Mocks generated by Mockito 5.0.17 from annotations // in tmail_ui_user/test/features/thread/domain/usecases/get_emails_in_mailbox_interactor_test.dart. // Do not manually edit this file. @@ -13,17 +13,20 @@ import 'package:jmap_dart_client/jmap/core/unsigned_int.dart' as _i6; import 'package:jmap_dart_client/jmap/mail/email/email.dart' as _i13; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart' as _i11; import 'package:mockito/mockito.dart' as _i1; -import 'package:model/email/email_filter.dart' as _i8; +import 'package:model/model.dart' as _i8; import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart' as _i4; import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart' as _i2; // ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types /// A class which mocks [ThreadRepository]. /// @@ -58,7 +61,8 @@ class MockThreadRepository extends _i1.Mock implements _i2.ThreadRepository { {Set<_i7.Comparator>? sort, _i9.Properties? propertiesCreated, _i9.Properties? propertiesUpdated, - _i11.MailboxId? inMailboxId}) => + _i11.MailboxId? inMailboxId, + _i8.FilterMessageOption? filterOption}) => (super.noSuchMethod( Invocation.method(#refreshChanges, [ accountId, @@ -67,7 +71,8 @@ class MockThreadRepository extends _i1.Mock implements _i2.ThreadRepository { #sort: sort, #propertiesCreated: propertiesCreated, #propertiesUpdated: propertiesUpdated, - #inMailboxId: inMailboxId + #inMailboxId: inMailboxId, + #filterOption: filterOption }), returnValue: Stream<_i4.EmailsResponse>.empty()) as _i3.Stream<_i4.EmailsResponse>); diff --git a/test/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor_test.mocks.dart b/test/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor_test.mocks.dart index e8f720800..50a37e5bd 100644 --- a/test/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor_test.mocks.dart +++ b/test/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor_test.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.0.10 from annotations +// Mocks generated by Mockito 5.0.17 from annotations // in tmail_ui_user/test/features/thread/domain/usecases/refresh_changes_emails_in_mailbox_interactor_test.dart. // Do not manually edit this file. @@ -13,17 +13,20 @@ import 'package:jmap_dart_client/jmap/core/unsigned_int.dart' as _i6; import 'package:jmap_dart_client/jmap/mail/email/email.dart' as _i13; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart' as _i11; import 'package:mockito/mockito.dart' as _i1; -import 'package:model/email/email_filter.dart' as _i8; +import 'package:model/model.dart' as _i8; import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart' as _i4; import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart' as _i2; // ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types /// A class which mocks [ThreadRepository]. /// @@ -58,7 +61,8 @@ class MockThreadRepository extends _i1.Mock implements _i2.ThreadRepository { {Set<_i7.Comparator>? sort, _i9.Properties? propertiesCreated, _i9.Properties? propertiesUpdated, - _i11.MailboxId? inMailboxId}) => + _i11.MailboxId? inMailboxId, + _i8.FilterMessageOption? filterOption}) => (super.noSuchMethod( Invocation.method(#refreshChanges, [ accountId, @@ -67,7 +71,8 @@ class MockThreadRepository extends _i1.Mock implements _i2.ThreadRepository { #sort: sort, #propertiesCreated: propertiesCreated, #propertiesUpdated: propertiesUpdated, - #inMailboxId: inMailboxId + #inMailboxId: inMailboxId, + #filterOption: filterOption }), returnValue: Stream<_i4.EmailsResponse>.empty()) as _i3.Stream<_i4.EmailsResponse>);