TF-4128 Separate download handling into independent class to decouple from views on web

This commit is contained in:
dab246
2025-10-28 16:44:40 +07:00
committed by Dat H. Pham
parent 832f8e1391
commit be8eaf6258
37 changed files with 955 additions and 1095 deletions
@@ -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<List<DownloadTaskId>> downloadAttachments(
List<Attachment> attachments,
AccountId accountId,
String baseDownloadUrl,
AccountRequest accountRequest
);
Future<DownloadedResponse> exportAttachment(
Attachment attachment,
AccountId accountId,
@@ -72,16 +60,6 @@ abstract class EmailDataSource {
CancelToken cancelToken
);
Future<Uint8List> downloadAttachmentForWeb(
DownloadTaskId taskId,
Attachment attachment,
AccountId accountId,
String baseDownloadUrl,
AccountRequest accountRequest,
StreamController<Either<Failure, Success>> onReceiveController,
{CancelToken? cancelToken}
);
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
@@ -149,6 +127,7 @@ abstract class EmailDataSource {
AccountId accountId,
List<EmailId> emailIds,
);
Future<bool> deleteEmailPermanently(
Session session,
AccountId accountId,
@@ -214,17 +193,6 @@ abstract class EmailDataSource {
Future<EMLPreviewer> getPreviewEMLContentInMemory(String keyStored);
Future<void> downloadAllAttachmentsForWeb(
AccountId accountId,
EmailId emailId,
String baseDownloadAllUrl,
String outputFileName,
AccountRequest accountRequest,
DownloadTaskId taskId,
StreamController<Either<Failure, Success>> onReceiveController,
{CancelToken? cancelToken}
);
Future<DownloadedResponse> exportAllAttachments(
AccountId accountId,
EmailId emailId,
@@ -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<List<DownloadTaskId>> downloadAttachments(
List<Attachment> attachments,
AccountId accountId,
String baseDownloadUrl,
AccountRequest accountRequest
) {
return Future.sync(() async {
return await emailAPI.downloadAttachments(attachments, accountId, baseDownloadUrl, accountRequest);
}).catchError(_exceptionThrower.throwException);
}
@override
Future<DownloadedResponse> exportAttachment(
Attachment attachment,
@@ -261,28 +245,6 @@ class EmailDataSourceImpl extends EmailDataSource {
}).catchError(_exceptionThrower.throwException);
}
@override
Future<Uint8List> downloadAttachmentForWeb(
DownloadTaskId taskId,
Attachment attachment,
AccountId accountId,
String baseDownloadUrl,
AccountRequest accountRequest,
StreamController<Either<Failure, Success>> 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<EmailId> emailIdsSuccess,
@@ -567,29 +529,6 @@ class EmailDataSourceImpl extends EmailDataSource {
throw UnimplementedError();
}
@override
Future<void> downloadAllAttachmentsForWeb(
AccountId accountId,
EmailId emailId,
String baseDownloadAllUrl,
String outputFileName,
AccountRequest accountRequest,
DownloadTaskId taskId,
StreamController<Either<Failure, Success>> onReceiveController,
{CancelToken? cancelToken}
) => Future.sync(() async {
return await emailAPI.downloadAllAttachmentsForWeb(
accountId,
emailId,
baseDownloadAllUrl,
outputFileName,
accountRequest,
taskId,
onReceiveController,
cancelToken: cancelToken,
);
}).catchError(_exceptionThrower.throwException);
@override
Future<DownloadedResponse> exportAllAttachments(
AccountId accountId,
@@ -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<Uint8List> downloadAttachmentForWeb(
DownloadTaskId taskId,
Attachment attachment,
AccountId accountId,
String baseDownloadUrl,
AccountRequest accountRequest,
StreamController<Either<Failure, Success>> onReceiveController,
{CancelToken? cancelToken}
) {
throw UnimplementedError();
}
@override
Future<List<DownloadTaskId>> downloadAttachments(List<Attachment> attachments, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest) {
throw UnimplementedError();
}
@override
Future<DownloadedResponse> 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<void> downloadAllAttachmentsForWeb(
AccountId accountId,
EmailId emailId,
String baseDownloadAllUrl,
String outputFileName,
AccountRequest accountRequest,
DownloadTaskId taskId,
StreamController<Either<Failure, Success>> onReceiveController,
{CancelToken? cancelToken}
) {
throw UnimplementedError();
}
@override
Future<DownloadedResponse> exportAllAttachments(AccountId accountId, EmailId emailId, String baseDownloadAllUrl, String outputFileName, AccountRequest accountRequest, {CancelToken? cancelToken}) {
throw UnimplementedError();
@@ -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<Uint8List> downloadAttachmentForWeb(DownloadTaskId taskId, Attachment attachment, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest, StreamController<Either<Failure, Success>> onReceiveController, {CancelToken? cancelToken}) {
throw UnimplementedError();
}
@override
Future<List<DownloadTaskId>> downloadAttachments(List<Attachment> attachments, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest) {
throw UnimplementedError();
}
@override
Future<DownloadedResponse> exportAttachment(Attachment attachment, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest, CancelToken cancelToken) {
throw UnimplementedError();
@@ -264,21 +249,7 @@ class EmailLocalStorageDataSourceImpl extends EmailDataSource {
Future<void> markAsForwarded(Session session, AccountId accountId, List<EmailId> emailIds) {
throw UnimplementedError();
}
@override
Future<void> downloadAllAttachmentsForWeb(
AccountId accountId,
EmailId emailId,
String baseDownloadAllUrl,
String outputFileName,
AccountRequest accountRequest,
DownloadTaskId taskId,
StreamController<Either<Failure, Success>> onReceiveController,
{CancelToken? cancelToken}
) {
throw UnimplementedError();
}
@override
Future<DownloadedResponse> exportAllAttachments(AccountId accountId, EmailId emailId, String baseDownloadAllUrl, String outputFileName, AccountRequest accountRequest, {CancelToken? cancelToken}) {
throw UnimplementedError();
@@ -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<Uint8List> downloadAttachmentForWeb(DownloadTaskId taskId, Attachment attachment, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest, StreamController<Either<Failure, Success>> onReceiveController, {CancelToken? cancelToken}) {
throw UnimplementedError();
}
@override
Future<List<DownloadTaskId>> downloadAttachments(List<Attachment> attachments, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest) {
throw UnimplementedError();
}
@override
Future<DownloadedResponse> exportAttachment(Attachment attachment, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest, CancelToken cancelToken) {
throw UnimplementedError();
@@ -247,21 +232,7 @@ class EmailSessionStorageDatasourceImpl extends EmailDataSource {
Future<void> markAsForwarded(Session session, AccountId accountId, List<EmailId> emailIds) {
throw UnimplementedError();
}
@override
Future<void> downloadAllAttachmentsForWeb(
AccountId accountId,
EmailId emailId,
String baseDownloadAllUrl,
String outputFileName,
AccountRequest accountRequest,
DownloadTaskId taskId,
StreamController<Either<Failure, Success>> onReceiveController,
{CancelToken? cancelToken}
) {
throw UnimplementedError();
}
@override
Future<DownloadedResponse> exportAllAttachments(AccountId accountId, EmailId emailId, String baseDownloadAllUrl, String outputFileName, AccountRequest accountRequest, {CancelToken? cancelToken}) {
throw UnimplementedError();
+14 -61
View File
@@ -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<List<DownloadTaskId>> downloadAttachments(
List<Attachment> 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<DownloadedResponse> exportAttachment(
Attachment attachment,
AccountId accountId,
@@ -361,14 +314,14 @@ class EmailAPI with HandleSetErrorMixin {
}
Future<Uint8List> downloadAttachmentForWeb(
DownloadTaskId taskId,
Attachment attachment,
AccountId accountId,
String baseDownloadUrl,
AccountRequest accountRequest,
StreamController<Either<Failure, Success>> onReceiveController,
{CancelToken? cancelToken}
) async {
DownloadTaskId taskId,
Attachment attachment,
AccountId accountId,
String baseDownloadUrl,
AccountRequest accountRequest, {
StreamController<Either<Failure, Success>>? 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<Either<Failure, Success>> onReceiveController,
{CancelToken? cancelToken}
) async {
DownloadTaskId taskId, {
StreamController<Either<Failure, Success>>? 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,
@@ -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<List<DownloadTaskId>> downloadAttachments(
List<Attachment> attachments,
AccountId accountId,
String baseDownloadUrl,
AccountRequest accountRequest
) {
return emailDataSource[DataSourceType.network]!.downloadAttachments(attachments, accountId, baseDownloadUrl, accountRequest);
}
@override
Future<DownloadedResponse> exportAttachment(
Attachment attachment,
@@ -395,26 +380,6 @@ class EmailRepositoryImpl extends EmailRepository {
return result;
}
@override
Future<Uint8List> downloadAttachmentForWeb(
DownloadTaskId taskId,
Attachment attachment,
AccountId accountId,
String baseDownloadUrl,
AccountRequest accountRequest,
StreamController<Either<Failure, Success>> onReceiveController,
{CancelToken? cancelToken}
) {
return emailDataSource[DataSourceType.network]!.downloadAttachmentForWeb(
taskId,
attachment,
accountId,
baseDownloadUrl,
accountRequest,
onReceiveController,
cancelToken: cancelToken);
}
@override
Future<({
List<EmailId> emailIdsSuccess,
@@ -575,28 +540,7 @@ class EmailRepositoryImpl extends EmailRepository {
htmlContent,
configuration);
}
@override
Future<void> downloadAllAttachmentsForWeb(
AccountId accountId,
EmailId emailId,
String baseDownloadAllUrl,
String outputFileName,
AccountRequest accountRequest,
DownloadTaskId taskId,
StreamController<Either<Failure, Success>> onReceiveController,
{CancelToken? cancelToken}
) => emailDataSource[DataSourceType.network]!.downloadAllAttachmentsForWeb(
accountId,
emailId,
baseDownloadAllUrl,
outputFileName,
accountRequest,
taskId,
onReceiveController,
cancelToken: cancelToken,
);
@override
Future<DownloadedResponse> exportAllAttachments(
AccountId accountId,
@@ -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<List<DownloadTaskId>> downloadAttachments(
List<Attachment> attachments,
AccountId accountId,
String baseDownloadUrl,
AccountRequest accountRequest
);
Future<DownloadedResponse> exportAttachment(
Attachment attachment,
AccountId accountId,
@@ -74,16 +62,6 @@ abstract class EmailRepository {
CancelToken cancelToken
);
Future<Uint8List> downloadAttachmentForWeb(
DownloadTaskId taskId,
Attachment attachment,
AccountId accountId,
String baseDownloadUrl,
AccountRequest accountRequest,
StreamController<Either<Failure, Success>> onReceiveController,
{CancelToken? cancelToken}
);
Future<({
List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors,
@@ -212,16 +190,7 @@ abstract class EmailRepository {
String htmlContent,
TransformConfiguration configuration);
Future<void> downloadAllAttachmentsForWeb(
AccountId accountId,
EmailId emailId,
String baseDownloadAllUrl,
String outputFileName,
AccountRequest accountRequest,
DownloadTaskId taskId,
StreamController<Either<Failure, Success>> onReceiveController,
{CancelToken? cancelToken}
);
Future<DownloadedResponse> exportAllAttachments(
AccountId accountId,
@@ -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<DownloadTaskId> taskIds;
DownloadAttachmentsSuccess(this.taskIds);
@override
List<Object> get props => [taskIds];
}
class DownloadAttachmentsFailure extends FeatureFailure {
DownloadAttachmentsFailure(dynamic exception) : super(exception: exception);
}
@@ -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<Either<Failure, Success>> onReceiveController,
{CancelToken? cancelToken}
) async* {
DownloadTaskId taskId, {
StreamController<Either<Failure, Success>>? 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
);
@@ -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<Either<Failure, Success>> execute(
DownloadTaskId taskId,
Attachment attachment,
AccountId accountId,
String baseDownloadUrl,
StreamController<Either<Failure, Success>> onReceiveController,
{CancelToken? cancelToken,
bool previewerSupported = false,
DownloadTaskId taskId,
Attachment attachment,
AccountId accountId,
String baseDownloadUrl, {
StreamController<Either<Failure, Success>>? onReceiveController,
CancelToken? cancelToken,
bool previewerSupported = false,
}) async* {
try {
yield Right<Failure, Success>(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
);
@@ -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<Either<Failure, Success>> execute(
List<Attachment> 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<Failure, Success>(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<Failure, Success>(DownloadAttachmentsFailure(exception));
}
}
}
Stream<Either<Failure, Success>> _retryDownloadAttachments(
AccountId accountId,
String baseDownloadUrl,
List<Attachment> 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<Failure, Success>(DownloadAttachmentsSuccess(taskIds));
} catch (e) {
logError('RefreshTokenOIDCInteractor::execute(): $e');
yield Left<Failure, Success>(DownloadAttachmentsFailure(e));
}
}
}
@@ -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<Either<Failure, Success>> execute(
AccountId accountId,
@@ -25,7 +30,6 @@ class GetHtmlContentFromAttachmentInteractor {
String baseDownloadUrl,
TransformConfiguration transformConfiguration,
) async* {
final onReceiveController = StreamController<Either<Failure, Success>>();
try {
yield Right(GettingHtmlContentFromAttachment(attachment: attachment));
final downloadState = await _downloadAttachmentForWebInteractor.execute(
@@ -33,7 +37,6 @@ class GetHtmlContentFromAttachmentInteractor {
attachment,
accountId,
baseDownloadUrl,
onReceiveController,
).last;
Either<Failure, Success>? 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(),
@@ -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<GetEmailContentInteractor>(),
Get.find<MarkAsEmailReadInteractor>(),
Get.find<DownloadAttachmentsInteractor>(),
Get.find<DeviceManager>(),
Get.find<ExportAttachmentInteractor>(),
Get.find<MarkAsStarEmailInteractor>(),
Get.find<DownloadAttachmentForWebInteractor>(),
Get.find<GetAllIdentitiesInteractor>(),
Get.find<StoreOpenedEmailInteractor>(),
Get.find<PrintEmailInteractor>(),
Get.find<ParseEmailByBlobIdInteractor>(),
Get.find<PreviewEmailFromEmlFileInteractor>(),
Get.find<GetHtmlContentFromAttachmentInteractor>(),
Get.find<DownloadAllAttachmentsForWebInteractor>(),
Get.find<ExportAllAttachmentsInteractor>(),
currentEmailId: currentEmailId,
), tag: tag);
@@ -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<EmailRepository>()));
Get.lazyPut(() => MarkAsEmailReadInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => DownloadAttachmentsInteractor(
Get.find<EmailRepository>(),
Get.find<CredentialRepository>(),
Get.find<AccountRepository>(),
Get.find<AuthenticationOIDCRepository>(),
Get.find<AuthorizationInterceptors>(),
));
Get.lazyPut(() => ExportAttachmentInteractor(
Get.find<EmailRepository>(),
Get.find<CredentialRepository>(),
@@ -133,23 +123,16 @@ class EmailInteractorBindings extends InteractorsBindings {
));
Get.lazyPut(() => MoveToMailboxInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => MarkAsStarEmailInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => DownloadAttachmentForWebInteractor(
Get.find<EmailRepository>(),
Get.find<CredentialRepository>(),
Get.find<AccountRepository>(),
Get.find<AuthenticationOIDCRepository>()));
Get.lazyPut(() => GetStoredEmailStateInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => StoreOpenedEmailInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => PrintEmailInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => ParseEmailByBlobIdInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => PreviewEmailFromEmlFileInteractor(Get.find<EmailRepository>()));
IdentityInteractorsBindings().dependencies();
Get.lazyPut(() => GetHtmlContentFromAttachmentInteractor(Get.find<DownloadAttachmentForWebInteractor>()));
Get.lazyPut(() => DownloadAllAttachmentsForWebInteractor(
Get.lazyPut(() => GetHtmlContentFromAttachmentInteractor(
Get.find<DownloadAttachmentForWebInteractor>(),
Get.find<EmailRepository>(),
Get.find<AccountRepository>(),
Get.find<AuthenticationOIDCRepository>(),
Get.find<CredentialRepository>()));
));
Get.lazyPut(() => ExportAllAttachmentsInteractor(
Get.find<EmailRepository>(),
Get.find<AccountRepository>(),
@@ -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 = <Worker>[];
late final EmailActionReactor emailActionReactor;
final StreamController<Either<Failure, Success>> _downloadProgressStateController =
StreamController<Either<Failure, Success>>.broadcast();
Stream<Either<Failure, Success>> 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<ThreadDetailController>();
_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<Attachment> 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<Attachment> 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<Failure, Success> 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 {
@@ -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<void> 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<Either<Failure, Success>> downloadMessageAsEML(
Session session,
AccountId accountId,
PresentationEmail presentationEmail, {
required StreamController<Either<Failure, Success>> 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<DynamicUrlInterceptors>()?.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, {
@@ -91,7 +91,7 @@ class _PDFViewerState extends State<PDFViewer> {
widget.attachment,
widget.accountId,
widget.downloadUrl,
_downloadAttachmentStreamController,
onReceiveController: _downloadAttachmentStreamController,
cancelToken: _downloadAttachmentCancelToken
).listen((viewState) {
viewState.fold(
@@ -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<EmailRepository>()));
Get.lazyPut(() => PreviewEmailFromEmlFileInteractor(
Get.find<EmailRepository>()));
Get.lazyPut(() => DownloadAttachmentForWebInteractor(
Get.find<EmailRepository>(),
Get.find<CredentialRepository>(),
Get.find<AccountRepository>(),
Get.find<AuthenticationOIDCRepository>()));
DownloadInteractorBindings().dependencies();
}
@override
@@ -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');
@@ -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<Uint8List> downloadAttachmentForWeb(
DownloadTaskId taskId,
Attachment attachment,
AccountId accountId,
String baseDownloadUrl,
AccountRequest accountRequest, {
StreamController<Either<Failure, Success>>? onReceiveController,
CancelToken? cancelToken,
});
Future<void> downloadAllAttachmentsForWeb(
AccountId accountId,
EmailId emailId,
String baseDownloadAllUrl,
String outputFileName,
AccountRequest accountRequest,
DownloadTaskId taskId, {
StreamController<Either<Failure, Success>>? onReceiveController,
CancelToken? cancelToken,
});
}
@@ -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<Uint8List> downloadAttachmentForWeb(
DownloadTaskId taskId,
Attachment attachment,
AccountId accountId,
String baseDownloadUrl,
AccountRequest accountRequest, {
StreamController<Either<Failure, Success>>? 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<void> downloadAllAttachmentsForWeb(
AccountId accountId,
EmailId emailId,
String baseDownloadAllUrl,
String outputFileName,
AccountRequest accountRequest,
DownloadTaskId taskId,
{StreamController<Either<Failure, Success>>? 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;
});
}
}
@@ -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<Uint8List> downloadAttachmentForWeb(
DownloadTaskId taskId,
Attachment attachment,
AccountId accountId,
String baseDownloadUrl,
AccountRequest accountRequest, {
StreamController<Either<Failure, Success>>? onReceiveController,
CancelToken? cancelToken,
}) {
return _datasource.downloadAttachmentForWeb(
taskId,
attachment,
accountId,
baseDownloadUrl,
accountRequest,
onReceiveController: onReceiveController,
cancelToken: cancelToken,
);
}
@override
Future<void> downloadAllAttachmentsForWeb(
AccountId accountId,
EmailId emailId,
String baseDownloadAllUrl,
String outputFileName,
AccountRequest accountRequest,
DownloadTaskId taskId, {
StreamController<Either<Failure, Success>>? onReceiveController,
CancelToken? cancelToken,
}) {
return _datasource.downloadAllAttachmentsForWeb(
accountId,
emailId,
baseDownloadAllUrl,
outputFileName,
accountRequest,
taskId,
onReceiveController: onReceiveController,
cancelToken: cancelToken,
);
}
}
@@ -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<Uint8List> downloadAttachmentForWeb(
DownloadTaskId taskId,
Attachment attachment,
AccountId accountId,
String baseDownloadUrl,
AccountRequest accountRequest, {
StreamController<Either<Failure, Success>>? onReceiveController,
CancelToken? cancelToken,
});
Future<void> downloadAllAttachmentsForWeb(
AccountId accountId,
EmailId emailId,
String baseDownloadAllUrl,
String outputFileName,
AccountRequest accountRequest,
DownloadTaskId taskId, {
StreamController<Either<Failure, Success>>? onReceiveController,
CancelToken? cancelToken,
});
}
@@ -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<Object?> get props => [];
}
class UpdateAttachmentsViewStateAction extends DownloadUIAction {
UpdateAttachmentsViewStateAction(this.blobId, this.success);
final Id? blobId;
final dynamic success;
@override
List<Object?> get props => [blobId, success];
}
class DownloadAttachmentsQuicklyAction extends DownloadUIAction {
DownloadAttachmentsQuicklyAction(this.attachment);
final Attachment attachment;
@override
List<Object?> get props => [attachment];
}
@@ -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<EmailAPI>(),
Get.find<RemoteExceptionThrower>(),
),
);
}
@override
void bindingsInteractor() {
Get.lazyPut(
() => DownloadAttachmentForWebInteractor(
Get.find<DownloadRepository>(),
Get.find<CredentialRepository>(),
Get.find<AccountRepository>(),
Get.find<AuthenticationOIDCRepository>(),
),
);
Get.lazyPut(
() => DownloadAllAttachmentsForWebInteractor(
Get.find<DownloadRepository>(),
Get.find<AccountRepository>(),
Get.find<AuthenticationOIDCRepository>(),
Get.find<CredentialRepository>(),
),
);
}
@override
void bindingsRepository() {
Get.lazyPut<DownloadRepository>(() => Get.find<DownloadRepositoryImpl>());
}
@override
void bindingsRepositoryImpl() {
Get.lazyPut(() => DownloadRepositoryImpl(Get.find<DownloadDatasource>()));
}
@override
void bindingsDataSource() {
Get.lazyPut<DownloadDatasource>(() => Get.find<DownloadDatasourceImpl>());
}
}
@@ -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<GetAppDashboardConfigurationInteractor>(),
Get.find<GetAppGridLinagraEcosystemInteractor>(),
));
Get.put(DownloadController());
Get.put(DownloadController(
Get.find<DownloadAttachmentForWebInteractor>(),
Get.find<DownloadAllAttachmentsForWebInteractor>(),
Get.find<DownloadManager>(),
));
Get.put(SearchController(
Get.find<QuickSearchEmailInteractor>(),
Get.find<SaveRecentSearchInteractor>(),
@@ -378,6 +386,8 @@ class MailboxDashBoardBindings extends BaseBindings {
Get.find<IdentityCreatorRepository>()
));
PaywallBindings().dependencies();
DownloadInteractorBindings().dependencies();
}
@override
@@ -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<DownloadTaskState>();
final hideDownloadTaskbar = RxBool(false);
final downloadUIAction = Rxn<DownloadUIAction>();
final _downloadProgressStateController =
StreamController<Either<Failure, Success>>.broadcast();
StreamSubscription<Either<Failure, Success>>?
_downloadProgressStateSubscription;
@override
void onInit() {
super.onInit();
_registerDownloadProgressState();
}
void _registerDownloadProgressState() {
_downloadProgressStateSubscription = _downloadProgressStateController.stream
.listen(_onDownloadProgressStateChanged);
}
void _onDownloadProgressStateChanged(Either<Failure, Success> 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<DynamicUrlInterceptors>()?.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();
}
}
@@ -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<Either<Failure, Success>> _progressStateController =
StreamController<Either<Failure, Success>>.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<void> _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();
}
}
@@ -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,
);
}
}
@@ -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<Failure, Success> 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);
}
}
@@ -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<MarkAsStarEmailInteractor>(),
Get.find<PrintEmailInteractor>(),
Get.find<GetEmailContentInteractor>(),
Get.find<DownloadAttachmentForWebInteractor>(),
Get.find<MarkAsStarMultipleEmailInteractor>(),
Get.find<MarkAsMultipleEmailReadInteractor>(),
));
@@ -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<NetworkConnectionController>();
final threadDetailManager = Get.find<ThreadDetailManager>();
final downloadManager = Get.find<DownloadManager>();
final downloadProgressState = StreamController<Either<Failure, Success>>();
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(
@@ -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<GetEmailContentInteractor>(),
MockSpec<MarkAsEmailReadInteractor>(),
MockSpec<DownloadAttachmentsInteractor>(),
MockSpec<DeviceManager>(),
MockSpec<ExportAttachmentInteractor>(),
MockSpec<MarkAsStarEmailInteractor>(),
MockSpec<DownloadAttachmentForWebInteractor>(),
MockSpec<GetAllIdentitiesInteractor>(),
MockSpec<StoreOpenedEmailInteractor>(),
MockSpec<MailboxDashBoardController>(fallbackGenerators: fallbackGenerators),
MockSpec<DownloadController>(fallbackGenerators: fallbackGenerators),
MockSpec<DownloadManager>(fallbackGenerators: fallbackGenerators),
MockSpec<CachingManager>(fallbackGenerators: fallbackGenerators),
MockSpec<LanguageCacheManager>(fallbackGenerators: fallbackGenerators),
@@ -109,7 +106,6 @@ const fallbackGenerators = {
MockSpec<DioClient>(),
MockSpec<GetHtmlContentFromAttachmentInteractor>(),
MockSpec<TwakeAppManager>(),
MockSpec<DownloadAllAttachmentsForWebInteractor>(),
MockSpec<ExportAllAttachmentsInteractor>(),
MockSpec<FileUploader>(),
])
@@ -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>(downloadController);
Get.put<MailboxDashBoardController>(mailboxDashboardController);
Get.put<DownloadManager>(downloadManager);
Get.put<CachingManager>(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>(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>(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>(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,
@@ -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();
@@ -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>(getTokenOIDCInteractor);
when(emailReceiveManager.pendingSharedFileInfo).thenAnswer((_) => BehaviorSubject.seeded([]));
when(downloadController.downloadUIAction).thenAnswer((_) => Rxn(DownloadUIAction.idle));
searchController = SearchController(
quickSearchEmailInteractor,
@@ -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>(mailboxDashboardController);