diff --git a/lib/features/email/data/datasource/email_datasource.dart b/lib/features/email/data/datasource/email_datasource.dart index 8a9fce660..dec9c104c 100644 --- a/lib/features/email/data/datasource/email_datasource.dart +++ b/lib/features/email/data/datasource/email_datasource.dart @@ -1,10 +1,6 @@ import 'dart:async'; -import 'dart:typed_data'; import 'package:core/data/network/download/downloaded_response.dart'; -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:dartz/dartz.dart'; import 'package:dio/dio.dart'; import 'package:email_recovery/email_recovery/email_recovery_action.dart'; import 'package:email_recovery/email_recovery/email_recovery_action_id.dart'; @@ -16,7 +12,6 @@ import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:model/account/account_request.dart'; -import 'package:model/download/download_task_id.dart'; import 'package:model/email/attachment.dart'; import 'package:model/email/mark_star_action.dart'; import 'package:model/email/read_actions.dart'; @@ -57,13 +52,6 @@ abstract class EmailDataSource { ReadActions readActions, ); - Future> downloadAttachments( - List attachments, - AccountId accountId, - String baseDownloadUrl, - AccountRequest accountRequest - ); - Future exportAttachment( Attachment attachment, AccountId accountId, @@ -72,16 +60,6 @@ abstract class EmailDataSource { CancelToken cancelToken ); - Future downloadAttachmentForWeb( - DownloadTaskId taskId, - Attachment attachment, - AccountId accountId, - String baseDownloadUrl, - AccountRequest accountRequest, - StreamController> onReceiveController, - {CancelToken? cancelToken} - ); - Future<({ List emailIdsSuccess, Map mapErrors, @@ -149,6 +127,7 @@ abstract class EmailDataSource { AccountId accountId, List emailIds, ); + Future deleteEmailPermanently( Session session, AccountId accountId, @@ -214,17 +193,6 @@ abstract class EmailDataSource { Future getPreviewEMLContentInMemory(String keyStored); - Future downloadAllAttachmentsForWeb( - AccountId accountId, - EmailId emailId, - String baseDownloadAllUrl, - String outputFileName, - AccountRequest accountRequest, - DownloadTaskId taskId, - StreamController> onReceiveController, - {CancelToken? cancelToken} - ); - Future exportAllAttachments( AccountId accountId, EmailId emailId, diff --git a/lib/features/email/data/datasource_impl/email_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_datasource_impl.dart index 5e5fb69c2..d48546f68 100644 --- a/lib/features/email/data/datasource_impl/email_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_datasource_impl.dart @@ -1,19 +1,15 @@ import 'dart:async'; -import 'dart:typed_data'; import 'package:core/data/model/preview_attachment.dart'; import 'package:core/data/network/download/downloaded_response.dart'; import 'package:core/domain/extensions/datetime_extension.dart'; import 'package:core/presentation/extensions/html_extension.dart'; import 'package:core/presentation/resources/image_paths.dart'; -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; import 'package:core/presentation/utils/html_transformer/transform_configuration.dart'; import 'package:core/utils/app_logger.dart'; import 'package:core/utils/file_utils.dart'; import 'package:core/utils/platform_info.dart'; import 'package:core/utils/preview_eml_file_utils.dart'; -import 'package:dartz/dartz.dart'; import 'package:dio/dio.dart'; import 'package:email_recovery/email_recovery/email_recovery_action.dart'; import 'package:email_recovery/email_recovery/email_recovery_action_id.dart'; @@ -109,18 +105,6 @@ class EmailDataSourceImpl extends EmailDataSource { }).catchError(_exceptionThrower.throwException); } - @override - Future> downloadAttachments( - List attachments, - AccountId accountId, - String baseDownloadUrl, - AccountRequest accountRequest - ) { - return Future.sync(() async { - return await emailAPI.downloadAttachments(attachments, accountId, baseDownloadUrl, accountRequest); - }).catchError(_exceptionThrower.throwException); - } - @override Future exportAttachment( Attachment attachment, @@ -261,28 +245,6 @@ class EmailDataSourceImpl extends EmailDataSource { }).catchError(_exceptionThrower.throwException); } - @override - Future downloadAttachmentForWeb( - DownloadTaskId taskId, - Attachment attachment, - AccountId accountId, - String baseDownloadUrl, - AccountRequest accountRequest, - StreamController> onReceiveController, - {CancelToken? cancelToken} - ) { - return Future.sync(() async { - return await emailAPI.downloadAttachmentForWeb( - taskId, - attachment, - accountId, - baseDownloadUrl, - accountRequest, - onReceiveController, - cancelToken: cancelToken); - }).catchError(_exceptionThrower.throwException); - } - @override Future<({ List emailIdsSuccess, @@ -567,29 +529,6 @@ class EmailDataSourceImpl extends EmailDataSource { throw UnimplementedError(); } - @override - Future downloadAllAttachmentsForWeb( - AccountId accountId, - EmailId emailId, - String baseDownloadAllUrl, - String outputFileName, - AccountRequest accountRequest, - DownloadTaskId taskId, - StreamController> onReceiveController, - {CancelToken? cancelToken} - ) => Future.sync(() async { - return await emailAPI.downloadAllAttachmentsForWeb( - accountId, - emailId, - baseDownloadAllUrl, - outputFileName, - accountRequest, - taskId, - onReceiveController, - cancelToken: cancelToken, - ); - }).catchError(_exceptionThrower.throwException); - @override Future exportAllAttachments( AccountId accountId, diff --git a/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart index c83c92c3f..4297ac76d 100644 --- a/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart @@ -1,13 +1,9 @@ import 'dart:async'; -import 'dart:typed_data'; import 'package:core/data/network/download/downloaded_response.dart'; -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; import 'package:core/utils/app_logger.dart'; import 'package:core/utils/file_utils.dart'; -import 'package:dartz/dartz.dart'; import 'package:dio/dio.dart'; import 'package:email_recovery/email_recovery/email_recovery_action.dart'; import 'package:email_recovery/email_recovery/email_recovery_action_id.dart'; @@ -20,7 +16,6 @@ import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart'; import 'package:model/account/account_request.dart'; -import 'package:model/download/download_task_id.dart'; import 'package:model/email/attachment.dart'; import 'package:model/email/mark_star_action.dart'; import 'package:model/email/read_actions.dart'; @@ -112,24 +107,6 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource { ); } - @override - Future downloadAttachmentForWeb( - DownloadTaskId taskId, - Attachment attachment, - AccountId accountId, - String baseDownloadUrl, - AccountRequest accountRequest, - StreamController> onReceiveController, - {CancelToken? cancelToken} - ) { - throw UnimplementedError(); - } - - @override - Future> downloadAttachments(List attachments, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest) { - throw UnimplementedError(); - } - @override Future exportAttachment(Attachment attachment, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest, CancelToken cancelToken) { throw UnimplementedError(); @@ -589,20 +566,6 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource { throw UnimplementedError(); } - @override - Future downloadAllAttachmentsForWeb( - AccountId accountId, - EmailId emailId, - String baseDownloadAllUrl, - String outputFileName, - AccountRequest accountRequest, - DownloadTaskId taskId, - StreamController> onReceiveController, - {CancelToken? cancelToken} - ) { - throw UnimplementedError(); - } - @override Future exportAllAttachments(AccountId accountId, EmailId emailId, String baseDownloadAllUrl, String outputFileName, AccountRequest accountRequest, {CancelToken? cancelToken}) { throw UnimplementedError(); diff --git a/lib/features/email/data/datasource_impl/email_local_storage_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_local_storage_datasource_impl.dart index 1e16f17f7..e32b5abc1 100644 --- a/lib/features/email/data/datasource_impl/email_local_storage_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_local_storage_datasource_impl.dart @@ -1,18 +1,14 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:typed_data'; import 'package:core/data/model/preview_attachment.dart'; import 'package:core/data/network/download/downloaded_response.dart'; import 'package:core/domain/extensions/datetime_extension.dart'; import 'package:core/presentation/extensions/html_extension.dart'; import 'package:core/presentation/resources/image_paths.dart'; -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; import 'package:core/utils/file_utils.dart'; import 'package:core/utils/preview_eml_file_utils.dart'; -import 'package:dartz/dartz.dart'; import 'package:dio/dio.dart'; import 'package:email_recovery/email_recovery/email_recovery_action.dart'; import 'package:email_recovery/email_recovery/email_recovery_action_id.dart'; @@ -26,7 +22,6 @@ import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:model/account/account_request.dart'; -import 'package:model/download/download_task_id.dart'; import 'package:model/email/attachment.dart'; import 'package:model/email/mark_star_action.dart'; import 'package:model/email/read_actions.dart'; @@ -80,16 +75,6 @@ class EmailLocalStorageDataSourceImpl extends EmailDataSource { throw UnimplementedError(); } - @override - Future downloadAttachmentForWeb(DownloadTaskId taskId, Attachment attachment, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest, StreamController> onReceiveController, {CancelToken? cancelToken}) { - throw UnimplementedError(); - } - - @override - Future> downloadAttachments(List attachments, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest) { - throw UnimplementedError(); - } - @override Future exportAttachment(Attachment attachment, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest, CancelToken cancelToken) { throw UnimplementedError(); @@ -264,21 +249,7 @@ class EmailLocalStorageDataSourceImpl extends EmailDataSource { Future markAsForwarded(Session session, AccountId accountId, List emailIds) { throw UnimplementedError(); } - - @override - Future downloadAllAttachmentsForWeb( - AccountId accountId, - EmailId emailId, - String baseDownloadAllUrl, - String outputFileName, - AccountRequest accountRequest, - DownloadTaskId taskId, - StreamController> onReceiveController, - {CancelToken? cancelToken} - ) { - throw UnimplementedError(); - } - + @override Future exportAllAttachments(AccountId accountId, EmailId emailId, String baseDownloadAllUrl, String outputFileName, AccountRequest accountRequest, {CancelToken? cancelToken}) { throw UnimplementedError(); diff --git a/lib/features/email/data/datasource_impl/email_session_storage_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_session_storage_datasource_impl.dart index 71d9159e3..1c6030111 100644 --- a/lib/features/email/data/datasource_impl/email_session_storage_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_session_storage_datasource_impl.dart @@ -1,12 +1,8 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:typed_data'; import 'package:core/data/network/download/downloaded_response.dart'; -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:dartz/dartz.dart'; import 'package:dio/dio.dart'; import 'package:email_recovery/email_recovery/email_recovery_action.dart'; import 'package:email_recovery/email_recovery/email_recovery_action_id.dart'; @@ -18,7 +14,6 @@ import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:model/account/account_request.dart'; -import 'package:model/download/download_task_id.dart'; import 'package:model/email/attachment.dart'; import 'package:model/email/mark_star_action.dart'; import 'package:model/email/read_actions.dart'; @@ -65,16 +60,6 @@ class EmailSessionStorageDatasourceImpl extends EmailDataSource { throw UnimplementedError(); } - @override - Future downloadAttachmentForWeb(DownloadTaskId taskId, Attachment attachment, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest, StreamController> onReceiveController, {CancelToken? cancelToken}) { - throw UnimplementedError(); - } - - @override - Future> downloadAttachments(List attachments, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest) { - throw UnimplementedError(); - } - @override Future exportAttachment(Attachment attachment, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest, CancelToken cancelToken) { throw UnimplementedError(); @@ -247,21 +232,7 @@ class EmailSessionStorageDatasourceImpl extends EmailDataSource { Future markAsForwarded(Session session, AccountId accountId, List emailIds) { throw UnimplementedError(); } - - @override - Future downloadAllAttachmentsForWeb( - AccountId accountId, - EmailId emailId, - String baseDownloadAllUrl, - String outputFileName, - AccountRequest accountRequest, - DownloadTaskId taskId, - StreamController> onReceiveController, - {CancelToken? cancelToken} - ) { - throw UnimplementedError(); - } - + @override Future exportAllAttachments(AccountId accountId, EmailId emailId, String baseDownloadAllUrl, String outputFileName, AccountRequest accountRequest, {CancelToken? cancelToken}) { throw UnimplementedError(); diff --git a/lib/features/email/data/network/email_api.dart b/lib/features/email/data/network/email_api.dart index 62ab65589..ddae14b9e 100644 --- a/lib/features/email/data/network/email_api.dart +++ b/lib/features/email/data/network/email_api.dart @@ -12,8 +12,6 @@ import 'package:email_recovery/email_recovery/get/get_email_recovery_action_meth import 'package:email_recovery/email_recovery/get/get_email_recovery_action_response.dart'; import 'package:email_recovery/email_recovery/set/set_email_recovery_action_method.dart'; import 'package:email_recovery/email_recovery/set/set_email_recovery_action_response.dart'; -import 'package:external_path/external_path.dart'; -import 'package:flutter_downloader/flutter_downloader.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'; @@ -57,7 +55,6 @@ 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/composer/domain/exceptions/set_method_exception.dart'; @@ -67,7 +64,6 @@ import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_reques import 'package:tmail_ui_user/features/email/domain/model/restore_deleted_message_request.dart'; import 'package:tmail_ui_user/features/email/domain/state/download_all_attachments_for_web_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; -import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart'; import 'package:tmail_ui_user/main/error/capability_validator.dart'; @@ -298,49 +294,6 @@ class EmailAPI with HandleSetErrorMixin { return (emailIdsSuccess: updatedEmailIds, mapErrors: mapErrors); } - Future> downloadAttachments( - List attachments, - AccountId accountId, - String baseDownloadUrl, - AccountRequest accountRequest - ) async { - if (accountRequest.authenticationType == AuthenticationType.oidc && - accountRequest.token?.isExpired == true && - accountRequest.token?.refreshToken.isNotEmpty == true) { - throw DownloadAttachmentHasTokenExpiredException(accountRequest.token!.refreshToken); - } - - String externalStorageDirPath; - if (Platform.isAndroid) { - externalStorageDirPath = await ExternalPath.getExternalStoragePublicDirectory(ExternalPath.DIRECTORY_DOWNLOAD); - } else if (Platform.isIOS) { - externalStorageDirPath = (await getApplicationDocumentsDirectory()).absolute.path; - } else { - throw DeviceNotSupportedException(); - } - - final authentication = accountRequest.authenticationType == AuthenticationType.oidc - ? accountRequest.bearerToken - : accountRequest.basicAuth; - - final taskIds = await Future.wait( - attachments.map((attachment) async => await FlutterDownloader.enqueue( - url: attachment.getDownloadUrl(baseDownloadUrl, accountId), - savedDir: externalStorageDirPath, - headers: { - HttpHeaders.authorizationHeader: authentication, - HttpHeaders.acceptHeader: DioClient.jmapHeader - }, - fileName: attachment.name, - showNotification: true, - openFileFromNotification: true))); - - return taskIds - .where((taskId) => taskId != null) - .map((taskId) => DownloadTaskId(taskId!)) - .toList(); - } - Future exportAttachment( Attachment attachment, AccountId accountId, @@ -361,14 +314,14 @@ class EmailAPI with HandleSetErrorMixin { } Future downloadAttachmentForWeb( - DownloadTaskId taskId, - Attachment attachment, - AccountId accountId, - String baseDownloadUrl, - AccountRequest accountRequest, - StreamController> onReceiveController, - {CancelToken? cancelToken} - ) async { + DownloadTaskId taskId, + Attachment attachment, + AccountId accountId, + String baseDownloadUrl, + AccountRequest accountRequest, { + StreamController>? onReceiveController, + CancelToken? cancelToken, + }) async { final authentication = accountRequest.authenticationType == AuthenticationType.oidc ? accountRequest.bearerToken : accountRequest.basicAuth; @@ -392,7 +345,7 @@ class EmailAPI with HandleSetErrorMixin { progress = (downloaded / total) * 100; } log('EmailAPI::downloadFileForWeb(): progress = ${progress.round()}%'); - onReceiveController.add(Right(DownloadingAttachmentForWeb( + onReceiveController?.add(Right(DownloadingAttachmentForWeb( taskId, attachment, progress, @@ -410,10 +363,10 @@ class EmailAPI with HandleSetErrorMixin { String baseDownloadAllUrl, String outputFileName, AccountRequest accountRequest, - DownloadTaskId taskId, - StreamController> onReceiveController, - {CancelToken? cancelToken} - ) async { + DownloadTaskId taskId, { + StreamController>? onReceiveController, + CancelToken? cancelToken, + }) async { final authentication = accountRequest.authenticationType == AuthenticationType.oidc ? accountRequest.bearerToken : accountRequest.basicAuth; @@ -441,7 +394,7 @@ class EmailAPI with HandleSetErrorMixin { progress = (downloaded / total) * 100; } log('EmailAPI::downloadFileForWeb(): progress = ${progress.round()}%'); - onReceiveController.add(Right(DownloadingAllAttachmentsForWeb( + onReceiveController?.add(Right(DownloadingAllAttachmentsForWeb( taskId, downloadFileName, progress, diff --git a/lib/features/email/data/repository/email_repository_impl.dart b/lib/features/email/data/repository/email_repository_impl.dart index 5857a1ad5..3ef7133a2 100644 --- a/lib/features/email/data/repository/email_repository_impl.dart +++ b/lib/features/email/data/repository/email_repository_impl.dart @@ -1,13 +1,9 @@ import 'dart:async'; -import 'dart:typed_data'; import 'package:core/data/model/source_type/data_source_type.dart'; import 'package:core/data/network/download/downloaded_response.dart'; -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; import 'package:core/presentation/utils/html_transformer/transform_configuration.dart'; import 'package:core/utils/app_logger.dart'; -import 'package:dartz/dartz.dart'; import 'package:dio/dio.dart'; import 'package:email_recovery/email_recovery/email_recovery_action.dart'; import 'package:email_recovery/email_recovery/email_recovery_action_id.dart'; @@ -19,7 +15,6 @@ import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:model/account/account_request.dart'; -import 'package:model/download/download_task_id.dart'; import 'package:model/email/attachment.dart'; import 'package:model/email/email_content.dart'; import 'package:model/email/mark_star_action.dart'; @@ -142,16 +137,6 @@ class EmailRepositoryImpl extends EmailRepository { return result; } - @override - Future> downloadAttachments( - List attachments, - AccountId accountId, - String baseDownloadUrl, - AccountRequest accountRequest - ) { - return emailDataSource[DataSourceType.network]!.downloadAttachments(attachments, accountId, baseDownloadUrl, accountRequest); - } - @override Future exportAttachment( Attachment attachment, @@ -395,26 +380,6 @@ class EmailRepositoryImpl extends EmailRepository { return result; } - @override - Future downloadAttachmentForWeb( - DownloadTaskId taskId, - Attachment attachment, - AccountId accountId, - String baseDownloadUrl, - AccountRequest accountRequest, - StreamController> onReceiveController, - {CancelToken? cancelToken} - ) { - return emailDataSource[DataSourceType.network]!.downloadAttachmentForWeb( - taskId, - attachment, - accountId, - baseDownloadUrl, - accountRequest, - onReceiveController, - cancelToken: cancelToken); - } - @override Future<({ List emailIdsSuccess, @@ -575,28 +540,7 @@ class EmailRepositoryImpl extends EmailRepository { htmlContent, configuration); } - - @override - Future downloadAllAttachmentsForWeb( - AccountId accountId, - EmailId emailId, - String baseDownloadAllUrl, - String outputFileName, - AccountRequest accountRequest, - DownloadTaskId taskId, - StreamController> onReceiveController, - {CancelToken? cancelToken} - ) => emailDataSource[DataSourceType.network]!.downloadAllAttachmentsForWeb( - accountId, - emailId, - baseDownloadAllUrl, - outputFileName, - accountRequest, - taskId, - onReceiveController, - cancelToken: cancelToken, - ); - + @override Future exportAllAttachments( AccountId accountId, diff --git a/lib/features/email/domain/repository/email_repository.dart b/lib/features/email/domain/repository/email_repository.dart index 58e2d2f58..64e59e10b 100644 --- a/lib/features/email/domain/repository/email_repository.dart +++ b/lib/features/email/domain/repository/email_repository.dart @@ -1,11 +1,7 @@ import 'dart:async'; -import 'dart:typed_data'; import 'package:core/data/network/download/downloaded_response.dart'; -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; import 'package:core/presentation/utils/html_transformer/transform_configuration.dart'; -import 'package:dartz/dartz.dart'; import 'package:dio/dio.dart'; import 'package:email_recovery/email_recovery/email_recovery_action.dart'; import 'package:email_recovery/email_recovery/email_recovery_action_id.dart'; @@ -17,7 +13,6 @@ import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:model/account/account_request.dart'; -import 'package:model/download/download_task_id.dart'; import 'package:model/email/attachment.dart'; import 'package:model/email/email_content.dart'; import 'package:model/email/mark_star_action.dart'; @@ -59,13 +54,6 @@ abstract class EmailRepository { ReadActions readActions, ); - Future> downloadAttachments( - List attachments, - AccountId accountId, - String baseDownloadUrl, - AccountRequest accountRequest - ); - Future exportAttachment( Attachment attachment, AccountId accountId, @@ -74,16 +62,6 @@ abstract class EmailRepository { CancelToken cancelToken ); - Future downloadAttachmentForWeb( - DownloadTaskId taskId, - Attachment attachment, - AccountId accountId, - String baseDownloadUrl, - AccountRequest accountRequest, - StreamController> onReceiveController, - {CancelToken? cancelToken} - ); - Future<({ List emailIdsSuccess, Map mapErrors, @@ -212,16 +190,7 @@ abstract class EmailRepository { String htmlContent, TransformConfiguration configuration); - Future downloadAllAttachmentsForWeb( - AccountId accountId, - EmailId emailId, - String baseDownloadAllUrl, - String outputFileName, - AccountRequest accountRequest, - DownloadTaskId taskId, - StreamController> onReceiveController, - {CancelToken? cancelToken} - ); + Future exportAllAttachments( AccountId accountId, diff --git a/lib/features/email/domain/state/download_attachments_state.dart b/lib/features/email/domain/state/download_attachments_state.dart deleted file mode 100644 index 42f2e6a99..000000000 --- a/lib/features/email/domain/state/download_attachments_state.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:model/download/download_task_id.dart'; - -class DownloadAttachmentsSuccess extends UIState { - final List taskIds; - - DownloadAttachmentsSuccess(this.taskIds); - - @override - List get props => [taskIds]; -} - -class DownloadAttachmentsFailure extends FeatureFailure { - - DownloadAttachmentsFailure(dynamic exception) : super(exception: exception); -} \ No newline at end of file diff --git a/lib/features/email/domain/usecases/download_all_attachments_for_web_interactor.dart b/lib/features/email/domain/usecases/download_all_attachments_for_web_interactor.dart index 755981deb..5a3fef917 100644 --- a/lib/features/email/domain/usecases/download_all_attachments_for_web_interactor.dart +++ b/lib/features/email/domain/usecases/download_all_attachments_for_web_interactor.dart @@ -13,21 +13,21 @@ import 'package:model/account/authentication_type.dart'; import 'package:model/account/password.dart'; import 'package:model/download/download_task_id.dart'; import 'package:model/email/attachment.dart'; -import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; import 'package:tmail_ui_user/features/email/domain/state/download_all_attachments_for_web_state.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart'; class DownloadAllAttachmentsForWebInteractor { const DownloadAllAttachmentsForWebInteractor( - this._emailRepository, + this._downloadRepository, this._accountRepository, this._authenticationOIDCRepository, this.credentialRepository, ); - final EmailRepository _emailRepository; + final DownloadRepository _downloadRepository; final AccountRepository _accountRepository; final AuthenticationOIDCRepository _authenticationOIDCRepository; final CredentialRepository credentialRepository; @@ -37,16 +37,16 @@ class DownloadAllAttachmentsForWebInteractor { EmailId emailId, String baseDownloadAllUrl, Attachment attachment, - DownloadTaskId taskId, - StreamController> onReceiveController, - {CancelToken? cancelToken} - ) async* { + DownloadTaskId taskId, { + StreamController>? onReceiveController, + CancelToken? cancelToken, + }) async* { try { yield Right(StartDownloadAllAttachmentsForWeb( taskId, attachment, )); - onReceiveController.add(Right(StartDownloadAllAttachmentsForWeb( + onReceiveController?.add(Right(StartDownloadAllAttachmentsForWeb( taskId, attachment, cancelToken: cancelToken, @@ -64,14 +64,14 @@ class DownloadAllAttachmentsForWebInteractor { ); } - await _emailRepository.downloadAllAttachmentsForWeb( + await _downloadRepository.downloadAllAttachmentsForWeb( accountId, emailId, baseDownloadAllUrl, attachment.name!, accountRequest, taskId, - onReceiveController, + onReceiveController: onReceiveController, cancelToken: cancelToken ); diff --git a/lib/features/email/domain/usecases/download_attachment_for_web_interactor.dart b/lib/features/email/domain/usecases/download_attachment_for_web_interactor.dart index b15bc4580..f625e0874 100644 --- a/lib/features/email/domain/usecases/download_attachment_for_web_interactor.dart +++ b/lib/features/email/domain/usecases/download_attachment_for_web_interactor.dart @@ -11,36 +11,36 @@ import 'package:model/account/authentication_type.dart'; import 'package:model/account/password.dart'; import 'package:model/download/download_task_id.dart'; import 'package:model/email/attachment.dart'; -import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart'; class DownloadAttachmentForWebInteractor { - final EmailRepository emailRepository; + final DownloadRepository _downloadRepository; final CredentialRepository credentialRepository; final AccountRepository _accountRepository; final AuthenticationOIDCRepository _authenticationOIDCRepository; DownloadAttachmentForWebInteractor( - this.emailRepository, + this._downloadRepository, this.credentialRepository, this._accountRepository, this._authenticationOIDCRepository); Stream> execute( - DownloadTaskId taskId, - Attachment attachment, - AccountId accountId, - String baseDownloadUrl, - StreamController> onReceiveController, - {CancelToken? cancelToken, - bool previewerSupported = false, + DownloadTaskId taskId, + Attachment attachment, + AccountId accountId, + String baseDownloadUrl, { + StreamController>? onReceiveController, + CancelToken? cancelToken, + bool previewerSupported = false, }) async* { try { yield Right(StartDownloadAttachmentForWeb(taskId, attachment, cancelToken, previewerSupported)); - onReceiveController.add(Right(StartDownloadAttachmentForWeb(taskId, attachment, cancelToken, previewerSupported))); + onReceiveController?.add(Right(StartDownloadAttachmentForWeb(taskId, attachment, cancelToken, previewerSupported))); final currentAccount = await _accountRepository.getCurrentAccount(); AccountRequest? accountRequest; @@ -56,13 +56,13 @@ class DownloadAttachmentForWebInteractor { ); } - final bytesDownloaded = await emailRepository.downloadAttachmentForWeb( + final bytesDownloaded = await _downloadRepository.downloadAttachmentForWeb( taskId, attachment, accountId, baseDownloadUrl, accountRequest, - onReceiveController, + onReceiveController: onReceiveController, cancelToken: cancelToken ); diff --git a/lib/features/email/domain/usecases/download_attachments_interactor.dart b/lib/features/email/domain/usecases/download_attachments_interactor.dart deleted file mode 100644 index 70484be10..000000000 --- a/lib/features/email/domain/usecases/download_attachments_interactor.dart +++ /dev/null @@ -1,130 +0,0 @@ -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/jmap/account_id.dart'; -import 'package:jmap_dart_client/jmap/core/user_name.dart'; -import 'package:model/account/account_request.dart'; -import 'package:model/account/authentication_type.dart'; -import 'package:model/account/password.dart'; -import 'package:model/account/personal_account.dart'; -import 'package:model/email/attachment.dart'; -import 'package:model/oidc/token_oidc.dart'; -import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; -import 'package:tmail_ui_user/features/email/domain/state/download_attachments_state.dart'; -import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart'; -import 'package:tmail_ui_user/features/login/domain/exceptions/authentication_exception.dart'; -import 'package:tmail_ui_user/features/login/domain/extensions/oidc_configuration_extensions.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; - -class DownloadAttachmentsInteractor { - final EmailRepository emailRepository; - final CredentialRepository credentialRepository; - final AccountRepository _accountRepository; - final AuthenticationOIDCRepository _authenticationOIDCRepository; - final AuthorizationInterceptors _authorizationInterceptors; - - DownloadAttachmentsInteractor( - this.emailRepository, - this.credentialRepository, - this._accountRepository, - this._authenticationOIDCRepository, - this._authorizationInterceptors, - ); - - Stream> execute( - List attachments, - AccountId accountId, - String baseDownloadUrl - ) async* { - try { - final currentAccount = await _accountRepository.getCurrentAccount(); - - AccountRequest? accountRequest; - - if (currentAccount.authenticationType == AuthenticationType.oidc) { - final tokenOidc = await _authenticationOIDCRepository.getStoredTokenOIDC(currentAccount.id); - accountRequest = AccountRequest.withOidc(token: tokenOidc); - } else { - final authenticationInfoCache = await credentialRepository.getAuthenticationInfoStored(); - accountRequest = AccountRequest.withBasic( - userName: UserName(authenticationInfoCache.username), - password: Password(authenticationInfoCache.password), - ); - } - - final taskIds = await emailRepository.downloadAttachments( - attachments, - accountId, - baseDownloadUrl, - accountRequest - ); - - yield Right(DownloadAttachmentsSuccess(taskIds)); - } catch (exception) { - logError('DownloadAttachmentsInteractor::execute(): $exception'); - if (exception is DownloadAttachmentHasTokenExpiredException && - exception.refreshToken.isNotEmpty) { - yield* _retryDownloadAttachments( - accountId, - baseDownloadUrl, - attachments, - exception.refreshToken); - } else { - yield Left(DownloadAttachmentsFailure(exception)); - } - } - } - - Stream> _retryDownloadAttachments( - AccountId accountId, - String baseDownloadUrl, - List attachments, - String refreshToken) async* { - log('DownloadAttachmentsInteractor::_retryDownloadAttachments(): $refreshToken'); - try { - final accountCurrent = await _accountRepository.getCurrentAccount(); - final oidcConfig = await _authenticationOIDCRepository.getStoredOidcConfiguration(); - final newTokenOIDC = await _authenticationOIDCRepository.refreshingTokensOIDC( - oidcConfig.clientId, - oidcConfig.redirectUrl, - oidcConfig.discoveryUrl, - oidcConfig.scopes, - refreshToken); - - await _accountRepository.deleteCurrentAccount(accountCurrent.id); - - await _authenticationOIDCRepository.persistTokenOIDC(newTokenOIDC); - - await _accountRepository.setCurrentAccount( - PersonalAccount( - newTokenOIDC.tokenIdHash, - AuthenticationType.oidc, - isSelected: true, - accountId: accountId, - apiUrl: accountCurrent.apiUrl, - userName: accountCurrent.userName - ) - ); - - _authorizationInterceptors.setTokenAndAuthorityOidc( - newToken: newTokenOIDC, - newConfig: oidcConfig); - - final accountRequest = AccountRequest.withOidc(token: newTokenOIDC); - - final taskIds = await emailRepository.downloadAttachments( - attachments, - accountId, - baseDownloadUrl, - accountRequest); - - yield Right(DownloadAttachmentsSuccess(taskIds)); - } catch (e) { - logError('RefreshTokenOIDCInteractor::execute(): $e'); - yield Left(DownloadAttachmentsFailure(e)); - } - } -} \ No newline at end of file diff --git a/lib/features/email/domain/usecases/get_html_content_from_attachment_interactor.dart b/lib/features/email/domain/usecases/get_html_content_from_attachment_interactor.dart index 8f3efc3eb..3e4e5e262 100644 --- a/lib/features/email/domain/usecases/get_html_content_from_attachment_interactor.dart +++ b/lib/features/email/domain/usecases/get_html_content_from_attachment_interactor.dart @@ -9,14 +9,19 @@ import 'package:dartz/dartz.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:model/download/download_task_id.dart'; import 'package:model/email/attachment.dart'; +import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/get_html_content_from_attachment_state.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart'; class GetHtmlContentFromAttachmentInteractor { - GetHtmlContentFromAttachmentInteractor(this._downloadAttachmentForWebInteractor); + GetHtmlContentFromAttachmentInteractor( + this._downloadAttachmentForWebInteractor, + this._emailRepository, + ); final DownloadAttachmentForWebInteractor _downloadAttachmentForWebInteractor; + final EmailRepository _emailRepository; Stream> execute( AccountId accountId, @@ -25,7 +30,6 @@ class GetHtmlContentFromAttachmentInteractor { String baseDownloadUrl, TransformConfiguration transformConfiguration, ) async* { - final onReceiveController = StreamController>(); try { yield Right(GettingHtmlContentFromAttachment(attachment: attachment)); final downloadState = await _downloadAttachmentForWebInteractor.execute( @@ -33,7 +37,6 @@ class GetHtmlContentFromAttachmentInteractor { attachment, accountId, baseDownloadUrl, - onReceiveController, ).last; Either? sanitizeState; @@ -62,7 +65,6 @@ class GetHtmlContentFromAttachmentInteractor { }, ); - onReceiveController.close(); if (sanitizeState != null) { yield sanitizeState!; } else { @@ -74,7 +76,6 @@ class GetHtmlContentFromAttachmentInteractor { } catch (e) { logError('GetHtmlContentFromAttachmentInteractor:exception: $e'); - onReceiveController.close(); yield Left(GetHtmlContentFromAttachmentFailure( exception: e, attachment: attachment, @@ -88,12 +89,10 @@ class GetHtmlContentFromAttachmentInteractor { Attachment attachment, ) async { try { - final sanitizedHtmlContent = await _downloadAttachmentForWebInteractor - .emailRepository - .sanitizeHtmlContent( - htmlContent, - transformConfiguration, - ); + final sanitizedHtmlContent = await _emailRepository.sanitizeHtmlContent( + htmlContent, + transformConfiguration, + ); return Right(GetHtmlContentFromAttachmentSuccess( sanitizedHtmlContent: sanitizedHtmlContent, htmlAttachmentTitle: attachment.generateFileName(), diff --git a/lib/features/email/presentation/bindings/email_bindings.dart b/lib/features/email/presentation/bindings/email_bindings.dart index 1e4a434bd..3f663b602 100644 --- a/lib/features/email/presentation/bindings/email_bindings.dart +++ b/lib/features/email/presentation/bindings/email_bindings.dart @@ -1,9 +1,5 @@ -import 'package:core/core.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; -import 'package:tmail_ui_user/features/email/domain/usecases/download_all_attachments_for_web_interactor.dart'; -import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart'; -import 'package:tmail_ui_user/features/email/domain/usecases/download_attachments_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/export_all_attachments_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/export_attachment_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart'; @@ -29,18 +25,14 @@ class EmailBindings extends Bindings { Get.put(SingleEmailController( Get.find(), Get.find(), - Get.find(), - Get.find(), Get.find(), Get.find(), - Get.find(), Get.find(), Get.find(), Get.find(), Get.find(), Get.find(), Get.find(), - Get.find(), Get.find(), currentEmailId: currentEmailId, ), tag: tag); diff --git a/lib/features/email/presentation/bindings/email_interactor_bindings.dart b/lib/features/email/presentation/bindings/email_interactor_bindings.dart index 5a1674e99..a084986b9 100644 --- a/lib/features/email/presentation/bindings/email_interactor_bindings.dart +++ b/lib/features/email/presentation/bindings/email_interactor_bindings.dart @@ -16,9 +16,7 @@ import 'package:tmail_ui_user/features/email/data/local/html_analyzer.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; import 'package:tmail_ui_user/features/email/data/repository/email_repository_impl.dart'; import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; -import 'package:tmail_ui_user/features/email/domain/usecases/download_all_attachments_for_web_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart'; -import 'package:tmail_ui_user/features/email/domain/usecases/download_attachments_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/export_all_attachments_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/export_attachment_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart'; @@ -32,7 +30,6 @@ import 'package:tmail_ui_user/features/email/domain/usecases/parse_email_by_blob import 'package:tmail_ui_user/features/email/domain/usecases/preview_email_from_eml_file_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/print_email_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/store_opened_email_interactor.dart'; -import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; @@ -118,13 +115,6 @@ class EmailInteractorBindings extends InteractorsBindings { void bindingsInteractor() { Get.lazyPut(() => GetEmailContentInteractor(Get.find())); Get.lazyPut(() => MarkAsEmailReadInteractor(Get.find())); - Get.lazyPut(() => DownloadAttachmentsInteractor( - Get.find(), - Get.find(), - Get.find(), - Get.find(), - Get.find(), - )); Get.lazyPut(() => ExportAttachmentInteractor( Get.find(), Get.find(), @@ -133,23 +123,16 @@ class EmailInteractorBindings extends InteractorsBindings { )); Get.lazyPut(() => MoveToMailboxInteractor(Get.find())); Get.lazyPut(() => MarkAsStarEmailInteractor(Get.find())); - Get.lazyPut(() => DownloadAttachmentForWebInteractor( - Get.find(), - Get.find(), - Get.find(), - Get.find())); Get.lazyPut(() => GetStoredEmailStateInteractor(Get.find())); Get.lazyPut(() => StoreOpenedEmailInteractor(Get.find())); Get.lazyPut(() => PrintEmailInteractor(Get.find())); Get.lazyPut(() => ParseEmailByBlobIdInteractor(Get.find())); Get.lazyPut(() => PreviewEmailFromEmlFileInteractor(Get.find())); IdentityInteractorsBindings().dependencies(); - Get.lazyPut(() => GetHtmlContentFromAttachmentInteractor(Get.find())); - Get.lazyPut(() => DownloadAllAttachmentsForWebInteractor( + Get.lazyPut(() => GetHtmlContentFromAttachmentInteractor( + Get.find(), Get.find(), - Get.find(), - Get.find(), - Get.find())); + )); Get.lazyPut(() => ExportAllAttachmentsInteractor( Get.find(), Get.find(), diff --git a/lib/features/email/presentation/controller/single_email_controller.dart b/lib/features/email/presentation/controller/single_email_controller.dart index 12569227e..c791d076e 100644 --- a/lib/features/email/presentation/controller/single_email_controller.dart +++ b/lib/features/email/presentation/controller/single_email_controller.dart @@ -1,13 +1,12 @@ import 'dart:async'; import 'dart:io'; -import 'package:open_file/open_file.dart' as open_file; import 'package:core/core.dart'; -import 'package:core/presentation/utils/html_transformer/text/sanitize_autolink_unescape_html_transformer.dart'; +import 'package:core/presentation/utils/html_transformer/dom/sanitize_hyper_link_tag_in_html_transformers.dart'; import 'package:core/presentation/utils/html_transformer/text/new_line_transformer.dart'; +import 'package:core/presentation/utils/html_transformer/text/sanitize_autolink_unescape_html_transformer.dart'; import 'package:core/presentation/utils/html_transformer/text/standardize_html_sanitizing_transformers.dart'; import 'package:core/utils/html/html_utils.dart'; -import 'package:core/presentation/utils/html_transformer/dom/sanitize_hyper_link_tag_in_html_transformers.dart'; import 'package:dartz/dartz.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; @@ -15,8 +14,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_file_dialog/flutter_file_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:get/get_navigation/src/dialog/dialog_route.dart'; -import 'package:http_parser/http_parser.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/id.dart'; @@ -31,10 +28,9 @@ import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart'; import 'package:jmap_dart_client/jmap/mdn/disposition.dart'; import 'package:jmap_dart_client/jmap/mdn/mdn.dart'; -import 'package:model/email/eml_attachment.dart'; import 'package:model/error_type_handler/unknown_uri_exception.dart'; import 'package:model/model.dart'; -import 'package:permission_handler/permission_handler.dart'; +import 'package:open_file/open_file.dart' as open_file; import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; @@ -53,9 +49,7 @@ import 'package:tmail_ui_user/features/email/domain/state/calendar_event_counter import 'package:tmail_ui_user/features/email/domain/state/calendar_event_maybe_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/calendar_event_reject_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/calendar_event_reply_state.dart'; -import 'package:tmail_ui_user/features/email/domain/state/download_all_attachments_for_web_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; -import 'package:tmail_ui_user/features/email/domain/state/download_attachments_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/export_all_attachments_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/export_attachment_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart'; @@ -72,22 +66,19 @@ import 'package:tmail_ui_user/features/email/domain/state/send_receipt_to_sender import 'package:tmail_ui_user/features/email/domain/state/unsubscribe_email_state.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_accept_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_counter_accept_interactor.dart'; -import 'package:tmail_ui_user/features/email/domain/usecases/download_all_attachments_for_web_interactor.dart'; -import 'package:tmail_ui_user/features/email/domain/usecases/export_all_attachments_interactor.dart'; -import 'package:tmail_ui_user/features/email/domain/usecases/get_entire_message_as_document_interactor.dart'; -import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_star_email_interactor.dart'; -import 'package:tmail_ui_user/features/email/domain/usecases/maybe_calendar_event_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_reject_interactor.dart'; -import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart'; -import 'package:tmail_ui_user/features/email/domain/usecases/download_attachments_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/export_all_attachments_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/export_attachment_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/get_entire_message_as_document_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/get_html_content_from_attachment_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_star_email_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/maybe_calendar_event_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/parse_calendar_event_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/parse_email_by_blob_id_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/preview_email_from_eml_file_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/print_email_interactor.dart'; -import 'package:tmail_ui_user/features/email/domain/usecases/get_html_content_from_attachment_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/send_receipt_to_sender_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/store_opened_email_interactor.dart'; import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart'; @@ -112,11 +103,12 @@ import 'package:tmail_ui_user/features/email_previewer/email_previewer_dialog_vi import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart'; import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/action/mailbox_ui_action.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_download_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/update_current_emails_flags_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/dashboard_routes.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/download/download_task_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/create_new_rule_filter_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_identities_state.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/create_new_email_rule_filter_interactor.dart'; @@ -137,12 +129,6 @@ import 'package:tmail_ui_user/main/routes/navigation_router.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; import 'package:tmail_ui_user/main/routes/route_utils.dart'; import 'package:tmail_ui_user/main/utils/app_utils.dart'; -import 'package:twake_previewer_flutter/core/constants/supported_charset.dart'; -import 'package:twake_previewer_flutter/core/previewer_options/options/previewer_state.dart'; -import 'package:twake_previewer_flutter/core/previewer_options/options/top_bar_options.dart'; -import 'package:twake_previewer_flutter/core/previewer_options/previewer_options.dart'; -import 'package:twake_previewer_flutter/twake_image_previewer/twake_image_previewer.dart'; -import 'package:twake_previewer_flutter/twake_plain_text_previewer/twake_plain_text_previewer.dart'; class SingleEmailController extends BaseController with AppLoaderMixin { @@ -152,18 +138,14 @@ class SingleEmailController extends BaseController with AppLoaderMixin { final GetEmailContentInteractor _getEmailContentInteractor; final MarkAsEmailReadInteractor _markAsEmailReadInteractor; - final DownloadAttachmentsInteractor _downloadAttachmentsInteractor; - final DeviceManager _deviceManager; final ExportAttachmentInteractor _exportAttachmentInteractor; final MarkAsStarEmailInteractor _markAsStarEmailInteractor; - final DownloadAttachmentForWebInteractor _downloadAttachmentForWebInteractor; final GetAllIdentitiesInteractor _getAllIdentitiesInteractor; final StoreOpenedEmailInteractor _storeOpenedEmailInteractor; final PrintEmailInteractor _printEmailInteractor; final ParseEmailByBlobIdInteractor _parseEmailByBlobIdInteractor; final PreviewEmailFromEmlFileInteractor _previewEmailFromEmlFileInteractor; final GetHtmlContentFromAttachmentInteractor _getHtmlContentFromAttachmentInteractor; - final DownloadAllAttachmentsForWebInteractor _downloadAllAttachmentsForWebInteractor; final ExportAllAttachmentsInteractor _exportAllAttachmentsInteractor; final EmailId? _currentEmailId; @@ -197,10 +179,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin { final obxListeners = []; late final EmailActionReactor emailActionReactor; - final StreamController> _downloadProgressStateController = - StreamController>.broadcast(); - Stream> get downloadProgressState => _downloadProgressStateController.stream; - ThreadDetailController? get threadDetailController => _threadDetailController; PresentationEmail? get currentEmail { @@ -235,18 +213,14 @@ class SingleEmailController extends BaseController with AppLoaderMixin { SingleEmailController( this._getEmailContentInteractor, this._markAsEmailReadInteractor, - this._downloadAttachmentsInteractor, - this._deviceManager, this._exportAttachmentInteractor, this._markAsStarEmailInteractor, - this._downloadAttachmentForWebInteractor, this._getAllIdentitiesInteractor, this._storeOpenedEmailInteractor, this._printEmailInteractor, this._parseEmailByBlobIdInteractor, this._previewEmailFromEmlFileInteractor, this._getHtmlContentFromAttachmentInteractor, - this._downloadAllAttachmentsForWebInteractor, this._exportAllAttachmentsInteractor, { EmailId? currentEmailId, }) : _currentEmailId = currentEmailId; @@ -259,14 +233,12 @@ class SingleEmailController extends BaseController with AppLoaderMixin { _threadDetailController = getBinding(); _injectCalendarEventBindings(session, accountId); _registerObxStreamListener(); - _listenDownloadAttachmentProgressState(); emailActionReactor = EmailActionReactor( _markAsEmailReadInteractor, _markAsStarEmailInteractor, _createNewEmailRuleFilterInteractor, _printEmailInteractor, _getEmailContentInteractor, - _downloadAttachmentForWebInteractor, ); super.onInit(); } @@ -274,7 +246,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin { @override void onClose() { _threadDetailController = null; - _downloadProgressStateController.close(); CalendarEventInteractorBindings().dispose(); MdnInteractorBindings().dispose(); super.onClose(); @@ -282,7 +253,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin { @override void handleSuccessViewState(Success success) { - super.handleSuccessViewState(success); if (success is GetEmailContentSuccess) { _getEmailContentSuccess(success); } else if (success is GetEmailContentFromCacheSuccess) { @@ -295,14 +265,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin { _exportAllAttachmentsSuccessAction(success); } else if (success is MarkAsStarEmailSuccess) { _markAsEmailStarSuccess(success); - } else if (success is DownloadAttachmentForWebSuccess) { - _downloadAttachmentForWebSuccessAction(success); - } else if (success is StartDownloadAttachmentForWeb) { - _updateAttachmentsViewState(success.attachment.blobId, Right(success)); - } else if (success is DownloadingAttachmentForWeb) { - _updateAttachmentsViewState(success.attachment.blobId, Right(success)); - } else if (success is DownloadAllAttachmentsForWebSuccess) { - mailboxDashBoardController.deleteDownloadTask(success.taskId); } else if (success is GetAllIdentitiesSuccess) { _getAllIdentitiesSuccess(success); } else if (success is SendReceiptToSenderSuccess) { @@ -337,24 +299,19 @@ class SingleEmailController extends BaseController with AppLoaderMixin { )); } else if (success is GettingHtmlContentFromAttachment) { _updateAttachmentsViewState(success.attachment.blobId, Right(success)); + } else { + super.handleSuccessViewState(success); } } @override void handleFailureViewState(Failure failure) { - super.handleFailureViewState(failure); if (failure is MarkAsEmailReadFailure) { _handleMarkAsEmailReadFailure(failure); - } else if (failure is DownloadAttachmentsFailure) { - _downloadAttachmentsFailure(failure); } else if (failure is ExportAttachmentFailure) { _exportAttachmentFailureAction(failure); } else if (failure is ExportAllAttachmentsFailure) { _exportAllAttachmentsFailureAction(failure); - } else if (failure is DownloadAttachmentForWebFailure) { - _downloadAttachmentForWebFailureAction(failure); - } else if (failure is DownloadAllAttachmentsForWebFailure) { - _downloadAllAttachmentsForWebFailure(failure); } else if (failure is ParseCalendarEventFailure) { _handleParseCalendarEventFailure(failure); } else if (failure is GetEmailContentFailure) { @@ -371,6 +328,8 @@ class SingleEmailController extends BaseController with AppLoaderMixin { _handleGetHtmlContentFromAttachmentFailure(failure); } else if (failure is PreviewPDFFileFailure) { _handlePreviewPDFFileFailure(failure); + } else { + super.handleFailureViewState(failure); } } @@ -463,6 +422,15 @@ class SingleEmailController extends BaseController with AppLoaderMixin { } }); })); + + obxListeners.add(ever( + mailboxDashBoardController.downloadController.downloadUIAction, + (action) { + if (action is UpdateAttachmentsViewStateAction) { + _updateAttachmentsViewState(action.blobId, Right(action.success)); + } + }, + )); } void _handleOpenEmailDetailedView() { @@ -494,78 +462,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin { _getEmailContentAction(emailId); } - void _listenDownloadAttachmentProgressState() { - downloadProgressState.listen((state) { - log('SingleEmailController::_listenDownloadAttachmentProgressState(): $state'); - state.fold( - (failure) => null, - (success) { - if (success is StartDownloadAttachmentForWeb && !success.previewerSupported) { - mailboxDashBoardController.addDownloadTask( - DownloadTaskState( - taskId: success.taskId, - attachment: success.attachment, - onCancel: () => success.cancelToken?.cancel(), - ), - ); - - if (currentOverlayContext != null && currentContext != null && !success.previewerSupported) { - appToast.showToastMessage( - currentOverlayContext!, - AppLocalizations.of(currentContext!).your_download_has_started, - leadingSVGIconColor: AppColor.primaryColor, - leadingSVGIcon: imagePaths.icDownload); - } - } else if (success is DownloadingAttachmentForWeb) { - final percent = success.progress.round(); - log('SingleEmailController::DownloadingAttachmentForWeb(): $percent%'); - - mailboxDashBoardController.updateDownloadTask( - success.taskId, - (currentTask) { - final newTask = currentTask.copyWith( - progress: success.progress, - downloaded: success.downloaded, - total: success.total); - - return newTask; - }); - } else if (success is StartDownloadAllAttachmentsForWeb) { - mailboxDashBoardController.addDownloadTask( - DownloadTaskState( - taskId: success.taskId, - attachment: success.attachment, - onCancel: () => success.cancelToken?.cancel(), - ), - ); - - if (currentOverlayContext != null && currentContext != null) { - appToast.showToastSuccessMessage( - currentOverlayContext!, - AppLocalizations.of(currentContext!).creatingAnArchiveForDownloading, - leadingSVGIconColor: Colors.white, - leadingSVGIcon: imagePaths.icDownloadAll, - ); - } - } else if (success is DownloadingAllAttachmentsForWeb) { - final percent = success.progress.round(); - log('SingleEmailController::DownloadingAttachmentForWeb(): $percent%'); - - mailboxDashBoardController.updateDownloadTask( - success.taskId, - (currentTask) { - final newTask = currentTask.copyWith( - progress: success.progress, - downloaded: success.downloaded, - total: success.total); - - return newTask; - }); - } - }); - }); - } - void _injectAndGetInteractorBindings(Session? session, AccountId accountId) { injectRuleFilterBindings(session, accountId); injectMdnBindings(session, accountId); @@ -850,71 +746,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin { _threadDetailController?.markCollapsedEmailReadSuccess(success); } - void downloadAttachments(BuildContext context, List attachments) async { - final needRequestPermission = await _deviceManager.isNeedRequestStoragePermissionOnAndroid(); - - if (needRequestPermission) { - final status = await Permission.storage.status; - switch (status) { - case PermissionStatus.granted: - _downloadAttachmentsAction(attachments); - break; - case PermissionStatus.permanentlyDenied: - if (context.mounted && currentOverlayContext != null && currentContext != null) { - appToast.showToastMessage( - currentOverlayContext!, - AppLocalizations.of(context).you_need_to_grant_files_permission_to_download_attachments, - ); - } - break; - default: { - final requested = await Permission.storage.request(); - switch (requested) { - case PermissionStatus.granted: - _downloadAttachmentsAction(attachments); - break; - default: - if (context.mounted && currentOverlayContext != null && currentContext != null) { - appToast.showToastMessage( - currentOverlayContext!, - AppLocalizations.of(context).you_need_to_grant_files_permission_to_download_attachments, - ); - } - break; - } - } - } - } else { - _downloadAttachmentsAction(attachments); - } - } - - void _downloadAttachmentsAction(List attachments) { - if (accountId != null && session != null) { - try { - final baseDownloadUrl = session!.getDownloadUrl( - jmapUrl: dynamicUrlInterceptors.jmapUrl, - ); - consumeState(_downloadAttachmentsInteractor.execute( - attachments, - accountId!, - baseDownloadUrl, - )); - } catch (e) { - logError('SingleEmailController::_downloadAttachmentsAction(): $e'); - consumeState(Stream.value(Left(DownloadAttachmentsFailure(e)))); - } - } - } - - void _downloadAttachmentsFailure(DownloadAttachmentsFailure failure) { - if (currentOverlayContext != null && currentContext != null) { - appToast.showToastErrorMessage( - currentOverlayContext!, - AppLocalizations.of(currentContext!).attachment_download_failed); - } - } - void exportAttachment(Attachment attachment) { final cancelToken = CancelToken(); _showDownloadingFileDialog(attachment.name ?? '', cancelToken: cancelToken); @@ -1082,76 +913,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin { await FlutterFileDialog.saveFile(params: params); } - void downloadAttachmentForWeb(Attachment attachment, {bool previewerSupported = false}) { - if (accountId != null && session != null) { - final generateTaskId = DownloadTaskId(uuid.v4()); - try { - final baseDownloadUrl = session!.getDownloadUrl( - jmapUrl: dynamicUrlInterceptors.jmapUrl, - ); - final cancelToken = CancelToken(); - consumeState(_downloadAttachmentForWebInteractor.execute( - generateTaskId, - attachment, - accountId!, - baseDownloadUrl, - _downloadProgressStateController, - cancelToken: cancelToken, - previewerSupported: previewerSupported, - )); - } catch (e) { - logError('SingleEmailController::downloadAttachmentForWeb(): $e'); - consumeState(Stream.value(Left(DownloadAttachmentForWebFailure(attachment: attachment, taskId: generateTaskId, exception: e)))); - } - } else { - consumeState(Stream.value( - Left(DownloadAttachmentForWebFailure( - attachment: attachment, - exception: NotFoundSessionException())) - )); - } - } - - void downloadAllAttachmentsForWeb(String outputFileName) { - final taskId = DownloadTaskId(uuid.v4()); - final session = this.session; - final accountId = this.accountId; - - if (accountId == null || session == null) { - consumeState(Stream.value(Left(DownloadAllAttachmentsForWebFailure( - exception: NotFoundSessionException(), - taskId: taskId, - )))); - return; - } - - final downloadAllSupported = session.isDownloadAllSupported(accountId); - final emailId = currentEmail?.id; - - if (!downloadAllSupported || emailId == null) { - consumeState(Stream.value(Left(DownloadAllAttachmentsForWebFailure( - taskId: taskId, - )))); - return; - } - - final baseDownloadAllUrl = session.getDownloadAllCapability(accountId)!.endpoint!; - final downloadAttachment = Attachment( - name: outputFileName, - type: MediaType('application', 'zip'), - ); - final cancelToken = CancelToken(); - consumeState(_downloadAllAttachmentsForWebInteractor.execute( - accountId, - emailId, - baseDownloadAllUrl, - downloadAttachment, - taskId, - _downloadProgressStateController, - cancelToken: cancelToken, - )); - } - bool isDownloadAllSupported() { return session?.isDownloadAllSupported(accountId) ?? false; } @@ -1160,74 +921,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin { return isDownloadAllSupported() && attachments.length > 1; } - void _downloadAllAttachmentsForWebFailure( - DownloadAllAttachmentsForWebFailure failure, - ) { - mailboxDashBoardController.deleteDownloadTask(failure.taskId); - if (currentOverlayContext == null || currentContext == null) return; - String message = AppLocalizations.of(currentContext!).attachment_download_failed; - if (failure.cancelToken?.isCancelled == true) { - message = AppLocalizations.of(currentContext!).downloadAttachmentHasBeenCancelled; - } - appToast.showToastErrorMessage(currentOverlayContext!, message); - } - - void _downloadAttachmentForWebSuccessAction(DownloadAttachmentForWebSuccess success) { - log('SingleEmailController::_downloadAttachmentForWebSuccessAction():'); - - _updateAttachmentsViewState(success.attachment.blobId, Right(success)); - - mailboxDashBoardController.deleteDownloadTask(success.taskId); - - if (!success.previewerSupported) { - _downloadManager.createAnchorElementDownloadFileWeb( - success.bytes, - success.attachment.generateFileName()); - return; - } - - if (success.attachment.isImage) { - _updateAttachmentsViewState(success.attachment.blobId, Right(success)); - Navigator.of(currentContext!).push(GetDialogRoute( - pageBuilder: (context, _, __) => PointerInterceptor(child: TwakeImagePreviewer( - bytes: success.bytes, - zoomable: true, - previewerOptions: const PreviewerOptions( - previewerState: PreviewerState.success, - ), - topBarOptions: TopBarOptions( - title: success.attachment.generateFileName(), - onClose: () => Navigator.maybePop(context), - onDownload: currentContext == null - ? null - : () => handleDownloadAttachmentAction(success.attachment), - ), - )), - barrierDismissible: false, - )); - } else if (success.attachment.isText || success.attachment.isJson) { - _updateAttachmentsViewState(success.attachment.blobId, Right(success)); - Navigator.of(currentContext!).push(GetDialogRoute( - pageBuilder: (context, _, __) => PointerInterceptor(child: TwakePlainTextPreviewer( - supportedCharset: SupportedCharset.utf8, - bytes: success.bytes, - previewerOptions: PreviewerOptions( - previewerState: PreviewerState.success, - width: currentContext == null ? 200 : currentContext!.width * 0.8, - ), - topBarOptions: TopBarOptions( - title: success.attachment.generateFileName(), - onClose: () => Navigator.maybePop(context), - onDownload: currentContext == null - ? null - : () => handleDownloadAttachmentAction(success.attachment), - ), - )), - barrierDismissible: false, - )); - } - } - void _downloadPDFFile(Uint8List bytes, String fileName) { _downloadManager.createAnchorElementDownloadFileWeb(bytes, fileName); } @@ -1236,28 +929,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin { await _printUtils.printPDFFile(bytes, fileName); } - void _downloadAttachmentForWebFailureAction(DownloadAttachmentForWebFailure failure) { - log('SingleEmailController::_downloadAttachmentForWebFailureAction(): $failure'); - if (failure.taskId != null) { - mailboxDashBoardController.deleteDownloadTask(failure.taskId!); - } - - if (failure.attachment != null) { - _updateAttachmentsViewState(failure.attachment?.blobId, Left(failure)); - } - - if (currentOverlayContext == null || currentContext == null) return; - - String message = AppLocalizations.of(currentContext!).attachment_download_failed; - if (failure.attachment is EMLAttachment) { - message = AppLocalizations.of(currentContext!).downloadMessageAsEMLFailed; - } else if (failure.cancelToken?.isCancelled == true) { - message = AppLocalizations.of(currentContext!).downloadAttachmentHasBeenCancelled; - } - - appToast.showToastErrorMessage(currentOverlayContext!, message); - } - void _updateAttachmentsViewState( Id? attachmentBlobId, Either viewState) { @@ -1444,7 +1115,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { _printEmail(presentationEmail); break; case EmailActionType.downloadMessageAsEML: - _downloadMessageAsEML(presentationEmail); + mailboxDashBoardController.downloadMessageAsEML(presentationEmail); break; case EmailActionType.editAsNewEmail: _editAsNewEmail(presentationEmail); @@ -1981,17 +1652,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin { toastManager.showMessageFailure(failure); } - void _downloadMessageAsEML(PresentationEmail presentationEmail) { - if (accountId == null || session == null) return; - - consumeState(emailActionReactor.downloadMessageAsEML( - session!, - accountId!, - presentationEmail, - downloadProgressStateController: _downloadProgressStateController, - )); - } - void _editAsNewEmail(PresentationEmail presentationEmail) { if (accountId == null || session == null) return; @@ -2030,7 +1690,10 @@ class SingleEmailController extends BaseController with AppLoaderMixin { {bool previewerSupported = false} ) { if (PlatformInfo.isWeb) { - downloadAttachmentForWeb(attachment, previewerSupported: previewerSupported); + mailboxDashBoardController.downloadAttachmentForWeb( + attachment: attachment, + previewerSupported: previewerSupported, + ); } else if (PlatformInfo.isMobile) { exportAttachment(attachment); } else { @@ -2040,7 +1703,10 @@ class SingleEmailController extends BaseController with AppLoaderMixin { void handleDownloadAllAttachmentsAction(String outputFileName) { if (PlatformInfo.isWeb) { - downloadAllAttachmentsForWeb(outputFileName); + mailboxDashBoardController.downloadAllAttachmentsForWeb( + outputFileName: outputFileName, + currentEmail: currentEmail, + ); } else if (PlatformInfo.isMobile) { exportAllAttachments(outputFileName); } else { diff --git a/lib/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart b/lib/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart index 9db5f0531..00628c316 100644 --- a/lib/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart +++ b/lib/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:core/data/network/config/dynamic_url_interceptors.dart'; import 'package:core/domain/extensions/datetime_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/resources/image_paths.dart'; @@ -11,10 +10,8 @@ import 'package:core/presentation/utils/html_transformer/transform_configuration import 'package:core/presentation/utils/responsive_utils.dart'; import 'package:core/presentation/utils/theme_utils.dart'; import 'package:core/presentation/views/bottom_popup/confirmation_dialog_action_sheet_builder.dart'; -import 'package:core/utils/app_logger.dart'; import 'package:core/utils/platform_info.dart'; import 'package:dartz/dartz.dart'; -import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; @@ -23,7 +20,6 @@ import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; -import 'package:model/download/download_task_id.dart'; import 'package:model/email/email_action_type.dart'; import 'package:model/email/mark_star_action.dart'; import 'package:model/email/presentation_email.dart'; @@ -32,21 +28,17 @@ import 'package:model/extensions/email_address_extension.dart'; import 'package:model/extensions/list_email_address_extension.dart'; import 'package:model/extensions/presentation_email_extension.dart'; import 'package:model/extensions/presentation_mailbox_extension.dart'; -import 'package:model/extensions/session_extension.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_manager.dart'; import 'package:tmail_ui_user/features/base/widget/context_menu/context_menu_item_action.dart'; import 'package:tmail_ui_user/features/base/widget/popup_menu/popup_menu_item_action_widget.dart'; import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart'; -import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart'; import 'package:tmail_ui_user/features/email/domain/model/email_print.dart'; import 'package:tmail_ui_user/features/email/domain/model/mark_read_action.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_action.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart'; -import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/print_email_state.dart'; -import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_star_email_interactor.dart'; @@ -70,7 +62,6 @@ import 'package:tmail_ui_user/main/routes/navigation_router.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; import 'package:tmail_ui_user/main/routes/route_utils.dart'; import 'package:tmail_ui_user/main/utils/app_utils.dart'; -import 'package:uuid/uuid.dart'; typedef OpenBottomSheetContextMenuAction = Future Function({ required BuildContext context, @@ -93,7 +84,6 @@ class EmailActionReactor { this._createNewEmailRuleFilterInteractor, this._printEmailInteractor, this._getEmailContentInteractor, - this._downloadAttachmentForWebInteractor, ); final MarkAsEmailReadInteractor _markAsEmailReadInteractor; @@ -101,7 +91,6 @@ class EmailActionReactor { final CreateNewEmailRuleFilterInteractor? _createNewEmailRuleFilterInteractor; final PrintEmailInteractor _printEmailInteractor; final GetEmailContentInteractor _getEmailContentInteractor; - final DownloadAttachmentForWebInteractor _downloadAttachmentForWebInteractor; static final _isEmailAddressDialogOpened = false.obs; static bool get isDialogOpened => _isEmailAddressDialogOpened.value; @@ -429,45 +418,6 @@ class EmailActionReactor { ) ); } - - Stream> downloadMessageAsEML( - Session session, - AccountId accountId, - PresentationEmail presentationEmail, { - required StreamController> downloadProgressStateController, - }) async* { - final emlAttachment = presentationEmail.createEMLAttachment(); - if (emlAttachment.blobId == null) { - yield* Stream.value(Left(DownloadAttachmentForWebFailure( - exception: NotFoundEmailBlobIdException(), - ))); - return; - } - - final generateTaskId = DownloadTaskId(const Uuid().v4()); - try { - final baseDownloadUrl = session.getDownloadUrl( - jmapUrl: getBinding()?.jmapUrl, - ); - final cancelToken = CancelToken(); - yield* _downloadAttachmentForWebInteractor.execute( - generateTaskId, - emlAttachment, - accountId, - baseDownloadUrl, - downloadProgressStateController, - cancelToken: cancelToken, - previewerSupported: false, - ); - } catch (e) { - logError('SingleEmailController::downloadAttachmentForWeb(): $e'); - yield* Stream.value(Left(DownloadAttachmentForWebFailure( - attachment: emlAttachment, - taskId: generateTaskId, - exception: e, - ))); - } - } void editAsNewEmail( PresentationEmail presentationEmail, { diff --git a/lib/features/email/presentation/widgets/pdf_viewer/pdf_viewer.dart b/lib/features/email/presentation/widgets/pdf_viewer/pdf_viewer.dart index 2145d0f6c..98a2e6acb 100644 --- a/lib/features/email/presentation/widgets/pdf_viewer/pdf_viewer.dart +++ b/lib/features/email/presentation/widgets/pdf_viewer/pdf_viewer.dart @@ -91,7 +91,7 @@ class _PDFViewerState extends State { widget.attachment, widget.accountId, widget.downloadUrl, - _downloadAttachmentStreamController, + onReceiveController: _downloadAttachmentStreamController, cancelToken: _downloadAttachmentCancelToken ).listen((viewState) { viewState.fold( diff --git a/lib/features/email_previewer/email_previewer_bindings.dart b/lib/features/email_previewer/email_previewer_bindings.dart index ba6e56c22..b7cee5274 100644 --- a/lib/features/email_previewer/email_previewer_bindings.dart +++ b/lib/features/email_previewer/email_previewer_bindings.dart @@ -30,12 +30,10 @@ import 'package:tmail_ui_user/features/email/domain/usecases/parse_email_by_blob import 'package:tmail_ui_user/features/email/domain/usecases/preview_email_from_eml_file_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/remove_preview_email_eml_content_shared_interactor.dart'; import 'package:tmail_ui_user/features/email_previewer/email_previewer_controller.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; -import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource_impl/state_datasource_impl.dart'; import 'package:tmail_ui_user/features/mailbox/data/local/state_cache_manager.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/bindings/download_interactor_bindings.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/new_email_cache_manager.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/new_email_cache_worker_queue.dart'; import 'package:tmail_ui_user/features/offline_mode/manager/opened_email_cache_manager.dart'; @@ -119,11 +117,8 @@ class EmailPreviewerBindings extends BaseBindings { Get.find())); Get.lazyPut(() => PreviewEmailFromEmlFileInteractor( Get.find())); - Get.lazyPut(() => DownloadAttachmentForWebInteractor( - Get.find(), - Get.find(), - Get.find(), - Get.find())); + + DownloadInteractorBindings().dependencies(); } @override diff --git a/lib/features/email_previewer/email_previewer_controller.dart b/lib/features/email_previewer/email_previewer_controller.dart index 371142912..30068fc35 100644 --- a/lib/features/email_previewer/email_previewer_controller.dart +++ b/lib/features/email_previewer/email_previewer_controller.dart @@ -320,7 +320,7 @@ class EmailPreviewerController extends ReloadableController { attachment, _accountId!, _session!.getDownloadUrl(jmapUrl: dynamicUrlInterceptors.jmapUrl), - _downloadAttachmentStreamController!) + onReceiveController: _downloadAttachmentStreamController!) .listen(_handleDownloadAttachmentViewState); } catch (e) { logError('EmailPreviewerController::_handleParseEmailByBlobIdSuccess(): $e'); diff --git a/lib/features/mailbox_dashboard/data/datasource/download_datasource.dart b/lib/features/mailbox_dashboard/data/datasource/download_datasource.dart new file mode 100644 index 000000000..8f13f70fb --- /dev/null +++ b/lib/features/mailbox_dashboard/data/datasource/download_datasource.dart @@ -0,0 +1,35 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:dio/dio.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:model/account/account_request.dart'; +import 'package:model/download/download_task_id.dart'; +import 'package:model/email/attachment.dart'; + +abstract class DownloadDatasource { + Future downloadAttachmentForWeb( + DownloadTaskId taskId, + Attachment attachment, + AccountId accountId, + String baseDownloadUrl, + AccountRequest accountRequest, { + StreamController>? onReceiveController, + CancelToken? cancelToken, + }); + + Future downloadAllAttachmentsForWeb( + AccountId accountId, + EmailId emailId, + String baseDownloadAllUrl, + String outputFileName, + AccountRequest accountRequest, + DownloadTaskId taskId, { + StreamController>? onReceiveController, + CancelToken? cancelToken, + }); +} diff --git a/lib/features/mailbox_dashboard/data/datasource_impl/download_datasource_impl.dart b/lib/features/mailbox_dashboard/data/datasource_impl/download_datasource_impl.dart new file mode 100644 index 000000000..1e0903488 --- /dev/null +++ b/lib/features/mailbox_dashboard/data/datasource_impl/download_datasource_impl.dart @@ -0,0 +1,75 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:dio/dio.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:model/account/account_request.dart'; +import 'package:model/download/download_task_id.dart'; +import 'package:model/email/attachment.dart'; +import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/download_datasource.dart'; +import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart'; + +class DownloadDatasourceImpl extends DownloadDatasource { + final EmailAPI _emailAPI; + final ExceptionThrower _exceptionThrower; + + DownloadDatasourceImpl(this._emailAPI, this._exceptionThrower); + + @override + Future downloadAttachmentForWeb( + DownloadTaskId taskId, + Attachment attachment, + AccountId accountId, + String baseDownloadUrl, + AccountRequest accountRequest, { + StreamController>? onReceiveController, + CancelToken? cancelToken, + }) { + return Future.sync(() async { + return await _emailAPI.downloadAttachmentForWeb( + taskId, + attachment, + accountId, + baseDownloadUrl, + accountRequest, + onReceiveController: onReceiveController, + cancelToken: cancelToken, + ); + }).catchError((error, stackTrace) async { + await _exceptionThrower.throwException(error, stackTrace); + throw error; + }); + } + + @override + Future downloadAllAttachmentsForWeb( + AccountId accountId, + EmailId emailId, + String baseDownloadAllUrl, + String outputFileName, + AccountRequest accountRequest, + DownloadTaskId taskId, + {StreamController>? onReceiveController, + CancelToken? cancelToken}) { + return Future.sync(() async { + return await _emailAPI.downloadAllAttachmentsForWeb( + accountId, + emailId, + baseDownloadAllUrl, + outputFileName, + accountRequest, + taskId, + onReceiveController: onReceiveController, + cancelToken: cancelToken, + ); + }).catchError((error, stackTrace) async { + await _exceptionThrower.throwException(error, stackTrace); + throw error; + }); + } +} diff --git a/lib/features/mailbox_dashboard/data/repository/download_repository_impl.dart b/lib/features/mailbox_dashboard/data/repository/download_repository_impl.dart new file mode 100644 index 000000000..f53b122ff --- /dev/null +++ b/lib/features/mailbox_dashboard/data/repository/download_repository_impl.dart @@ -0,0 +1,64 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:dio/dio.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:model/account/account_request.dart'; +import 'package:model/download/download_task_id.dart'; +import 'package:model/email/attachment.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/download_datasource.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart'; + +class DownloadRepositoryImpl extends DownloadRepository { + final DownloadDatasource _datasource; + + DownloadRepositoryImpl(this._datasource); + + @override + Future downloadAttachmentForWeb( + DownloadTaskId taskId, + Attachment attachment, + AccountId accountId, + String baseDownloadUrl, + AccountRequest accountRequest, { + StreamController>? onReceiveController, + CancelToken? cancelToken, + }) { + return _datasource.downloadAttachmentForWeb( + taskId, + attachment, + accountId, + baseDownloadUrl, + accountRequest, + onReceiveController: onReceiveController, + cancelToken: cancelToken, + ); + } + + @override + Future downloadAllAttachmentsForWeb( + AccountId accountId, + EmailId emailId, + String baseDownloadAllUrl, + String outputFileName, + AccountRequest accountRequest, + DownloadTaskId taskId, { + StreamController>? onReceiveController, + CancelToken? cancelToken, + }) { + return _datasource.downloadAllAttachmentsForWeb( + accountId, + emailId, + baseDownloadAllUrl, + outputFileName, + accountRequest, + taskId, + onReceiveController: onReceiveController, + cancelToken: cancelToken, + ); + } +} diff --git a/lib/features/mailbox_dashboard/domain/repository/download_repository.dart b/lib/features/mailbox_dashboard/domain/repository/download_repository.dart new file mode 100644 index 000000000..26d4a1854 --- /dev/null +++ b/lib/features/mailbox_dashboard/domain/repository/download_repository.dart @@ -0,0 +1,35 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; +import 'package:dartz/dartz.dart'; +import 'package:dio/dio.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; +import 'package:model/account/account_request.dart'; +import 'package:model/download/download_task_id.dart'; +import 'package:model/email/attachment.dart'; + +abstract class DownloadRepository { + Future downloadAttachmentForWeb( + DownloadTaskId taskId, + Attachment attachment, + AccountId accountId, + String baseDownloadUrl, + AccountRequest accountRequest, { + StreamController>? onReceiveController, + CancelToken? cancelToken, + }); + + Future downloadAllAttachmentsForWeb( + AccountId accountId, + EmailId emailId, + String baseDownloadAllUrl, + String outputFileName, + AccountRequest accountRequest, + DownloadTaskId taskId, { + StreamController>? onReceiveController, + CancelToken? cancelToken, + }); +} diff --git a/lib/features/mailbox_dashboard/presentation/action/download_ui_action.dart b/lib/features/mailbox_dashboard/presentation/action/download_ui_action.dart new file mode 100644 index 000000000..627831359 --- /dev/null +++ b/lib/features/mailbox_dashboard/presentation/action/download_ui_action.dart @@ -0,0 +1,31 @@ +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:model/email/attachment.dart'; +import 'package:tmail_ui_user/features/base/action/ui_action.dart'; + +class DownloadUIAction extends UIAction { + static final idle = DownloadUIAction(); + + DownloadUIAction() : super(); + + @override + List get props => []; +} + +class UpdateAttachmentsViewStateAction extends DownloadUIAction { + UpdateAttachmentsViewStateAction(this.blobId, this.success); + + final Id? blobId; + final dynamic success; + + @override + List get props => [blobId, success]; +} + +class DownloadAttachmentsQuicklyAction extends DownloadUIAction { + DownloadAttachmentsQuicklyAction(this.attachment); + + final Attachment attachment; + + @override + List get props => [attachment]; +} diff --git a/lib/features/mailbox_dashboard/presentation/bindings/download_interactor_bindings.dart b/lib/features/mailbox_dashboard/presentation/bindings/download_interactor_bindings.dart new file mode 100644 index 000000000..8390f7308 --- /dev/null +++ b/lib/features/mailbox_dashboard/presentation/bindings/download_interactor_bindings.dart @@ -0,0 +1,60 @@ +import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/base/interactors_bindings.dart'; +import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/download_all_attachments_for_web_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart'; +import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; +import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; +import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/download_datasource.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource_impl/download_datasource_impl.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/data/repository/download_repository_impl.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart'; +import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart'; + +class DownloadInteractorBindings extends InteractorsBindings { + @override + void bindingsDataSourceImpl() { + Get.lazyPut( + () => DownloadDatasourceImpl( + Get.find(), + Get.find(), + ), + ); + } + + @override + void bindingsInteractor() { + Get.lazyPut( + () => DownloadAttachmentForWebInteractor( + Get.find(), + Get.find(), + Get.find(), + Get.find(), + ), + ); + Get.lazyPut( + () => DownloadAllAttachmentsForWebInteractor( + Get.find(), + Get.find(), + Get.find(), + Get.find(), + ), + ); + } + + @override + void bindingsRepository() { + Get.lazyPut(() => Get.find()); + } + + @override + void bindingsRepositoryImpl() { + Get.lazyPut(() => DownloadRepositoryImpl(Get.find())); + } + + @override + void bindingsDataSource() { + Get.lazyPut(() => Get.find()); + } +} diff --git a/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart b/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart index 3a6f8c8d2..6b56b5843 100644 --- a/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart +++ b/lib/features/mailbox_dashboard/presentation/bindings/mailbox_dashboard_bindings.dart @@ -1,4 +1,5 @@ import 'package:core/data/model/source_type/data_source_type.dart'; +import 'package:core/data/network/download/download_manager.dart'; import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/utils/html_transformer/html_transform.dart'; import 'package:core/utils/config/app_config_loader.dart'; @@ -31,6 +32,8 @@ import 'package:tmail_ui_user/features/email/data/repository/email_repository_im import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/delete_email_permanently_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/delete_multiple_emails_permanently_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/download_all_attachments_for_web_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_restored_deleted_message_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_star_email_interactor.dart'; @@ -93,6 +96,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_re import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_email_sort_order_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_last_time_dismissed_spam_reported_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_spam_report_state_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/bindings/download_interactor_bindings.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/download/download_controller.dart'; @@ -167,7 +171,11 @@ class MailboxDashBoardBindings extends BaseBindings { Get.find(), Get.find(), )); - Get.put(DownloadController()); + Get.put(DownloadController( + Get.find(), + Get.find(), + Get.find(), + )); Get.put(SearchController( Get.find(), Get.find(), @@ -378,6 +386,8 @@ class MailboxDashBoardBindings extends BaseBindings { Get.find() )); PaywallBindings().dependencies(); + + DownloadInteractorBindings().dependencies(); } @override diff --git a/lib/features/mailbox_dashboard/presentation/controller/download/download_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/download/download_controller.dart index 76de53d0f..f7d78aaea 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/download/download_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/download/download_controller.dart @@ -1,15 +1,168 @@ +import 'dart:async'; + +import 'package:core/data/network/config/dynamic_url_interceptors.dart'; +import 'package:core/data/network/download/download_manager.dart'; +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/state/failure.dart'; +import 'package:core/presentation/state/success.dart'; import 'package:core/utils/app_logger.dart'; +import 'package:core/utils/platform_info.dart'; +import 'package:dartz/dartz.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:get/get_navigation/src/dialog/dialog_route.dart'; +import 'package:http_parser/http_parser.dart'; +import 'package:jmap_dart_client/jmap/account_id.dart'; +import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:model/download/download_task_id.dart'; +import 'package:model/email/attachment.dart'; +import 'package:model/email/eml_attachment.dart'; +import 'package:model/email/presentation_email.dart'; +import 'package:model/extensions/presentation_email_extension.dart'; +import 'package:model/extensions/session_extension.dart'; +import 'package:pointer_interceptor/pointer_interceptor.dart'; +import 'package:tmail_ui_user/features/base/base_controller.dart'; +import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart'; +import 'package:tmail_ui_user/features/email/domain/state/download_all_attachments_for_web_state.dart'; +import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/download_all_attachments_for_web_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart'; +import 'package:tmail_ui_user/features/email/presentation/extensions/attachment_extension.dart'; +import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart'; +import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/download/download_task_state.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; +import 'package:tmail_ui_user/main/routes/route_navigation.dart'; +import 'package:twake_previewer_flutter/core/constants/supported_charset.dart'; +import 'package:twake_previewer_flutter/core/previewer_options/options/previewer_state.dart'; +import 'package:twake_previewer_flutter/core/previewer_options/options/top_bar_options.dart'; +import 'package:twake_previewer_flutter/core/previewer_options/previewer_options.dart'; +import 'package:twake_previewer_flutter/twake_image_previewer/twake_image_previewer.dart'; +import 'package:twake_previewer_flutter/twake_plain_text_previewer/twake_plain_text_previewer.dart'; +import 'package:uuid/uuid.dart'; typedef UpdateDownloadTaskStateCallback = DownloadTaskState Function(DownloadTaskState currentState); -class DownloadController extends GetxController { +class DownloadController extends BaseController { + final DownloadAttachmentForWebInteractor _downloadAttachmentForWebInteractor; + final DownloadAllAttachmentsForWebInteractor + _downloadAllAttachmentsForWebInteractor; + final DownloadManager _downloadManager; + + DownloadController( + this._downloadAttachmentForWebInteractor, + this._downloadAllAttachmentsForWebInteractor, + this._downloadManager, + ); final listDownloadTaskState = RxList(); final hideDownloadTaskbar = RxBool(false); + final downloadUIAction = Rxn(); + + final _downloadProgressStateController = + StreamController>.broadcast(); + StreamSubscription>? + _downloadProgressStateSubscription; + + @override + void onInit() { + super.onInit(); + _registerDownloadProgressState(); + } + + void _registerDownloadProgressState() { + _downloadProgressStateSubscription = _downloadProgressStateController.stream + .listen(_onDownloadProgressStateChanged); + } + + void _onDownloadProgressStateChanged(Either state) { + state.fold((_) => null, (success) { + if (success is StartDownloadAttachmentForWeb) { + _handleStartSingleDownload(success); + } else if (success is DownloadingAttachmentForWeb) { + _updateDownloadProgress( + success.taskId, + success.progress, + success.downloaded, + success.total, + ); + } else if (success is StartDownloadAllAttachmentsForWeb) { + _handleStartAllDownload(success); + } else if (success is DownloadingAllAttachmentsForWeb) { + _updateDownloadProgress( + success.taskId, + success.progress, + success.downloaded, + success.total, + ); + } + }); + } + + void _handleStartSingleDownload(StartDownloadAttachmentForWeb success) { + if (success.previewerSupported) return; + + addDownloadTask( + DownloadTaskState( + taskId: success.taskId, + attachment: success.attachment, + onCancel: success.cancelToken?.cancel, + ), + ); + + if (_hasValidContext) { + appToast.showToastMessage( + currentOverlayContext!, + AppLocalizations.of(currentContext!).your_download_has_started, + leadingSVGIconColor: AppColor.primaryColor, + leadingSVGIcon: imagePaths.icDownload, + ); + } + } + + void _handleStartAllDownload(StartDownloadAllAttachmentsForWeb success) { + addDownloadTask( + DownloadTaskState( + taskId: success.taskId, + attachment: success.attachment, + onCancel: () => success.cancelToken?.cancel(), + ), + ); + + if (_hasValidContext) { + appToast.showToastSuccessMessage( + currentOverlayContext!, + AppLocalizations.of(currentContext!).creatingAnArchiveForDownloading, + leadingSVGIconColor: Colors.white, + leadingSVGIcon: imagePaths.icDownloadAll, + ); + } + } + + void _updateDownloadProgress( + DownloadTaskId taskId, + double progress, + int downloaded, + int total, + ) { + final percent = progress.round(); + log('$runtimeType::_updateDownloadProgress(): $percent%'); + + updateDownloadTaskByTaskId(taskId, (currentTask) { + return currentTask.copyWith( + progress: progress, + downloaded: downloaded, + total: total, + ); + }); + } + + bool get _hasValidContext => + currentOverlayContext != null && currentContext != null; bool get notEmptyListDownloadTask => listDownloadTaskState.isNotEmpty; @@ -43,4 +196,312 @@ class DownloadController extends GetxController { hideDownloadTaskbar.value = true; } } + + void downloadAttachmentForWeb({ + required Attachment attachment, + required AccountId? accountId, + required Session? session, + bool previewerSupported = false, + }) { + if (accountId == null || session == null) { + consumeState(Stream.value( + Left(DownloadAttachmentForWebFailure( + attachment: attachment, + exception: NotFoundSessionException(), + )), + )); + return; + } + + final generateTaskId = DownloadTaskId(uuid.v4()); + try { + final baseDownloadUrl = session.getDownloadUrl( + jmapUrl: dynamicUrlInterceptors.jmapUrl, + ); + final cancelToken = CancelToken(); + consumeState(_downloadAttachmentForWebInteractor.execute( + generateTaskId, + attachment, + accountId, + baseDownloadUrl, + onReceiveController: _downloadProgressStateController, + cancelToken: cancelToken, + previewerSupported: previewerSupported, + )); + } catch (e) { + consumeState(Stream.value( + Left(DownloadAttachmentForWebFailure( + attachment: attachment, + taskId: generateTaskId, + exception: e, + )), + )); + } + } + + void downloadAllAttachmentsForWeb({ + required String outputFileName, + required PresentationEmail? currentEmail, + required AccountId? accountId, + required Session? session, + bool previewerSupported = false, + }) { + final taskId = DownloadTaskId(uuid.v4()); + + if (accountId == null || session == null) { + consumeState(Stream.value( + Left(DownloadAllAttachmentsForWebFailure( + exception: NotFoundSessionException(), + taskId: taskId, + )), + )); + return; + } + + final downloadAllSupported = session.isDownloadAllSupported(accountId); + final emailId = currentEmail?.id; + + if (!downloadAllSupported || emailId == null) { + consumeState(Stream.value( + Left(DownloadAllAttachmentsForWebFailure(taskId: taskId)), + )); + return; + } + + final baseDownloadAllUrl = session.getDownloadAllCapability(accountId)!.endpoint!; + final downloadAttachment = Attachment( + name: outputFileName, + type: MediaType('application', 'zip'), + ); + final cancelToken = CancelToken(); + consumeState(_downloadAllAttachmentsForWebInteractor.execute( + accountId, + emailId, + baseDownloadAllUrl, + downloadAttachment, + taskId, + onReceiveController: _downloadProgressStateController, + cancelToken: cancelToken, + )); + } + + void downloadMessageAsEML({ + required PresentationEmail presentationEmail, + required AccountId? accountId, + required Session? session, + }) { + if (accountId == null || session == null) return; + + final emlAttachment = presentationEmail.createEMLAttachment(); + if (emlAttachment.blobId == null) { + consumeState(Stream.value( + Left(DownloadAttachmentForWebFailure( + exception: NotFoundEmailBlobIdException(), + )), + )); + return; + } + + final generateTaskId = DownloadTaskId(const Uuid().v4()); + try { + final baseDownloadUrl = session.getDownloadUrl( + jmapUrl: getBinding()?.jmapUrl, + ); + final cancelToken = CancelToken(); + consumeState(_downloadAttachmentForWebInteractor.execute( + generateTaskId, + emlAttachment, + accountId, + baseDownloadUrl, + onReceiveController: _downloadProgressStateController, + cancelToken: cancelToken, + previewerSupported: false, + )); + } catch (e) { + consumeState(Stream.value(Left(DownloadAttachmentForWebFailure( + attachment: emlAttachment, + taskId: generateTaskId, + exception: e, + )))); + } + } + + void _pushDownloadUIAction(DownloadUIAction action) { + downloadUIAction.value = action; + } + + void _handleDownloadAttachmentForWebSuccess( + DownloadAttachmentForWebSuccess success, + ) { + _pushDownloadUIAction(UpdateAttachmentsViewStateAction( + success.attachment.blobId, + success, + )); + + deleteDownloadTask(success.taskId); + + if (!success.previewerSupported) { + _downloadManager.createAnchorElementDownloadFileWeb( + success.bytes, + success.attachment.generateFileName(), + ); + return; + } + + if (success.attachment.isImage) { + _previewImageFile(attachment: success.attachment, bytes: success.bytes); + } else if (success.attachment.isText || success.attachment.isJson) { + _previewTextPlainFile( + attachment: success.attachment, + bytes: success.bytes, + ); + } + } + + void _downloadAttachmentQuickly(Attachment attachment) { + if (PlatformInfo.isWeb) { + _pushDownloadUIAction(DownloadAttachmentsQuicklyAction(attachment)); + } + } + + void _previewImageFile({ + required Uint8List bytes, + required Attachment attachment, + }) { + if (currentContext == null) return; + + Navigator.of(currentContext!).push(GetDialogRoute( + pageBuilder: (context, _, __) => PointerInterceptor( + child: TwakeImagePreviewer( + bytes: bytes, + zoomable: true, + previewerOptions: const PreviewerOptions( + previewerState: PreviewerState.success, + ), + topBarOptions: TopBarOptions( + title: attachment.generateFileName(), + onClose: () => Navigator.maybePop(context), + onDownload: currentContext == null + ? null + : () => _downloadAttachmentQuickly(attachment), + ), + ), + ), + barrierDismissible: false, + )); + } + + void _previewTextPlainFile({ + required Uint8List bytes, + required Attachment attachment, + }) { + if (currentContext == null) return; + + Navigator.of(currentContext!).push(GetDialogRoute( + pageBuilder: (context, _, __) => PointerInterceptor( + child: TwakePlainTextPreviewer( + supportedCharset: SupportedCharset.utf8, + bytes: bytes, + previewerOptions: PreviewerOptions( + previewerState: PreviewerState.success, + width: currentContext == null ? 200 : currentContext!.width * 0.8, + ), + topBarOptions: TopBarOptions( + title: attachment.generateFileName(), + onClose: () => Navigator.maybePop(context), + onDownload: currentContext == null + ? null + : () => _downloadAttachmentQuickly(attachment), + ), + ), + ), + barrierDismissible: false, + )); + } + + void clearDownloadUIAction() { + downloadUIAction.value = null; + } + + void _downloadAllAttachmentsForWebFailure( + DownloadAllAttachmentsForWebFailure failure, + ) { + deleteDownloadTask(failure.taskId); + + if (currentOverlayContext == null || currentContext == null) return; + + final appLocalizations = AppLocalizations.of(currentContext!); + + String message = failure.cancelToken?.isCancelled == true + ? appLocalizations.downloadAttachmentHasBeenCancelled + : appLocalizations.attachment_download_failed; + + appToast.showToastErrorMessage(currentOverlayContext!, message); + } + + void _downloadAttachmentForWebFailureAction(DownloadAttachmentForWebFailure failure) { + if (failure.taskId != null) { + deleteDownloadTask(failure.taskId!); + } + + if (failure.attachment != null) { + _pushDownloadUIAction( + UpdateAttachmentsViewStateAction(failure.attachment?.blobId, failure), + ); + } + + if (currentOverlayContext == null || currentContext == null) return; + + final appLocalizations = AppLocalizations.of(currentContext!); + + String message = appLocalizations.attachment_download_failed; + if (failure.attachment is EMLAttachment) { + message = appLocalizations.downloadMessageAsEMLFailed; + } else if (failure.cancelToken?.isCancelled == true) { + message = appLocalizations.downloadAttachmentHasBeenCancelled; + } + + appToast.showToastErrorMessage(currentOverlayContext!, message); + } + + + @override + void handleSuccessViewState(Success success) { + if (success is DownloadAttachmentForWebSuccess) { + _handleDownloadAttachmentForWebSuccess(success); + } else if (success is StartDownloadAttachmentForWeb) { + _pushDownloadUIAction(UpdateAttachmentsViewStateAction( + success.attachment.blobId, + success, + )); + } else if (success is DownloadingAttachmentForWeb) { + _pushDownloadUIAction(UpdateAttachmentsViewStateAction( + success.attachment.blobId, + success, + )); + } else if (success is DownloadAllAttachmentsForWebSuccess) { + deleteDownloadTask(success.taskId); + } else { + super.handleSuccessViewState(success); + } + } + + @override + void handleFailureViewState(Failure failure) { + if (failure is DownloadAllAttachmentsForWebFailure) { + _downloadAllAttachmentsForWebFailure(failure); + } else if (failure is DownloadAttachmentForWebFailure) { + _downloadAttachmentForWebFailureAction(failure); + } else { + super.handleFailureViewState(failure); + } + } + + @override + void onClose() { + _downloadProgressStateSubscription?.cancel(); + _downloadProgressStateSubscription = null; + _downloadProgressStateController.close(); + super.onClose(); + } } \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart index ed66d84b9..17a8ea63e 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart @@ -111,6 +111,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_email_drafts_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_email_sort_order_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/download/download_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart' as search; @@ -120,6 +121,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_action_type_for_email_selection.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_clear_mailbox_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_create_new_rule_filter.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_download_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_preferences_setting_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_reactive_obx_variable_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_save_email_as_draft_extension.dart'; @@ -313,6 +315,7 @@ class MailboxDashBoardController extends ReloadableController PaywallController? paywallController; Worker? advancedSearchVisibleWorker; Worker? searchInputFocusWorker; + Worker? _downloadUIActionWorker; final StreamController> _progressStateController = StreamController>.broadcast(); @@ -748,12 +751,26 @@ class MailboxDashBoardController extends ReloadableController .listen(_handleRefreshActionWhenBackToApp); _registerLocalNotificationStreamListener(); + + _registerDownloadUIActionListener(); } void _registerLocalNotificationStreamListener() { _notificationManager.localNotificationStream.listen(_handleClickLocalNotificationOnForeground); } + void _registerDownloadUIActionListener() { + _downloadUIActionWorker = ever( + downloadController.downloadUIAction, + (action) { + if (action is DownloadAttachmentsQuicklyAction) { + downloadAttachmentForWeb(attachment: action.attachment); + downloadController.clearDownloadUIAction(); + } + }, + ); + } + Future _handleClickNotificationOnAndroidInTerminated() async { _notificationManager.activatedNotificationClickedOnTerminate = true; final notificationResponse = await _notificationManager.getCurrentNotificationResponse(); @@ -3325,6 +3342,8 @@ class MailboxDashBoardController extends ReloadableController twakeAppManager.setHasComposer(false); paywallController?.onClose(); paywallController = null; + _downloadUIActionWorker?.dispose(); + _downloadUIActionWorker = null; super.onClose(); } } \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/extensions/handle_download_extension.dart b/lib/features/mailbox_dashboard/presentation/extensions/handle_download_extension.dart new file mode 100644 index 000000000..dd9f1ecb7 --- /dev/null +++ b/lib/features/mailbox_dashboard/presentation/extensions/handle_download_extension.dart @@ -0,0 +1,37 @@ +import 'package:model/email/attachment.dart'; +import 'package:model/email/presentation_email.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; + +extension HandleDownloadExtension on MailboxDashBoardController { + void downloadMessageAsEML(PresentationEmail presentationEmail) { + downloadController.downloadMessageAsEML( + presentationEmail: presentationEmail, + accountId: accountId.value, + session: sessionCurrent, + ); + } + + void downloadAttachmentForWeb({ + required Attachment attachment, + bool previewerSupported = false, + }) { + downloadController.downloadAttachmentForWeb( + attachment: attachment, + accountId: accountId.value, + session: sessionCurrent, + previewerSupported: previewerSupported, + ); + } + + void downloadAllAttachmentsForWeb({ + required String outputFileName, + PresentationEmail? currentEmail, + }) { + downloadController.downloadAllAttachmentsForWeb( + outputFileName: outputFileName, + currentEmail: currentEmail, + accountId: accountId.value, + session: sessionCurrent, + ); + } +} diff --git a/lib/features/thread_detail/presentation/extension/handle_collapsed_email_download_states.dart b/lib/features/thread_detail/presentation/extension/handle_collapsed_email_download_states.dart deleted file mode 100644 index 19b0f31d8..000000000 --- a/lib/features/thread_detail/presentation/extension/handle_collapsed_email_download_states.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:core/presentation/extensions/color_extension.dart'; -import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:dartz/dartz.dart'; -import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/download/download_task_state.dart'; -import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_controller.dart'; -import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -import 'package:tmail_ui_user/main/routes/route_navigation.dart'; - -extension HandleCollapsedEmailDownloadStates on ThreadDetailController { - void handleDownloadProgressState(Either state) { - state.fold( - (failure) => null, - (success) { - if (success is StartDownloadAttachmentForWeb && - !success.previewerSupported - ) { - mailboxDashBoardController.addDownloadTask( - DownloadTaskState( - taskId: success.taskId, - attachment: success.attachment, - onCancel: () => success.cancelToken?.cancel(), - ), - ); - - if (currentOverlayContext != null && - currentContext != null && - !success.previewerSupported - ) { - appToast.showToastMessage( - currentOverlayContext!, - AppLocalizations.of(currentContext!).your_download_has_started, - leadingSVGIconColor: AppColor.primaryColor, - leadingSVGIcon: imagePaths.icDownload, - ); - } - } else if (success is DownloadingAttachmentForWeb) { - mailboxDashBoardController.updateDownloadTask( - success.taskId, - (currentTask) { - final newTask = currentTask.copyWith( - progress: success.progress, - downloaded: success.downloaded, - total: success.total, - ); - - return newTask; - }, - ); - } - } - ); - } - - void handleDownloadSuccess(DownloadAttachmentForWebSuccess success) { - mailboxDashBoardController.deleteDownloadTask(success.taskId); - - downloadManager.createAnchorElementDownloadFileWeb( - success.bytes, - success.attachment.generateFileName()); - } - - void handleDownloadFailure(DownloadAttachmentForWebFailure failure) { - if (failure.taskId != null) { - mailboxDashBoardController.deleteDownloadTask(failure.taskId!); - } - - if (currentOverlayContext == null || currentContext == null) return; - - final message = AppLocalizations.of(currentContext!).downloadMessageAsEMLFailed; - appToast.showToastErrorMessage(currentOverlayContext!, message); - } -} \ No newline at end of file diff --git a/lib/features/thread_detail/presentation/thread_detail_bindings.dart b/lib/features/thread_detail/presentation/thread_detail_bindings.dart index 3a3884a0a..85162fd7b 100644 --- a/lib/features/thread_detail/presentation/thread_detail_bindings.dart +++ b/lib/features/thread_detail/presentation/thread_detail_bindings.dart @@ -2,7 +2,6 @@ import 'package:core/data/model/source_type/data_source_type.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/http/http_client.dart'; import 'package:tmail_ui_user/features/base/base_bindings.dart'; -import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_star_email_interactor.dart'; @@ -44,7 +43,6 @@ class ThreadDetailBindings extends BaseBindings { Get.find(), Get.find(), Get.find(), - Get.find(), Get.find(), Get.find(), )); diff --git a/lib/features/thread_detail/presentation/thread_detail_controller.dart b/lib/features/thread_detail/presentation/thread_detail_controller.dart index c0cd5952f..e3e53d7ef 100644 --- a/lib/features/thread_detail/presentation/thread_detail_controller.dart +++ b/lib/features/thread_detail/presentation/thread_detail_controller.dart @@ -1,11 +1,9 @@ -import 'package:debounce_throttle/debounce_throttle.dart'; -import 'package:flutter/material.dart'; import 'dart:async'; import 'package:core/data/network/download/download_manager.dart'; import 'package:core/presentation/state/failure.dart'; -import 'package:core/presentation/state/success.dart'; -import 'package:dartz/dartz.dart'; +import 'package:debounce_throttle/debounce_throttle.dart'; +import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/core/properties/properties.dart'; @@ -18,16 +16,14 @@ import 'package:model/email/presentation_email.dart'; import 'package:model/extensions/keyword_identifier_extension.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:tmail_ui_user/features/base/base_controller.dart'; -import 'package:tmail_ui_user/features/email/domain/state/print_email_state.dart'; -import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart'; -import 'package:tmail_ui_user/features/email/presentation/model/email_loaded.dart'; -import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_state.dart'; -import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart'; +import 'package:tmail_ui_user/features/email/domain/state/print_email_state.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_star_email_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/print_email_interactor.dart'; +import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/email_loaded.dart'; import 'package:tmail_ui_user/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/manage_account/domain/state/create_new_rule_filter_state.dart'; @@ -40,10 +36,10 @@ import 'package:tmail_ui_user/features/thread/domain/state/mark_as_star_multiple 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/mark_as_star_multiple_email_interactor.dart'; import 'package:tmail_ui_user/features/thread_detail/domain/model/email_in_thread_detail_info.dart'; -import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart'; import 'package:tmail_ui_user/features/thread_detail/domain/state/get_emails_by_ids_state.dart'; -import 'package:tmail_ui_user/features/thread_detail/domain/usecases/get_thread_by_id_interactor.dart'; +import 'package:tmail_ui_user/features/thread_detail/domain/state/get_thread_by_id_state.dart'; import 'package:tmail_ui_user/features/thread_detail/domain/usecases/get_emails_by_ids_interactor.dart'; +import 'package:tmail_ui_user/features/thread_detail/domain/usecases/get_thread_by_id_interactor.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/action/thread_detail_ui_action.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_email_moved_action.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_get_email_ids_by_thread_id_success.dart'; @@ -53,14 +49,13 @@ import 'package:tmail_ui_user/features/thread_detail/presentation/extension/hand import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_mail_shortcut_actions_extension.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_refresh_thread_detail_action.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/extension/initialize_thread_detail_emails.dart'; +import 'package:tmail_ui_user/features/thread_detail/presentation/extension/mark_collapsed_email_unread_success.dart'; +import 'package:tmail_ui_user/features/thread_detail/presentation/extension/quick_create_rule_from_collapsed_email_success.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/extension/thread_detail_on_selected_email_updated.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/model/mail_view_shortcut_action_view_event.dart'; import 'package:tmail_ui_user/features/thread_detail/presentation/thread_detail_manager.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; -import 'package:tmail_ui_user/features/thread_detail/presentation/extension/handle_collapsed_email_download_states.dart'; -import 'package:tmail_ui_user/features/thread_detail/presentation/extension/mark_collapsed_email_unread_success.dart'; -import 'package:tmail_ui_user/features/thread_detail/presentation/extension/quick_create_rule_from_collapsed_email_success.dart'; class ThreadDetailController extends BaseController { final GetThreadByIdInteractor _getEmailIdsByThreadIdInteractor; @@ -69,7 +64,6 @@ class ThreadDetailController extends BaseController { final MarkAsStarEmailInteractor _markAsStarEmailInteractor; final PrintEmailInteractor _printEmailInteractor; final GetEmailContentInteractor _getEmailContentInteractor; - final DownloadAttachmentForWebInteractor _downloadAttachmentForWebInteractor; final MarkAsStarMultipleEmailInteractor markAsStarMultipleEmailInteractor; final MarkAsMultipleEmailReadInteractor markAsMultipleEmailReadInteractor; @@ -80,7 +74,6 @@ class ThreadDetailController extends BaseController { this._markAsStarEmailInteractor, this._printEmailInteractor, this._getEmailContentInteractor, - this._downloadAttachmentForWebInteractor, this.markAsStarMultipleEmailInteractor, this.markAsMultipleEmailReadInteractor, ); @@ -120,7 +113,6 @@ class ThreadDetailController extends BaseController { final networkConnectionController = Get.find(); final threadDetailManager = Get.find(); final downloadManager = Get.find(); - final downloadProgressState = StreamController>(); ScrollController? scrollController; CreateNewEmailRuleFilterInteractor? _createNewEmailRuleFilterInteractor; @@ -170,10 +162,8 @@ class ThreadDetailController extends BaseController { _createNewEmailRuleFilterInteractor, _printEmailInteractor, _getEmailContentInteractor, - _downloadAttachmentForWebInteractor, ); }); - downloadProgressState.stream.listen(handleDownloadProgressState); ever(mailboxDashBoardController.selectedEmail, (presentationEmail) async { onSelectedEmailUpdated( presentationEmail, @@ -271,8 +261,6 @@ class ThreadDetailController extends BaseController { ); } else if (success is CreateNewRuleFilterSuccess) { quickCreateRuleFromCollapsedEmailSuccess(success); - } else if (success is DownloadAttachmentForWebSuccess) { - handleDownloadSuccess(success); } else { super.handleSuccessViewState(success); } @@ -288,10 +276,6 @@ class ThreadDetailController extends BaseController { showRetryToast(failure); return; } - if (failure is DownloadAttachmentForWebFailure) { - handleDownloadFailure(failure); - return; - } if (failure is PrintEmailFailure) { if (currentOverlayContext != null && currentContext != null) { appToast.showToastErrorMessage( diff --git a/test/features/email/presentation/controller/single_email_controller_test.dart b/test/features/email/presentation/controller/single_email_controller_test.dart index d6e4f1202..770ae1649 100644 --- a/test/features/email/presentation/controller/single_email_controller_test.dart +++ b/test/features/email/presentation/controller/single_email_controller_test.dart @@ -29,9 +29,6 @@ import 'package:tmail_ui_user/features/email/domain/state/get_email_content_stat import 'package:tmail_ui_user/features/email/domain/state/parse_calendar_event_state.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_accept_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_reject_interactor.dart'; -import 'package:tmail_ui_user/features/email/domain/usecases/download_all_attachments_for_web_interactor.dart'; -import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart'; -import 'package:tmail_ui_user/features/email/domain/usecases/download_attachments_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/export_all_attachments_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/export_attachment_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart'; @@ -50,6 +47,8 @@ import 'package:tmail_ui_user/features/email/presentation/model/blob_calendar_ev import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/download/download_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; @@ -76,14 +75,12 @@ const fallbackGenerators = { @GenerateNiceMocks([ MockSpec(), MockSpec(), - MockSpec(), - MockSpec(), MockSpec(), MockSpec(), - MockSpec(), MockSpec(), MockSpec(), MockSpec(fallbackGenerators: fallbackGenerators), + MockSpec(fallbackGenerators: fallbackGenerators), MockSpec(fallbackGenerators: fallbackGenerators), MockSpec(fallbackGenerators: fallbackGenerators), MockSpec(fallbackGenerators: fallbackGenerators), @@ -109,7 +106,6 @@ const fallbackGenerators = { MockSpec(), MockSpec(), MockSpec(), - MockSpec(), MockSpec(), MockSpec(), ]) @@ -118,15 +114,12 @@ void main() { final getEmailContentInteractor = MockGetEmailContentInteractor(); final markAsEmailReadInteractor = MockMarkAsEmailReadInteractor(); - final downloadAttachmentsInteractor = MockDownloadAttachmentsInteractor(); - final deviceManager = MockDeviceManager(); final exportAttachmentInteractor = MockExportAttachmentInteractor(); final markAsStarEmailInteractor = MockMarkAsStarEmailInteractor(); - final downloadAttachmentForWebInteractor = - MockDownloadAttachmentForWebInteractor(); final getAllIdentitiesInteractor = MockGetAllIdentitiesInteractor(); final storeOpenedEmailInteractor = MockStoreOpenedEmailInteractor(); final mailboxDashboardController = MockMailboxDashBoardController(); + final downloadController = MockDownloadController(); final downloadManager = MockDownloadManager(); final cachingManager = MockCachingManager(); final languageCacheManager = MockLanguageCacheManager(); @@ -148,7 +141,6 @@ void main() { final getHtmlContentFromAttachmentInteractor = MockGetHtmlContentFromAttachmentInteractor(); final mockTwakeAppManager = MockTwakeAppManager(); - final downloadAllAttachmentsForWebInteractor = MockDownloadAllAttachmentsForWebInteractor(); final exportAllAttachmentsInteractor = MockExportAllAttachmentsInteractor(); late SingleEmailController singleEmailController; @@ -164,6 +156,7 @@ void main() { const testTaskId = 'taskId'; setUpAll(() { + Get.put(downloadController); Get.put(mailboxDashboardController); Get.put(downloadManager); Get.put(cachingManager); @@ -194,18 +187,14 @@ void main() { singleEmailController = SingleEmailController( getEmailContentInteractor, markAsEmailReadInteractor, - downloadAttachmentsInteractor, - deviceManager, exportAttachmentInteractor, markAsStarEmailInteractor, - downloadAttachmentForWebInteractor, getAllIdentitiesInteractor, storeOpenedEmailInteractor, printEmailInteractor, parseEmailByBlobIdInteractor, previewEmailFromEmlFileInteractor, getHtmlContentFromAttachmentInteractor, - downloadAllAttachmentsForWebInteractor, exportAllAttachmentsInteractor, ); }); @@ -231,6 +220,8 @@ void main() { when(mailboxDashboardController.emailUIAction).thenReturn(Rxn(null)); when(mailboxDashboardController.viewState).thenReturn(Rx(Right(UIState.idle))); when(mailboxDashboardController.sessionCurrent).thenReturn(testSession); + when(mailboxDashboardController.downloadController).thenReturn(downloadController); + when(downloadController.downloadUIAction).thenAnswer((_) => Rxn(DownloadUIAction.idle)); Get.put(acceptCalendarEventInteractor); singleEmailController.onInit(); mailboxDashboardController.accountId.refresh(); @@ -259,6 +250,8 @@ void main() { when(mailboxDashboardController.emailUIAction).thenReturn(Rxn(null)); when(mailboxDashboardController.viewState).thenReturn(Rx(Right(UIState.idle))); when(mailboxDashboardController.sessionCurrent).thenReturn(testSession); + when(mailboxDashboardController.downloadController).thenReturn(downloadController); + when(downloadController.downloadUIAction).thenAnswer((_) => Rxn(DownloadUIAction.idle)); Get.put(maybeCalendarEventInteractor); singleEmailController.onInit(); mailboxDashboardController.accountId.refresh(); @@ -287,6 +280,8 @@ void main() { when(mailboxDashboardController.emailUIAction).thenReturn(Rxn(null)); when(mailboxDashboardController.viewState).thenReturn(Rx(Right(UIState.idle))); when(mailboxDashboardController.sessionCurrent).thenReturn(testSession); + when(mailboxDashboardController.downloadController).thenReturn(downloadController); + when(downloadController.downloadUIAction).thenAnswer((_) => Rxn(DownloadUIAction.idle)); Get.put(rejectCalendarEventInteractor); singleEmailController.onInit(); mailboxDashboardController.accountId.refresh(); @@ -364,6 +359,8 @@ void main() { when(mailboxDashboardController.emailUIAction).thenReturn(Rxn(EmailUIAction())); when(mailboxDashboardController.viewState).thenReturn(Rx(Right(UIState.idle))); when(mailboxDashboardController.accountId).thenReturn(Rxn(AccountFixtures.aliceAccountId)); + when(mailboxDashboardController.downloadController).thenReturn(downloadController); + when(downloadController.downloadUIAction).thenAnswer((_) => Rxn(DownloadUIAction.idle)); singleEmailController.onInit(); mailboxDashboardController.accountId.refresh(); @@ -422,6 +419,8 @@ void main() { when(mailboxDashboardController.emailUIAction).thenReturn(Rxn(EmailUIAction())); when(mailboxDashboardController.viewState).thenReturn(Rx(Right(UIState.idle))); when(mailboxDashboardController.accountId).thenReturn(Rxn(AccountFixtures.aliceAccountId)); + when(mailboxDashboardController.downloadController).thenReturn(downloadController); + when(downloadController.downloadUIAction).thenAnswer((_) => Rxn(DownloadUIAction.idle)); singleEmailController.onInit(); mailboxDashboardController.accountId.refresh(); @@ -466,6 +465,8 @@ void main() { when(mailboxDashboardController.selectedEmail).thenReturn(Rxn(PresentationEmail())); when(mailboxDashboardController.emailUIAction).thenReturn(Rxn(EmailUIAction())); when(mailboxDashboardController.viewState).thenReturn(Rx(Right(UIState.idle))); + when(mailboxDashboardController.downloadController).thenReturn(downloadController); + when(downloadController.downloadUIAction).thenAnswer((_) => Rxn(DownloadUIAction.idle)); when(appToast.showToastMessageWithMultipleActions( any, any, diff --git a/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart b/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart index 9a4db5d7c..bcd2152ad 100644 --- a/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart +++ b/test/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller_test.dart @@ -67,6 +67,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_email_drafts_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_recent_search_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_email_sort_order_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/download/download_controller.dart'; @@ -396,6 +397,7 @@ void main() { getEmailsInMailboxInteractor = MockGetEmailsInMailboxInteractor(); when(emailReceiveManager.pendingSharedFileInfo).thenAnswer((_) => BehaviorSubject.seeded([])); + when(downloadController.downloadUIAction).thenAnswer((_) => Rxn(DownloadUIAction.idle)); Get.put(mailboxDashboardController); mailboxDashboardController.onReady(); @@ -441,6 +443,7 @@ void main() { // arrange when(context.owner).thenReturn(BuildOwner(focusManager: FocusManager())); when(context.mounted).thenReturn(true); + when(downloadController.downloadUIAction).thenAnswer((_) => Rxn(DownloadUIAction.idle)); // expect query in search controller update as expected mailboxDashboardController.searchEmailByQueryString(queryString); @@ -630,6 +633,7 @@ void main() { getEmailsInMailboxInteractor = MockGetEmailsInMailboxInteractor(); when(emailReceiveManager.pendingSharedFileInfo).thenAnswer((_) => BehaviorSubject.seeded([])); + when(downloadController.downloadUIAction).thenAnswer((_) => Rxn(DownloadUIAction.idle)); Get.put(mailboxDashboardController); mailboxDashboardController.onReady(); diff --git a/test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart b/test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart index d80af5984..997c2a9a1 100644 --- a/test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart +++ b/test/features/mailbox_dashboard/presentation/view/mailbox_dashboard_view_widget_test.dart @@ -65,6 +65,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_email_drafts_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_recent_search_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_email_sort_order_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/download/download_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; @@ -347,6 +348,7 @@ void main() { Get.put(getTokenOIDCInteractor); when(emailReceiveManager.pendingSharedFileInfo).thenAnswer((_) => BehaviorSubject.seeded([])); + when(downloadController.downloadUIAction).thenAnswer((_) => Rxn(DownloadUIAction.idle)); searchController = SearchController( quickSearchEmailInteractor, diff --git a/test/features/search/verify_before_time_in_search_email_filter_test.dart b/test/features/search/verify_before_time_in_search_email_filter_test.dart index 04fc2524f..781d6fb68 100644 --- a/test/features/search/verify_before_time_in_search_email_filter_test.dart +++ b/test/features/search/verify_before_time_in_search_email_filter_test.dart @@ -52,6 +52,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/remove_email_drafts_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_recent_search_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_email_sort_order_interactor.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/download/download_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; @@ -390,6 +391,7 @@ void main() { ); when(emailReceiveManager.pendingSharedFileInfo).thenAnswer((_) => BehaviorSubject.seeded([])); + when(downloadController.downloadUIAction).thenAnswer((_) => Rxn(DownloadUIAction.idle)); Get.put(mailboxDashboardController);