From b83eb876758bce0a3a96c2f6aa59dc612375b4e8 Mon Sep 17 00:00:00 2001 From: dab246 Date: Thu, 3 Aug 2023 16:01:29 +0700 Subject: [PATCH] TF-1710 Add empty spam folder interactor (cherry picked from commit f0d22cd8925185607b5dea74f2cf40052980d19c) --- .../data/datasource/thread_datasource.dart | 2 +- .../local_thread_datasource_impl.dart | 2 +- .../thread_datasource_impl.dart | 4 +- ...rt => empty_mailbox_folder_arguments.dart} | 10 ++-- .../data/network/thread_isolate_worker.dart | 53 +++++++++++-------- .../repository/thread_repository_impl.dart | 21 +++++++- .../domain/exceptions/thread_exceptions.dart | 2 + .../domain/repository/thread_repository.dart | 6 +++ .../domain/state/empty_spam_folder_state.dart | 26 +++++++++ .../state/empty_trash_folder_state.dart | 18 +++++-- .../empty_spam_folder_interactor.dart | 45 ++++++++++++++++ .../empty_trash_folder_interactor.dart | 17 +++--- 12 files changed, 158 insertions(+), 48 deletions(-) rename lib/features/thread/data/model/{empty_trash_folder_arguments.dart => empty_mailbox_folder_arguments.dart} (80%) create mode 100644 lib/features/thread/domain/exceptions/thread_exceptions.dart create mode 100644 lib/features/thread/domain/state/empty_spam_folder_state.dart create mode 100644 lib/features/thread/domain/usecases/empty_spam_folder_interactor.dart diff --git a/lib/features/thread/data/datasource/thread_datasource.dart b/lib/features/thread/data/datasource/thread_datasource.dart index 974096f66..ac6c1b02c 100644 --- a/lib/features/thread/data/datasource/thread_datasource.dart +++ b/lib/features/thread/data/datasource/thread_datasource.dart @@ -50,7 +50,7 @@ abstract class ThreadDataSource { Future update(AccountId accountId, UserName userName, {List? updated, List? created, List? destroyed}); - Future> emptyTrashFolder( + Future> emptyMailboxFolder( Session session, AccountId accountId, MailboxId mailboxId, 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 6398214e5..6047005d0 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 @@ -91,7 +91,7 @@ class LocalThreadDataSourceImpl extends ThreadDataSource { } @override - Future> emptyTrashFolder( + Future> emptyMailboxFolder( Session session, AccountId accountId, MailboxId mailboxId, 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 dcd62de75..1175f8dc0 100644 --- a/lib/features/thread/data/datasource_impl/thread_datasource_impl.dart +++ b/lib/features/thread/data/datasource_impl/thread_datasource_impl.dart @@ -85,14 +85,14 @@ class ThreadDataSourceImpl extends ThreadDataSource { } @override - Future> emptyTrashFolder( + Future> emptyMailboxFolder( Session session, AccountId accountId, MailboxId mailboxId, Future Function(List? newDestroyed) updateDestroyedEmailCache ) { return Future.sync(() async { - return await _threadIsolateWorker.emptyTrashFolder( + return await _threadIsolateWorker.emptyMailboxFolder( session, accountId, mailboxId, diff --git a/lib/features/thread/data/model/empty_trash_folder_arguments.dart b/lib/features/thread/data/model/empty_mailbox_folder_arguments.dart similarity index 80% rename from lib/features/thread/data/model/empty_trash_folder_arguments.dart rename to lib/features/thread/data/model/empty_mailbox_folder_arguments.dart index ade64b69e..d45d8f24e 100644 --- a/lib/features/thread/data/model/empty_trash_folder_arguments.dart +++ b/lib/features/thread/data/model/empty_mailbox_folder_arguments.dart @@ -5,19 +5,19 @@ import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart'; -class EmptyTrashFolderArguments with EquatableMixin { +class EmptyMailboxFolderArguments with EquatableMixin { final Session session; final AccountId accountId; - final MailboxId trashMailboxId; + final MailboxId mailboxId; final ThreadAPI threadAPI; final EmailAPI emailAPI; - EmptyTrashFolderArguments( + EmptyMailboxFolderArguments( this.session, this.threadAPI, this.emailAPI, this.accountId, - this.trashMailboxId, + this.mailboxId, ); @override @@ -26,6 +26,6 @@ class EmptyTrashFolderArguments with EquatableMixin { accountId, emailAPI, threadAPI, - trashMailboxId + mailboxId ]; } diff --git a/lib/features/thread/data/network/thread_isolate_worker.dart b/lib/features/thread/data/network/thread_isolate_worker.dart index 2b8c61757..89c75ea49 100644 --- a/lib/features/thread/data/network/thread_isolate_worker.dart +++ b/lib/features/thread/data/network/thread_isolate_worker.dart @@ -14,8 +14,9 @@ import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/email/email_property.dart'; import 'package:model/extensions/list_email_extension.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; -import 'package:tmail_ui_user/features/thread/data/model/empty_trash_folder_arguments.dart'; +import 'package:tmail_ui_user/features/thread/data/model/empty_mailbox_folder_arguments.dart'; import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart'; +import 'package:tmail_ui_user/features/thread/domain/exceptions/thread_exceptions.dart'; import 'package:worker_manager/worker_manager.dart'; class ThreadIsolateWorker { @@ -25,29 +26,35 @@ class ThreadIsolateWorker { ThreadIsolateWorker(this._threadAPI, this._emailAPI, this._isolateExecutor); - Future> emptyTrashFolder( + Future> emptyMailboxFolder( Session session, AccountId accountId, MailboxId mailboxId, Future Function(List? newDestroyed) updateDestroyedEmailCache, ) async { if (PlatformInfo.isWeb) { - return _emptyTrashFolderOnWeb(session, accountId, mailboxId, updateDestroyedEmailCache); + return _emptyMailboxFolderOnWeb(session, accountId, mailboxId, updateDestroyedEmailCache); } else { final result = await _isolateExecutor.execute( - arg1: EmptyTrashFolderArguments(session, _threadAPI, _emailAPI, accountId, mailboxId), - fun1: _emptyTrashFolderAction, - notification: (value) { - if (value is List) { - updateDestroyedEmailCache.call(value); - log('ThreadIsolateWorker::emptyTrashFolder(): onUpdateProgress: PERCENT ${value.length}'); - } - }); - return result; + arg1: EmptyMailboxFolderArguments(session, _threadAPI, _emailAPI, accountId, mailboxId), + fun1: _emptyMailboxFolderAction, + notification: (value) { + if (value is List) { + updateDestroyedEmailCache.call(value); + log('ThreadIsolateWorker::emptyMailboxFolder(): onUpdateProgress: PERCENT ${value.length}'); + } + } + ); + + if (result.isEmpty) { + throw NotFoundEmailsDeletedException(); + } else { + return result; + } } } - static Future> _emptyTrashFolderAction(EmptyTrashFolderArguments args, TypeSendPort sendPort) async { + static Future> _emptyMailboxFolderAction(EmptyMailboxFolderArguments args, TypeSendPort sendPort) async { List emailListCompleted = List.empty(growable: true); try { var hasEmails = true; @@ -60,7 +67,7 @@ class ThreadIsolateWorker { sort: {}..add( EmailComparator(EmailComparatorProperty.receivedAt) ..setIsAscending(false)), - filter: EmailFilterCondition(inMailbox: args.trashMailboxId, before: lastEmail?.receivedAt), + filter: EmailFilterCondition(inMailbox: args.mailboxId, before: lastEmail?.receivedAt), properties: Properties({EmailProperty.id})); var newEmailList = emailsResponse.emailList ?? []; @@ -68,7 +75,7 @@ class ThreadIsolateWorker { newEmailList = newEmailList.where((email) => email.id != lastEmail!.id).toList(); } - log('ThreadIsolateWorker::_emptyTrashFolderAction(): ${newEmailList.length}'); + log('ThreadIsolateWorker::_emptyMailboxFolderAction(): ${newEmailList.length}'); if (newEmailList.isNotEmpty) { lastEmail = newEmailList.last; @@ -86,16 +93,16 @@ class ThreadIsolateWorker { } } } catch (e) { - log('ThreadIsolateWorker::_emptyTrashFolderAction(): ERROR: $e'); + log('ThreadIsolateWorker::_emptyMailboxFolderAction(): ERROR: $e'); } - log('ThreadIsolateWorker::_emptyTrashFolderAction(): TOTAL_REMOVE: ${emailListCompleted.length}'); + log('ThreadIsolateWorker::_emptyMailboxFolderAction(): TOTAL_REMOVE: ${emailListCompleted.length}'); return emailListCompleted; } - Future> _emptyTrashFolderOnWeb( + Future> _emptyMailboxFolderOnWeb( Session session, AccountId accountId, - MailboxId trashMailboxId, + MailboxId mailboxId, Future Function(List newDestroyed) updateDestroyedEmailCache, ) async { List emailListCompleted = List.empty(growable: true); @@ -110,7 +117,7 @@ class ThreadIsolateWorker { sort: {}..add( EmailComparator(EmailComparatorProperty.receivedAt) ..setIsAscending(false)), - filter: EmailFilterCondition(inMailbox: trashMailboxId, before: lastEmail?.receivedAt), + filter: EmailFilterCondition(inMailbox: mailboxId, before: lastEmail?.receivedAt), properties: Properties({EmailProperty.id})); var newEmailList = emailsResponse.emailList ?? []; @@ -118,7 +125,7 @@ class ThreadIsolateWorker { newEmailList = newEmailList.where((email) => email.id != lastEmail!.id).toList(); } - log('ThreadIsolateWorker::_emptyTrashFolderOnWeb(): ${newEmailList.length}'); + log('ThreadIsolateWorker::_emptyMailboxFolderOnWeb(): ${newEmailList.length}'); if (newEmailList.isNotEmpty) { lastEmail = newEmailList.last; @@ -135,9 +142,9 @@ class ThreadIsolateWorker { } } } catch (e) { - log('ThreadIsolateWorker::_emptyTrashFolderOnWeb(): ERROR: $e'); + log('ThreadIsolateWorker::_emptyMailboxFolderOnWeb(): ERROR: $e'); } - log('ThreadIsolateWorker::_emptyTrashFolderOnWeb(): TOTAL_REMOVE: ${emailListCompleted.length}'); + log('ThreadIsolateWorker::_emptyMailboxFolderOnWeb(): TOTAL_REMOVE: ${emailListCompleted.length}'); return emailListCompleted; } } diff --git a/lib/features/thread/data/repository/thread_repository_impl.dart b/lib/features/thread/data/repository/thread_repository_impl.dart index c83753742..acd9a2a8a 100644 --- a/lib/features/thread/data/repository/thread_repository_impl.dart +++ b/lib/features/thread/data/repository/thread_repository_impl.dart @@ -1,5 +1,6 @@ -import 'package:core/core.dart'; +import 'package:core/data/model/source_type/data_source_type.dart'; +import 'package:core/utils/app_logger.dart'; import 'package:dartz/dartz.dart' as dartz; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/filter/filter.dart'; @@ -300,7 +301,7 @@ class ThreadRepositoryImpl extends ThreadRepository { @override Future> emptyTrashFolder(Session session, AccountId accountId, MailboxId trashMailboxId) async { - return mapDataSource[DataSourceType.network]!.emptyTrashFolder( + return mapDataSource[DataSourceType.network]!.emptyMailboxFolder( session, accountId, trashMailboxId, @@ -377,4 +378,20 @@ class ThreadRepositoryImpl extends ThreadRepository { ) { return mapDataSource[DataSourceType.network]!.getEmailById(session, accountId, emailId, properties: properties); } + + @override + Future> emptySpamFolder(Session session, AccountId accountId, MailboxId spamMailboxId) { + return mapDataSource[DataSourceType.network]!.emptyMailboxFolder( + session, + accountId, + spamMailboxId, + (listEmailIdDeleted) async { + await _updateEmailCache( + accountId, + session.username, + newDestroyed: listEmailIdDeleted + ); + }, + ); + } } \ No newline at end of file diff --git a/lib/features/thread/domain/exceptions/thread_exceptions.dart b/lib/features/thread/domain/exceptions/thread_exceptions.dart new file mode 100644 index 000000000..134c9ce63 --- /dev/null +++ b/lib/features/thread/domain/exceptions/thread_exceptions.dart @@ -0,0 +1,2 @@ + +class NotFoundEmailsDeletedException implements Exception {} \ 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 109db2ae0..90cbd1712 100644 --- a/lib/features/thread/domain/repository/thread_repository.dart +++ b/lib/features/thread/domain/repository/thread_repository.dart @@ -64,4 +64,10 @@ abstract class ThreadRepository { EmailId emailId, {Properties? properties} ); + + Future> emptySpamFolder( + Session session, + AccountId accountId, + MailboxId spamMailboxId, + ); } \ No newline at end of file diff --git a/lib/features/thread/domain/state/empty_spam_folder_state.dart b/lib/features/thread/domain/state/empty_spam_folder_state.dart new file mode 100644 index 000000000..f0bfbad41 --- /dev/null +++ b/lib/features/thread/domain/state/empty_spam_folder_state.dart @@ -0,0 +1,26 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:tmail_ui_user/features/base/state/ui_action_state.dart'; +import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; + +class EmptySpamFolderLoading extends LoadingState {} + +class EmptySpamFolderSuccess extends UIActionState { + + final List emailIds; + + EmptySpamFolderSuccess( + this.emailIds, { + jmap.State? currentEmailState, + jmap.State? currentMailboxState, + }) : super(currentEmailState, currentMailboxState); + + @override + List get props => [emailIds, ...super.props]; +} + +class EmptySpamFolderFailure extends FeatureFailure { + + EmptySpamFolderFailure(dynamic exception) : super(exception: exception); +} \ No newline at end of file diff --git a/lib/features/thread/domain/state/empty_trash_folder_state.dart b/lib/features/thread/domain/state/empty_trash_folder_state.dart index 1d826af13..8f9c6b62f 100644 --- a/lib/features/thread/domain/state/empty_trash_folder_state.dart +++ b/lib/features/thread/domain/state/empty_trash_folder_state.dart @@ -1,13 +1,23 @@ import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:tmail_ui_user/features/base/state/ui_action_state.dart'; import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; +class EmptyTrashFolderLoading extends LoadingState {} + class EmptyTrashFolderSuccess extends UIActionState { - EmptyTrashFolderSuccess({ - jmap.State? currentEmailState, - jmap.State? currentMailboxState, - }) : super(currentEmailState, currentMailboxState); + final List emailIds; + + EmptyTrashFolderSuccess( + this.emailIds, { + jmap.State? currentEmailState, + jmap.State? currentMailboxState, + }) : super(currentEmailState, currentMailboxState); + + @override + List get props => [emailIds, ...super.props]; } class EmptyTrashFolderFailure extends FeatureFailure { diff --git a/lib/features/thread/domain/usecases/empty_spam_folder_interactor.dart b/lib/features/thread/domain/usecases/empty_spam_folder_interactor.dart new file mode 100644 index 000000000..38e864a94 --- /dev/null +++ b/lib/features/thread/domain/usecases/empty_spam_folder_interactor.dart @@ -0,0 +1,45 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart'; +import 'package:tmail_ui_user/features/thread/domain/repository/thread_repository.dart'; +import 'package:tmail_ui_user/features/thread/domain/state/empty_spam_folder_state.dart'; + +class EmptySpamFolderInteractor { + final ThreadRepository threadRepository; + final MailboxRepository _mailboxRepository; + final EmailRepository _emailRepository; + + EmptySpamFolderInteractor( + this.threadRepository, + this._mailboxRepository, + this._emailRepository + ); + + Stream> execute(Session session, AccountId accountId, MailboxId spamMailboxId) async* { + try { + yield Right(EmptySpamFolderLoading()); + + final listState = await Future.wait([ + _mailboxRepository.getMailboxState(session, accountId), + _emailRepository.getEmailState(session, accountId), + ], eagerError: true); + + final currentMailboxState = listState.first; + final currentEmailState = listState.last; + + final emailIdDeleted = await threadRepository.emptySpamFolder(session, accountId, spamMailboxId); + yield Right(EmptySpamFolderSuccess( + emailIdDeleted, + currentMailboxState: currentMailboxState, + currentEmailState: currentEmailState, + )); + } catch (e) { + yield Left(EmptySpamFolderFailure(e)); + } + } +} \ No newline at end of file diff --git a/lib/features/thread/domain/usecases/empty_trash_folder_interactor.dart b/lib/features/thread/domain/usecases/empty_trash_folder_interactor.dart index d5160b788..4a22143ed 100644 --- a/lib/features/thread/domain/usecases/empty_trash_folder_interactor.dart +++ b/lib/features/thread/domain/usecases/empty_trash_folder_interactor.dart @@ -21,7 +21,7 @@ class EmptyTrashFolderInteractor { Stream> execute(Session session, AccountId accountId, MailboxId trashMailboxId) async* { try { - yield Right(LoadingState()); + yield Right(EmptyTrashFolderLoading()); final listState = await Future.wait([ _mailboxRepository.getMailboxState(session, accountId), @@ -31,15 +31,12 @@ class EmptyTrashFolderInteractor { final currentMailboxState = listState.first; final currentEmailState = listState.last; - final result = await threadRepository.emptyTrashFolder(session, accountId, trashMailboxId); - if (result.isNotEmpty) { - yield Right(EmptyTrashFolderSuccess( - currentMailboxState: currentMailboxState, - currentEmailState: currentEmailState, - )); - } else { - yield Left(EmptyTrashFolderFailure(null)); - } + final emailIdDeleted = await threadRepository.emptyTrashFolder(session, accountId, trashMailboxId); + yield Right(EmptyTrashFolderSuccess( + emailIdDeleted, + currentMailboxState: currentMailboxState, + currentEmailState: currentEmailState, + )); } catch (e) { yield Left(EmptyTrashFolderFailure(e)); }