TF-4050 Preview and download uploaded file in composer on web

This commit is contained in:
dab246
2025-11-07 01:12:20 +07:00
committed by Dat H. Pham
parent ecd3ac0295
commit 972b20ec16
72 changed files with 2438 additions and 1751 deletions
@@ -0,0 +1,5 @@
class DownloadAttachmentInteractorIsNull implements Exception {}
class CapabilityDownloadAllNotSupportedException implements Exception {}
class DownloadUrlIsNullException implements Exception {}
@@ -0,0 +1,4 @@
enum DownloadSourceView {
emailView,
composerView;
}
@@ -0,0 +1,80 @@
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:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/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/domain/model/preview_email_eml_request.dart';
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.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,
});
Future<String> sanitizeHtmlContent(
String htmlContent,
TransformConfiguration configuration,
);
Future<List<Email>> parseEmailByBlobIds(AccountId accountId, Set<Id> blobIds);
Future<String> generatePreviewEmailEMLContent(
PreviewEmailEMLRequest previewEmailEMLRequest,
);
Future<void> sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer);
Future<void> storePreviewEMLContentToSessionStorage(
EMLPreviewer emlPreviewer,
);
Future<EMLPreviewer> getPreviewEmailEMLContentShared(String keyStored);
Future<void> removePreviewEmailEMLContentShared(String keyStored);
Future<EMLPreviewer> getPreviewEMLContentInMemory(String keyStored);
Future<DownloadedResponse> exportAttachment(
Attachment attachment,
AccountId accountId,
String baseDownloadUrl,
AccountRequest accountRequest,
CancelToken cancelToken,
);
Future<DownloadedResponse> exportAllAttachments(
AccountId accountId,
EmailId emailId,
String baseDownloadAllUrl,
String outputFileName,
AccountRequest accountRequest,
CancelToken cancelToken,
);
}
@@ -0,0 +1,54 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:dio/dio.dart';
import 'package:model/download/download_task_id.dart';
import 'package:model/email/attachment.dart';
class StartDownloadAllAttachmentsForWeb extends UIState {
StartDownloadAllAttachmentsForWeb(this.taskId, this.attachment, {this.cancelToken});
final DownloadTaskId taskId;
final Attachment attachment;
final CancelToken? cancelToken;
@override
List<Object?> get props => [taskId, attachment, cancelToken];
}
class DownloadingAllAttachmentsForWeb extends LoadingState {
DownloadingAllAttachmentsForWeb(
this.taskId,
this.fileName,
this.progress,
this.downloaded,
this.total,
);
final DownloadTaskId taskId;
final String fileName;
final double progress;
final int downloaded;
final int total;
@override
List<Object?> get props => [taskId, fileName, progress, downloaded, total];
}
class DownloadAllAttachmentsForWebSuccess extends UIState {
DownloadAllAttachmentsForWebSuccess({required this.taskId});
final DownloadTaskId taskId;
@override
List<Object> get props => [taskId];
}
class DownloadAllAttachmentsForWebFailure extends FeatureFailure {
DownloadAllAttachmentsForWebFailure({super.exception, required this.taskId, this.cancelToken});
final DownloadTaskId taskId;
final CancelToken? cancelToken;
@override
List<Object?> get props => [exception, taskId, cancelToken];
}
@@ -0,0 +1,62 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:model/email/attachment.dart';
import 'package:tmail_ui_user/features/download/domain/model/download_source_view.dart';
class DownloadAndGettingHtmlContentFromAttachment extends LoadingState {
DownloadAndGettingHtmlContentFromAttachment({
required this.blobId,
this.sourceView,
});
final Id? blobId;
final DownloadSourceView? sourceView;
@override
List<Object?> get props => [blobId, sourceView];
}
class DownloadAndGetHtmlContentFromAttachmentSuccess extends UIState {
DownloadAndGetHtmlContentFromAttachmentSuccess({
required this.sanitizedHtmlContent,
required this.htmlAttachmentTitle,
required this.attachment,
required this.accountId,
required this.session,
this.sourceView,
});
final String sanitizedHtmlContent;
final String htmlAttachmentTitle;
final Attachment attachment;
final AccountId accountId;
final Session session;
final DownloadSourceView? sourceView;
@override
List<Object?> get props => [
sanitizedHtmlContent,
htmlAttachmentTitle,
attachment,
accountId,
session,
sourceView,
];
}
class DownloadAndGetHtmlContentFromAttachmentFailure extends FeatureFailure {
DownloadAndGetHtmlContentFromAttachmentFailure({
super.exception,
required this.blobId,
this.sourceView,
});
final Id? blobId;
final DownloadSourceView? sourceView;
@override
List<Object?> get props => [blobId, sourceView];
}
@@ -0,0 +1,115 @@
import 'dart:typed_data';
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:dio/dio.dart';
import 'package:model/download/download_task_id.dart';
import 'package:model/email/attachment.dart';
import 'package:tmail_ui_user/features/download/domain/model/download_source_view.dart';
class IdleDownloadAttachmentForWeb extends UIState {}
class StartDownloadAttachmentForWeb extends UIState {
final DownloadTaskId taskId;
final Attachment attachment;
final CancelToken? cancelToken;
final bool previewerSupported;
final DownloadSourceView? sourceView;
StartDownloadAttachmentForWeb(
this.taskId,
this.attachment, [
this.cancelToken,
this.previewerSupported = false,
this.sourceView,
]);
@override
List<Object?> get props => [
taskId,
attachment,
cancelToken,
previewerSupported,
sourceView,
];
}
class DownloadingAttachmentForWeb extends UIState {
final DownloadTaskId taskId;
final Attachment attachment;
final double progress;
final int downloaded;
final int total;
final DownloadSourceView? sourceView;
DownloadingAttachmentForWeb(
this.taskId,
this.attachment,
this.progress,
this.downloaded,
this.total,
this.sourceView,
);
@override
List<Object?> get props => [
taskId,
attachment,
progress,
downloaded,
total,
sourceView,
];
}
class DownloadAttachmentForWebSuccess extends UIState {
final DownloadTaskId taskId;
final Attachment attachment;
final Uint8List bytes;
final bool previewerSupported;
final DownloadSourceView? sourceView;
DownloadAttachmentForWebSuccess(
this.taskId,
this.attachment,
this.bytes,
this.previewerSupported,
this.sourceView,
);
@override
List<Object?> get props => [
taskId,
attachment,
bytes,
previewerSupported,
sourceView,
];
}
class DownloadAttachmentForWebFailure extends FeatureFailure {
final DownloadTaskId? taskId;
final Attachment? attachment;
final CancelToken? cancelToken;
final DownloadSourceView? sourceView;
DownloadAttachmentForWebFailure({
this.attachment,
this.taskId,
this.cancelToken,
this.sourceView,
super.exception,
});
@override
List<Object?> get props => [
attachment,
taskId,
cancelToken,
sourceView,
...super.props,
];
}
@@ -0,0 +1,18 @@
import 'package:core/data/network/download/downloaded_response.dart';
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
class ExportingAllAttachments extends LoadingState {}
class ExportAllAttachmentsSuccess extends UIState {
ExportAllAttachmentsSuccess(this.downloadedResponse);
final DownloadedResponse downloadedResponse;
@override
List<Object> get props => [downloadedResponse];
}
class ExportAllAttachmentsFailure extends FeatureFailure {
ExportAllAttachmentsFailure({super.exception});
}
@@ -0,0 +1,17 @@
import 'package:core/data/network/download/downloaded_response.dart';
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
class ExportAttachmentSuccess extends UIState {
final DownloadedResponse downloadedResponse;
ExportAttachmentSuccess(this.downloadedResponse);
@override
List<Object> get props => [downloadedResponse];
}
class ExportAttachmentFailure extends FeatureFailure {
ExportAttachmentFailure(dynamic exception) : super(exception: exception);
}
@@ -0,0 +1,36 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:model/email/attachment.dart';
class GettingHtmlContentFromUploadFile extends LoadingState {}
class GetHtmlContentFromUploadFileSuccess extends UIState {
GetHtmlContentFromUploadFileSuccess({
required this.sanitizedHtmlContent,
required this.htmlAttachmentTitle,
required this.attachment,
required this.accountId,
required this.session,
});
final String sanitizedHtmlContent;
final String htmlAttachmentTitle;
final Attachment attachment;
final AccountId? accountId;
final Session? session;
@override
List<Object?> get props => [
sanitizedHtmlContent,
htmlAttachmentTitle,
attachment,
accountId,
session,
];
}
class GetHtmlContentFromUploadFileFailure extends FeatureFailure {
GetHtmlContentFromUploadFileFailure({super.exception});
}
@@ -0,0 +1,27 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart';
class GettingPreviewEmailEMLContentShared extends LoadingState {}
class GetPreviewEmailEMLContentSharedSuccess extends UIState {
final EMLPreviewer emlPreviewer;
GetPreviewEmailEMLContentSharedSuccess(this.emlPreviewer);
@override
List<Object?> get props => [emlPreviewer];
}
class GetPreviewEmailEMLContentSharedFailure extends FeatureFailure {
final String? keyStored;
GetPreviewEmailEMLContentSharedFailure({
this.keyStored,
dynamic exception,
}) : super(exception: exception);
@override
List<Object?> get props => [keyStored, exception];
}
@@ -0,0 +1,26 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart';
class GettingPreviewEMLContentInMemory extends LoadingState {}
class GetPreviewEMLContentInMemorySuccess extends UIState {
final EMLPreviewer emlPreviewer;
GetPreviewEMLContentInMemorySuccess(this.emlPreviewer);
@override
List<Object?> get props => [emlPreviewer];
}
class GetPreviewEMLContentInMemoryFailure extends FeatureFailure {
final String? keyStored;
GetPreviewEMLContentInMemoryFailure({
this.keyStored,
dynamic exception,
}) : super(exception: exception);
@override
List<Object?> get props => [keyStored, exception];
}
@@ -0,0 +1,11 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
class MovingPreviewEmailEMLContentFromPersistentToMemory extends LoadingState {}
class MovePreviewEmailEMLContentFromPersistentToMemorySuccess extends UIState {}
class MovePreviewEmailEMLContentFromPersistentToMemoryFailure extends FeatureFailure {
MovePreviewEmailEMLContentFromPersistentToMemoryFailure(dynamic exception) : super(exception: exception);
}
@@ -0,0 +1,37 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
class ParsingEmailByBlobId extends LoadingState {}
class ParseEmailByBlobIdSuccess extends UIState {
final AccountId accountId;
final Session session;
final String ownEmailAddress;
final Email email;
final Id blobId;
ParseEmailByBlobIdSuccess({
required this.email,
required this.blobId,
required this.accountId,
required this.session,
required this.ownEmailAddress,
});
@override
List<Object?> get props => [
email,
blobId,
accountId,
session,
ownEmailAddress,
];
}
class ParseEmailByBlobIdFailure extends FeatureFailure {
ParseEmailByBlobIdFailure(dynamic exception) : super(exception: exception);
}
@@ -0,0 +1,38 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class PreviewingEmailFromEmlFile extends LoadingState {}
class PreviewEmailFromEmlFileSuccess extends UIState {
final EMLPreviewer emlPreviewer;
final AccountId accountId;
final Session session;
final String ownEmailAddress;
final AppLocalizations appLocalizations;
PreviewEmailFromEmlFileSuccess(
this.emlPreviewer,
this.accountId,
this.session,
this.ownEmailAddress,
this.appLocalizations,
);
@override
List<Object?> get props => [
emlPreviewer,
accountId,
session,
ownEmailAddress,
appLocalizations,
];
}
class PreviewEmailFromEmlFileFailure extends FeatureFailure {
PreviewEmailFromEmlFileFailure(dynamic exception)
: super(exception: exception);
}
@@ -0,0 +1,11 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
class RemovingPreviewEmailEMLContentShared extends LoadingState {}
class RemovePreviewEmailEMLContentSharedSuccess extends UIState {}
class RemovePreviewEmailEMLContentSharedFailure extends FeatureFailure {
RemovePreviewEmailEMLContentSharedFailure(dynamic exception) : super(exception: exception);
}
@@ -0,0 +1,88 @@
import 'dart:async';
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:core/utils/app_logger.dart';
import 'package:dartz/dartz.dart';
import 'package:dio/dio.dart';
import 'package:jmap_dart_client/jmap/account_id.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/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/download/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/download/domain/repository/download_repository.dart';
class DownloadAllAttachmentsForWebInteractor {
const DownloadAllAttachmentsForWebInteractor(
this._downloadRepository,
this._accountRepository,
this._authenticationOIDCRepository,
this.credentialRepository,
);
final DownloadRepository _downloadRepository;
final AccountRepository _accountRepository;
final AuthenticationOIDCRepository _authenticationOIDCRepository;
final CredentialRepository credentialRepository;
Stream<Either<Failure, Success>> execute(
AccountId accountId,
EmailId emailId,
String baseDownloadAllUrl,
Attachment attachment,
DownloadTaskId taskId, {
StreamController<Either<Failure, Success>>? onReceiveController,
CancelToken? cancelToken,
}) async* {
try {
yield Right(StartDownloadAllAttachmentsForWeb(
taskId,
attachment,
));
onReceiveController?.add(Right(StartDownloadAllAttachmentsForWeb(
taskId,
attachment,
cancelToken: cancelToken,
)));
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),
);
}
await _downloadRepository.downloadAllAttachmentsForWeb(
accountId,
emailId,
baseDownloadAllUrl,
attachment.name!,
accountRequest,
taskId,
onReceiveController: onReceiveController,
cancelToken: cancelToken
);
yield Right(DownloadAllAttachmentsForWebSuccess(taskId: taskId));
} catch (e) {
logError('DownloadAllAttachmentsForWebInteractor::execute():EXCEPTION: $e');
yield Left(DownloadAllAttachmentsForWebFailure(
exception: e,
taskId: taskId,
cancelToken: cancelToken,
));
}
}
}
@@ -0,0 +1,128 @@
import 'dart:async';
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/string_convert.dart';
import 'package:dartz/dartz.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:model/download/download_task_id.dart';
import 'package:model/email/attachment.dart';
import 'package:tmail_ui_user/features/download/domain/model/download_source_view.dart';
import 'package:tmail_ui_user/features/download/domain/state/download_attachment_for_web_state.dart';
import 'package:tmail_ui_user/features/download/domain/state/download_and_get_html_content_from_attachment_state.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/download_attachment_for_web_interactor.dart';
class DownloadAndGetHtmlContentFromAttachmentInteractor {
DownloadAndGetHtmlContentFromAttachmentInteractor(
this._downloadAttachmentForWebInteractor,
);
final DownloadAttachmentForWebInteractor _downloadAttachmentForWebInteractor;
Stream<Either<Failure, Success>> execute(
AccountId accountId,
Session session,
Attachment attachment,
DownloadTaskId taskId,
String baseDownloadUrl,
TransformConfiguration transformConfiguration, {
DownloadSourceView? sourceView,
}) async* {
try {
yield Right(DownloadAndGettingHtmlContentFromAttachment(
blobId: attachment.blobId,
sourceView: sourceView,
));
final downloadState = await _downloadAttachmentForWebInteractor.execute(
taskId,
attachment,
accountId,
baseDownloadUrl,
sourceView: sourceView,
).last;
Either<Failure, Success>? sanitizeState;
await downloadState.fold(
(failure) {
sanitizeState = Left(DownloadAndGetHtmlContentFromAttachmentFailure(
exception: failure is FeatureFailure ? failure.exception : null,
blobId: attachment.blobId,
sourceView: sourceView,
));
},
(success) async {
if (success is! DownloadAttachmentForWebSuccess) {
sanitizeState = Right(DownloadAndGettingHtmlContentFromAttachment(
blobId: attachment.blobId,
sourceView: sourceView,
));
} else {
final htmlContent = StringConvert.decodeFromBytes(
success.bytes,
charset: success.attachment.charset,
isHtml: true,
);
sanitizeState = await _sanitizeHtmlContent(
htmlContent,
transformConfiguration,
attachment,
accountId,
session,
sourceView,
);
}
},
);
if (sanitizeState != null) {
yield sanitizeState!;
} else {
yield Left(DownloadAndGetHtmlContentFromAttachmentFailure(
exception: null,
blobId: attachment.blobId,
sourceView: sourceView,
));
}
} catch (e) {
logError('GetHtmlContentFromAttachmentInteractor:exception: $e');
yield Left(DownloadAndGetHtmlContentFromAttachmentFailure(
exception: e,
blobId: attachment.blobId,
sourceView: sourceView,
));
}
}
Future<Either<Failure, Success>?> _sanitizeHtmlContent(
String htmlContent,
TransformConfiguration transformConfiguration,
Attachment attachment,
AccountId accountId,
Session session,
DownloadSourceView? sourceView,
) async {
try {
final sanitizedHtmlContent = await _downloadAttachmentForWebInteractor
.downloadRepository
.sanitizeHtmlContent(htmlContent, transformConfiguration);
return Right(DownloadAndGetHtmlContentFromAttachmentSuccess(
sanitizedHtmlContent: sanitizedHtmlContent,
htmlAttachmentTitle: attachment.generateFileName(),
attachment: attachment,
accountId: accountId,
session: session,
sourceView: sourceView,
));
} catch (e) {
return Left(DownloadAndGetHtmlContentFromAttachmentFailure(
exception: e,
blobId: attachment.blobId,
sourceView: sourceView,
));
}
}
}
@@ -0,0 +1,101 @@
import 'dart:async';
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/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/download/download_task_id.dart';
import 'package:model/email/attachment.dart';
import 'package:tmail_ui_user/features/download/domain/model/download_source_view.dart';
import 'package:tmail_ui_user/features/download/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/download/domain/repository/download_repository.dart';
class DownloadAttachmentForWebInteractor {
final DownloadRepository downloadRepository;
final CredentialRepository credentialRepository;
final AccountRepository _accountRepository;
final AuthenticationOIDCRepository _authenticationOIDCRepository;
DownloadAttachmentForWebInteractor(
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,
DownloadSourceView? sourceView,
}) async* {
try {
final loadingState = Right<Failure, Success>(
StartDownloadAttachmentForWeb(
taskId,
attachment,
cancelToken,
previewerSupported,
sourceView,
),
);
yield loadingState;
onReceiveController?.add(loadingState);
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 bytesDownloaded = await downloadRepository.downloadAttachmentForWeb(
taskId,
attachment,
accountId,
baseDownloadUrl,
accountRequest,
onReceiveController: onReceiveController,
cancelToken: cancelToken
);
yield Right<Failure, Success>(
DownloadAttachmentForWebSuccess(
taskId,
attachment,
bytesDownloaded,
previewerSupported,
sourceView,
)
);
} catch (exception) {
yield Left<Failure, Success>(
DownloadAttachmentForWebFailure(
attachment: attachment,
taskId: taskId,
sourceView: sourceView,
cancelToken: cancelToken,
exception: exception,
)
);
}
}
}
@@ -0,0 +1,68 @@
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:dio/dio.dart';
import 'package:jmap_dart_client/jmap/account_id.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/account/authentication_type.dart';
import 'package:model/account/password.dart';
import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
import 'package:tmail_ui_user/features/download/domain/state/export_all_attachments_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';
class ExportAllAttachmentsInteractor {
const ExportAllAttachmentsInteractor(
this._downloadRepository,
this._accountRepository,
this._authenticationOIDCRepository,
this._credentialRepository,
);
final DownloadRepository _downloadRepository;
final AccountRepository _accountRepository;
final AuthenticationOIDCRepository _authenticationOIDCRepository;
final CredentialRepository _credentialRepository;
Stream<Either<Failure, Success>> execute(
AccountId accountId,
EmailId emailId,
String baseDownloadAllUrl,
String outputFileName,
CancelToken cancelToken,
) async* {
try {
yield Right(ExportingAllAttachments());
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 result = await _downloadRepository.exportAllAttachments(
accountId,
emailId,
baseDownloadAllUrl,
outputFileName,
accountRequest,
cancelToken,
);
yield Right(ExportAllAttachmentsSuccess(result));
} catch (e) {
logError('ExportAllAttachmentsInteractor::execute():EXCEPTION: $e');
yield Left(ExportAllAttachmentsFailure(exception: e));
}
}
}
@@ -0,0 +1,64 @@
import 'dart:async';
import 'package:core/core.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/core/user_name.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
import 'package:tmail_ui_user/features/download/domain/state/export_attachment_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';
class ExportAttachmentInteractor {
final DownloadRepository _downloadRepository;
final CredentialRepository _credentialRepository;
final AccountRepository _accountRepository;
final AuthenticationOIDCRepository _authenticationOIDCRepository;
ExportAttachmentInteractor(
this._downloadRepository,
this._credentialRepository,
this._accountRepository,
this._authenticationOIDCRepository,
);
Stream<Either<Failure, Success>> execute(
Attachment attachment,
AccountId accountId,
String baseDownloadUrl,
CancelToken cancelToken
) 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 downloadedResponse = await _downloadRepository.exportAttachment(
attachment,
accountId,
baseDownloadUrl,
accountRequest,
cancelToken
);
yield Right<Failure, Success>(ExportAttachmentSuccess(downloadedResponse));
} catch (exception) {
log('ExportAttachmentInteractor::execute(): exception: $exception');
yield Left<Failure, Success>(ExportAttachmentFailure(exception));
}
}
}
@@ -0,0 +1,57 @@
import 'dart:async';
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.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/standardize_html_sanitizing_transformers.dart';
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
import 'package:core/utils/string_convert.dart';
import 'package:dartz/dartz.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:tmail_ui_user/features/download/domain/state/get_html_content_from_upload_file_state.dart';
import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart';
class GetHtmlContentFromUploadFileInteractor {
final DownloadRepository _downloadRepository;
GetHtmlContentFromUploadFileInteractor(this._downloadRepository);
Stream<Either<Failure, Success>> execute({
required UploadFileState uploadFile,
required Session? session,
required AccountId? accountId,
}) async* {
try {
yield Right(GettingHtmlContentFromUploadFile());
final htmlContent = StringConvert.decodeFromBytes(
uploadFile.file!.bytes!,
charset: uploadFile.attachment!.charset,
isHtml: true,
);
final sanitizedHtmlContent =
await _downloadRepository.sanitizeHtmlContent(
htmlContent,
TransformConfiguration.create(
customDomTransformers: [SanitizeHyperLinkTagInHtmlTransformer()],
customTextTransformers: [
const StandardizeHtmlSanitizingTransformers()
],
),
);
yield Right(GetHtmlContentFromUploadFileSuccess(
sanitizedHtmlContent: sanitizedHtmlContent,
htmlAttachmentTitle: uploadFile.attachment!.generateFileName(),
attachment: uploadFile.attachment!,
accountId: accountId,
session: session,
));
} catch (e) {
yield Left(GetHtmlContentFromUploadFileFailure(exception: e));
}
}
}
@@ -0,0 +1,27 @@
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/download/domain/state/get_preview_email_eml_content_shared_state.dart';
import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
class GetPreviewEmailEMLContentSharedInteractor {
final DownloadRepository _downloadRepository;
const GetPreviewEmailEMLContentSharedInteractor(this._downloadRepository);
Stream<Either<Failure, Success>> execute(String keyStored) async* {
try {
yield Right<Failure, Success>(GettingPreviewEmailEMLContentShared());
final previewEMLContent = await _downloadRepository
.getPreviewEmailEMLContentShared(keyStored);
yield Right<Failure, Success>(GetPreviewEmailEMLContentSharedSuccess(previewEMLContent));
} catch (e) {
yield Left<Failure, Success>(GetPreviewEmailEMLContentSharedFailure(
keyStored: keyStored,
exception: e,
));
}
}
}
@@ -0,0 +1,24 @@
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/download/domain/state/get_preview_eml_content_in_memory_state.dart';
import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
class GetPreviewEmlContentInMemoryInteractor {
final DownloadRepository _downloadRepository;
const GetPreviewEmlContentInMemoryInteractor(this._downloadRepository);
Stream<Either<Failure, Success>> execute(String keyStored) async* {
try {
yield Right<Failure, Success>(GettingPreviewEMLContentInMemory());
final emlPreviewer = await _downloadRepository.getPreviewEMLContentInMemory(keyStored);
yield Right<Failure, Success>(GetPreviewEMLContentInMemorySuccess(emlPreviewer));
} catch (e) {
yield Left<Failure, Success>(GetPreviewEMLContentInMemoryFailure(
keyStored: keyStored,
exception: e,
));
}
}
}
@@ -0,0 +1,27 @@
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/download/domain/state/move_preview_eml_content_from_persistent_to_memory_state.dart';
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart';
import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
class MovePreviewEmlContentFromPersistentToMemoryInteractor {
final DownloadRepository _downloadRepository;
const MovePreviewEmlContentFromPersistentToMemoryInteractor(
this._downloadRepository,
);
Stream<Either<Failure, Success>> execute(EMLPreviewer emlPreviewer) async* {
try {
yield Right<Failure, Success>(MovingPreviewEmailEMLContentFromPersistentToMemory());
await Future.wait([
_downloadRepository.removePreviewEmailEMLContentShared(emlPreviewer.id),
_downloadRepository.storePreviewEMLContentToSessionStorage(emlPreviewer),
]);
yield Right<Failure, Success>(MovePreviewEmailEMLContentFromPersistentToMemorySuccess());
} catch (e) {
yield Left<Failure, Success>(MovePreviewEmailEMLContentFromPersistentToMemoryFailure(e));
}
}
}
@@ -0,0 +1,40 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:dartz/dartz.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:tmail_ui_user/features/download/domain/state/parse_email_by_blob_id_state.dart';
import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
class ParseEmailByBlobIdInteractor {
final DownloadRepository _downloadRepository;
const ParseEmailByBlobIdInteractor(this._downloadRepository);
Stream<Either<Failure, Success>> execute(
AccountId accountId,
Session session,
String ownEmailAddress,
Id blobId,
) async* {
try {
yield Right<Failure, Success>(ParsingEmailByBlobId());
final emailParsed = await _downloadRepository.parseEmailByBlobIds(
accountId,
{blobId},
);
yield Right<Failure, Success>(ParseEmailByBlobIdSuccess(
email: emailParsed.first,
blobId: blobId,
session: session,
accountId: accountId,
ownEmailAddress: ownEmailAddress,
));
} catch (e) {
yield Left<Failure, Success>(ParseEmailByBlobIdFailure(e));
}
}
}
@@ -0,0 +1,53 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:core/utils/platform_info.dart';
import 'package:dartz/dartz.dart';
import 'package:tmail_ui_user/features/email/domain/model/preview_email_eml_request.dart';
import 'package:tmail_ui_user/features/download/domain/state/preview_email_from_eml_file_state.dart';
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart';
import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
class PreviewEmailFromEmlFileInteractor {
final DownloadRepository _downloadRepository;
const PreviewEmailFromEmlFileInteractor(this._downloadRepository);
Stream<Either<Failure, Success>> execute(
PreviewEmailEMLRequest previewEmailEMLRequest,
) async* {
try {
yield Right<Failure, Success>(PreviewingEmailFromEmlFile());
final previewEMLContent = await _downloadRepository
.generatePreviewEmailEMLContent(previewEmailEMLRequest);
final emlPreviewer = EMLPreviewer(
id: previewEmailEMLRequest.keyStored,
title: previewEmailEMLRequest.title,
content: previewEMLContent,
);
if (PlatformInfo.isWeb) {
if (previewEmailEMLRequest.isShared) {
await _downloadRepository.sharePreviewEmailEMLContent(emlPreviewer);
} else {
await _downloadRepository.storePreviewEMLContentToSessionStorage(
emlPreviewer,
);
}
}
yield Right<Failure, Success>(
PreviewEmailFromEmlFileSuccess(
emlPreviewer,
previewEmailEMLRequest.accountId,
previewEmailEMLRequest.session,
previewEmailEMLRequest.ownEmailAddress,
previewEmailEMLRequest.appLocalizations,
),
);
} catch (e) {
yield Left<Failure, Success>(PreviewEmailFromEmlFileFailure(e));
}
}
}
@@ -0,0 +1,21 @@
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/download/domain/state/remove_preview_email_eml_content_shared_state.dart';
import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
class RemovePreviewEmailEmlContentSharedInteractor {
final DownloadRepository _downloadRepository;
const RemovePreviewEmailEmlContentSharedInteractor(this._downloadRepository);
Stream<Either<Failure, Success>> execute(String keyStored) async* {
try {
yield Right<Failure, Success>(RemovingPreviewEmailEMLContentShared());
await _downloadRepository.removePreviewEmailEMLContentShared(keyStored);
yield Right<Failure, Success>(RemovePreviewEmailEMLContentSharedSuccess());
} catch (e) {
yield Left<Failure, Success>(RemovePreviewEmailEMLContentSharedFailure(e));
}
}
}