TF-4050 refactor: move attachment preview & download logic to download controller

This commit is contained in:
dab246
2025-10-23 12:14:39 +07:00
committed by Dat H. Pham
parent 1398a30ed6
commit 92286b48e1
33 changed files with 1055 additions and 651 deletions
+3
View File
@@ -35,6 +35,8 @@ export 'presentation/utils/app_toast.dart';
export 'presentation/utils/html_transformer/html_transform.dart';
export 'presentation/utils/html_transformer/transform_configuration.dart';
export 'presentation/utils/html_transformer/text/persist_preformatted_text_transformer.dart';
export 'presentation/utils/html_transformer/text/standardize_html_sanitizing_transformers.dart';
export 'presentation/utils/html_transformer/dom/sanitize_hyper_link_tag_in_html_transformers.dart';
export 'data/utils/device_manager.dart';
export 'utils/app_logger.dart';
export 'utils/benchmark.dart';
@@ -56,6 +58,7 @@ export 'utils/mail/named_address.dart';
export 'utils/application_manager.dart';
export 'utils/preview_eml_file_utils.dart';
export 'utils/logger/log_tracking.dart';
export 'utils/html/html_utils.dart';
// Views
export 'presentation/views/text/slogan_builder.dart';
@@ -26,11 +26,9 @@ import 'package:tmail_ui_user/features/email/data/datasource/print_file_datasour
import 'package:tmail_ui_user/features/email/domain/model/detailed_email.dart';
import 'package:tmail_ui_user/features/email/domain/model/email_print.dart';
import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart';
import 'package:tmail_ui_user/features/email/domain/model/preview_email_eml_request.dart';
import 'package:tmail_ui_user/features/email/domain/model/restore_deleted_message_request.dart';
import 'package:tmail_ui_user/features/email/domain/model/view_entire_message_request.dart';
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart';
import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart';
import 'package:tmail_ui_user/features/mailbox/data/model/state_type.dart';
import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart';
@@ -496,51 +494,6 @@ class EmailRepositoryImpl extends EmailRepository {
return _printFileDataSource.printEmail(emailPrint);
}
@override
Future<List<Email>> parseEmailByBlobIds(AccountId accountId, Set<Id> blobIds) {
return emailDataSource[DataSourceType.network]!.parseEmailByBlobIds(accountId, blobIds);
}
@override
Future<String> generatePreviewEmailEMLContent(PreviewEmailEMLRequest previewEmailEMLRequest) {
return emailDataSource[DataSourceType.network]!.generatePreviewEmailEMLContent(previewEmailEMLRequest);
}
@override
Future<void> sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer) {
return emailDataSource[DataSourceType.local]!.sharePreviewEmailEMLContent(emlPreviewer);
}
@override
Future<EMLPreviewer> getPreviewEmailEMLContentShared(String keyStored) {
return emailDataSource[DataSourceType.local]!.getPreviewEmailEMLContentShared(keyStored);
}
@override
Future<void> removePreviewEmailEMLContentShared(String keyStored) {
return emailDataSource[DataSourceType.local]!.removePreviewEmailEMLContentShared(keyStored);
}
@override
Future<void> storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer) {
return emailDataSource[DataSourceType.session]!.storePreviewEMLContentToSessionStorage(emlPreviewer);
}
@override
Future<EMLPreviewer> getPreviewEMLContentInMemory(String keyStored) {
return emailDataSource[DataSourceType.session]!.getPreviewEMLContentInMemory(keyStored);
}
@override
Future<String> sanitizeHtmlContent(
String htmlContent,
TransformConfiguration configuration
) {
return _htmlDataSource.transformHtmlEmailContent(
htmlContent,
configuration);
}
@override
Future<DownloadedResponse> exportAllAttachments(
AccountId accountId,
@@ -2,11 +2,13 @@ import 'package:equatable/equatable.dart';
import 'package:flutter/material.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';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class PreviewEmailEMLRequest with EquatableMixin {
final AccountId accountId;
final Session session;
final String ownEmailAddress;
final Id blobId;
final Email email;
@@ -17,6 +19,7 @@ class PreviewEmailEMLRequest with EquatableMixin {
PreviewEmailEMLRequest({
required this.accountId,
required this.session,
required this.ownEmailAddress,
required this.blobId,
required this.email,
@@ -35,6 +38,7 @@ class PreviewEmailEMLRequest with EquatableMixin {
@override
List<Object?> get props => [
accountId,
session,
ownEmailAddress,
blobId,
email,
@@ -21,10 +21,8 @@ import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'
import 'package:tmail_ui_user/features/email/domain/model/detailed_email.dart';
import 'package:tmail_ui_user/features/email/domain/model/email_print.dart';
import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart';
import 'package:tmail_ui_user/features/email/domain/model/preview_email_eml_request.dart';
import 'package:tmail_ui_user/features/email/domain/model/restore_deleted_message_request.dart';
import 'package:tmail_ui_user/features/email/domain/model/view_entire_message_request.dart';
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart';
import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart';
abstract class EmailRepository {
@@ -172,26 +170,6 @@ abstract class EmailRepository {
Future<void> printEmail(EmailPrint emailPrint);
Future<List<Email>> parseEmailByBlobIds(AccountId accountId, Set<Id> blobIds);
Future<String> generatePreviewEmailEMLContent(PreviewEmailEMLRequest previewEmailEMLRequest);
Future<void> sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer);
Future<EMLPreviewer> getPreviewEmailEMLContentShared(String keyStored);
Future<void> removePreviewEmailEMLContentShared(String keyStored);
Future<void> storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer);
Future<EMLPreviewer> getPreviewEMLContentInMemory(String keyStored);
Future<String> sanitizeHtmlContent(
String htmlContent,
TransformConfiguration configuration);
Future<DownloadedResponse> exportAllAttachments(
AccountId accountId,
EmailId emailId,
@@ -1,21 +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(this.email, this.blobId);
ParseEmailByBlobIdSuccess({
required this.email,
required this.blobId,
required this.accountId,
required this.session,
required this.ownEmailAddress,
});
@override
List<Object> get props => [email, blobId];
List<Object?> get props => [
email,
blobId,
accountId,
session,
ownEmailAddress,
];
}
class ParseEmailByBlobIdFailure extends FeatureFailure {
ParseEmailByBlobIdFailure(dynamic exception) : super(exception: exception);
}
}
@@ -1,20 +1,34 @@
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';
class PreviewingEmailFromEmlFile extends LoadingState {}
class PreviewEmailFromEmlFileSuccess extends UIState {
final EMLPreviewer emlPreviewer;
final AccountId? accountId;
final Session? session;
final String ownEmailAddress;
PreviewEmailFromEmlFileSuccess(this.emlPreviewer);
PreviewEmailFromEmlFileSuccess(
this.emlPreviewer,
this.accountId,
this.session,
this.ownEmailAddress,
);
@override
List<Object?> get props => [emlPreviewer];
List<Object?> get props => [
emlPreviewer,
accountId,
session,
ownEmailAddress,
];
}
class PreviewEmailFromEmlFileFailure extends FeatureFailure {
PreviewEmailFromEmlFileFailure(dynamic exception) : super(exception: exception);
}
PreviewEmailFromEmlFileFailure(dynamic exception)
: super(exception: exception);
}
@@ -1,6 +0,0 @@
import 'package:core/presentation/state/failure.dart';
class PreviewPDFFileFailure extends FeatureFailure {
PreviewPDFFileFailure(dynamic exception) : super(exception: exception);
}
@@ -18,13 +18,13 @@ import 'package:tmail_ui_user/features/login/domain/repository/credential_reposi
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart';
class DownloadAttachmentForWebInteractor {
final DownloadRepository _downloadRepository;
final DownloadRepository downloadRepository;
final CredentialRepository credentialRepository;
final AccountRepository _accountRepository;
final AuthenticationOIDCRepository _authenticationOIDCRepository;
DownloadAttachmentForWebInteractor(
this._downloadRepository,
this.downloadRepository,
this.credentialRepository,
this._accountRepository,
this._authenticationOIDCRepository);
@@ -56,7 +56,7 @@ class DownloadAttachmentForWebInteractor {
);
}
final bytesDownloaded = await _downloadRepository.downloadAttachmentForWeb(
final bytesDownloaded = await downloadRepository.downloadAttachmentForWeb(
taskId,
attachment,
accountId,
@@ -9,7 +9,6 @@ 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';
@@ -17,11 +16,9 @@ import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment
class GetHtmlContentFromAttachmentInteractor {
GetHtmlContentFromAttachmentInteractor(
this._downloadAttachmentForWebInteractor,
this._emailRepository,
);
final DownloadAttachmentForWebInteractor _downloadAttachmentForWebInteractor;
final EmailRepository _emailRepository;
Stream<Either<Failure, Success>> execute(
AccountId accountId,
@@ -89,10 +86,10 @@ class GetHtmlContentFromAttachmentInteractor {
Attachment attachment,
) async {
try {
final sanitizedHtmlContent = await _emailRepository.sanitizeHtmlContent(
htmlContent,
transformConfiguration,
);
final sanitizedHtmlContent = await _downloadAttachmentForWebInteractor
.downloadRepository
.sanitizeHtmlContent(htmlContent, transformConfiguration);
return Right(GetHtmlContentFromAttachmentSuccess(
sanitizedHtmlContent: sanitizedHtmlContent,
htmlAttachmentTitle: attachment.generateFileName(),
@@ -1,19 +1,19 @@
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/repository/email_repository.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_preview_email_eml_content_shared_state.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart';
class GetPreviewEmailEMLContentSharedInteractor {
final EmailRepository _emailRepository;
final DownloadRepository _downloadRepository;
const GetPreviewEmailEMLContentSharedInteractor(this._emailRepository);
const GetPreviewEmailEMLContentSharedInteractor(this._downloadRepository);
Stream<Either<Failure, Success>> execute(String keyStored) async* {
try {
yield Right<Failure, Success>(GettingPreviewEmailEMLContentShared());
final previewEMLContent = await _emailRepository
final previewEMLContent = await _downloadRepository
.getPreviewEmailEMLContentShared(keyStored);
yield Right<Failure, Success>(GetPreviewEmailEMLContentSharedSuccess(previewEMLContent));
@@ -1,18 +1,18 @@
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/repository/email_repository.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_preview_eml_content_in_memory_state.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart';
class GetPreviewEmlContentInMemoryInteractor {
final EmailRepository _emailRepository;
final DownloadRepository _downloadRepository;
const GetPreviewEmlContentInMemoryInteractor(this._emailRepository);
const GetPreviewEmlContentInMemoryInteractor(this._downloadRepository);
Stream<Either<Failure, Success>> execute(String keyStored) async* {
try {
yield Right<Failure, Success>(GettingPreviewEMLContentInMemory());
final emlPreviewer = await _emailRepository.getPreviewEMLContentInMemory(keyStored);
final emlPreviewer = await _downloadRepository.getPreviewEMLContentInMemory(keyStored);
yield Right<Failure, Success>(GetPreviewEMLContentInMemorySuccess(emlPreviewer));
} catch (e) {
yield Left<Failure, Success>(GetPreviewEMLContentInMemoryFailure(
@@ -1,21 +1,23 @@
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/repository/email_repository.dart';
import 'package:tmail_ui_user/features/email/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/mailbox_dashboard/domain/repository/download_repository.dart';
class MovePreviewEmlContentFromPersistentToMemoryInteractor {
final EmailRepository _emailRepository;
final DownloadRepository _downloadRepository;
const MovePreviewEmlContentFromPersistentToMemoryInteractor(this._emailRepository);
const MovePreviewEmlContentFromPersistentToMemoryInteractor(
this._downloadRepository,
);
Stream<Either<Failure, Success>> execute(EMLPreviewer emlPreviewer) async* {
try {
yield Right<Failure, Success>(MovingPreviewEmailEMLContentFromPersistentToMemory());
await Future.wait([
_emailRepository.removePreviewEmailEMLContentShared(emlPreviewer.id),
_emailRepository.storePreviewEMLContentToSessionStorage(emlPreviewer),
_downloadRepository.removePreviewEmailEMLContentShared(emlPreviewer.id),
_downloadRepository.storePreviewEMLContentToSessionStorage(emlPreviewer),
]);
yield Right<Failure, Success>(MovePreviewEmailEMLContentFromPersistentToMemorySuccess());
} catch (e) {
@@ -3,29 +3,38 @@ 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:tmail_ui_user/features/email/domain/repository/email_repository.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:tmail_ui_user/features/email/domain/state/parse_email_by_blob_id_state.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart';
class ParseEmailByBlobIdInteractor {
final EmailRepository _emailRepository;
final DownloadRepository _downloadRepository;
const ParseEmailByBlobIdInteractor(this._emailRepository);
const ParseEmailByBlobIdInteractor(this._downloadRepository);
Stream<Either<Failure, Success>> execute(AccountId accountId, Id blobId) async* {
Stream<Either<Failure, Success>> execute(
AccountId accountId,
Session session,
String ownEmailAddress,
Id blobId,
) async* {
try {
yield Right<Failure, Success>(ParsingEmailByBlobId());
final emailParsed = await _emailRepository.parseEmailByBlobIds(
final emailParsed = await _downloadRepository.parseEmailByBlobIds(
accountId,
{blobId},
);
yield Right<Failure, Success>(ParseEmailByBlobIdSuccess(
emailParsed.first,
blobId,
email: emailParsed.first,
blobId: blobId,
session: session,
accountId: accountId,
ownEmailAddress: ownEmailAddress,
));
} catch (e) {
yield Left<Failure, Success>(ParseEmailByBlobIdFailure(e));
}
}
}
}
@@ -3,14 +3,14 @@ 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/email/domain/repository/email_repository.dart';
import 'package:tmail_ui_user/features/email/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/mailbox_dashboard/domain/repository/download_repository.dart';
class PreviewEmailFromEmlFileInteractor {
final EmailRepository _emailRepository;
final DownloadRepository _downloadRepository;
const PreviewEmailFromEmlFileInteractor(this._emailRepository);
const PreviewEmailFromEmlFileInteractor(this._downloadRepository);
Stream<Either<Failure, Success>> execute(
PreviewEmailEMLRequest previewEmailEMLRequest,
@@ -18,7 +18,7 @@ class PreviewEmailFromEmlFileInteractor {
try {
yield Right<Failure, Success>(PreviewingEmailFromEmlFile());
final previewEMLContent = await _emailRepository
final previewEMLContent = await _downloadRepository
.generatePreviewEmailEMLContent(previewEmailEMLRequest);
final emlPreviewer = EMLPreviewer(
@@ -29,13 +29,22 @@ class PreviewEmailFromEmlFileInteractor {
if (PlatformInfo.isWeb) {
if (previewEmailEMLRequest.isShared) {
await _emailRepository.sharePreviewEmailEMLContent(emlPreviewer);
await _downloadRepository.sharePreviewEmailEMLContent(emlPreviewer);
} else {
await _emailRepository.storePreviewEMLContentToSessionStorage(emlPreviewer);
await _downloadRepository.storePreviewEMLContentToSessionStorage(
emlPreviewer,
);
}
}
yield Right<Failure, Success>(PreviewEmailFromEmlFileSuccess(emlPreviewer));
yield Right<Failure, Success>(
PreviewEmailFromEmlFileSuccess(
emlPreviewer,
previewEmailEMLRequest.accountId,
previewEmailEMLRequest.session,
previewEmailEMLRequest.ownEmailAddress,
),
);
} catch (e) {
yield Left<Failure, Success>(PreviewEmailFromEmlFileFailure(e));
}
@@ -1,18 +1,18 @@
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/repository/email_repository.dart';
import 'package:tmail_ui_user/features/email/domain/state/remove_preview_email_eml_content_shared_state.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart';
class RemovePreviewEmailEmlContentSharedInteractor {
final EmailRepository _emailRepository;
final DownloadRepository _downloadRepository;
const RemovePreviewEmailEmlContentSharedInteractor(this._emailRepository);
const RemovePreviewEmailEmlContentSharedInteractor(this._downloadRepository);
Stream<Either<Failure, Success>> execute(String keyStored) async* {
try {
yield Right<Failure, Success>(RemovingPreviewEmailEMLContentShared());
await _emailRepository.removePreviewEmailEMLContentShared(keyStored);
await _downloadRepository.removePreviewEmailEMLContentShared(keyStored);
yield Right<Failure, Success>(RemovePreviewEmailEMLContentSharedSuccess());
} catch (e) {
yield Left<Failure, Success>(RemovePreviewEmailEMLContentSharedFailure(e));
@@ -3,11 +3,8 @@ import 'package:jmap_dart_client/jmap/mail/email/email.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_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/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/store_opened_email_interactor.dart';
import 'package:tmail_ui_user/features/email/presentation/bindings/email_interactor_bindings.dart';
@@ -30,9 +27,6 @@ class EmailBindings extends Bindings {
Get.find<GetAllIdentitiesInteractor>(),
Get.find<StoreOpenedEmailInteractor>(),
Get.find<PrintEmailInteractor>(),
Get.find<ParseEmailByBlobIdInteractor>(),
Get.find<PreviewEmailFromEmlFileInteractor>(),
Get.find<GetHtmlContentFromAttachmentInteractor>(),
Get.find<ExportAllAttachmentsInteractor>(),
currentEmailId: currentEmailId,
), tag: tag);
@@ -16,18 +16,14 @@ 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_attachment_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/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/get_stored_email_state_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/move_to_mailbox_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/store_opened_email_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart';
@@ -126,13 +122,7 @@ class EmailInteractorBindings extends InteractorsBindings {
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.find<EmailRepository>(),
));
Get.lazyPut(() => ExportAllAttachmentsInteractor(
Get.find<EmailRepository>(),
Get.find<AccountRepository>(),
@@ -2,15 +2,11 @@ import 'dart:async';
import 'dart:io';
import 'package:core/core.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:dartz/dartz.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
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';
@@ -36,12 +32,10 @@ import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart';
import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_manager.dart';
import 'package:tmail_ui_user/features/base/state/button_state.dart';
import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart';
import 'package:tmail_ui_user/features/email/domain/extensions/list_attachments_extension.dart';
import 'package:tmail_ui_user/features/email/domain/model/detailed_email.dart';
import 'package:tmail_ui_user/features/email/domain/model/event_action.dart';
import 'package:tmail_ui_user/features/email/domain/model/mark_read_action.dart';
import 'package:tmail_ui_user/features/email/domain/model/preview_email_eml_request.dart';
import 'package:tmail_ui_user/features/email/domain/model/send_receipt_to_sender_request.dart';
import 'package:tmail_ui_user/features/email/domain/model/view_entire_message_request.dart';
import 'package:tmail_ui_user/features/email/domain/state/calendar_event_accept_state.dart';
@@ -54,13 +48,9 @@ import 'package:tmail_ui_user/features/email/domain/state/export_all_attachments
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';
import 'package:tmail_ui_user/features/email/domain/state/get_entire_message_as_document_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/state/mark_as_email_read_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_star_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/parse_calendar_event_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/parse_email_by_blob_id_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/preview_email_from_eml_file_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/preview_pdf_file_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/print_email_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/send_receipt_to_sender_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/unsubscribe_email_state.dart';
@@ -71,13 +61,10 @@ import 'package:tmail_ui_user/features/email/domain/usecases/export_all_attachme
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/send_receipt_to_sender_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/store_opened_email_interactor.dart';
@@ -87,8 +74,8 @@ import 'package:tmail_ui_user/features/email/presentation/bindings/mdn_interacto
import 'package:tmail_ui_user/features/email/presentation/extensions/attachment_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/calendar_attendee_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/calendar_organizer_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/handle_open_attachment_list_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/handle_mail_action_by_shortcut_action_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/handle_open_attachment_list_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/update_attendance_status_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/model/blob_calendar_event.dart';
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
@@ -97,15 +84,13 @@ import 'package:tmail_ui_user/features/email/presentation/model/email_unsubscrib
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart';
import 'package:tmail_ui_user/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart';
import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/html_attachment_previewer.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/pdf_viewer/pdf_viewer.dart';
import 'package:tmail_ui_user/features/email_previewer/email_previewer_dialog_view.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/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/handle_preview_attachment_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';
@@ -133,8 +118,6 @@ import 'package:tmail_ui_user/main/utils/app_utils.dart';
class SingleEmailController extends BaseController with AppLoaderMixin {
final mailboxDashBoardController = Get.find<MailboxDashBoardController>();
final _downloadManager = Get.find<DownloadManager>();
final _printUtils = Get.find<PrintUtils>();
final GetEmailContentInteractor _getEmailContentInteractor;
final MarkAsEmailReadInteractor _markAsEmailReadInteractor;
@@ -143,9 +126,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
final GetAllIdentitiesInteractor _getAllIdentitiesInteractor;
final StoreOpenedEmailInteractor _storeOpenedEmailInteractor;
final PrintEmailInteractor _printEmailInteractor;
final ParseEmailByBlobIdInteractor _parseEmailByBlobIdInteractor;
final PreviewEmailFromEmlFileInteractor _previewEmailFromEmlFileInteractor;
final GetHtmlContentFromAttachmentInteractor _getHtmlContentFromAttachmentInteractor;
final ExportAllAttachmentsInteractor _exportAllAttachmentsInteractor;
final EmailId? _currentEmailId;
@@ -218,9 +198,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
this._getAllIdentitiesInteractor,
this._storeOpenedEmailInteractor,
this._printEmailInteractor,
this._parseEmailByBlobIdInteractor,
this._previewEmailFromEmlFileInteractor,
this._getHtmlContentFromAttachmentInteractor,
this._exportAllAttachmentsInteractor, {
EmailId? currentEmailId,
}) : _currentEmailId = currentEmailId;
@@ -281,24 +258,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
_handlePrintEmailSuccess(success);
} else if (success is CalendarEventReplySuccess) {
calendarEventSuccess(success);
} else if (success is ParseEmailByBlobIdSuccess) {
_handleParseEmailByBlobIdSuccess(success);
} else if (success is PreviewEmailFromEmlFileSuccess) {
_handlePreviewEmailFromEMLFileSuccess(success);
} else if (success is GetHtmlContentFromAttachmentSuccess) {
_updateAttachmentsViewState(success.attachment.blobId, Right(success));
Get.dialog(HtmlAttachmentPreviewer(
title: success.htmlAttachmentTitle,
htmlContent: success.sanitizedHtmlContent,
mailToClicked: openMailToLink,
downloadAttachmentClicked: () {
if (currentContext == null || attachments.isEmpty) return;
handleDownloadAttachmentAction(success.attachment);
},
responsiveUtils: responsiveUtils,
));
} else if (success is GettingHtmlContentFromAttachment) {
_updateAttachmentsViewState(success.attachment.blobId, Right(success));
} else {
super.handleSuccessViewState(success);
}
@@ -320,14 +279,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
_showMessageWhenEmailPrintingFailed(failure);
} else if (failure is CalendarEventReplyFailure) {
_calendarEventFailure(failure);
} else if (failure is ParseEmailByBlobIdFailure) {
_handleParseEmailByBlobIdFailure(failure);
} else if (failure is PreviewEmailFromEmlFileFailure) {
_handlePreviewEmailFromEMLFileFailure(failure);
} else if (failure is GetHtmlContentFromAttachmentFailure) {
_handleGetHtmlContentFromAttachmentFailure(failure);
} else if (failure is PreviewPDFFileFailure) {
_handlePreviewPDFFileFailure(failure);
} else {
super.handleFailureViewState(failure);
}
@@ -427,7 +378,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
mailboxDashBoardController.downloadController.downloadUIAction,
(action) {
if (action is UpdateAttachmentsViewStateAction) {
_updateAttachmentsViewState(action.blobId, Right(action.success));
_updateAttachmentsViewState(action.blobId, action.viewState);
}
},
));
@@ -921,20 +872,13 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
return isDownloadAllSupported() && attachments.length > 1;
}
void _downloadPDFFile(Uint8List bytes, String fileName) {
_downloadManager.createAnchorElementDownloadFileWeb(bytes, fileName);
}
void _printPDFFile(Uint8List bytes, String fileName) async {
await _printUtils.printPDFFile(bytes, fileName);
}
void _updateAttachmentsViewState(
Id? attachmentBlobId,
Either<Failure, Success> viewState) {
if (attachmentBlobId != null) {
attachmentsViewState[attachmentBlobId] = viewState;
}
Either<Failure, Success> viewState,
) {
if (attachmentBlobId != null) {
attachmentsViewState[attachmentBlobId] = viewState;
}
}
void moveToMailbox(PresentationEmail email) async {
@@ -1156,23 +1100,8 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
);
}
Future<void> openMailToLink(Uri? uri) async {
if (uri == null) return;
final navigationRouter = RouteUtils.generateNavigationRouterFromMailtoLink(uri.toString());
log('SingleEmailController::openMailToLink(): ${uri.toString()}');
if (!RouteUtils.canOpenComposerFromNavigationRouter(navigationRouter)) return;
mailboxDashBoardController.openComposer(
ComposerArguments.fromMailtoUri(
listEmailAddress: navigationRouter.listEmailAddress,
cc: navigationRouter.cc,
bcc: navigationRouter.bcc,
subject: navigationRouter.subject,
body: navigationRouter.body
)
);
}
Future<void> openMailToLink(Uri? uri) =>
mailboxDashBoardController.openMailToLink(uri);
void deleteEmailPermanently(PresentationEmail email) {
emailActionReactor.deleteEmailPermanently(
@@ -1697,7 +1626,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
} else if (PlatformInfo.isMobile) {
exportAttachment(attachment);
} else {
log('EmailView::handleDownloadAttachmentAction: THE PLATFORM IS SUPPORTED');
log('$runtimeType::handleDownloadAttachmentAction: THE PLATFORM IS SUPPORTED');
}
}
@@ -1715,258 +1644,18 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
}
void handleViewAttachmentAction(BuildContext context, Attachment attachment) {
if (PlatformInfo.isWeb && PlatformInfo.isCanvasKit && attachment.isPDFFile) {
previewPDFFileAction(context, attachment);
} else if (attachment.isEMLFile) {
previewEMLFileAction(attachment.blobId, AppLocalizations.of(context));
} else if (attachment.isHTMLFile) {
final attachmentEvaluation = evaluateAttachment(attachment);
if (!attachmentEvaluation.canDownloadAttachment) {
consumeState(Stream.value(Left(GetHtmlContentFromAttachmentFailure(
exception: null,
attachment: attachment,
))));
return;
}
consumeState(_getHtmlContentFromAttachmentInteractor.execute(
attachmentEvaluation.accountId!,
attachment,
DownloadTaskId(attachmentEvaluation.blobId!.value),
attachmentEvaluation.downloadUrl!,
TransformConfiguration.create(
customDomTransformers: [SanitizeHyperLinkTagInHtmlTransformer()],
customTextTransformers: [const StandardizeHtmlSanitizingTransformers()],
),
));
} else {
handleDownloadAttachmentAction(
attachment,
previewerSupported: attachmentPreviewSupported(attachment),
);
}
}
bool attachmentPreviewSupported(Attachment attachment) {
return attachment.isImage || attachment.isText || attachment.isJson;
}
({
bool canDownloadAttachment,
AccountId? accountId,
String? downloadUrl,
Id? blobId,
}) evaluateAttachment(
Attachment attachment,
) {
final accountId = mailboxDashBoardController.accountId.value;
dynamic downloadUrl;
try {
downloadUrl = mailboxDashBoardController.sessionCurrent
?.getDownloadUrl(jmapUrl: dynamicUrlInterceptors.jmapUrl);
} catch (e) {
logError('SingleEmailController::_getEmailContentAction(): $e');
downloadUrl = null;
}
final blobId = attachment.blobId;
if (accountId == null || downloadUrl == null || blobId == null) {
return (
canDownloadAttachment: false,
accountId: accountId,
downloadUrl: downloadUrl,
blobId: blobId,
);
}
return (
canDownloadAttachment: true,
accountId: accountId,
downloadUrl: downloadUrl,
blobId: blobId,
);
}
Future<void> previewPDFFileAction(BuildContext context, Attachment attachment) async {
if (accountId == null || session == null) {
appToast.showToastErrorMessage(
context,
AppLocalizations.of(context).noPreviewAvailable);
return;
}
try {
final downloadUrl = session!.getDownloadUrl(
jmapUrl: dynamicUrlInterceptors.jmapUrl,
);
await Get.generalDialog(
barrierColor: Colors.black.withValues(alpha: 0.8),
pageBuilder: (_, __, ___) {
return PointerInterceptor(
child: PDFViewer(
attachment: attachment,
accountId: accountId!,
downloadUrl: downloadUrl,
downloadAction: _downloadPDFFile,
printAction: _printPDFFile,
)
);
},
);
} catch (e) {
logError('SingleEmailController::previewPDFFileAction(): $e');
consumeState(Stream.value(Left(PreviewEmailFromEmlFileFailure(e))));
}
}
void previewEMLFileAction(Id? blobId, AppLocalizations appLocalizations) {
SmartDialog.showLoading(
msg: appLocalizations.loadingPleaseWait,
maskColor: Colors.black38,
);
if (accountId == null) {
consumeState(Stream.value(Left(ParseEmailByBlobIdFailure(NotFoundAccountIdException()))));
return;
}
if (blobId == null) {
consumeState(Stream.value(Left(ParseEmailByBlobIdFailure(NotFoundBlobIdException([])))));
return;
}
consumeState(_parseEmailByBlobIdInteractor.execute(accountId!, blobId));
}
void _handleParseEmailByBlobIdSuccess(ParseEmailByBlobIdSuccess success) {
if (session == null) {
consumeState(Stream.value(Left(PreviewEmailFromEmlFileFailure(NotFoundSessionException()))));
return;
}
if (accountId == null) {
consumeState(Stream.value(Left(PreviewEmailFromEmlFileFailure(NotFoundAccountIdException()))));
return;
}
if (currentContext == null) {
consumeState(Stream.value(Left(PreviewEmailFromEmlFileFailure(NotFoundContextException()))));
return;
}
try {
consumeState(_previewEmailFromEmlFileInteractor.execute(
PreviewEmailEMLRequest(
accountId: accountId!,
ownEmailAddress: ownEmailAddress,
blobId: success.blobId,
email: success.email,
locale: Localizations.localeOf(currentContext!),
appLocalizations: AppLocalizations.of(currentContext!),
baseDownloadUrl: session!.getDownloadUrl(jmapUrl: dynamicUrlInterceptors.jmapUrl),
),
));
} catch (e) {
logError('SingleEmailController::_handleParseEmailByBlobIdSuccess(): $e');
consumeState(Stream.value(Left(PreviewEmailFromEmlFileFailure(e))));
}
}
void _handleParseEmailByBlobIdFailure(ParseEmailByBlobIdFailure failure) {
SmartDialog.dismiss();
toastManager.showMessageFailure(failure);
}
void _handlePreviewPDFFileFailure(PreviewPDFFileFailure failure) {
SmartDialog.dismiss();
toastManager.showMessageFailure(failure);
}
void _handlePreviewEmailFromEMLFileFailure(PreviewEmailFromEmlFileFailure failure) {
SmartDialog.dismiss();
toastManager.showMessageFailure(failure);
}
void _handlePreviewEmailFromEMLFileSuccess(PreviewEmailFromEmlFileSuccess success) {
SmartDialog.dismiss();
if (PlatformInfo.isWeb) {
bool isOpen = HtmlUtils.openNewWindowByUrl(
RouteUtils.createUrlWebLocationBar(
AppRoutes.emailEMLPreviewer,
previewId: success.emlPreviewer.id
).toString(),
);
if (!isOpen) {
toastManager.showMessageFailure(PreviewEmailFromEmlFileFailure(CannotOpenNewWindowException()));
}
} else if (PlatformInfo.isMobile) {
if (currentContext == null) {
toastManager.showMessageFailure(PreviewEmailFromEmlFileFailure(NotFoundContextException()));
return;
}
if (PlatformInfo.isAndroid) {
showModalSheetToPreviewEMLAttachment(
currentContext!,
success.emlPreviewer,
);
} if (PlatformInfo.isIOS) {
showDialogToPreviewEMLAttachment(
currentContext!,
success.emlPreviewer,
);
}
}
}
void showModalSheetToPreviewEMLAttachment(
BuildContext context,
EMLPreviewer emlPreviewer,
) {
showModalBottomSheet(
mailboxDashBoardController.previewAttachmentAction(
context: context,
showDragHandle: true,
useSafeArea: true,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0),
topRight: Radius.circular(20.0),
),
),
builder: (_) {
return DraggableScrollableSheet(
initialChildSize: 1.0,
builder: (context, ___) => EmailPreviewerDialogView(
emlPreviewer: emlPreviewer,
imagePaths: imagePaths,
onMailtoDelegateAction: openMailToLink,
onPreviewEMLDelegateAction: (uri) => _openEMLPreviewer(context, uri),
onDownloadAttachmentDelegateAction: (uri) =>
_downloadAttachmentInEMLPreview(uri),
),
attachment: attachment,
onDownloadAttachment: (attachment) {
handleDownloadAttachmentAction(
attachment,
previewerSupported: attachment.isPreviewSupported,
);
},
);
}
void showDialogToPreviewEMLAttachment(BuildContext context, EMLPreviewer emlPreviewer) {
Get.dialog(
EmailPreviewerDialogView(
emlPreviewer: emlPreviewer,
imagePaths: imagePaths,
onMailtoDelegateAction: openMailToLink,
onPreviewEMLDelegateAction: (uri) => _openEMLPreviewer(context, uri),
onDownloadAttachmentDelegateAction: (uri) =>
_downloadAttachmentInEMLPreview(uri),
),
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
);
}
Future<void> _openEMLPreviewer(BuildContext context, Uri? uri) async {
log('SingleEmailController::_openEMLPreviewer:uri = $uri');
if (uri == null) return;
@@ -1975,7 +1664,10 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
log('SingleEmailController::_openEMLPreviewer:blobId = $blobId');
if (blobId.isEmpty) return;
previewEMLFileAction(Id(blobId), AppLocalizations.of(context));
mailboxDashBoardController.previewEMLFileAction(
appLocalizations: AppLocalizations.of(context),
blobId: Id(blobId),
);
}
Future<void> _downloadAttachmentInEMLPreview(Uri? uri) async {
@@ -2009,15 +1701,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
);
}
void _handleGetHtmlContentFromAttachmentFailure(GetHtmlContentFromAttachmentFailure failure) {
_updateAttachmentsViewState(failure.attachment.blobId, Left(failure));
if (currentOverlayContext != null && currentContext != null) {
appToast.showToastErrorMessage(
currentOverlayContext!,
AppLocalizations.of(currentContext!).thisHtmlAttachmentCannotBePreviewed);
}
}
void onHtmlContentClippedAction(bool isClipped) {
log('SingleEmailController::onHtmlContentClippedAction:isClipped = $isClipped');
isEmailContentClipped.value = isClipped;
@@ -2054,13 +1737,17 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
onSuccess: (success) {
SmartDialog.dismiss();
showDialogToPreviewEMLAttachment(
context,
EMLPreviewer(
mailboxDashBoardController.showDialogToPreviewEMLAttachment(
context: context,
emlPreviewer: EMLPreviewer(
id: presentationEmail.id?.asString ?? '',
title: presentationEmail.subject ?? '',
content: success.messageDocument,
),
imagePaths: imagePaths,
onMailtoAction: openMailToLink,
onPreviewAction: (uri) => _openEMLPreviewer(context, uri),
onDownloadAction: _downloadAttachmentInEMLPreview,
);
},
onFailure: (_) {
@@ -48,4 +48,8 @@ extension AttachmentExtension on Attachment {
bool get isJson => (type?.isJsonFile() ?? false)
|| name?.endsWith('.json') == true;
bool get isPreviewSupported {
return isImage || isText || isJson || isPDFFile || isEMLFile || isHTMLFile;
}
}
@@ -0,0 +1,444 @@
import 'dart:typed_data';
import 'package:core/core.dart';
import 'package:dartz/dartz.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.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';
import 'package:model/download/download_task_id.dart';
import 'package:model/email/attachment.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/model/preview_email_eml_request.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/state/parse_email_by_blob_id_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/preview_email_from_eml_file_state.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/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/presentation/extensions/attachment_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/pdf_viewer/pdf_viewer.dart';
import 'package:tmail_ui_user/features/email_previewer/email_previewer_dialog_view.dart';
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
import 'package:tmail_ui_user/main/routes/route_utils.dart';
import 'package:tmail_ui_user/main/utils/toast_manager.dart';
typedef OnDownloadAttachmentAction = void Function(Attachment attachment);
mixin PreviewAttachmentMixin {
final DownloadManager _downloadManager = Get.find<DownloadManager>();
final PrintUtils _printUtils = Get.find<PrintUtils>();
final AppToast _appToast = Get.find<AppToast>();
final ToastManager _toastManager = Get.find<ToastManager>();
final DynamicUrlInterceptors _dynamicUrlInterceptors =
Get.find<DynamicUrlInterceptors>();
void previewAttachmentAction({
required BuildContext context,
required Attachment attachment,
required AccountId? accountId,
required Session? session,
required String ownEmailAddress,
required BaseController controller,
required ParseEmailByBlobIdInteractor parseEmailInteractor,
required GetHtmlContentFromAttachmentInteractor getHtmlInteractor,
required OnDownloadAttachmentAction onDownloadAttachment,
}) {
if (PlatformInfo.isWeb && attachment.isPDFFile) {
_previewPDFFile(
context: context,
attachment: attachment,
accountId: accountId,
session: session,
);
} else if (attachment.isEMLFile) {
previewEMLFileAction(
appLocalizations: AppLocalizations.of(context),
accountId: accountId,
session: session,
ownEmailAddress: ownEmailAddress,
blobId: attachment.blobId,
controller: controller,
parseEmailByBlobIdInteractor: parseEmailInteractor,
);
} else if (attachment.isHTMLFile) {
_previewHtmlFileAction(
session: session,
accountId: accountId,
attachment: attachment,
controller: controller,
getHtmlInteractor: getHtmlInteractor,
);
} else {
onDownloadAttachment(attachment);
}
}
Future<void> _previewPDFFile({
required BuildContext context,
required Attachment attachment,
required AccountId? accountId,
required Session? session,
}) async {
if (accountId == null || session == null) {
_appToast.showToastErrorMessage(
context,
AppLocalizations.of(context).noPreviewAvailable,
);
return;
}
try {
final downloadUrl = session.getDownloadUrl(
jmapUrl: _dynamicUrlInterceptors.jmapUrl,
);
await Get.generalDialog(
barrierColor: Colors.black.withValues(alpha: 0.8),
pageBuilder: (_, __, ___) {
return PointerInterceptor(
child: PDFViewer(
attachment: attachment,
accountId: accountId,
downloadUrl: downloadUrl,
downloadAction: _downloadPDFFile,
printAction: _printPDFFile,
),
);
},
);
} catch (e) {
if (context.mounted) {
_appToast.showToastErrorMessage(
context,
AppLocalizations.of(context).noPreviewAvailable,
);
}
}
}
void _downloadPDFFile(Uint8List bytes, String fileName) {
_downloadManager.createAnchorElementDownloadFileWeb(bytes, fileName);
}
Future<void> _printPDFFile(Uint8List bytes, String fileName) async {
await _printUtils.printPDFFile(bytes, fileName);
}
void previewEMLFileAction({
required AppLocalizations appLocalizations,
required AccountId? accountId,
required Session? session,
required String ownEmailAddress,
required Id? blobId,
required ParseEmailByBlobIdInteractor parseEmailByBlobIdInteractor,
required BaseController controller,
}) {
SmartDialog.showLoading(
msg: appLocalizations.loadingPleaseWait,
maskColor: Colors.black38,
);
if (session == null) {
_emitFailure(
controller: controller,
failure: ParseEmailByBlobIdFailure(NotFoundSessionException()),
);
return;
}
if (accountId == null) {
_emitFailure(
controller: controller,
failure: ParseEmailByBlobIdFailure(NotFoundAccountIdException()),
);
return;
}
if (blobId == null) {
_emitFailure(
controller: controller,
failure: ParseEmailByBlobIdFailure(NotFoundBlobIdException([])),
);
return;
}
controller.consumeState(
parseEmailByBlobIdInteractor.execute(
accountId,
session,
ownEmailAddress,
blobId,
),
);
}
({
bool canDownloadAttachment,
AccountId? accountId,
String? downloadUrl,
Id? blobId,
}) evaluateAttachment({
required Session? session,
required AccountId? accountId,
required Attachment attachment,
}) {
String? downloadUrl;
try {
downloadUrl = session?.getDownloadUrl(
jmapUrl: _dynamicUrlInterceptors.jmapUrl,
);
} catch (e) {
downloadUrl = null;
}
final blobId = attachment.blobId;
if (accountId == null || downloadUrl == null || blobId == null) {
return (
canDownloadAttachment: false,
accountId: accountId,
downloadUrl: downloadUrl,
blobId: blobId,
);
}
return (
canDownloadAttachment: true,
accountId: accountId,
downloadUrl: downloadUrl,
blobId: blobId,
);
}
void _previewHtmlFileAction({
required Session? session,
required AccountId? accountId,
required Attachment attachment,
required BaseController controller,
required GetHtmlContentFromAttachmentInteractor getHtmlInteractor,
}) {
final attachmentEvaluation = evaluateAttachment(
accountId: accountId,
session: session,
attachment: attachment,
);
if (!attachmentEvaluation.canDownloadAttachment) {
_emitFailure(
controller: controller,
failure: GetHtmlContentFromAttachmentFailure(
exception: null,
attachment: attachment,
),
);
return;
}
controller.consumeState(
getHtmlInteractor.execute(
attachmentEvaluation.accountId!,
attachment,
DownloadTaskId(attachmentEvaluation.blobId!.value),
attachmentEvaluation.downloadUrl!,
TransformConfiguration.create(
customDomTransformers: [SanitizeHyperLinkTagInHtmlTransformer()],
customTextTransformers: [
const StandardizeHtmlSanitizingTransformers()
],
),
),
);
}
void _emitFailure({
required BaseController controller,
required FeatureFailure failure,
}) {
controller.consumeState(Stream.value(Left(failure)));
}
void handleParseEmailByBlobIdSuccess({
required BuildContext? context,
required AccountId? accountId,
required Session? session,
required String ownEmailAddress,
required Id blobId,
required Email email,
required BaseController controller,
required PreviewEmailFromEmlFileInteractor previewInteractor,
}) {
if (session == null) {
_emitFailure(
controller: controller,
failure: PreviewEmailFromEmlFileFailure(NotFoundSessionException()),
);
return;
}
if (accountId == null) {
_emitFailure(
controller: controller,
failure: PreviewEmailFromEmlFileFailure(NotFoundAccountIdException()),
);
return;
}
if (context == null) {
_emitFailure(
controller: controller,
failure: PreviewEmailFromEmlFileFailure(NotFoundContextException()),
);
return;
}
try {
controller.consumeState(previewInteractor.execute(
PreviewEmailEMLRequest(
accountId: accountId,
session: session,
ownEmailAddress: ownEmailAddress,
blobId: blobId,
email: email,
locale: Localizations.localeOf(context),
appLocalizations: AppLocalizations.of(context),
baseDownloadUrl: session.getDownloadUrl(
jmapUrl: _dynamicUrlInterceptors.jmapUrl,
),
),
));
} catch (e) {
_emitFailure(
controller: controller,
failure: PreviewEmailFromEmlFileFailure(e),
);
}
}
void handleParseEmailByBlobIdFailure(ParseEmailByBlobIdFailure failure) {
SmartDialog.dismiss();
_toastManager.showMessageFailure(failure);
}
void handlePreviewEmailFromEMLFileFailure(
PreviewEmailFromEmlFileFailure failure,
) {
SmartDialog.dismiss();
_toastManager.showMessageFailure(failure);
}
void handlePreviewEmailFromEMLFileSuccess({
required EMLPreviewer emlPreviewer,
required BuildContext? context,
required ImagePaths imagePaths,
required OnMailtoDelegateAction onMailtoAction,
required OnDownloadAttachmentDelegateAction onDownloadAction,
required OnPreviewEMLDelegateAction onPreviewAction,
}) {
SmartDialog.dismiss();
if (PlatformInfo.isWeb) {
bool isOpen = HtmlUtils.openNewWindowByUrl(
RouteUtils.createUrlWebLocationBar(
AppRoutes.emailEMLPreviewer,
previewId: emlPreviewer.id,
).toString(),
);
if (!isOpen) {
_toastManager.showMessageFailure(
PreviewEmailFromEmlFileFailure(CannotOpenNewWindowException()),
);
}
} else if (PlatformInfo.isMobile) {
if (context == null) {
_toastManager.showMessageFailure(
PreviewEmailFromEmlFileFailure(NotFoundContextException()),
);
return;
}
if (PlatformInfo.isAndroid) {
showModalSheetToPreviewEMLAttachment(
context: context,
emlPreviewer: emlPreviewer,
imagePaths: imagePaths,
onMailtoAction: onMailtoAction,
onDownloadAction: onDownloadAction,
onPreviewAction: onPreviewAction,
);
} else if (PlatformInfo.isIOS) {
showDialogToPreviewEMLAttachment(
context: context,
emlPreviewer: emlPreviewer,
imagePaths: imagePaths,
onMailtoAction: onMailtoAction,
onDownloadAction: onDownloadAction,
onPreviewAction: onPreviewAction,
);
}
}
}
void showModalSheetToPreviewEMLAttachment({
required BuildContext context,
required EMLPreviewer emlPreviewer,
required ImagePaths imagePaths,
required OnMailtoDelegateAction onMailtoAction,
required OnDownloadAttachmentDelegateAction onDownloadAction,
required OnPreviewEMLDelegateAction onPreviewAction,
}) {
showModalBottomSheet(
context: context,
showDragHandle: true,
useSafeArea: true,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0),
topRight: Radius.circular(20.0),
),
),
builder: (_) {
return DraggableScrollableSheet(
initialChildSize: 1.0,
builder: (context, ___) => EmailPreviewerDialogView(
emlPreviewer: emlPreviewer,
imagePaths: imagePaths,
onMailtoDelegateAction: onMailtoAction,
onPreviewEMLDelegateAction: onPreviewAction,
onDownloadAttachmentDelegateAction: onDownloadAction,
),
);
},
);
}
void showDialogToPreviewEMLAttachment({
required BuildContext context,
required EMLPreviewer emlPreviewer,
required ImagePaths imagePaths,
required OnMailtoDelegateAction onMailtoAction,
required OnDownloadAttachmentDelegateAction onDownloadAction,
required OnPreviewEMLDelegateAction onPreviewAction,
}) {
Get.dialog(
EmailPreviewerDialogView(
emlPreviewer: emlPreviewer,
imagePaths: imagePaths,
onMailtoDelegateAction: onMailtoAction,
onPreviewEMLDelegateAction: onPreviewAction,
onDownloadAttachmentDelegateAction: onDownloadAction,
),
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
);
}
}
@@ -1,27 +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/utils/file_utils.dart';
import 'package:core/utils/preview_eml_file_utils.dart';
import 'package:core/utils/print_utils.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/base_bindings.dart';
import 'package:tmail_ui_user/features/caching/utils/local_storage_manager.dart';
import 'package:tmail_ui_user/features/caching/utils/session_storage_manager.dart';
import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart';
import 'package:tmail_ui_user/features/email/data/datasource/html_datasource.dart';
import 'package:tmail_ui_user/features/email/data/datasource/print_file_datasource.dart';
import 'package:tmail_ui_user/features/email/data/datasource_impl/email_datasource_impl.dart';
import 'package:tmail_ui_user/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart';
import 'package:tmail_ui_user/features/email/data/datasource_impl/email_local_storage_datasource_impl.dart';
import 'package:tmail_ui_user/features/email/data/datasource_impl/email_session_storage_datasource_impl.dart';
import 'package:tmail_ui_user/features/email/data/datasource_impl/html_datasource_impl.dart';
import 'package:tmail_ui_user/features/email/data/datasource_impl/print_file_datasource_impl.dart';
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_attachment_for_web_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_preview_email_eml_content_shared_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_preview_eml_content_in_memory_interactor.dart';
@@ -30,25 +8,14 @@ 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/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';
import 'package:tmail_ui_user/features/offline_mode/manager/opened_email_cache_worker_queue.dart';
import 'package:tmail_ui_user/features/offline_mode/manager/sending_email_cache_manager.dart';
import 'package:tmail_ui_user/features/thread/data/local/email_cache_manager.dart';
import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart';
import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart';
import 'package:tmail_ui_user/main/utils/ios_sharing_manager.dart';
class EmailPreviewerBindings extends BaseBindings {
class EmailPreviewerBindings extends Bindings {
@override
void bindingsController() {
Get.lazyPut(() => EmailPreviewerController(
void dependencies() {
DownloadInteractorBindings().dependencies();
Get.lazyPut(
() => EmailPreviewerController(
Get.find<GetPreviewEmailEMLContentSharedInteractor>(),
Get.find<MovePreviewEmlContentFromPersistentToMemoryInteractor>(),
Get.find<RemovePreviewEmailEmlContentSharedInteractor>(),
@@ -56,87 +23,8 @@ class EmailPreviewerBindings extends BaseBindings {
Get.find<ParseEmailByBlobIdInteractor>(),
Get.find<PreviewEmailFromEmlFileInteractor>(),
Get.find<DownloadAttachmentForWebInteractor>(),
Get.find<DownloadManager>()));
Get.find<DownloadManager>(),
),
);
}
@override
void bindingsDataSource() {
Get.lazyPut<EmailDataSource>(() => Get.find<EmailDataSourceImpl>());
Get.lazyPut<HtmlDataSource>(() => Get.find<HtmlDataSourceImpl>());
Get.lazyPut<StateDataSource>(() => Get.find<StateDataSourceImpl>());
Get.lazyPut<PrintFileDataSource>(() => Get.find<PrintFileDataSourceImpl>());
}
@override
void bindingsDataSourceImpl() {
Get.lazyPut(() => EmailDataSourceImpl(
Get.find<EmailAPI>(),
Get.find<RemoteExceptionThrower>()));
Get.lazyPut(() => HtmlDataSourceImpl(
Get.find<HtmlAnalyzer>(),
Get.find<CacheExceptionThrower>()));
Get.lazyPut(() => StateDataSourceImpl(
Get.find<StateCacheManager>(),
Get.find<IOSSharingManager>(),
Get.find<CacheExceptionThrower>()));
Get.lazyPut(() => PrintFileDataSourceImpl(
Get.find<PrintUtils>(),
Get.find<ImagePaths>(),
Get.find<FileUtils>(),
Get.find<HtmlAnalyzer>(),
Get.find<CacheExceptionThrower>()));
Get.lazyPut(() => EmailHiveCacheDataSourceImpl(
Get.find<NewEmailCacheManager>(),
Get.find<OpenedEmailCacheManager>(),
Get.find<NewEmailCacheWorkerQueue>(),
Get.find<OpenedEmailCacheWorkerQueue>(),
Get.find<EmailCacheManager>(),
Get.find<SendingEmailCacheManager>(),
Get.find<FileUtils>(),
Get.find<CacheExceptionThrower>()));
Get.lazyPut(() => EmailLocalStorageDataSourceImpl(
Get.find<LocalStorageManager>(),
Get.find<PreviewEmlFileUtils>(),
Get.find<CacheExceptionThrower>()));
Get.lazyPut(() => EmailSessionStorageDatasourceImpl(
Get.find<SessionStorageManager>(),
Get.find<CacheExceptionThrower>()));
}
@override
void bindingsInteractor() {
Get.lazyPut(() => GetPreviewEmailEMLContentSharedInteractor(
Get.find<EmailRepository>()));
Get.lazyPut(() => MovePreviewEmlContentFromPersistentToMemoryInteractor(
Get.find<EmailRepository>()));
Get.lazyPut(() => RemovePreviewEmailEmlContentSharedInteractor(
Get.find<EmailRepository>()));
Get.lazyPut(() => GetPreviewEmlContentInMemoryInteractor(
Get.find<EmailRepository>()));
Get.lazyPut(() => ParseEmailByBlobIdInteractor(
Get.find<EmailRepository>()));
Get.lazyPut(() => PreviewEmailFromEmlFileInteractor(
Get.find<EmailRepository>()));
DownloadInteractorBindings().dependencies();
}
@override
void bindingsRepository() {
Get.lazyPut<EmailRepository>(() => Get.find<EmailRepositoryImpl>());
}
@override
void bindingsRepositoryImpl() {
Get.lazyPut(() => EmailRepositoryImpl(
{
DataSourceType.network: Get.find<EmailDataSource>(),
DataSourceType.hiveCache: Get.find<EmailHiveCacheDataSourceImpl>(),
DataSourceType.local: Get.find<EmailLocalStorageDataSourceImpl>(),
DataSourceType.session: Get.find<EmailSessionStorageDatasourceImpl>(),
},
Get.find<HtmlDataSource>(),
Get.find<StateDataSource>(),
Get.find<PrintFileDataSource>()));
}
}
}
@@ -169,7 +169,12 @@ class EmailPreviewerController extends ReloadableController {
_accountId = session.accountId;
if (_keyStored != null) {
_parseEmailByBlobId(session.accountId, _keyStored!);
_parseEmailByBlobId(
accountId: _accountId!,
session: _session!,
ownEmailAddress: session.getOwnEmailAddressOrEmpty(),
blobId: _keyStored!,
);
}
}
@@ -244,9 +249,16 @@ class EmailPreviewerController extends ReloadableController {
return uri.toString();
}
void _parseEmailByBlobId(AccountId accountId, String blobId) {
void _parseEmailByBlobId({
required AccountId accountId,
required Session session,
required String ownEmailAddress,
required String blobId,
}) {
consumeState(_parseEmailByBlobIdInteractor.execute(
accountId,
session,
ownEmailAddress,
Id(blobId),
));
}
@@ -271,6 +283,7 @@ class EmailPreviewerController extends ReloadableController {
consumeState(_previewEmailFromEmlFileInteractor.execute(
PreviewEmailEMLRequest(
accountId: _accountId!,
session: _session!,
ownEmailAddress: _session!.getOwnEmailAddressOrUsername(),
blobId: success.blobId,
email: success.email,
@@ -1,22 +1,32 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
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/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/datasource/email_datasource.dart';
import 'package:tmail_ui_user/features/email/data/datasource/html_datasource.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';
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;
final DownloadDatasource _downloadDatasource;
final Map<DataSourceType, EmailDataSource> _emailDataSource;
final HtmlDataSource _htmlDataSource;
DownloadRepositoryImpl(this._datasource);
DownloadRepositoryImpl(
this._downloadDatasource,
this._emailDataSource,
this._htmlDataSource,
);
@override
Future<Uint8List> downloadAttachmentForWeb(
@@ -28,7 +38,7 @@ class DownloadRepositoryImpl extends DownloadRepository {
StreamController<Either<Failure, Success>>? onReceiveController,
CancelToken? cancelToken,
}) {
return _datasource.downloadAttachmentForWeb(
return _downloadDatasource.downloadAttachmentForWeb(
taskId,
attachment,
accountId,
@@ -50,7 +60,7 @@ class DownloadRepositoryImpl extends DownloadRepository {
StreamController<Either<Failure, Success>>? onReceiveController,
CancelToken? cancelToken,
}) {
return _datasource.downloadAllAttachmentsForWeb(
return _downloadDatasource.downloadAllAttachmentsForWeb(
accountId,
emailId,
baseDownloadAllUrl,
@@ -61,4 +71,64 @@ class DownloadRepositoryImpl extends DownloadRepository {
cancelToken: cancelToken,
);
}
@override
Future<String> sanitizeHtmlContent(
String htmlContent,
TransformConfiguration configuration,
) {
return _htmlDataSource.transformHtmlEmailContent(
htmlContent,
configuration,
);
}
@override
Future<List<Email>> parseEmailByBlobIds(
AccountId accountId,
Set<Id> blobIds,
) {
return _emailDataSource[DataSourceType.network]!
.parseEmailByBlobIds(accountId, blobIds);
}
@override
Future<String> generatePreviewEmailEMLContent(
PreviewEmailEMLRequest previewEmailEMLRequest,
) {
return _emailDataSource[DataSourceType.network]!
.generatePreviewEmailEMLContent(previewEmailEMLRequest);
}
@override
Future<void> storePreviewEMLContentToSessionStorage(
EMLPreviewer emlPreviewer,
) {
return _emailDataSource[DataSourceType.session]!
.storePreviewEMLContentToSessionStorage(emlPreviewer);
}
@override
Future<void> sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer) {
return _emailDataSource[DataSourceType.local]!
.sharePreviewEmailEMLContent(emlPreviewer);
}
@override
Future<EMLPreviewer> getPreviewEmailEMLContentShared(String keyStored) {
return _emailDataSource[DataSourceType.local]!
.getPreviewEmailEMLContentShared(keyStored);
}
@override
Future<void> removePreviewEmailEMLContentShared(String keyStored) {
return _emailDataSource[DataSourceType.local]!
.removePreviewEmailEMLContentShared(keyStored);
}
@override
Future<EMLPreviewer> getPreviewEMLContentInMemory(String keyStored) {
return _emailDataSource[DataSourceType.session]!
.getPreviewEMLContentInMemory(keyStored);
}
}
@@ -3,13 +3,17 @@ import 'dart:typed_data';
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(
@@ -32,4 +36,27 @@ abstract class DownloadRepository {
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);
}
@@ -1,3 +1,6 @@
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/core/id.dart';
import 'package:model/email/attachment.dart';
import 'package:tmail_ui_user/features/base/action/ui_action.dart';
@@ -12,13 +15,13 @@ class DownloadUIAction extends UIAction {
}
class UpdateAttachmentsViewStateAction extends DownloadUIAction {
UpdateAttachmentsViewStateAction(this.blobId, this.success);
UpdateAttachmentsViewStateAction(this.blobId, this.viewState);
final Id? blobId;
final dynamic success;
final Either<Failure, Success> viewState;
@override
List<Object?> get props => [blobId, success];
List<Object?> get props => [blobId, viewState];
}
class DownloadAttachmentsQuicklyAction extends DownloadUIAction {
@@ -29,3 +32,12 @@ class DownloadAttachmentsQuicklyAction extends DownloadUIAction {
@override
List<Object?> get props => [attachment];
}
class OpenMailtoLinkFromPreviewAttachmentAction extends DownloadUIAction {
OpenMailtoLinkFromPreviewAttachmentAction(this.uri);
final Uri? uri;
@override
List<Object?> get props => [uri];
}
@@ -1,8 +1,25 @@
import 'package:core/core.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/interactors_bindings.dart';
import 'package:tmail_ui_user/features/caching/utils/local_storage_manager.dart';
import 'package:tmail_ui_user/features/caching/utils/session_storage_manager.dart';
import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.dart';
import 'package:tmail_ui_user/features/email/data/datasource/html_datasource.dart';
import 'package:tmail_ui_user/features/email/data/datasource_impl/email_datasource_impl.dart';
import 'package:tmail_ui_user/features/email/data/datasource_impl/email_local_storage_datasource_impl.dart';
import 'package:tmail_ui_user/features/email/data/datasource_impl/email_session_storage_datasource_impl.dart';
import 'package:tmail_ui_user/features/email/data/datasource_impl/html_datasource_impl.dart';
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/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_html_content_from_attachment_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_preview_email_eml_content_shared_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_preview_eml_content_in_memory_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/move_preview_eml_content_from_persistent_to_memory_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/remove_preview_email_eml_content_shared_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';
@@ -10,6 +27,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/downloa
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/cache_exception_thrower.dart';
import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart';
class DownloadInteractorBindings extends InteractorsBindings {
@@ -21,6 +39,31 @@ class DownloadInteractorBindings extends InteractorsBindings {
Get.find<RemoteExceptionThrower>(),
),
);
Get.lazyPut(
() => EmailDataSourceImpl(
Get.find<EmailAPI>(),
Get.find<RemoteExceptionThrower>(),
),
);
Get.lazyPut(
() => HtmlDataSourceImpl(
Get.find<HtmlAnalyzer>(),
Get.find<CacheExceptionThrower>(),
),
);
Get.lazyPut(
() => EmailSessionStorageDatasourceImpl(
Get.find<SessionStorageManager>(),
Get.find<CacheExceptionThrower>(),
),
);
Get.lazyPut(
() => EmailLocalStorageDataSourceImpl(
Get.find<LocalStorageManager>(),
Get.find<PreviewEmlFileUtils>(),
Get.find<CacheExceptionThrower>(),
),
);
}
@override
@@ -41,6 +84,38 @@ class DownloadInteractorBindings extends InteractorsBindings {
Get.find<CredentialRepository>(),
),
);
Get.lazyPut(
() => ParseEmailByBlobIdInteractor(Get.find<DownloadRepository>()),
);
Get.lazyPut(
() => PreviewEmailFromEmlFileInteractor(Get.find<DownloadRepository>()),
);
Get.lazyPut(
() => GetHtmlContentFromAttachmentInteractor(
Get.find<DownloadAttachmentForWebInteractor>(),
),
);
Get.lazyPut(
() => MovePreviewEmlContentFromPersistentToMemoryInteractor(
Get.find<DownloadRepository>(),
),
);
Get.lazyPut(
() => RemovePreviewEmailEmlContentSharedInteractor(
Get.find<DownloadRepository>(),
),
);
Get.lazyPut(
() => GetPreviewEmailEMLContentSharedInteractor(
Get.find<DownloadRepository>(),
),
);
Get.lazyPut(
() => GetPreviewEmlContentInMemoryInteractor(
Get.find<DownloadRepository>(),
),
);
}
@override
@@ -50,11 +125,23 @@ class DownloadInteractorBindings extends InteractorsBindings {
@override
void bindingsRepositoryImpl() {
Get.lazyPut(() => DownloadRepositoryImpl(Get.find<DownloadDatasource>()));
Get.lazyPut(
() => DownloadRepositoryImpl(
Get.find<DownloadDatasource>(),
{
DataSourceType.session: Get.find<EmailSessionStorageDatasourceImpl>(),
DataSourceType.local: Get.find<EmailLocalStorageDataSourceImpl>(),
DataSourceType.network: Get.find<EmailDataSource>(),
},
Get.find<HtmlDataSource>(),
),
);
}
@override
void bindingsDataSource() {
Get.lazyPut<DownloadDatasource>(() => Get.find<DownloadDatasourceImpl>());
Get.lazyPut<EmailDataSource>(() => Get.find<EmailDataSourceImpl>());
Get.lazyPut<HtmlDataSource>(() => Get.find<HtmlDataSourceImpl>());
}
}
@@ -34,10 +34,13 @@ import 'package:tmail_ui_user/features/email/domain/usecases/delete_email_perman
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_html_content_from_attachment_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';
import 'package:tmail_ui_user/features/email/domain/usecases/move_to_mailbox_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/restore_deleted_message_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/unsubscribe_email_interactor.dart';
import 'package:tmail_ui_user/features/email/presentation/bindings/email_bindings.dart';
@@ -175,6 +178,9 @@ class MailboxDashBoardBindings extends BaseBindings {
Get.find<DownloadAttachmentForWebInteractor>(),
Get.find<DownloadAllAttachmentsForWebInteractor>(),
Get.find<DownloadManager>(),
Get.find<ParseEmailByBlobIdInteractor>(),
Get.find<PreviewEmailFromEmlFileInteractor>(),
Get.find<GetHtmlContentFromAttachmentInteractor>(),
));
Get.put(SearchController(
Get.find<QuickSearchEmailInteractor>(),
@@ -16,6 +16,7 @@ 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/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';
@@ -28,9 +29,18 @@ 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/state/get_html_content_from_attachment_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/parse_email_by_blob_id_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/preview_email_from_eml_file_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/domain/usecases/get_html_content_from_attachment_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/presentation/extensions/attachment_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/mixin/preview_attachment_mixin.dart';
import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/html_attachment_previewer.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';
@@ -47,16 +57,23 @@ import 'package:uuid/uuid.dart';
typedef UpdateDownloadTaskStateCallback = DownloadTaskState Function(DownloadTaskState currentState);
class DownloadController extends BaseController {
class DownloadController extends BaseController with PreviewAttachmentMixin {
final DownloadAttachmentForWebInteractor _downloadAttachmentForWebInteractor;
final DownloadAllAttachmentsForWebInteractor
_downloadAllAttachmentsForWebInteractor;
final DownloadManager _downloadManager;
final ParseEmailByBlobIdInteractor parseEmailByBlobIdInteractor;
final PreviewEmailFromEmlFileInteractor previewEmailFromEmlFileInteractor;
final GetHtmlContentFromAttachmentInteractor
getHtmlContentFromAttachmentInteractor;
DownloadController(
this._downloadAttachmentForWebInteractor,
this._downloadAllAttachmentsForWebInteractor,
this._downloadManager,
this.parseEmailByBlobIdInteractor,
this.previewEmailFromEmlFileInteractor,
this.getHtmlContentFromAttachmentInteractor,
);
final listDownloadTaskState = RxList<DownloadTaskState>();
@@ -335,7 +352,7 @@ class DownloadController extends BaseController {
) {
_pushDownloadUIAction(UpdateAttachmentsViewStateAction(
success.attachment.blobId,
success,
Right<Failure, Success>(success),
));
deleteDownloadTask(success.taskId);
@@ -446,7 +463,10 @@ class DownloadController extends BaseController {
if (failure.attachment != null) {
_pushDownloadUIAction(
UpdateAttachmentsViewStateAction(failure.attachment?.blobId, failure),
UpdateAttachmentsViewStateAction(
failure.attachment?.blobId,
Left<Failure, Success>(failure),
),
);
}
@@ -464,7 +484,6 @@ class DownloadController extends BaseController {
appToast.showToastErrorMessage(currentOverlayContext!, message);
}
@override
void handleSuccessViewState(Success success) {
if (success is DownloadAttachmentForWebSuccess) {
@@ -472,15 +491,77 @@ class DownloadController extends BaseController {
} else if (success is StartDownloadAttachmentForWeb) {
_pushDownloadUIAction(UpdateAttachmentsViewStateAction(
success.attachment.blobId,
success,
Right<Failure, Success>(success),
));
} else if (success is DownloadingAttachmentForWeb) {
_pushDownloadUIAction(UpdateAttachmentsViewStateAction(
success.attachment.blobId,
success,
Right<Failure, Success>(success),
));
} else if (success is DownloadAllAttachmentsForWebSuccess) {
deleteDownloadTask(success.taskId);
} else if (success is ParseEmailByBlobIdSuccess) {
handleParseEmailByBlobIdSuccess(
context: currentContext,
accountId: success.accountId,
session: success.session,
ownEmailAddress: success.ownEmailAddress,
blobId: success.blobId,
email: success.email,
controller: this,
previewInteractor: previewEmailFromEmlFileInteractor,
);
} else if (success is PreviewEmailFromEmlFileSuccess) {
handlePreviewEmailFromEMLFileSuccess(
emlPreviewer: success.emlPreviewer,
context: currentContext,
imagePaths: imagePaths,
onMailtoAction: _openMailtoLink,
onDownloadAction: (uri) async {
if (uri == null) return;
final attachment = EmailUtils.parsingAttachmentByUri(uri);
if (attachment == null) return;
_downloadAttachmentQuickly(attachment);
},
onPreviewAction: (uri) async {
if (currentContext != null && uri?.path.isNotEmpty == true) {
previewEMLFileAction(
appLocalizations: AppLocalizations.of(currentContext!),
accountId: success.accountId,
session: success.session,
ownEmailAddress: success.ownEmailAddress,
blobId: Id(uri!.path),
controller: this,
parseEmailByBlobIdInteractor: parseEmailByBlobIdInteractor,
);
}
},
);
} else if (success is GetHtmlContentFromAttachmentSuccess) {
_pushDownloadUIAction(
UpdateAttachmentsViewStateAction(
success.attachment.blobId,
Right<Failure, Success>(success),
),
);
Get.dialog(HtmlAttachmentPreviewer(
title: success.htmlAttachmentTitle,
htmlContent: success.sanitizedHtmlContent,
mailToClicked: _openMailtoLink,
downloadAttachmentClicked: () =>
_downloadAttachmentQuickly(success.attachment),
responsiveUtils: responsiveUtils,
));
} else if (success is GettingHtmlContentFromAttachment) {
_pushDownloadUIAction(
UpdateAttachmentsViewStateAction(
success.attachment.blobId,
Right<Failure, Success>(success),
),
);
} else {
super.handleSuccessViewState(success);
}
@@ -492,11 +573,40 @@ class DownloadController extends BaseController {
_downloadAllAttachmentsForWebFailure(failure);
} else if (failure is DownloadAttachmentForWebFailure) {
_downloadAttachmentForWebFailureAction(failure);
} else if (failure is ParseEmailByBlobIdFailure) {
handleParseEmailByBlobIdFailure(failure);
} else if (failure is PreviewEmailFromEmlFileFailure) {
handlePreviewEmailFromEMLFileFailure(failure);
} else if (failure is GetHtmlContentFromAttachmentFailure) {
_handleGetHtmlContentFromAttachmentFailure(failure);
} else {
super.handleFailureViewState(failure);
}
}
void _handleGetHtmlContentFromAttachmentFailure(
GetHtmlContentFromAttachmentFailure failure,
) {
_pushDownloadUIAction(
UpdateAttachmentsViewStateAction(
failure.attachment.blobId,
Left<Failure, Success>(failure),
),
);
if (currentOverlayContext != null && currentContext != null) {
appToast.showToastErrorMessage(
currentOverlayContext!,
AppLocalizations.of(currentContext!)
.thisHtmlAttachmentCannotBePreviewed,
);
}
}
Future<void> _openMailtoLink(Uri? uri) async {
_pushDownloadUIAction(OpenMailtoLinkFromPreviewAttachmentAction(uri));
}
@override
void onClose() {
_downloadProgressStateSubscription?.cancel();
@@ -123,6 +123,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions
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_preview_attachment_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';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_store_email_sort_order_extension.dart';
@@ -767,6 +768,9 @@ class MailboxDashBoardController extends ReloadableController
if (action is DownloadAttachmentsQuicklyAction) {
downloadAttachmentForWeb(attachment: action.attachment);
downloadController.clearDownloadUIAction();
} else if (action is OpenMailtoLinkFromPreviewAttachmentAction) {
openMailToLink(action.uri);
downloadController.clearDownloadUIAction();
}
},
);
@@ -0,0 +1,84 @@
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/views/html_viewer/html_content_viewer_widget.dart';
import 'package:core/utils/app_logger.dart';
import 'package:flutter/cupertino.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:model/email/attachment.dart';
import 'package:tmail_ui_user/features/email/presentation/mixin/preview_attachment_mixin.dart';
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.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/open_and_close_composer_extension.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/route_utils.dart';
extension HandlePreviewAttachmentExtension on MailboxDashBoardController {
void previewAttachmentAction({
required BuildContext context,
required Attachment attachment,
required OnDownloadAttachmentAction onDownloadAttachment,
}) =>
downloadController.previewAttachmentAction(
context: context,
attachment: attachment,
accountId: accountId.value,
session: sessionCurrent,
ownEmailAddress: ownEmailAddress.value,
controller: downloadController,
parseEmailInteractor: downloadController.parseEmailByBlobIdInteractor,
getHtmlInteractor:
downloadController.getHtmlContentFromAttachmentInteractor,
onDownloadAttachment: onDownloadAttachment,
);
void previewEMLFileAction({
required AppLocalizations appLocalizations,
required Id? blobId,
}) =>
downloadController.previewEMLFileAction(
appLocalizations: appLocalizations,
accountId: accountId.value,
session: sessionCurrent,
ownEmailAddress: ownEmailAddress.value,
blobId: blobId,
controller: downloadController,
parseEmailByBlobIdInteractor:
downloadController.parseEmailByBlobIdInteractor,
);
void showDialogToPreviewEMLAttachment({
required BuildContext context,
required EMLPreviewer emlPreviewer,
required ImagePaths imagePaths,
required OnMailtoDelegateAction onMailtoAction,
required OnDownloadAttachmentDelegateAction onDownloadAction,
required OnPreviewEMLDelegateAction onPreviewAction,
}) =>
downloadController.showDialogToPreviewEMLAttachment(
context: context,
emlPreviewer: emlPreviewer,
imagePaths: imagePaths,
onMailtoAction: onMailtoAction,
onDownloadAction: onDownloadAction,
onPreviewAction: onPreviewAction,
);
Future<void> openMailToLink(Uri? uri) async {
if (uri == null) return;
final navigationRouter = RouteUtils.generateNavigationRouterFromMailtoLink(
uri.toString(),
);
log('$runtimeType::openMailToLink(): ${uri.toString()}');
if (!RouteUtils.canOpenComposerFromNavigationRouter(navigationRouter)) {
return;
}
openComposer(ComposerArguments.fromMailtoUri(
listEmailAddress: navigationRouter.listEmailAddress,
cc: navigationRouter.cc,
bcc: navigationRouter.bcc,
subject: navigationRouter.subject,
body: navigationRouter.body,
));
}
}
-1
View File
@@ -1,5 +1,4 @@
import 'package:core/core.dart';
import 'package:core/utils/html/html_utils.dart';
import 'package:date_format/date_format.dart' as date_format;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -5,28 +5,30 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
import 'package:model/extensions/session_extension.dart';
import 'package:tmail_ui_user/features/email/domain/state/parse_email_by_blob_id_state.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/parse_email_by_blob_id_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart';
import '../../../../fixtures/account_fixtures.dart';
import '../../../../fixtures/email_fixtures.dart';
import '../../../../fixtures/session_fixtures.dart';
import 'parse_email_by_blob_id_interactor_test.mocks.dart';
@GenerateNiceMocks([MockSpec<EmailRepository>()])
@GenerateNiceMocks([MockSpec<DownloadRepository>()])
void main() {
group('ParseEmailByBlobIdInteractor::execute::', () {
late MockEmailRepository mockEmailRepository;
late MockDownloadRepository mockDownloadRepository;
late ParseEmailByBlobIdInteractor parseEmailByBlobIdInteractor;
setUp(() {
mockEmailRepository = MockEmailRepository();
mockDownloadRepository = MockDownloadRepository();
parseEmailByBlobIdInteractor =
ParseEmailByBlobIdInteractor(mockEmailRepository);
ParseEmailByBlobIdInteractor(mockDownloadRepository);
});
final accountId = AccountFixtures.aliceAccountId;
final session = SessionFixtures.aliceSession;
final blobId = Id('blob-id');
test(
@@ -35,11 +37,16 @@ void main() {
// Arrange
final parsedEmail = EmailFixtures.email1;
when(mockEmailRepository.parseEmailByBlobIds(accountId, {blobId}))
when(mockDownloadRepository.parseEmailByBlobIds(accountId, {blobId}))
.thenAnswer((_) async => [parsedEmail]);
// Act
final result = parseEmailByBlobIdInteractor.execute(accountId, blobId);
final result = parseEmailByBlobIdInteractor.execute(
accountId,
session,
session.getOwnEmailAddressOrEmpty(),
blobId,
);
// Assert
@@ -47,13 +54,19 @@ void main() {
result,
emitsInOrder([
Right<Failure, Success>(ParsingEmailByBlobId()),
Right<Failure, Success>(ParseEmailByBlobIdSuccess(parsedEmail, blobId)),
Right<Failure, Success>(ParseEmailByBlobIdSuccess(
email: parsedEmail,
blobId: blobId,
accountId: accountId,
session: session,
ownEmailAddress: session.getOwnEmailAddressOrEmpty(),
)),
]),
);
verify(mockEmailRepository.parseEmailByBlobIds(accountId, {blobId}))
verify(mockDownloadRepository.parseEmailByBlobIds(accountId, {blobId}))
.called(1);
verifyNoMoreInteractions(mockEmailRepository);
verifyNoMoreInteractions(mockDownloadRepository);
});
test(
@@ -62,11 +75,16 @@ void main() {
// Arrange
final exception = Exception('Failed to parse email');
when(mockEmailRepository.parseEmailByBlobIds(accountId, {blobId}))
when(mockDownloadRepository.parseEmailByBlobIds(accountId, {blobId}))
.thenThrow(exception);
// Act
final result = parseEmailByBlobIdInteractor.execute(accountId, blobId);
final result = parseEmailByBlobIdInteractor.execute(
accountId,
session,
session.getOwnEmailAddressOrEmpty(),
blobId,
);
// Assert
await expectLater(
@@ -77,9 +95,9 @@ void main() {
]),
);
verify(mockEmailRepository.parseEmailByBlobIds(accountId, {blobId}))
verify(mockDownloadRepository.parseEmailByBlobIds(accountId, {blobId}))
.called(1);
verifyNoMoreInteractions(mockEmailRepository);
verifyNoMoreInteractions(mockDownloadRepository);
});
});
}
@@ -32,13 +32,10 @@ import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_reje
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_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/store_opened_email_interactor.dart';
import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart';
@@ -97,14 +94,11 @@ const fallbackGenerators = {
MockSpec<AcceptCalendarEventInteractor>(),
MockSpec<MaybeCalendarEventInteractor>(),
MockSpec<RejectCalendarEventInteractor>(),
MockSpec<ParseEmailByBlobIdInteractor>(),
MockSpec<PreviewEmailFromEmlFileInteractor>(),
MockSpec<PrintUtils>(),
MockSpec<ApplicationManager>(),
MockSpec<ToastManager>(),
MockSpec<CalendarEventDataSource>(),
MockSpec<DioClient>(),
MockSpec<GetHtmlContentFromAttachmentInteractor>(),
MockSpec<TwakeAppManager>(),
MockSpec<ExportAllAttachmentsInteractor>(),
MockSpec<FileUploader>(),
@@ -133,12 +127,9 @@ void main() {
final responsiveUtils = MockResponsiveUtils();
final uuid = MockUuid();
final printEmailInteractor = MockPrintEmailInteractor();
final parseEmailByBlobIdInteractor = MockParseEmailByBlobIdInteractor();
final previewEmailFromEmlFileInteractor = MockPreviewEmailFromEmlFileInteractor();
final printUtils = MockPrintUtils();
final applicationManager = MockApplicationManager();
final mockToastManager = MockToastManager();
final getHtmlContentFromAttachmentInteractor = MockGetHtmlContentFromAttachmentInteractor();
final mockTwakeAppManager = MockTwakeAppManager();
final exportAllAttachmentsInteractor = MockExportAllAttachmentsInteractor();
@@ -192,9 +183,6 @@ void main() {
getAllIdentitiesInteractor,
storeOpenedEmailInteractor,
printEmailInteractor,
parseEmailByBlobIdInteractor,
previewEmailFromEmlFileInteractor,
getHtmlContentFromAttachmentInteractor,
exportAllAttachmentsInteractor,
);
});