TF-3416 Implement preview EML attachment

This commit is contained in:
dab246
2025-01-13 10:02:50 +07:00
committed by Dat H. Pham
parent 7cbdfddb6d
commit d304083b2e
54 changed files with 1489 additions and 148 deletions
@@ -0,0 +1 @@
class NotFoundDataWithThisKeyException implements Exception {}
@@ -0,0 +1,27 @@
import 'package:collection/collection.dart';
import 'package:tmail_ui_user/features/caching/exceptions/local_storage_exception.dart';
import 'package:universal_html/html.dart' as html;
class LocalStorageManager {
void save(String key, String value) {
html.window.localStorage.addAll({key: value});
}
String get(String key) {
final entry = html.window.localStorage
.entries
.firstWhereOrNull((entry) => entry.key == key);
if (entry != null) {
return entry.value;
} else {
throw NotFoundDataWithThisKeyException();
}
}
void remove(String key) {
html.window.localStorage.remove(key);
}
}
@@ -2,6 +2,7 @@ import 'package:core/core.dart';
import 'package:core/utils/application_manager.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/composer/data/datasource/composer_datasource.dart';
import 'package:tmail_ui_user/features/composer/data/datasource/contact_datasource.dart';
import 'package:tmail_ui_user/features/composer/data/datasource_impl/composer_datasource_impl.dart';
@@ -24,6 +25,7 @@ import 'package:tmail_ui_user/features/email/data/datasource/html_datasource.dar
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/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';
@@ -131,6 +133,9 @@ class ComposerBindings extends BaseBindings {
Get.find<SendingEmailCacheManager>(),
Get.find<FileUtils>(),
Get.find<CacheExceptionThrower>()));
Get.lazyPut(() => EmailLocalStorageDataSourceImpl(
Get.find<LocalStorageManager>(),
Get.find<CacheExceptionThrower>()));
Get.lazyPut(() => RemoteServerSettingsDataSourceImpl(
Get.find<ServerSettingsAPI>(),
Get.find<RemoteExceptionThrower>()));
@@ -170,7 +175,8 @@ class ComposerBindings extends BaseBindings {
Get.lazyPut(() => EmailRepositoryImpl(
{
DataSourceType.network: Get.find<EmailDataSource>(),
DataSourceType.hiveCache: Get.find<EmailHiveCacheDataSourceImpl>()
DataSourceType.hiveCache: Get.find<EmailHiveCacheDataSourceImpl>(),
DataSourceType.local: Get.find<EmailLocalStorageDataSourceImpl>(),
},
Get.find<HtmlDataSource>(),
Get.find<StateDataSource>(),
@@ -24,6 +24,7 @@ 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/event_action.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/mailbox/domain/model/create_new_mailbox_request.dart';
import 'package:tmail_ui_user/features/sending_queue/domain/model/sending_email.dart';
@@ -187,4 +188,10 @@ abstract class EmailDataSource {
List<EmailId> emailIds);
Future<List<Email>> parseEmailByBlobIds(AccountId accountId, Set<Id> blobIds);
Future<String> generatePreviewEmailEMLContent(PreviewEmailEMLRequest previewEmailEMLRequest);
Future<void> sharePreviewEmailEMLContent(String keyStored, String previewEMLContent);
Future<String> getPreviewEmailEMLContentShared(String keyStored);
}
@@ -1,13 +1,23 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:core/data/model/print_attachment.dart';
import 'package:core/data/network/download/downloaded_response.dart';
import 'package:core/domain/extensions/datetime_extension.dart';
import 'package:core/presentation/extensions/html_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/file_utils.dart';
import 'package:core/utils/platform_info.dart';
import 'package:core/utils/preview_eml_file_utils.dart';
import 'package:dartz/dartz.dart';
import 'package:dio/dio.dart';
import 'package:email_recovery/email_recovery/email_recovery_action.dart';
import 'package:email_recovery/email_recovery/email_recovery_action_id.dart';
import 'package:filesize/filesize.dart';
import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/error/set_error.dart';
@@ -19,11 +29,14 @@ import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/model.dart';
import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart';
import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.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/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/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/presentation/extensions/attachment_extension.dart';
import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart';
import 'package:tmail_ui_user/features/sending_queue/domain/model/sending_email.dart';
import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart';
@@ -34,6 +47,10 @@ class EmailDataSourceImpl extends EmailDataSource {
final EmailAPI emailAPI;
final ExceptionThrower _exceptionThrower;
final ExceptionThrower _sendEmailExceptionThrower = Get.find<SendEmailExceptionThrower>();
final PreviewEmlFileUtils _previewEmailUtils = Get.find<PreviewEmlFileUtils>();
final HtmlAnalyzer _htmlAnalyzer = Get.find<HtmlAnalyzer>();
final FileUtils _fileUtils = Get.find<FileUtils>();
final ImagePaths _imagePaths = Get.find<ImagePaths>();
EmailDataSourceImpl(this.emailAPI, this._exceptionThrower);
@@ -385,4 +402,125 @@ class EmailDataSourceImpl extends EmailDataSource {
return await emailAPI.parseEmailByBlobIds(accountId, blobIds);
}).catchError(_exceptionThrower.throwException);
}
@override
Future<String> generatePreviewEmailEMLContent(PreviewEmailEMLRequest previewEmailEMLRequest) {
return Future.sync(() async {
final email = previewEmailEMLRequest.email;
final appLocalizations = previewEmailEMLRequest.appLocalizations;
final locale = previewEmailEMLRequest.locale.toLanguageTag();
final listAttachments = email
.allAttachments
.getListAttachmentsDisplayedOutside(email.htmlBodyAttachments);
final listInlineImages = email
.allAttachments
.listAttachmentsDisplayedInContent;
final mapCidImageDownloadUrl = listInlineImages.toMapCidImageDownloadUrl(
accountId: previewEmailEMLRequest.accountId,
downloadUrl: previewEmailEMLRequest.baseDownloadUrl,
);
final emailContentEscaped = await _transformEmailContent(
email.emailContentList,
mapCidImageDownloadUrl,
);
final sender = email.from?.isNotEmpty == true
? email.from!.first
: null;
final receiveTime = email.getReceivedAt(
newLocale: locale,
pattern: email.receivedAt?.value.toLocal().toPatternForPrinting(locale)
);
final sentTime = email.getSentAt(
newLocale: locale,
pattern: email.sentAt?.value.toLocal().toPatternForPrinting(locale)
);
final List<PrintAttachment> listPrintAttachment = [];
if (listAttachments.isNotEmpty) {
await Future.forEach<Attachment>(listAttachments, (attachment) async {
final iconBase64Data = await _fileUtils.convertImageAssetToBase64(
attachment.getIcon(_imagePaths));
final printAttachment = PrintAttachment(
iconBase64Data: iconBase64Data,
name: attachment.name.escapeLtGtHtmlString(),
size: filesize(attachment.size?.value),
);
listPrintAttachment.add(printAttachment);
});
}
final attachmentIconBase64Data = email.hasAttachment == true
? await _fileUtils.convertImageAssetToBase64(_imagePaths.icAttachment)
: '';
final previewEmlHtmlDocument = _previewEmailUtils.generatePreviewEml(
appName: appLocalizations.app_name,
userName: previewEmailEMLRequest.userName.value,
subjectPrefix: appLocalizations.subject,
subject: previewEmailEMLRequest.email.subject?.escapeLtGtHtmlString() ?? '',
emailContent: emailContentEscaped,
senderName: sender?.name.escapeLtGtHtmlString() ?? '',
senderEmailAddress: sender?.email ?? '',
dateTime: receiveTime.isNotEmpty ? receiveTime : sentTime,
fromPrefix: appLocalizations.from_email_address_prefix,
toPrefix: appLocalizations.to_email_address_prefix,
ccPrefix: appLocalizations.cc_email_address_prefix,
bccPrefix: appLocalizations.bcc_email_address_prefix,
replyToPrefix: appLocalizations.replyToEmailAddressPrefix,
titleAttachment: appLocalizations.attachments.toLowerCase(),
attachmentIcon: attachmentIconBase64Data,
toAddress: email.to?.listEmailAddressToString(isFullEmailAddress: true),
ccAddress: email.cc?.listEmailAddressToString(isFullEmailAddress: true),
bccAddress: email.bcc?.listEmailAddressToString(isFullEmailAddress: true),
replyToAddress: email.replyTo?.listEmailAddressToString(isFullEmailAddress: true),
listAttachment: listPrintAttachment,
);
return previewEmlHtmlDocument;
}).catchError(_exceptionThrower.throwException);
}
Future<String> _transformEmailContent(
List<EmailContent> emailContents,
Map<String, String> mapCidImageDownloadUrl,
) async {
try {
final transformConfiguration = PlatformInfo.isWeb
? TransformConfiguration.forPreviewEmailOnWeb()
: TransformConfiguration.forPreviewEmail();
final listEmailContent = await Future.wait(emailContents
.map((emailContent) async => await _htmlAnalyzer.transformEmailContent(
emailContent,
mapCidImageDownloadUrl,
transformConfiguration,
))
.toList());
return listEmailContent.asHtmlString;
} catch (e) {
logError('EmailDataSourceImpl::_transformEmailContent:Exception = $e');
return '';
}
}
@override
Future<void> sharePreviewEmailEMLContent(String keyStored, String previewEMLContent) {
throw UnimplementedError();
}
@override
Future<String> getPreviewEmailEMLContentShared(String keyStored) {
throw UnimplementedError();
}
}
@@ -34,6 +34,7 @@ import 'package:tmail_ui_user/features/email/domain/extensions/detailed_email_hi
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/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/mailbox/domain/model/create_new_mailbox_request.dart';
import 'package:tmail_ui_user/features/offline_mode/extensions/list_sending_email_hive_cache_extension.dart';
@@ -531,4 +532,19 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
Future<List<Email>> parseEmailByBlobIds(AccountId accountId, Set<Id> blobIds) {
throw UnimplementedError();
}
@override
Future<String> generatePreviewEmailEMLContent(PreviewEmailEMLRequest previewEmailEMLRequest) {
throw UnimplementedError();
}
@override
Future<void> sharePreviewEmailEMLContent(String keyStored, String previewEMLContent) {
throw UnimplementedError();
}
@override
Future<String> getPreviewEmailEMLContentShared(String keyStored) {
throw UnimplementedError();
}
}
@@ -0,0 +1,224 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:core/data/network/download/downloaded_response.dart';
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:dartz/dartz.dart';
import 'package:dio/dio.dart';
import 'package:email_recovery/email_recovery/email_recovery_action.dart';
import 'package:email_recovery/email_recovery/email_recovery_action_id.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/error/set_error.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/core/properties/properties.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/core/user_name.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/account/account_request.dart';
import 'package:model/download/download_task_id.dart';
import 'package:model/email/attachment.dart';
import 'package:model/email/mark_star_action.dart';
import 'package:model/email/read_actions.dart';
import 'package:tmail_ui_user/features/caching/utils/local_storage_manager.dart';
import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart';
import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.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/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/mailbox/domain/model/create_new_mailbox_request.dart';
import 'package:tmail_ui_user/features/sending_queue/domain/model/sending_email.dart';
import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart';
class EmailLocalStorageDataSourceImpl extends EmailDataSource {
final LocalStorageManager _localStorageManager;
final ExceptionThrower _exceptionThrower;
EmailLocalStorageDataSourceImpl(
this._localStorageManager,
this._exceptionThrower,
);
@override
Future<bool> deleteEmailPermanently(Session session, AccountId accountId, EmailId emailId, {CancelToken? cancelToken}) {
throw UnimplementedError();
}
@override
Future<({List<EmailId> emailIdsSuccess, Map<Id, SetError> mapErrors})> deleteMultipleEmailsPermanently(Session session, AccountId accountId, List<EmailId> emailIds) {
throw UnimplementedError();
}
@override
Future<void> deleteMultipleSendingEmail(AccountId accountId, UserName userName, List<String> sendingIds) {
throw UnimplementedError();
}
@override
Future<void> deleteSendingEmail(AccountId accountId, UserName userName, String sendingId) {
throw UnimplementedError();
}
@override
Future<Uint8List> downloadAttachmentForWeb(DownloadTaskId taskId, Attachment attachment, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest, StreamController<Either<Failure, Success>> onReceiveController, {CancelToken? cancelToken}) {
throw UnimplementedError();
}
@override
Future<List<DownloadTaskId>> downloadAttachments(List<Attachment> attachments, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest) {
throw UnimplementedError();
}
@override
Future<DownloadedResponse> exportAttachment(Attachment attachment, AccountId accountId, String baseDownloadUrl, AccountRequest accountRequest, CancelToken cancelToken) {
throw UnimplementedError();
}
@override
Future<String> generatePreviewEmailEMLContent(PreviewEmailEMLRequest previewEmailEMLRequest) {
throw UnimplementedError();
}
@override
Future<List<SendingEmail>> getAllSendingEmails(AccountId accountId, UserName userName) {
throw UnimplementedError();
}
@override
Future<Email> getDetailedEmailById(Session session, AccountId accountId, EmailId emailId) {
throw UnimplementedError();
}
@override
Future<Email> getEmailContent(Session session, AccountId accountId, EmailId emailId, {Properties? additionalProperties}) {
throw UnimplementedError();
}
@override
Future<EmailRecoveryAction> getRestoredDeletedMessage(EmailRecoveryActionId emailRecoveryActionId) {
throw UnimplementedError();
}
@override
Future<Email> getStoredEmail(Session session, AccountId accountId, EmailId emailId) {
throw UnimplementedError();
}
@override
Future<DetailedEmail> getStoredNewEmail(Session session, AccountId accountId, EmailId emailId) {
throw UnimplementedError();
}
@override
Future<DetailedEmail> getStoredOpenedEmail(Session session, AccountId accountId, EmailId emailId) {
throw UnimplementedError();
}
@override
Future<SendingEmail> getStoredSendingEmail(AccountId accountId, UserName userName, String sendingId) {
throw UnimplementedError();
}
@override
Future<({List<EmailId> emailIdsSuccess, Map<Id, SetError> mapErrors})> markAsRead(Session session, AccountId accountId, List<EmailId> emailIds, ReadActions readActions) {
throw UnimplementedError();
}
@override
Future<({List<EmailId> emailIdsSuccess, Map<Id, SetError> mapErrors})> markAsStar(Session session, AccountId accountId, List<EmailId> emailIds, MarkStarAction markStarAction) {
throw UnimplementedError();
}
@override
Future<({List<EmailId> emailIdsSuccess, Map<Id, SetError> mapErrors})> moveToMailbox(Session session, AccountId accountId, MoveToMailboxRequest moveRequest) {
throw UnimplementedError();
}
@override
Future<List<Email>> parseEmailByBlobIds(AccountId accountId, Set<Id> blobIds) {
throw UnimplementedError();
}
@override
Future<bool> removeEmailDrafts(Session session, AccountId accountId, EmailId emailId, {CancelToken? cancelToken}) {
throw UnimplementedError();
}
@override
Future<EmailRecoveryAction> restoreDeletedMessage(RestoredDeletedMessageRequest restoredDeletedMessageRequest) {
throw UnimplementedError();
}
@override
Future<Email> saveEmailAsDrafts(Session session, AccountId accountId, Email email, {CancelToken? cancelToken}) {
throw UnimplementedError();
}
@override
Future<void> sendEmail(Session session, AccountId accountId, EmailRequest emailRequest, {CreateNewMailboxRequest? mailboxRequest, CancelToken? cancelToken}) {
throw UnimplementedError();
}
@override
Future<void> sharePreviewEmailEMLContent(String keyStored, String previewEMLContent) {
return Future.sync(() async {
return _localStorageManager.save(keyStored, previewEMLContent);
}).catchError(_exceptionThrower.throwException);
}
@override
Future<void> storeDetailedNewEmail(Session session, AccountId accountId, DetailedEmail detailedEmail) {
throw UnimplementedError();
}
@override
Future<void> storeEmail(Session session, AccountId accountId, Email email) {
throw UnimplementedError();
}
@override
Future<void> storeEventAttendanceStatus(Session session, AccountId accountId, EmailId emailId, EventActionType eventActionType) {
throw UnimplementedError();
}
@override
Future<void> storeOpenedEmail(Session session, AccountId accountId, DetailedEmail detailedEmail) {
throw UnimplementedError();
}
@override
Future<SendingEmail> storeSendingEmail(AccountId accountId, UserName userName, SendingEmail sendingEmail) {
throw UnimplementedError();
}
@override
Future<void> unsubscribeMail(Session session, AccountId accountId, EmailId emailId) {
throw UnimplementedError();
}
@override
Future<Email> updateEmailDrafts(Session session, AccountId accountId, Email newEmail, EmailId oldEmailId, {CancelToken? cancelToken}) {
throw UnimplementedError();
}
@override
Future<List<SendingEmail>> updateMultipleSendingEmail(AccountId accountId, UserName userName, List<SendingEmail> newSendingEmails) {
throw UnimplementedError();
}
@override
Future<SendingEmail> updateSendingEmail(AccountId accountId, UserName userName, SendingEmail newSendingEmail) {
throw UnimplementedError();
}
@override
Future<String> getPreviewEmailEMLContentShared(String keyStored) {
return Future.sync(() async {
return _localStorageManager.get(keyStored);
}).catchError(_exceptionThrower.throwException);
}
}
@@ -902,7 +902,9 @@ class EmailAPI with HandleSetErrorMixin {
Future<List<Email>> parseEmailByBlobIds(AccountId accountId, Set<Id> blobIds) async {
final requestBuilder = JmapRequestBuilder(_httpClient, ProcessingInvocation());
final parseEmailMethod = ParseEmailMethod(accountId, blobIds);
final parseEmailMethod = ParseEmailMethod(accountId, blobIds)
..addProperties(ThreadConstants.propertiesParseEmailByBlobId)
..addFetchHTMLBodyValues(true);
final parseEmailInvocation = requestBuilder.invocation(parseEmailMethod);
final response = await (requestBuilder
@@ -32,6 +32,7 @@ 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/event_action.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/repository/email_repository.dart';
import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart';
@@ -486,6 +487,21 @@ class EmailRepositoryImpl extends EmailRepository {
@override
Future<List<Email>> parseEmailByBlobIds(AccountId accountId, Set<Id> blobIds) {
throw UnimplementedError();
return emailDataSource[DataSourceType.network]!.parseEmailByBlobIds(accountId, blobIds);
}
@override
Future<String> generatePreviewEmailEMLContent(PreviewEmailEMLRequest previewEmailEMLRequest) {
return emailDataSource[DataSourceType.network]!.generatePreviewEmailEMLContent(previewEmailEMLRequest);
}
@override
Future<void> sharePreviewEmailEMLContent(String keyStored, String previewEMLContent) {
return emailDataSource[DataSourceType.local]!.sharePreviewEmailEMLContent(keyStored, previewEMLContent);
}
@override
Future<String> getPreviewEmailEMLContentShared(String keyStored) {
return emailDataSource[DataSourceType.local]!.getPreviewEmailEMLContentShared(keyStored);
}
}
@@ -0,0 +1,40 @@
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/user_name.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 UserName userName;
final Id blobId;
final Email email;
final Locale locale;
final AppLocalizations appLocalizations;
final String baseDownloadUrl;
PreviewEmailEMLRequest({
required this.accountId,
required this.userName,
required this.blobId,
required this.email,
required this.locale,
required this.appLocalizations,
required this.baseDownloadUrl,
});
String get keyStored => blobId.value;
@override
List<Object?> get props => [
accountId,
userName,
blobId,
email,
locale,
appLocalizations,
baseDownloadUrl,
];
}
@@ -27,6 +27,7 @@ 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/event_action.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/mailbox/domain/model/create_new_mailbox_request.dart';
@@ -181,4 +182,10 @@ abstract class EmailRepository {
EventActionType eventActionType);
Future<List<Email>> parseEmailByBlobIds(AccountId accountId, Set<Id> blobIds);
Future<String> generatePreviewEmailEMLContent(PreviewEmailEMLRequest previewEmailEMLRequest);
Future<void> sharePreviewEmailEMLContent(String keyStored, String previewEMLContent);
Future<String> getPreviewEmailEMLContentShared(String keyStored);
}
@@ -0,0 +1,19 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
class GettingPreviewEmailEMLContentShared extends LoadingState {}
class GetPreviewEmailEMLContentSharedSuccess extends UIState {
final String previewEMLContent;
GetPreviewEmailEMLContentSharedSuccess(this.previewEMLContent);
@override
List<Object?> get props => [previewEMLContent];
}
class GetPreviewEmailEMLContentSharedFailure extends FeatureFailure {
GetPreviewEmailEMLContentSharedFailure(dynamic exception) : super(exception: exception);
}
@@ -1,5 +1,6 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
class ParsingEmailByBlobId extends LoadingState {}
@@ -7,11 +8,12 @@ class ParsingEmailByBlobId extends LoadingState {}
class ParseEmailByBlobIdSuccess extends UIState {
final Email email;
final Id blobId;
ParseEmailByBlobIdSuccess(this.email);
ParseEmailByBlobIdSuccess(this.email, this.blobId);
@override
List<Object> get props => [email];
List<Object> get props => [email, blobId];
}
class ParseEmailByBlobIdFailure extends FeatureFailure {
@@ -0,0 +1,19 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
class PreviewingEmailFromEmlFile extends LoadingState {}
class PreviewEmailFromEmlFileSuccess extends UIState {
final String storeKey;
PreviewEmailFromEmlFileSuccess(this.storeKey);
@override
List<Object?> get props => [storeKey];
}
class PreviewEmailFromEmlFileFailure extends FeatureFailure {
PreviewEmailFromEmlFileFailure(dynamic exception) : super(exception: exception);
}
@@ -0,0 +1,24 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:dartz/dartz.dart';
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_preview_email_content_shared_state.dart';
class GetPreviewEmailEMLContentSharedInteractor {
final EmailRepository _emailRepository;
const GetPreviewEmailEMLContentSharedInteractor(this._emailRepository);
Stream<Either<Failure, Success>> execute(String keyStored) async* {
try {
yield Right<Failure, Success>(GettingPreviewEmailEMLContentShared());
final previewEMLContent = await _emailRepository
.getPreviewEmailEMLContentShared(keyStored);
yield Right<Failure, Success>(GetPreviewEmailEMLContentSharedSuccess(previewEMLContent));
} catch (e) {
yield Left<Failure, Success>(GetPreviewEmailEMLContentSharedFailure(e));
}
}
}
@@ -20,7 +20,10 @@ class ParseEmailByBlobIdInteractor {
{blobId},
);
yield Right<Failure, Success>(ParseEmailByBlobIdSuccess(emailParsed.first));
yield Right<Failure, Success>(ParseEmailByBlobIdSuccess(
emailParsed.first,
blobId,
));
} catch (e) {
yield Left<Failure, Success>(ParseEmailByBlobIdFailure(e));
}
@@ -0,0 +1,31 @@
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/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';
class PreviewEmailFromEmlFileInteractor {
final EmailRepository _emailRepository;
const PreviewEmailFromEmlFileInteractor(this._emailRepository);
Stream<Either<Failure, Success>> execute(
PreviewEmailEMLRequest previewEmailEMLRequest,
) async* {
try {
yield Right<Failure, Success>(PreviewingEmailFromEmlFile());
final previewEMLContent = await _emailRepository
.generatePreviewEmailEMLContent(previewEmailEMLRequest);
final keyStored = previewEmailEMLRequest.keyStored;
await _emailRepository.sharePreviewEmailEMLContent(keyStored, previewEMLContent);
yield Right<Failure, Success>(PreviewEmailFromEmlFileSuccess(keyStored));
} catch (e) {
yield Left<Failure, Success>(PreviewEmailFromEmlFileFailure(e));
}
}
}
@@ -1,11 +1,13 @@
import 'package:core/core.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/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/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';
@@ -20,6 +22,8 @@ import 'package:tmail_ui_user/features/email/domain/usecases/get_stored_email_st
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_event_attendance_status_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/store_opened_email_interactor.dart';
@@ -70,6 +74,8 @@ class EmailBindings extends BaseBindings {
Get.find<StoreOpenedEmailInteractor>(),
Get.find<PrintEmailInteractor>(),
Get.find<StoreEventAttendanceStatusInteractor>(),
Get.find<ParseEmailByBlobIdInteractor>(),
Get.find<PreviewEmailFromEmlFileInteractor>(),
));
}
@@ -118,6 +124,9 @@ class EmailBindings extends BaseBindings {
Get.find<SendingEmailCacheManager>(),
Get.find<FileUtils>(),
Get.find<CacheExceptionThrower>()));
Get.lazyPut(() => EmailLocalStorageDataSourceImpl(
Get.find<LocalStorageManager>(),
Get.find<CacheExceptionThrower>()));
}
@override
@@ -148,6 +157,8 @@ class EmailBindings extends BaseBindings {
Get.lazyPut(() => StoreOpenedEmailInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => PrintEmailInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => StoreEventAttendanceStatusInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => ParseEmailByBlobIdInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => PreviewEmailFromEmlFileInteractor(Get.find<EmailRepository>()));
IdentityInteractorsBindings().dependencies();
}
@@ -169,7 +180,8 @@ class EmailBindings extends BaseBindings {
Get.lazyPut(() => EmailRepositoryImpl(
{
DataSourceType.network: Get.find<EmailDataSource>(),
DataSourceType.hiveCache: Get.find<EmailHiveCacheDataSourceImpl>()
DataSourceType.hiveCache: Get.find<EmailHiveCacheDataSourceImpl>(),
DataSourceType.local: Get.find<EmailLocalStorageDataSourceImpl>(),
},
Get.find<HtmlDataSource>(),
Get.find<StateDataSource>(),
@@ -7,10 +7,12 @@ import 'package:core/core.dart';
import 'package:core/presentation/utils/html_transformer/text/sanitize_autolink_unescape_html_transformer.dart';
import 'package:core/presentation/utils/html_transformer/text/new_line_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/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
@@ -46,6 +48,7 @@ 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/move_action.dart';
import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart';
import 'package:tmail_ui_user/features/email/domain/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/state/calendar_event_accept_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/calendar_event_maybe_state.dart';
@@ -59,6 +62,8 @@ import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_sta
import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_star_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/move_to_mailbox_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/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/store_event_attendance_status_state.dart';
@@ -74,6 +79,8 @@ import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_i
import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_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_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_event_attendance_status_interactor.dart';
@@ -138,6 +145,8 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
final StoreOpenedEmailInteractor _storeOpenedEmailInteractor;
final PrintEmailInteractor _printEmailInteractor;
final StoreEventAttendanceStatusInteractor _storeEventAttendanceStatusInteractor;
final ParseEmailByBlobIdInteractor _parseEmailByBlobIdInteractor;
final PreviewEmailFromEmlFileInteractor _previewEmailFromEmlFileInteractor;
CreateNewEmailRuleFilterInteractor? _createNewEmailRuleFilterInteractor;
SendReceiptToSenderInteractor? _sendReceiptToSenderInteractor;
@@ -191,6 +200,8 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
this._storeOpenedEmailInteractor,
this._printEmailInteractor,
this._storeEventAttendanceStatusInteractor,
this._parseEmailByBlobIdInteractor,
this._previewEmailFromEmlFileInteractor,
);
@override
@@ -246,6 +257,10 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
calendarEventSuccess(success);
} else if (success is StoreEventAttendanceStatusSuccess) {
_showToastMessageEventAttendanceSuccess(success);
} else if (success is ParseEmailByBlobIdSuccess) {
_handleParseEmailByBlobIdSuccess(success);
} else if (success is PreviewEmailFromEmlFileSuccess) {
_handlePreviewEmailFromEMLFileSuccess(success);
}
}
@@ -269,6 +284,10 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
} else if (failure is CalendarEventReplyFailure
|| failure is StoreEventAttendanceStatusFailure) {
_calendarEventFailure(failure);
} else if (failure is ParseEmailByBlobIdFailure) {
_handleParseEmailByBlobIdFailure(failure);
} else if (failure is PreviewEmailFromEmlFileFailure) {
_handlePreviewEmailFromEMLFileFailure(failure);
}
}
@@ -1896,16 +1915,12 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
}
void handleViewAttachmentAction(BuildContext context, Attachment attachment) {
if (PlatformInfo.isWeb) {
if (PlatformInfo.isCanvasKit && attachment.validatePDFIcon()) {
previewPDFFileAction(context, attachment);
} else {
downloadAttachmentForWeb(attachment);
}
} else if (PlatformInfo.isMobile) {
exportAttachment(context, attachment);
if (PlatformInfo.isWeb && PlatformInfo.isCanvasKit && attachment.isPDFFile) {
previewPDFFileAction(context, attachment);
} else if (attachment.isEMLFile) {
previewEMLFileAction(attachment, AppLocalizations.of(context));
} else {
log('EmailView::_handleViewAttachmentAction: THE PLATFORM IS SUPPORTED');
handleDownloadAttachmentAction(context, attachment);
}
}
@@ -1936,6 +1951,80 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
);
}
void previewEMLFileAction(Attachment attachment, AppLocalizations appLocalizations) {
SmartDialog.showLoading(
msg: appLocalizations.loadingPleaseWait,
maskColor: Colors.black38,
);
if (accountId == null) {
consumeState(Stream.value(Left(ParseEmailByBlobIdFailure(NotFoundAccountIdException()))));
return;
}
if (attachment.blobId == null) {
consumeState(Stream.value(Left(ParseEmailByBlobIdFailure(NotFoundBlobIdException([])))));
return;
}
consumeState(_parseEmailByBlobIdInteractor.execute(
accountId!,
attachment.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;
}
consumeState(_previewEmailFromEmlFileInteractor.execute(
PreviewEmailEMLRequest(
accountId: accountId!,
userName: userName!,
blobId: success.blobId,
email: success.email,
locale: Localizations.localeOf(currentContext!),
appLocalizations: AppLocalizations.of(currentContext!),
baseDownloadUrl: session!.getDownloadUrl(jmapUrl: dynamicUrlInterceptors.jmapUrl),
),
));
}
void _handleParseEmailByBlobIdFailure(ParseEmailByBlobIdFailure failure) {
SmartDialog.dismiss();
toastManager.showMessageFailure(failure);
}
void _handlePreviewEmailFromEMLFileFailure(PreviewEmailFromEmlFileFailure failure) {
SmartDialog.dismiss();
toastManager.showMessageFailure(failure);
}
void _handlePreviewEmailFromEMLFileSuccess(PreviewEmailFromEmlFileSuccess success) {
SmartDialog.dismiss();
if (PlatformInfo.isWeb) {
HtmlUtils.openNewWindowByUrl(
RouteUtils.createUrlWebLocationBar(
AppRoutes.emailEMLPreviewer,
previewId: success.storeKey
).toString(),
);
}
}
void handleMailToAttendees(CalendarOrganizer? organizer, List<CalendarAttendee>? attendees) {
final listEmailAddressAttendees = attendees
?.map((attendee) => EmailAddress(attendee.name?.name, attendee.mailto?.mailAddress.value))
@@ -1,9 +1,11 @@
import 'package:core/presentation/extensions/media_type_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:model/email/attachment.dart';
import 'package:tmail_ui_user/features/upload/domain/extensions/media_type_extension.dart';
extension AttachmentExtension on Attachment {
String getIcon(ImagePaths imagePaths) => type?.getIcon(imagePaths, fileName: name) ?? imagePaths.icFileEPup;
bool validatePDFIcon() => type?.validatePDFIcon(fileName: name) ?? false;
bool get isPDFFile => type?.isPDFFile(fileName: name) ?? false;
bool get isEMLFile => type?.isEMLFile ?? false;
}
@@ -0,0 +1,107 @@
import 'package:core/data/model/source_type/data_source_type.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/utils/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/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/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/get_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/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 {
@override
void bindingsController() {
Get.lazyPut(() => EmailPreviewerController(
Get.find<GetPreviewEmailEMLContentSharedInteractor>()));
}
@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<CacheExceptionThrower>()));
}
@override
void bindingsInteractor() {
Get.lazyPut(() => GetPreviewEmailEMLContentSharedInteractor(
Get.find<EmailRepository>()));
}
@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>(),
},
Get.find<HtmlDataSource>(),
Get.find<StateDataSource>(),
Get.find<PrintFileDataSource>()));
}
}
@@ -0,0 +1,41 @@
import 'package:dartz/dartz.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_preview_email_content_shared_state.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_preview_email_eml_content_shared_interactor.dart';
import 'package:tmail_ui_user/main/routes/route_utils.dart';
class EmailPreviewerController extends BaseController {
final GetPreviewEmailEMLContentSharedInteractor _getPreviewEmailEMLContentSharedInteractor;
EmailPreviewerController(
this._getPreviewEmailEMLContentSharedInteractor,
);
@override
void onReady() {
super.onReady();
_getPreviewEmailEMLContentShared();
}
void _getPreviewEmailEMLContentShared() {
final parameters = Get.parameters;
if (parameters.isEmpty) {
consumeState(Stream.value(Left(GetPreviewEmailEMLContentSharedFailure(
NotFoundBlobIdException([])))));
return;
}
final keyStored = parameters[RouteUtils.paramID];
if (keyStored?.trim().isNotEmpty == true) {
consumeState(_getPreviewEmailEMLContentSharedInteractor.execute(keyStored!));
} else {
consumeState(Stream.value(Left(GetPreviewEmailEMLContentSharedFailure(
NotFoundBlobIdException([])))));
}
}
}
@@ -0,0 +1,48 @@
import 'package:core/presentation/views/html_viewer/html_content_viewer_on_web_widget.dart';
import 'package:core/presentation/views/loading/cupertino_loading_widget.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_preview_email_content_shared_state.dart';
import 'package:tmail_ui_user/features/email_previewer/email_previewer_controller.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class EmailPreviewerView extends GetWidget<EmailPreviewerController> {
const EmailPreviewerView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Obx(() {
final viewState = controller.viewState.value;
return viewState.fold(
(failure) {
if (viewState is GetPreviewEmailEMLContentSharedFailure) {
return Center(
child: Text(AppLocalizations.of(context).previewEmailFromEMLFileFailed),
);
} else {
return const Center(
child: CupertinoLoadingWidget(),
);
}
},
(success) {
if (success is GetPreviewEmailEMLContentSharedSuccess) {
return HtmlContentViewerOnWeb(
widthContent: context.width,
heightContent: context.height,
contentHtml: success.previewEMLContent,
);
} else {
return const Center(
child: CupertinoLoadingWidget(),
);
}
});
}),
);
}
}
@@ -1,2 +1,6 @@
class NotFoundSessionException implements Exception {}
class NotFoundSessionException implements Exception {}
class NotFoundAccountIdException implements Exception {}
class NotFoundContextException implements Exception {}
@@ -7,6 +7,7 @@ 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/clients/recent_search_cache_client.dart';
import 'package:tmail_ui_user/features/caching/utils/local_storage_manager.dart';
import 'package:tmail_ui_user/features/composer/data/repository/contact_repository_impl.dart';
import 'package:tmail_ui_user/features/composer/domain/repository/contact_repository.dart';
import 'package:tmail_ui_user/features/composer/domain/usecases/send_email_interactor.dart';
@@ -15,6 +16,7 @@ import 'package:tmail_ui_user/features/email/data/datasource/html_datasource.dar
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/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';
@@ -254,6 +256,9 @@ class MailboxDashBoardBindings extends BaseBindings {
Get.find<SendingEmailCacheManager>(),
Get.find<FileUtils>(),
Get.find<CacheExceptionThrower>()));
Get.lazyPut(() => EmailLocalStorageDataSourceImpl(
Get.find<LocalStorageManager>(),
Get.find<CacheExceptionThrower>()));
Get.lazyPut(() => RemoteServerSettingsDataSourceImpl(
Get.find<ServerSettingsAPI>(),
Get.find<RemoteExceptionThrower>()
@@ -340,7 +345,8 @@ class MailboxDashBoardBindings extends BaseBindings {
Get.lazyPut(() => EmailRepositoryImpl(
{
DataSourceType.network: Get.find<EmailDataSource>(),
DataSourceType.hiveCache: Get.find<EmailHiveCacheDataSourceImpl>()
DataSourceType.hiveCache: Get.find<EmailHiveCacheDataSourceImpl>(),
DataSourceType.local: Get.find<EmailLocalStorageDataSourceImpl>(),
},
Get.find<HtmlDataSource>(),
Get.find<StateDataSource>(),
@@ -4,12 +4,14 @@ import 'package:core/utils/file_utils.dart';
import 'package:core/utils/print_utils.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/composer/domain/usecases/send_email_interactor.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/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';
@@ -89,6 +91,9 @@ class SendEmailInteractorBindings extends InteractorsBindings {
Get.find<SendingEmailCacheManager>(),
Get.find<FileUtils>(),
Get.find<CacheExceptionThrower>()));
Get.lazyPut(() => EmailLocalStorageDataSourceImpl(
Get.find<LocalStorageManager>(),
Get.find<CacheExceptionThrower>()));
Get.lazyPut(() => RemoteServerSettingsDataSourceImpl(
Get.find<ServerSettingsAPI>(),
Get.find<RemoteExceptionThrower>()));
@@ -111,7 +116,8 @@ class SendEmailInteractorBindings extends InteractorsBindings {
Get.lazyPut(() => EmailRepositoryImpl(
{
DataSourceType.network: Get.find<EmailDataSource>(),
DataSourceType.hiveCache: Get.find<EmailHiveCacheDataSourceImpl>()
DataSourceType.hiveCache: Get.find<EmailHiveCacheDataSourceImpl>(),
DataSourceType.local: Get.find<EmailLocalStorageDataSourceImpl>(),
},
Get.find<HtmlDataSource>(),
Get.find<StateDataSource>(),
@@ -4,11 +4,13 @@ import 'package:core/utils/file_utils.dart';
import 'package:core/utils/print_utils.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/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/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';
@@ -127,6 +129,9 @@ class FcmInteractorBindings extends InteractorsBindings {
Get.find<SendingEmailCacheManager>(),
Get.find<FileUtils>(),
Get.find<CacheExceptionThrower>()));
Get.lazyPut(() => EmailLocalStorageDataSourceImpl(
Get.find<LocalStorageManager>(),
Get.find<CacheExceptionThrower>()));
}
@override
@@ -180,7 +185,8 @@ class FcmInteractorBindings extends InteractorsBindings {
Get.lazyPut(() => EmailRepositoryImpl(
{
DataSourceType.network: Get.find<EmailDataSource>(),
DataSourceType.hiveCache: Get.find<EmailHiveCacheDataSourceImpl>()
DataSourceType.hiveCache: Get.find<EmailHiveCacheDataSourceImpl>(),
DataSourceType.local: Get.find<EmailLocalStorageDataSourceImpl>(),
},
Get.find<HtmlDataSource>(),
Get.find<StateDataSource>(),
@@ -2,10 +2,10 @@ import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:dartz/dartz.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:model/oidc/oidc_configuration.dart';
import 'package:model/oidc/token_oidc.dart';
import 'package:tip_dialog/tip_dialog.dart';
import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart';
import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart';
import 'package:tmail_ui_user/features/login/data/network/config/oidc_constant.dart';
@@ -44,7 +44,7 @@ class TwakeWelcomeController extends ReloadableController {
}
void onClickSignIn(BuildContext context) {
TipDialogHelper.loading(AppLocalizations.of(context).loadingPleaseWait);
SmartDialog.showLoading(msg: AppLocalizations.of(context).loadingPleaseWait);
final baseUri = Uri.tryParse(AppConfig.saasJmapServerUrl);
@@ -64,7 +64,7 @@ class TwakeWelcomeController extends ReloadableController {
}
void onSignUpTwakeWorkplace(BuildContext context) {
TipDialogHelper.loading(AppLocalizations.of(context).loadingPleaseWait);
SmartDialog.showLoading(msg: AppLocalizations.of(context).loadingPleaseWait);
final baseUri = Uri.tryParse(AppConfig.saasJmapServerUrl);
@@ -115,7 +115,7 @@ class TwakeWelcomeController extends ReloadableController {
@override
void handleReloaded(Session session) {
TipDialogHelper.dismiss();
SmartDialog.dismiss();
popAndPush(
RouteUtils.generateNavigationRoute(AppRoutes.dashboard),
@@ -124,7 +124,7 @@ class TwakeWelcomeController extends ReloadableController {
@override
void handleGetSessionFailure(GetSessionFailure failure) {
TipDialogHelper.dismiss();
SmartDialog.dismiss();
toastManager.showMessageFailure(failure);
}
@@ -147,13 +147,13 @@ class TwakeWelcomeController extends ReloadableController {
}
void _handleSignInTwakeWorkplaceFailure(SignInTwakeWorkplaceFailure failure) {
TipDialogHelper.dismiss();
SmartDialog.dismiss();
toastManager.showMessageFailure(failure);
}
void _handleSignUpTwakeWorkplaceFailure(SignUpTwakeWorkplaceFailure failure) {
TipDialogHelper.dismiss();
SmartDialog.dismiss();
toastManager.showMessageFailure(failure);
}
@@ -3,7 +3,6 @@ import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:linagora_design_flutter/linagora_design_flutter.dart';
import 'package:tip_dialog/tip_dialog.dart';
import 'package:tmail_ui_user/features/starting_page/presentation/twake_welcome/twake_welcome_controller.dart';
import 'package:tmail_ui_user/features/starting_page/presentation/twake_welcome/twake_welcome_view_style.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
@@ -19,42 +18,29 @@ class TwakeWelcomeView extends GetWidget<TwakeWelcomeController> {
DeviceOrientation.portraitDown
]);
return Stack(
children: [
TwakeWelcomeScreen(
logo: Padding(
padding: const EdgeInsetsDirectional.only(bottom: 16),
child: SvgPicture.asset(
controller.imagePaths.icLogoTwakeWelcome,
fit: BoxFit.fill,
),
),
focusColor: Colors.transparent,
hoverColor: Colors.transparent,
highlightColor: Colors.transparent,
overlayColor: WidgetStateProperty.all(Colors.transparent),
signInTitle: AppLocalizations.of(context).signIn.capitalizeFirst ?? '',
onSignInOnTap: () => controller.onClickSignIn(context),
onCreateTwakeIdOnTap: () => controller.onSignUpTwakeWorkplace(context),
createTwakeIdTitle: AppLocalizations.of(context).createTwakeId,
useCompanyServerTitle: AppLocalizations.of(context).useCompanyServer,
description: AppLocalizations.of(context).descriptionWelcomeTo,
descriptionTextStyle: TwakeWelcomeViewStyle.descriptionTextStyle,
privacyPolicy: AppLocalizations.of(context).privacyPolicy,
descriptionPrivacyPolicy: AppLocalizations.of(context).byContinuingYouAreAgreeingToOur,
onPrivacyPolicyOnTap: controller.onClickPrivacyPolicy,
onUseCompanyServerOnTap: controller.handleUseCompanyServer,
return TwakeWelcomeScreen(
logo: Padding(
padding: const EdgeInsetsDirectional.only(bottom: 16),
child: SvgPicture.asset(
controller.imagePaths.icLogoTwakeWelcome,
fit: BoxFit.fill,
),
TipDialogContainer(
duration: const Duration(seconds: 2),
outsideTouchable: true,
onOutsideTouch: (tipDialog) {
if (tipDialog is TipDialog && tipDialog.type == TipDialogType.LOADING) {
TipDialogHelper.dismiss();
}
}
)
],
),
focusColor: Colors.transparent,
hoverColor: Colors.transparent,
highlightColor: Colors.transparent,
overlayColor: WidgetStateProperty.all(Colors.transparent),
signInTitle: AppLocalizations.of(context).signIn.capitalizeFirst ?? '',
onSignInOnTap: () => controller.onClickSignIn(context),
onCreateTwakeIdOnTap: () => controller.onSignUpTwakeWorkplace(context),
createTwakeIdTitle: AppLocalizations.of(context).createTwakeId,
useCompanyServerTitle: AppLocalizations.of(context).useCompanyServer,
description: AppLocalizations.of(context).descriptionWelcomeTo,
descriptionTextStyle: TwakeWelcomeViewStyle.descriptionTextStyle,
privacyPolicy: AppLocalizations.of(context).privacyPolicy,
descriptionPrivacyPolicy: AppLocalizations.of(context).byContinuingYouAreAgreeingToOur,
onPrivacyPolicyOnTap: controller.onClickPrivacyPolicy,
onUseCompanyServerOnTap: controller.handleUseCompanyServer,
);
}
}
@@ -84,4 +84,28 @@ class ThreadConstants {
static const int defaultMaxHeightEmailItemOnBrowser = 40;
static const int defaultMaxHeightBrowser = 1200;
static final propertiesParseEmailByBlobId = Properties({
EmailProperty.id,
EmailProperty.blobId,
EmailProperty.subject,
EmailProperty.from,
EmailProperty.to,
EmailProperty.cc,
EmailProperty.bcc,
EmailProperty.keywords,
EmailProperty.size,
EmailProperty.receivedAt,
EmailProperty.sentAt,
EmailProperty.preview,
EmailProperty.hasAttachment,
EmailProperty.replyTo,
EmailProperty.mailboxIds,
EmailProperty.bodyValues,
EmailProperty.htmlBody,
EmailProperty.attachments,
EmailProperty.headers,
EmailProperty.messageId,
EmailProperty.references,
});
}
@@ -1,28 +0,0 @@
import 'package:core/domain/extensions/media_type_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:http_parser/http_parser.dart';
extension MediaTypeExtension on MediaType {
String getIcon(ImagePaths imagePaths, {String? fileName}) {
if (validatePDFIcon(fileName: fileName) == true) {
return imagePaths.icFilePdf;
} else if (isDocFile()) {
return imagePaths.icFileDocx;
} else if (isExcelFile()) {
return imagePaths.icFileXlsx;
} else if (isPowerPointFile()) {
return imagePaths.icFilePptx;
} else if (isPdfFile()) {
return imagePaths.icFilePdf;
} else if (isZipFile()) {
return imagePaths.icFileZip;
} else if (isImageFile()) {
return imagePaths.icFilePng;
} else {
return imagePaths.icFileEPup;
}
}
bool validatePDFIcon({required String? fileName}) => mimeType == 'application/pdf' ||
(mimeType == 'application/octet-stream' && fileName?.endsWith('.pdf') == true);
}
@@ -1,4 +1,5 @@
import 'package:core/presentation/extensions/media_type_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/utils/app_logger.dart';
import 'package:dio/dio.dart';
@@ -6,7 +7,6 @@ import 'package:equatable/equatable.dart';
import 'package:http_parser/http_parser.dart';
import 'package:model/email/attachment.dart';
import 'package:model/upload/file_info.dart';
import 'package:tmail_ui_user/features/upload/domain/extensions/media_type_extension.dart';
import 'package:tmail_ui_user/features/upload/domain/model/upload_task_id.dart';
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_status.dart';