From 369316f50dde59ab520b47fa31a1357244175702 Mon Sep 17 00:00:00 2001 From: dab246 Date: Wed, 29 Oct 2025 13:19:25 +0700 Subject: [PATCH] TF-4053 Add interactor/datasource/repository for move folder content action --- .../base/mixin/handle_error_mixin.dart | 2 +- lib/features/base/mixin/mail_api_mixin.dart | 304 ++++++++++++++++++ .../email/data/network/email_api.dart | 99 +----- .../data/datasource/mailbox_datasource.dart | 8 + .../mailbox_cache_datasource_impl.dart | 11 + .../mailbox_datasource_impl.dart | 31 ++ ...move_folder_content_isolate_arguments.dart | 41 +++ .../mailbox/data/network/mailbox_api.dart | 28 +- .../data/network/mailbox_isolate_worker.dart | 102 ++++++ .../repository/mailbox_repository_impl.dart | 16 + .../domain/exceptions/mailbox_exception.dart | 2 + .../model/move_folder_content_request.dart | 31 ++ .../domain/repository/mailbox_repository.dart | 8 + .../state/move_folder_content_state.dart | 38 +++ .../move_folder_content_interactor.dart | 37 +++ .../data/extensions/list_email_extension.dart | 3 + .../thread/data/network/thread_api.dart | 88 +---- .../extensions/list_email_id_extension.dart | 18 +- .../lib/extensions/mailbox_id_extension.dart | 16 +- 19 files changed, 695 insertions(+), 188 deletions(-) create mode 100644 lib/features/base/mixin/mail_api_mixin.dart create mode 100644 lib/features/mailbox/data/model/move_folder_content_isolate_arguments.dart create mode 100644 lib/features/mailbox/domain/model/move_folder_content_request.dart create mode 100644 lib/features/mailbox/domain/state/move_folder_content_state.dart create mode 100644 lib/features/mailbox/domain/usecases/move_folder_content_interactor.dart diff --git a/lib/features/base/mixin/handle_error_mixin.dart b/lib/features/base/mixin/handle_error_mixin.dart index 99e700c8a..71053bbc7 100644 --- a/lib/features/base/mixin/handle_error_mixin.dart +++ b/lib/features/base/mixin/handle_error_mixin.dart @@ -1,4 +1,4 @@ -import 'package:core/core.dart'; +import 'package:core/utils/app_logger.dart'; import 'package:dartz/dartz.dart'; import 'package:jmap_dart_client/jmap/core/error/set_error.dart'; import 'package:jmap_dart_client/jmap/core/id.dart'; diff --git a/lib/features/base/mixin/mail_api_mixin.dart b/lib/features/base/mixin/mail_api_mixin.dart new file mode 100644 index 000000000..6c3122a95 --- /dev/null +++ b/lib/features/base/mixin/mail_api_mixin.dart @@ -0,0 +1,304 @@ +import 'dart:async'; +import 'dart:math' hide log; + +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:core/utils/app_logger.dart'; +import 'package:dartz/dartz.dart'; +import 'package:jmap_dart_client/http/http_client.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; +import 'package:jmap_dart_client/jmap/core/capability/core_capability.dart'; +import 'package:jmap_dart_client/jmap/core/error/set_error.dart'; +import 'package:jmap_dart_client/jmap/core/filter/filter.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; +import 'package:jmap_dart_client/jmap/core/request/reference_path.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; +import 'package:jmap_dart_client/jmap/core/sort/comparator.dart'; +import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; +import 'package:jmap_dart_client/jmap/jmap_request.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/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/get/get_email_method.dart'; +import 'package:jmap_dart_client/jmap/mail/email/get/get_email_response.dart'; +import 'package:jmap_dart_client/jmap/mail/email/query/query_email_method.dart'; +import 'package:jmap_dart_client/jmap/mail/email/query/query_email_response.dart'; +import 'package:jmap_dart_client/jmap/mail/email/set/set_email_method.dart'; +import 'package:jmap_dart_client/jmap/mail/email/set/set_email_response.dart'; +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:model/extensions/list_email_id_extension.dart'; +import 'package:model/extensions/list_id_extension.dart'; +import 'package:model/extensions/session_extension.dart'; +import 'package:tmail_ui_user/features/base/mixin/handle_error_mixin.dart'; +import 'package:tmail_ui_user/features/email/domain/model/move_action.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/exceptions/mailbox_exception.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/state/move_folder_content_state.dart'; +import 'package:tmail_ui_user/features/thread/data/extensions/list_email_extension.dart'; +import 'package:tmail_ui_user/features/thread/data/extensions/list_email_id_extension.dart'; +import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart'; +import 'package:tmail_ui_user/main/error/capability_validator.dart'; + +mixin MailAPIMixin on HandleSetErrorMixin { + int getMaxObjectsInSetMethod(Session session, AccountId accountId) { + final coreCapability = session.getCapabilityProperties( + accountId, + CapabilityIdentifier.jmapCore, + ); + final maxObjectsInSetMethod = + coreCapability?.maxObjectsInSet?.value.toInt() ?? + CapabilityIdentifierExtension.defaultMaxObjectsInSet; + + final minOfMaxObjectsInSetMethod = min( + maxObjectsInSetMethod, + CapabilityIdentifierExtension.defaultMaxObjectsInSet, + ); + log('$runtimeType::_getMaxObjectsInSetMethod:minOfMaxObjectsInSetMethod = $minOfMaxObjectsInSetMethod'); + return minOfMaxObjectsInSetMethod; + } + + Future<({List emailIdsSuccess, Map mapErrors})> + moveEmailsBetweenMailboxes({ + required HttpClient httpClient, + required Session session, + required AccountId accountId, + required List emailIds, + required MailboxId currentMailboxId, + required MailboxId destinationMailboxId, + bool markAsRead = false, + }) async { + final maxObjects = getMaxObjectsInSetMethod(session, accountId); + final totalEmails = emailIds.length; + final maxBatches = min(totalEmails, maxObjects); + + final List updatedEmailIds = List.empty(growable: true); + final Map mapErrors = {}; + + for (int start = 0; start < totalEmails; start += maxBatches) { + int end = + (start + maxBatches < totalEmails) ? start + maxBatches : totalEmails; + log('$runtimeType::moveEmailsBetweenMailboxes:emails from ${start + 1} to $end'); + + final currentEmailIds = emailIds.sublist(start, end); + + final moveProperties = currentEmailIds.generateMapUpdateObjectMoveToMailbox( + currentMailboxId: currentMailboxId, + destinationMailboxId: destinationMailboxId, + markAsRead: markAsRead, + ); + + final setEmailMethod = SetEmailMethod(accountId) + ..addUpdates(moveProperties); + + final requestBuilder = + JmapRequestBuilder(httpClient, ProcessingInvocation()); + + final setEmailInvocation = requestBuilder.invocation(setEmailMethod); + + final capabilities = setEmailMethod.requiredCapabilities + .toCapabilitiesSupportTeamMailboxes(session, accountId); + + final response = + await (requestBuilder..usings(capabilities)).build().execute(); + + final setEmailResponse = response.parse( + setEmailInvocation.methodCallId, + SetEmailResponse.deserialize, + ); + + final listEmailIds = setEmailResponse?.updated?.keys.toEmailIds() ?? []; + final mapErrors = handleSetResponse([setEmailResponse]); + + updatedEmailIds.addAll(listEmailIds); + mapErrors.addAll(mapErrors); + } + + return (emailIdsSuccess: updatedEmailIds, mapErrors: mapErrors); + } + + Future fetchAllEmail({ + required HttpClient httpClient, + required Session session, + required AccountId accountId, + UnsignedInt? limit, + int? position, + Set? sort, + Filter? filter, + Properties? properties, + }) async { + final processingInvocation = ProcessingInvocation(); + + final jmapRequestBuilder = JmapRequestBuilder( + httpClient, + processingInvocation, + ); + + final queryEmailMethod = QueryEmailMethod(accountId); + + if (limit != null) queryEmailMethod.addLimit(limit); + + if (position != null) queryEmailMethod.addPosition(position); + + if (sort != null) queryEmailMethod.addSorts(sort); + + if (filter != null) queryEmailMethod.addFilters(filter); + + final queryEmailInvocation = + jmapRequestBuilder.invocation(queryEmailMethod); + + final getEmailMethod = GetEmailMethod(accountId); + + if (properties != null) getEmailMethod.addProperties(properties); + + getEmailMethod.addReferenceIds( + processingInvocation.createResultReference( + queryEmailInvocation.methodCallId, + ReferencePath.idsPath, + ), + ); + + final getEmailInvocation = jmapRequestBuilder.invocation(getEmailMethod); + + final capabilities = getEmailMethod.requiredCapabilities + .toCapabilitiesSupportTeamMailboxes(session, accountId); + + final result = + await (jmapRequestBuilder..usings(capabilities)).build().execute(); + + final responseOfGetEmailMethod = result.parse( + getEmailInvocation.methodCallId, + GetEmailResponse.deserialize, + ); + + final responseOfQueryEmailMethod = result.parse( + queryEmailInvocation.methodCallId, + QueryEmailResponse.deserialize, + ); + + final emailList = sortEmails( + getEmailResponse: responseOfGetEmailMethod, + queryEmailResponse: responseOfQueryEmailMethod, + ); + + final notFoundEmailIds = + responseOfGetEmailMethod?.notFound?.toEmailIds().toList(); + log('$runtimeType::getAllEmail:notFoundEmailIds = ${notFoundEmailIds!.asListString.toString()} | NewState = ${responseOfGetEmailMethod?.state.value}'); + return EmailsResponse( + emailList: emailList, + notFoundEmailIds: notFoundEmailIds, + state: responseOfGetEmailMethod?.state, + ); + } + + List? sortEmails({ + GetEmailResponse? getEmailResponse, + QueryEmailResponse? queryEmailResponse, + }) { + final listEmails = getEmailResponse?.list; + final listIds = queryEmailResponse?.ids.toList(); + + if (listEmails?.isNotEmpty != true || listIds?.isNotEmpty != true) { + return listEmails; + } + + final listSortedEmails = listEmails!.sortEmailsById(listIds!); + + return listSortedEmails; + } + + Future moveAllEmailsBetweenFolders({ + required HttpClient httpClient, + required Session session, + required AccountId accountId, + required MailboxId currentMailboxId, + required MailboxId destinationMailboxId, + required MoveAction moveAction, + int totalEmails = 0, + bool markAsRead = false, + StreamController>? onProgressController, + }) async { + int countEmailsCompleted = 0; + bool hasEmails = true; + Email? lastEmail; + + while (hasEmails) { + final listEmails = await getLatestEmails( + httpClient: httpClient, + session: session, + accountId: accountId, + mailboxId: currentMailboxId, + lastEmail: lastEmail, + ); + log('$runtimeType::moveAllEmailsBetweenFolders(): Length of emails = ${listEmails.length}'); + if (listEmails.isEmpty) { + hasEmails = false; + } else { + hasEmails = true; + lastEmail = listEmails.last; + + final movedEmails = await moveEmailsBetweenMailboxes( + httpClient: httpClient, + session: session, + accountId: accountId, + emailIds: listEmails.listEmailIds, + currentMailboxId: currentMailboxId, + destinationMailboxId: destinationMailboxId, + markAsRead: markAsRead, + ); + + countEmailsCompleted += movedEmails.emailIdsSuccess.length; + + onProgressController?.add( + Right(MoveFolderContentProgressState( + currentMailboxId, + countEmailsCompleted, + totalEmails, + )), + ); + } + } + log('$runtimeType::moveAllEmailsBetweenFolders(): Total emails moved = $countEmailsCompleted'); + if (moveAction == MoveAction.moving && + countEmailsCompleted < totalEmails && + totalEmails > 0) { + throw CannotMoveAllEmailException(); + } + } + + Future> getLatestEmails({ + required HttpClient httpClient, + required Session session, + required AccountId accountId, + required MailboxId mailboxId, + Email? lastEmail, + }) async { + final response = await fetchAllEmail( + httpClient: httpClient, + session: session, + accountId: accountId, + sort: {}..add( + EmailComparator( + EmailComparatorProperty.receivedAt, + )..setIsAscending(false), + ), + filter: EmailFilterCondition( + inMailbox: mailboxId, + before: lastEmail?.receivedAt, + ), + properties: Properties({ + EmailProperty.id, + EmailProperty.receivedAt, + }), + ); + + if (lastEmail?.id != null) { + return response.emailList?.withoutEmailWithId(lastEmail!.id!) ?? []; + } else { + return response.emailList ?? []; + } + } +} diff --git a/lib/features/email/data/network/email_api.dart b/lib/features/email/data/network/email_api.dart index ddae14b9e..c1982ecee 100644 --- a/lib/features/email/data/network/email_api.dart +++ b/lib/features/email/data/network/email_api.dart @@ -14,8 +14,6 @@ import 'package:email_recovery/email_recovery/set/set_email_recovery_action_meth import 'package:email_recovery/email_recovery/set/set_email_recovery_action_response.dart'; import 'package:jmap_dart_client/http/http_client.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; -import 'package:jmap_dart_client/jmap/core/capability/capability_identifier.dart'; -import 'package:jmap_dart_client/jmap/core/capability/core_capability.dart'; import 'package:jmap_dart_client/jmap/core/error/set_error.dart'; import 'package:jmap_dart_client/jmap/core/id.dart'; import 'package:jmap_dart_client/jmap/core/patch_object.dart'; @@ -55,8 +53,10 @@ import 'package:model/extensions/list_email_id_extension.dart'; import 'package:model/extensions/list_id_extension.dart'; import 'package:model/extensions/mailbox_id_extension.dart'; import 'package:model/extensions/session_extension.dart'; +import 'package:model/oidc/token_oidc.dart'; import 'package:path_provider/path_provider.dart'; import 'package:tmail_ui_user/features/base/mixin/handle_error_mixin.dart'; +import 'package:tmail_ui_user/features/base/mixin/mail_api_mixin.dart'; import 'package:tmail_ui_user/features/composer/domain/exceptions/set_method_exception.dart'; import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'; import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart'; @@ -70,7 +70,7 @@ import 'package:tmail_ui_user/main/error/capability_validator.dart'; import 'package:uri/uri.dart'; import 'package:uuid/uuid.dart'; -class EmailAPI with HandleSetErrorMixin { +class EmailAPI with HandleSetErrorMixin, MailAPIMixin { final HttpClient _httpClient; final DownloadManager _downloadManager; @@ -247,7 +247,7 @@ class EmailAPI with HandleSetErrorMixin { List emailIds, ReadActions readActions, ) async { - final maxObjects = _getMaxObjectsInSetMethod(session, accountId); + final maxObjects = getMaxObjectsInSetMethod(session, accountId); final totalEmails = emailIds.length; final maxBatches = min(totalEmails, maxObjects); @@ -455,13 +455,14 @@ class EmailAPI with HandleSetErrorMixin { final currentMailboxId = listMailboxIds[i]; final listEmailIds = moveRequest.currentMailboxes[currentMailboxId]!; log('EmailAPI::moveToMailbox:from mailbox ${currentMailboxId.asString} with ${listEmailIds.length} emails to mailbox ${moveRequest.destinationMailboxId.asString}'); - final resultRecords = await _moveEmailsBetweenMailboxes( + final resultRecords = await moveEmailsBetweenMailboxes( + httpClient: _httpClient, session: session, accountId: accountId, emailIds: listEmailIds, currentMailboxId: currentMailboxId, destinationMailboxId: moveRequest.destinationMailboxId, - isMovingToSpam: moveRequest.isMovingToSpam, + markAsRead: moveRequest.isMovingToSpam, ); listEmailIdResult.addAll(resultRecords.emailIdsSuccess); @@ -471,88 +472,6 @@ class EmailAPI with HandleSetErrorMixin { return (emailIdsSuccess: listEmailIdResult, mapErrors: mapErrors); } - Future<({ - List emailIdsSuccess, - Map mapErrors, - })> _moveEmailsBetweenMailboxes({ - required Session session, - required AccountId accountId, - required List emailIds, - required MailboxId currentMailboxId, - required MailboxId destinationMailboxId, - bool isMovingToSpam = false, - }) async { - final maxObjects = _getMaxObjectsInSetMethod(session, accountId); - final totalEmails = emailIds.length; - final maxBatches = min(totalEmails, maxObjects); - - final List updatedEmailIds = List.empty(growable: true); - final Map mapErrors = {}; - - for (int start = 0; start < totalEmails; start += maxBatches) { - int end = (start + maxBatches < totalEmails) - ? start + maxBatches - : totalEmails; - log('EmailAPI::_moveEmailsBetweenMailboxes:emails from ${start + 1} to $end'); - - final currentEmailIds = emailIds.sublist(start, end); - - final moveProperties = isMovingToSpam - ? currentEmailIds.generateMapUpdateObjectMoveToSpam( - currentMailboxId, - destinationMailboxId, - ) - : currentEmailIds.generateMapUpdateObjectMoveToMailbox( - currentMailboxId, - destinationMailboxId, - ); - - final setEmailMethod = SetEmailMethod(accountId) - ..addUpdates(moveProperties); - - final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation()); - - final setEmailInvocation = requestBuilder.invocation(setEmailMethod); - - final capabilities = setEmailMethod.requiredCapabilities - .toCapabilitiesSupportTeamMailboxes(session, accountId); - - final response = await (requestBuilder - ..usings(capabilities)) - .build() - .execute(); - - final setEmailResponse = response.parse( - setEmailInvocation.methodCallId, - SetEmailResponse.deserialize, - ); - - final listEmailIds = setEmailResponse?.updated?.keys.toEmailIds() ?? []; - final mapErrors = handleSetResponse([setEmailResponse]); - - updatedEmailIds.addAll(listEmailIds); - mapErrors.addAll(mapErrors); - } - - return (emailIdsSuccess: updatedEmailIds, mapErrors: mapErrors); - } - - int _getMaxObjectsInSetMethod(Session session, AccountId accountId) { - final coreCapability = session.getCapabilityProperties( - accountId, - CapabilityIdentifier.jmapCore, - ); - final maxObjectsInSetMethod = coreCapability?.maxObjectsInSet?.value.toInt() - ?? CapabilityIdentifierExtension.defaultMaxObjectsInSet; - - final minOfMaxObjectsInSetMethod = min( - maxObjectsInSetMethod, - CapabilityIdentifierExtension.defaultMaxObjectsInSet, - ); - log('EmailAPI::_getMaxObjectsInSetMethod:minOfMaxObjectsInSetMethod = $minOfMaxObjectsInSetMethod'); - return minOfMaxObjectsInSetMethod; - } - Future<({ List emailIdsSuccess, Map mapErrors, @@ -562,7 +481,7 @@ class EmailAPI with HandleSetErrorMixin { List emailIds, MarkStarAction markStarAction ) async { - final maxObjects = _getMaxObjectsInSetMethod(session, accountId); + final maxObjects = getMaxObjectsInSetMethod(session, accountId); final totalEmails = emailIds.length; final maxBatches = min(totalEmails, maxObjects); @@ -803,7 +722,7 @@ class EmailAPI with HandleSetErrorMixin { AccountId accountId, List emailIds ) async { - final maxObjects = _getMaxObjectsInSetMethod(session, accountId); + final maxObjects = getMaxObjectsInSetMethod(session, accountId); final totalEmails = emailIds.length; final maxBatches = min(totalEmails, maxObjects); diff --git a/lib/features/mailbox/data/datasource/mailbox_datasource.dart b/lib/features/mailbox/data/datasource/mailbox_datasource.dart index c50321e47..3c6218a98 100644 --- a/lib/features/mailbox/data/datasource/mailbox_datasource.dart +++ b/lib/features/mailbox/data/datasource/mailbox_datasource.dart @@ -17,6 +17,7 @@ import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_change_respons import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/get_mailbox_by_role_response.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/jmap_mailbox_response.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/move_folder_content_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/move_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/rename_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/mailbox_right_request.dart'; @@ -40,6 +41,13 @@ abstract class MailboxDataSource { Future moveMailbox(Session session, AccountId accountId, MoveMailboxRequest request); + Future moveFolderContent({ + required Session session, + required AccountId accountId, + required MoveFolderContentRequest request, + StreamController>? onProgressController, + }); + Future> markAsMailboxRead( Session session, AccountId accountId, diff --git a/lib/features/mailbox/data/datasource_impl/mailbox_cache_datasource_impl.dart b/lib/features/mailbox/data/datasource_impl/mailbox_cache_datasource_impl.dart index 0956ac80f..57f21ba6a 100644 --- a/lib/features/mailbox/data/datasource_impl/mailbox_cache_datasource_impl.dart +++ b/lib/features/mailbox/data/datasource_impl/mailbox_cache_datasource_impl.dart @@ -21,6 +21,7 @@ import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_change_respons import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/get_mailbox_by_role_response.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/jmap_mailbox_response.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/move_folder_content_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/move_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/rename_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/mailbox_right_request.dart'; @@ -166,4 +167,14 @@ class MailboxCacheDataSourceImpl extends MailboxDataSource { Future clearMailbox(Session session, AccountId accountId, MailboxId mailboxId) { throw UnimplementedError(); } + + @override + Future> moveFolderContent({ + required Session session, + required AccountId accountId, + required MoveFolderContentRequest request, + StreamController>? onProgressController, + }) { + throw UnimplementedError(); + } } \ No newline at end of file diff --git a/lib/features/mailbox/data/datasource_impl/mailbox_datasource_impl.dart b/lib/features/mailbox/data/datasource_impl/mailbox_datasource_impl.dart index 23a3495c2..8f3672d90 100644 --- a/lib/features/mailbox/data/datasource_impl/mailbox_datasource_impl.dart +++ b/lib/features/mailbox/data/datasource_impl/mailbox_datasource_impl.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; +import 'package:core/utils/platform_info.dart'; import 'package:dartz/dartz.dart' as dartz; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/error/set_error.dart'; @@ -20,6 +21,7 @@ import 'package:tmail_ui_user/features/mailbox/data/network/mailbox_isolate_work import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/get_mailbox_by_role_response.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/jmap_mailbox_response.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/move_folder_content_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/move_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/rename_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/mailbox_right_request.dart'; @@ -161,4 +163,33 @@ class MailboxDataSourceImpl extends MailboxDataSource { return await mailboxAPI.clearMailbox(session, accountId, mailboxId); }).catchError(_exceptionThrower.throwException); } + + @override + Future moveFolderContent({ + required Session session, + required AccountId accountId, + required MoveFolderContentRequest request, + StreamController>? onProgressController, + }) { + return Future.sync(() async { + if (PlatformInfo.isWeb) { + return await mailboxAPI.moveFolderContent( + session: session, + accountId: accountId, + request: request, + onProgressController: onProgressController, + ); + } else { + return await _mailboxIsolateWorker.moveFolderContent( + session: session, + accountId: accountId, + request: request, + onProgressController: onProgressController, + ); + } + }).catchError((error, stackTrace) async { + await _exceptionThrower.throwException(error, stackTrace); + throw error; + }); + } } \ No newline at end of file diff --git a/lib/features/mailbox/data/model/move_folder_content_isolate_arguments.dart b/lib/features/mailbox/data/model/move_folder_content_isolate_arguments.dart new file mode 100644 index 000000000..0b1fee905 --- /dev/null +++ b/lib/features/mailbox/data/model/move_folder_content_isolate_arguments.dart @@ -0,0 +1,41 @@ +import 'package:equatable/equatable.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/base/isolate/background_isolate_binary_messenger/background_isolate_binary_messenger.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 MoveFolderContentIsolateArguments with EquatableMixin { + final Session session; + final AccountId accountId; + final MailboxId currentMailboxId; + final MailboxId destinationMailboxId; + final bool markAsRead; + final ThreadAPI threadAPI; + final EmailAPI emailAPI; + final RootIsolateToken isolateToken; + + MoveFolderContentIsolateArguments({ + required this.session, + required this.threadAPI, + required this.emailAPI, + required this.accountId, + required this.currentMailboxId, + required this.destinationMailboxId, + required this.isolateToken, + this.markAsRead = false, + }); + + @override + List get props => [ + session, + accountId, + threadAPI, + emailAPI, + currentMailboxId, + destinationMailboxId, + isolateToken, + markAsRead, + ]; +} diff --git a/lib/features/mailbox/data/network/mailbox_api.dart b/lib/features/mailbox/data/network/mailbox_api.dart index 752a4ae49..ca50b2865 100644 --- a/lib/features/mailbox/data/network/mailbox_api.dart +++ b/lib/features/mailbox/data/network/mailbox_api.dart @@ -1,6 +1,9 @@ import 'dart:async'; +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; import 'package:core/utils/app_logger.dart'; +import 'package:dartz/dartz.dart' hide State; import 'package:flutter/material.dart' hide State; import 'package:get/get.dart'; import 'package:jmap_dart_client/http/http_client.dart'; @@ -32,6 +35,7 @@ import 'package:model/error_type_handler/set_method_error_handler_mixin.dart'; import 'package:model/mailbox/mailbox_constants.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/mixin/handle_error_mixin.dart'; +import 'package:tmail_ui_user/features/base/mixin/mail_api_mixin.dart'; import 'package:tmail_ui_user/features/composer/domain/exceptions/set_method_exception.dart'; import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_change_response.dart'; import 'package:tmail_ui_user/features/mailbox/domain/exceptions/mailbox_exception.dart'; @@ -41,17 +45,18 @@ import 'package:tmail_ui_user/features/mailbox/domain/extensions/role_extension. import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/get_mailbox_by_role_response.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/jmap_mailbox_response.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/mailbox_right_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/mailbox_subaddressing_action.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/mailbox_subscribe_state.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/move_folder_content_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/move_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/rename_mailbox_request.dart'; -import 'package:tmail_ui_user/features/mailbox/domain/model/mailbox_right_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/subscribe_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/subscribe_multiple_mailbox_request.dart'; import 'package:tmail_ui_user/main/error/capability_validator.dart'; import 'package:uuid/uuid.dart'; -class MailboxAPI with HandleSetErrorMixin { +class MailboxAPI with HandleSetErrorMixin, MailAPIMixin { final HttpClient httpClient; final Uuid _uuid; @@ -636,4 +641,23 @@ class MailboxAPI with HandleSetErrorMixin { throw NotFoundClearMailboxResponseException(); } } + + Future moveFolderContent({ + required Session session, + required AccountId accountId, + required MoveFolderContentRequest request, + StreamController>? onProgressController, + }) async { + return await moveAllEmailsBetweenFolders( + httpClient: httpClient, + accountId: accountId, + session: session, + moveAction: request.moveAction, + currentMailboxId: request.mailboxId, + destinationMailboxId: request.destinationMailboxId, + totalEmails: request.totalEmails, + markAsRead: request.markAsRead, + onProgressController: onProgressController, + ); + } } \ No newline at end of file diff --git a/lib/features/mailbox/data/network/mailbox_isolate_worker.dart b/lib/features/mailbox/data/network/mailbox_isolate_worker.dart index 2c627d838..c745247cd 100644 --- a/lib/features/mailbox/data/network/mailbox_isolate_worker.dart +++ b/lib/features/mailbox/data/network/mailbox_isolate_worker.dart @@ -23,8 +23,13 @@ import 'package:model/extensions/list_email_extension.dart'; import 'package:tmail_ui_user/features/base/isolate/background_isolate_binary_messenger/background_isolate_binary_messenger.dart'; import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; +import 'package:tmail_ui_user/features/email/domain/model/move_action.dart'; import 'package:tmail_ui_user/features/mailbox/data/model/mailbox_mark_as_read_arguments.dart'; +import 'package:tmail_ui_user/features/mailbox/data/model/move_folder_content_isolate_arguments.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/exceptions/mailbox_exception.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/move_folder_content_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/state/mark_as_mailbox_read_state.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/state/move_folder_content_state.dart'; import 'package:tmail_ui_user/features/thread/data/network/thread_api.dart'; import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart'; import 'package:tmail_ui_user/main/exceptions/isolate_exception.dart'; @@ -213,4 +218,101 @@ class MailboxIsolateWorker { log('MailboxIsolateWorker::_handleMarkAsMailboxReadActionOnWeb(): TOTAL_READ: ${emailIdsCompleted.length}'); return emailIdsCompleted; } + + Future moveFolderContent({ + required Session session, + required AccountId accountId, + required MoveFolderContentRequest request, + StreamController>? onProgressController, + }) async { + final rootIsolateToken = RootIsolateToken.instance; + if (rootIsolateToken == null) { + throw CanNotGetRootIsolateToken(); + } + + final countEmailsCompleted = await _isolateExecutor.execute( + arg1: MoveFolderContentIsolateArguments( + session: session, + accountId: accountId, + threadAPI: _threadApi, + emailAPI: _emailApi, + currentMailboxId: request.mailboxId, + destinationMailboxId: request.destinationMailboxId, + isolateToken: rootIsolateToken, + markAsRead: request.markAsRead, + ), + fun1: _moveFolderContentIsolateMethod, + notification: (value) { + if (value is int) { + log('$runtimeType::moveFolderContent(): Progress percent is ${value / request.totalEmails}'); + onProgressController?.add( + Right(MoveFolderContentProgressState( + request.mailboxId, + value, + request.totalEmails, + )), + ); + } + }, + ); + + if (request.moveAction == MoveAction.moving && + countEmailsCompleted < request.totalEmails && + request.totalEmails > 0) { + throw CannotMoveAllEmailException(); + } + } + + static Future _moveFolderContentIsolateMethod( + MoveFolderContentIsolateArguments args, + TypeSendPort sendPort, + ) async { + final rootIsolateToken = args.isolateToken; + BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken); + await HiveCacheConfig.instance.setUp(); + + int countEmailsCompleted = 0; + bool hasEmails = true; + Email? lastEmail; + + final threadAPI = args.threadAPI; + final httpClient = threadAPI.httpClient; + final session = args.session; + final accountId = args.accountId; + final currentMailboxId = args.currentMailboxId; + final destinationMailboxId = args.destinationMailboxId; + final markAsRead = args.markAsRead; + + while (hasEmails) { + final listEmails = await threadAPI.getLatestEmails( + httpClient: httpClient, + session: session, + accountId: accountId, + mailboxId: currentMailboxId, + lastEmail: lastEmail, + ); + log('MailboxIsolateWorker::_moveFolderContentIsolateMethod(): Length of emails = ${listEmails.length}'); + if (listEmails.isEmpty) { + hasEmails = false; + } else { + hasEmails = true; + lastEmail = listEmails.last; + + final movedEmails = await threadAPI.moveEmailsBetweenMailboxes( + httpClient: httpClient, + session: session, + accountId: accountId, + emailIds: listEmails.listEmailIds, + currentMailboxId: currentMailboxId, + destinationMailboxId: destinationMailboxId, + markAsRead: markAsRead, + ); + + countEmailsCompleted += movedEmails.emailIdsSuccess.length; + sendPort.send(countEmailsCompleted); + } + } + log('MailboxIsolateWorker::_moveFolderContentIsolateMethod(): Total emails moved = $countEmailsCompleted'); + return countEmailsCompleted; + } } diff --git a/lib/features/mailbox/data/repository/mailbox_repository_impl.dart b/lib/features/mailbox/data/repository/mailbox_repository_impl.dart index f2fd114e3..4e0b62c1d 100644 --- a/lib/features/mailbox/data/repository/mailbox_repository_impl.dart +++ b/lib/features/mailbox/data/repository/mailbox_repository_impl.dart @@ -28,6 +28,7 @@ import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_r import 'package:tmail_ui_user/features/mailbox/domain/model/get_mailbox_by_role_response.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/jmap_mailbox_response.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/mailbox_response.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/move_folder_content_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/move_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/rename_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/mailbox_right_request.dart'; @@ -305,4 +306,19 @@ class MailboxRepositoryImpl extends MailboxRepository { Future clearMailbox(Session session, AccountId accountId, MailboxId mailboxId) { return mapDataSource[DataSourceType.network]!.clearMailbox(session, accountId, mailboxId); } + + @override + Future moveFolderContent({ + required Session session, + required AccountId accountId, + required MoveFolderContentRequest request, + StreamController>? onProgressController, + }) { + return mapDataSource[DataSourceType.network]!.moveFolderContent( + session: session, + accountId: accountId, + request: request, + onProgressController: onProgressController, + ); + } } \ No newline at end of file diff --git a/lib/features/mailbox/domain/exceptions/mailbox_exception.dart b/lib/features/mailbox/domain/exceptions/mailbox_exception.dart index 68c8ace28..de530621d 100644 --- a/lib/features/mailbox/domain/exceptions/mailbox_exception.dart +++ b/lib/features/mailbox/domain/exceptions/mailbox_exception.dart @@ -4,3 +4,5 @@ class NotFoundInboxMailboxException implements Exception {} class NotFoundMailboxException implements Exception {} class NotFoundClearMailboxResponseException implements Exception {} + +class CannotMoveAllEmailException implements Exception {} diff --git a/lib/features/mailbox/domain/model/move_folder_content_request.dart b/lib/features/mailbox/domain/model/move_folder_content_request.dart new file mode 100644 index 000000000..963b994a6 --- /dev/null +++ b/lib/features/mailbox/domain/model/move_folder_content_request.dart @@ -0,0 +1,31 @@ +import 'package:equatable/equatable.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:tmail_ui_user/features/email/domain/model/move_action.dart'; + +class MoveFolderContentRequest with EquatableMixin { + final MailboxId mailboxId; + final MoveAction moveAction; + final MailboxId destinationMailboxId; + final String destinationMailboxDisplayName; + final int totalEmails; + final bool markAsRead; + + MoveFolderContentRequest({ + required this.mailboxId, + required this.moveAction, + required this.destinationMailboxId, + required this.destinationMailboxDisplayName, + this.totalEmails = 0, + this.markAsRead = false, + }); + + @override + List get props => [ + mailboxId, + moveAction, + destinationMailboxId, + destinationMailboxDisplayName, + totalEmails, + markAsRead, + ]; +} diff --git a/lib/features/mailbox/domain/repository/mailbox_repository.dart b/lib/features/mailbox/domain/repository/mailbox_repository.dart index 4504c5fad..8416e869a 100644 --- a/lib/features/mailbox/domain/repository/mailbox_repository.dart +++ b/lib/features/mailbox/domain/repository/mailbox_repository.dart @@ -15,6 +15,7 @@ import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/get_mailbox_by_role_response.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/mailbox_response.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/move_folder_content_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/move_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/rename_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/subscribe_mailbox_request.dart'; @@ -41,6 +42,13 @@ abstract class MailboxRepository { Future moveMailbox(Session session, AccountId accountId, MoveMailboxRequest request); + Future moveFolderContent({ + required Session session, + required AccountId accountId, + required MoveFolderContentRequest request, + StreamController>? onProgressController, + }); + Future getMailboxState(Session session, AccountId accountId); Future subscribeMailbox(Session session, AccountId accountId, SubscribeMailboxRequest request); diff --git a/lib/features/mailbox/domain/state/move_folder_content_state.dart b/lib/features/mailbox/domain/state/move_folder_content_state.dart new file mode 100644 index 000000000..b1084a87c --- /dev/null +++ b/lib/features/mailbox/domain/state/move_folder_content_state.dart @@ -0,0 +1,38 @@ +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/model/move_folder_content_request.dart'; + +class MovingFolderContent extends LoadingState {} + +class MoveFolderContentProgressState extends UIState { + final MailboxId mailboxId; + final int countEmailsCompleted; + final int totalEmails; + + MoveFolderContentProgressState( + this.mailboxId, + this.countEmailsCompleted, + this.totalEmails, + ); + + @override + List get props => [ + mailboxId, + countEmailsCompleted, + totalEmails, + ]; +} + +class MoveFolderContentSuccess extends UIState { + final MoveFolderContentRequest request; + + MoveFolderContentSuccess(this.request); + + @override + List get props => [request]; +} + +class MoveFolderContentFailure extends FeatureFailure { + MoveFolderContentFailure(dynamic exception) : super(exception: exception); +} diff --git a/lib/features/mailbox/domain/usecases/move_folder_content_interactor.dart b/lib/features/mailbox/domain/usecases/move_folder_content_interactor.dart new file mode 100644 index 000000000..860f2f293 --- /dev/null +++ b/lib/features/mailbox/domain/usecases/move_folder_content_interactor.dart @@ -0,0 +1,37 @@ +import 'dart:async'; + +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:tmail_ui_user/features/mailbox/domain/model/move_folder_content_request.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/repository/mailbox_repository.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/state/move_folder_content_state.dart'; +import 'package:tmail_ui_user/features/mailbox/domain/state/move_mailbox_state.dart'; + +class MoveFolderContentInteractor { + final MailboxRepository _mailboxRepository; + + MoveFolderContentInteractor(this._mailboxRepository); + + Stream execute({ + required Session session, + required AccountId accountId, + required MoveFolderContentRequest request, + StreamController>? onProgressController, + }) async* { + try { + yield Right(MovingFolderContent()); + await _mailboxRepository.moveFolderContent( + session: session, + accountId: accountId, + request: request, + onProgressController: onProgressController, + ); + yield Right(MoveFolderContentSuccess(request)); + } catch (e) { + yield Left(MoveMailboxFailure(e)); + } + } +} diff --git a/lib/features/thread/data/extensions/list_email_extension.dart b/lib/features/thread/data/extensions/list_email_extension.dart index 05befc913..57dadba70 100644 --- a/lib/features/thread/data/extensions/list_email_extension.dart +++ b/lib/features/thread/data/extensions/list_email_extension.dart @@ -61,4 +61,7 @@ extension ListEmailExtension on List { ), ).toList(); } + + List withoutEmailWithId(EmailId emailId) => + where((email) => email.id != emailId).toList(); } \ No newline at end of file diff --git a/lib/features/thread/data/network/thread_api.dart b/lib/features/thread/data/network/thread_api.dart index c9d3d2abb..205f39acd 100644 --- a/lib/features/thread/data/network/thread_api.dart +++ b/lib/features/thread/data/network/thread_api.dart @@ -21,7 +21,8 @@ import 'package:jmap_dart_client/jmap/mail/email/get/get_email_response.dart'; import 'package:jmap_dart_client/jmap/mail/email/query/query_email_method.dart'; import 'package:jmap_dart_client/jmap/mail/email/query/query_email_response.dart'; import 'package:model/extensions/list_id_extension.dart'; -import 'package:tmail_ui_user/features/thread/data/extensions/list_email_extension.dart'; +import 'package:tmail_ui_user/features/base/mixin/handle_error_mixin.dart'; +import 'package:tmail_ui_user/features/base/mixin/mail_api_mixin.dart'; import 'package:jmap_dart_client/jmap/mail/email/search_snippet/search_snippet.dart'; import 'package:jmap_dart_client/jmap/mail/email/search_snippet/search_snippet_get_method.dart'; import 'package:jmap_dart_client/jmap/mail/email/search_snippet/search_snippet_get_response.dart'; @@ -31,7 +32,7 @@ import 'package:tmail_ui_user/features/thread/domain/model/email_response.dart'; import 'package:tmail_ui_user/features/thread/domain/model/search_emails_response.dart'; import 'package:tmail_ui_user/main/error/capability_validator.dart'; -class ThreadAPI { +class ThreadAPI with HandleSetErrorMixin, MailAPIMixin { final HttpClient httpClient; @@ -48,81 +49,16 @@ class ThreadAPI { Properties? properties } ) async { - final processingInvocation = ProcessingInvocation(); - - final jmapRequestBuilder = JmapRequestBuilder(httpClient, processingInvocation); - - final queryEmailMethod = QueryEmailMethod(accountId); - - if (limit != null) queryEmailMethod.addLimit(limit); - - if (position != null) queryEmailMethod.addPosition(position); - - if (sort != null) queryEmailMethod.addSorts(sort); - - if (filter != null) queryEmailMethod.addFilters(filter); - - final queryEmailInvocation = jmapRequestBuilder.invocation(queryEmailMethod); - - final getEmailMethod = GetEmailMethod(accountId); - - if (properties != null) getEmailMethod.addProperties(properties); - - getEmailMethod.addReferenceIds(processingInvocation.createResultReference( - queryEmailInvocation.methodCallId, - ReferencePath.idsPath)); - - final getEmailInvocation = jmapRequestBuilder.invocation(getEmailMethod); - - final capabilities = getEmailMethod.requiredCapabilities - .toCapabilitiesSupportTeamMailboxes(session, accountId); - - final result = await (jmapRequestBuilder - ..usings(capabilities)) - .build() - .execute(); - - final responseOfGetEmailMethod = result.parse( - getEmailInvocation.methodCallId, - GetEmailResponse.deserialize, + return await fetchAllEmail( + httpClient: httpClient, + session: session, + accountId: accountId, + limit: limit, + position: position, + sort: sort, + filter: filter, + properties: properties, ); - - final responseOfQueryEmailMethod = result.parse( - queryEmailInvocation.methodCallId, - QueryEmailResponse.deserialize, - ); - - final emailList = sortEmails( - getEmailResponse: responseOfGetEmailMethod, - queryEmailResponse: responseOfQueryEmailMethod, - ); - - final notFoundEmailIds = responseOfGetEmailMethod - ?.notFound - ?.toEmailIds() - .toList(); - log('ThreadAPI::getAllEmail:notFoundEmailIds = ${notFoundEmailIds!.asListString.toString()} | NewState = ${responseOfGetEmailMethod?.state.value}'); - return EmailsResponse( - emailList: emailList, - notFoundEmailIds: notFoundEmailIds, - state: responseOfGetEmailMethod?.state, - ); - } - - List? sortEmails({ - GetEmailResponse? getEmailResponse, - QueryEmailResponse? queryEmailResponse, - }) { - final listEmails = getEmailResponse?.list; - final listIds = queryEmailResponse?.ids.toList(); - - if (listEmails?.isNotEmpty != true || listIds?.isNotEmpty != true) { - return listEmails; - } - - final listSortedEmails = listEmails!.sortEmailsById(listIds!); - - return listSortedEmails; } Future searchEmails( diff --git a/model/lib/extensions/list_email_id_extension.dart b/model/lib/extensions/list_email_id_extension.dart index 26d72e4fe..fbb844c99 100644 --- a/model/lib/extensions/list_email_id_extension.dart +++ b/model/lib/extensions/list_email_id_extension.dart @@ -16,10 +16,17 @@ extension ListEmailIdExtension on List { }; } - Map generateMapUpdateObjectMoveToMailbox(MailboxId currentMailboxId, MailboxId destinationMailboxId) { + Map generateMapUpdateObjectMoveToMailbox({ + required MailboxId currentMailboxId, + required MailboxId destinationMailboxId, + bool markAsRead = false, + }) { return { for (var emailId in this) - emailId.id: currentMailboxId.generateMoveToMailboxActionPath(destinationMailboxId) + emailId.id: currentMailboxId.generateMoveToMailboxActionPath( + destinationMailboxId: destinationMailboxId, + markAsRead: markAsRead, + ) }; } @@ -30,13 +37,6 @@ extension ListEmailIdExtension on List { }; } - Map generateMapUpdateObjectMoveToSpam(MailboxId currentMailboxId, MailboxId spamMailboxId) { - return { - for (var emailId in this) - emailId.id: currentMailboxId.generateMoveToSpamActionPath(currentMailboxId, spamMailboxId) - }; - } - Map generateMapUpdateObjectMarkAsAnswered() { return { for (var emailId in this) diff --git a/model/lib/extensions/mailbox_id_extension.dart b/model/lib/extensions/mailbox_id_extension.dart index 5cfd5e7a3..0b701caf5 100644 --- a/model/lib/extensions/mailbox_id_extension.dart +++ b/model/lib/extensions/mailbox_id_extension.dart @@ -13,10 +13,14 @@ extension MailboxIdExtension on MailboxId { } } - PatchObject generateMoveToMailboxActionPath(MailboxId destinationMailboxId) { + PatchObject generateMoveToMailboxActionPath({ + required MailboxId destinationMailboxId, + bool markAsRead = false, + }) { return PatchObject({ generatePath(): null, - destinationMailboxId.generatePath(): true + destinationMailboxId.generatePath(): true, + if (markAsRead) KeyWordIdentifier.emailSeen.generatePath(): true, }); } @@ -26,13 +30,5 @@ extension MailboxIdExtension on MailboxId { }); } - PatchObject generateMoveToSpamActionPath(MailboxId currentMailboxId, MailboxId spamMailboxId) { - return PatchObject({ - currentMailboxId.generatePath(): null, - spamMailboxId.generatePath(): true, - KeyWordIdentifier.emailSeen.generatePath(): true - }); - } - String get asString => id.value; } \ No newline at end of file