diff --git a/core/lib/data/constants/constant.dart b/core/lib/data/constants/constant.dart index 8ec6a6caa4..e6a50f300a 100644 --- a/core/lib/data/constants/constant.dart +++ b/core/lib/data/constants/constant.dart @@ -3,4 +3,5 @@ class Constant { static const contentTypeHeaderDefault = 'application/json'; static const pdfMimeType = 'application/pdf'; static const textHtmlMimeType = 'text/html'; + static const octetStreamMimeType = 'application/octet-stream'; } \ No newline at end of file diff --git a/lib/features/base/upgradeable/upgrade_hive_database_steps_v11.dart b/lib/features/base/upgradeable/upgrade_hive_database_steps_v11.dart new file mode 100644 index 0000000000..0f7de94c98 --- /dev/null +++ b/lib/features/base/upgradeable/upgrade_hive_database_steps_v11.dart @@ -0,0 +1,17 @@ + +import 'package:tmail_ui_user/features/base/upgradeable/upgrade_database_steps.dart'; +import 'package:tmail_ui_user/features/caching/caching_manager.dart'; + +class UpgradeHiveDatabaseStepsV11 extends UpgradeDatabaseSteps { + + final CachingManager _cachingManager; + + UpgradeHiveDatabaseStepsV11(this._cachingManager); + + @override + Future onUpgrade(int oldVersion, int newVersion) async { + if (oldVersion > 0 && oldVersion < newVersion && newVersion == 11) { + await _cachingManager.clearEmailCacheAndAllStateCache(); + } + } +} \ No newline at end of file diff --git a/lib/features/caching/config/cache_version.dart b/lib/features/caching/config/cache_version.dart index 6fafaf284c..304bfea834 100644 --- a/lib/features/caching/config/cache_version.dart +++ b/lib/features/caching/config/cache_version.dart @@ -1,4 +1,4 @@ class CacheVersion { - static const int hiveDBVersion = 10; + static const int hiveDBVersion = 11; } \ No newline at end of file diff --git a/lib/features/caching/config/hive_cache_config.dart b/lib/features/caching/config/hive_cache_config.dart index c2ad73acb3..40f2333b0f 100644 --- a/lib/features/caching/config/hive_cache_config.dart +++ b/lib/features/caching/config/hive_cache_config.dart @@ -7,6 +7,7 @@ import 'package:core/utils/platform_info.dart'; import 'package:hive/hive.dart'; import 'package:path_provider/path_provider.dart' as path_provider; import 'package:tmail_ui_user/features/base/upgradeable/upgrade_hive_database_steps_v10.dart'; +import 'package:tmail_ui_user/features/base/upgradeable/upgrade_hive_database_steps_v11.dart'; import 'package:tmail_ui_user/features/base/upgradeable/upgrade_hive_database_steps_v7.dart'; import 'package:tmail_ui_user/features/caching/caching_manager.dart'; import 'package:tmail_ui_user/features/caching/config/cache_version.dart'; @@ -65,8 +66,11 @@ class HiveCacheConfig { await UpgradeHiveDatabaseStepsV7(cachingManager).onUpgrade(oldVersion, newVersion); await UpgradeHiveDatabaseStepsV10(cachingManager).onUpgrade(oldVersion, newVersion); + await UpgradeHiveDatabaseStepsV11(cachingManager).onUpgrade(oldVersion, newVersion); - await cachingManager.storeCacheVersion(newVersion); + if (oldVersion != newVersion) { + await cachingManager.storeCacheVersion(newVersion); + } } Future initializeEncryptionKey() async { diff --git a/lib/features/composer/presentation/extensions/email_action_type_extension.dart b/lib/features/composer/presentation/extensions/email_action_type_extension.dart index 17a0e36752..5c0a523d15 100644 --- a/lib/features/composer/presentation/extensions/email_action_type_extension.dart +++ b/lib/features/composer/presentation/extensions/email_action_type_extension.dart @@ -133,6 +133,8 @@ extension EmailActionTypeExtension on EmailActionType { return imagePaths.icUnsubscribe; case EmailActionType.archiveMessage: return imagePaths.icMailboxArchived; + case EmailActionType.downloadMessageAsEML: + return imagePaths.icDownloadAttachment; default: return ''; } @@ -152,6 +154,8 @@ extension EmailActionTypeExtension on EmailActionType { return AppLocalizations.of(context).unsubscribe; case EmailActionType.archiveMessage: return AppLocalizations.of(context).archiveMessage; + case EmailActionType.downloadMessageAsEML: + return AppLocalizations.of(context).downloadMessageAsEML; default: return ''; } diff --git a/lib/features/email/data/network/email_api.dart b/lib/features/email/data/network/email_api.dart index 1612575dd5..2540ba130b 100644 --- a/lib/features/email/data/network/email_api.dart +++ b/lib/features/email/data/network/email_api.dart @@ -351,12 +351,12 @@ class EmailAPI with HandleSetErrorMixin { headers: headerParam, responseType: ResponseType.bytes), onReceiveProgress: (downloaded, total) { - log('DownloadClient::downloadFileForWeb(): downloaded = $downloaded | total: $total'); + log('EmailAPI::downloadFileForWeb(): downloaded = $downloaded | total: $total'); double progress = 0; - if (downloaded > 0 && total > downloaded) { + if (downloaded > 0 && total >= downloaded) { progress = (downloaded / total) * 100; } - log('DownloadClient::downloadFileForWeb(): progress = ${progress.round()}%'); + log('EmailAPI::downloadFileForWeb(): progress = ${progress.round()}%'); onReceiveController.add(Right(DownloadingAttachmentForWeb( taskId, attachment, diff --git a/lib/features/email/domain/exceptions/email_exceptions.dart b/lib/features/email/domain/exceptions/email_exceptions.dart index a5aa2441ec..8057441639 100644 --- a/lib/features/email/domain/exceptions/email_exceptions.dart +++ b/lib/features/email/domain/exceptions/email_exceptions.dart @@ -4,4 +4,6 @@ class NotFoundEmailContentException implements Exception {} class EmptyEmailContentException implements Exception {} -class NotFoundEmailRecoveryActionException implements Exception {} \ No newline at end of file +class NotFoundEmailRecoveryActionException implements Exception {} + +class NotFoundEmailBlobIdException implements Exception {} \ No newline at end of file diff --git a/lib/features/email/domain/state/download_attachment_for_web_state.dart b/lib/features/email/domain/state/download_attachment_for_web_state.dart index 72082f0571..77fcc7c9b6 100644 --- a/lib/features/email/domain/state/download_attachment_for_web_state.dart +++ b/lib/features/email/domain/state/download_attachment_for_web_state.dart @@ -1,7 +1,6 @@ import 'dart:typed_data'; 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:model/download/download_task_id.dart'; import 'package:model/email/attachment.dart'; @@ -59,14 +58,14 @@ class DownloadAttachmentForWebSuccess extends UIState { class DownloadAttachmentForWebFailure extends FeatureFailure { final DownloadTaskId? taskId; - final Id? attachmentBlobId; + final Attachment? attachment; DownloadAttachmentForWebFailure({ - required this.attachmentBlobId, + this.attachment, this.taskId, dynamic exception }) : super(exception: exception); @override - List get props => [taskId, ...super.props]; + List get props => [attachment, taskId, ...super.props]; } \ No newline at end of file diff --git a/lib/features/email/domain/state/view_attachment_for_web_state.dart b/lib/features/email/domain/state/view_attachment_for_web_state.dart index d1e812465a..96dcaab065 100644 --- a/lib/features/email/domain/state/view_attachment_for_web_state.dart +++ b/lib/features/email/domain/state/view_attachment_for_web_state.dart @@ -22,7 +22,7 @@ class ViewAttachmentForWebSuccess extends DownloadAttachmentForWebSuccess { class ViewAttachmentForWebFailure extends DownloadAttachmentForWebFailure { ViewAttachmentForWebFailure({ - required super.attachmentBlobId, + required super.attachment, super.taskId, super.exception, }); diff --git a/lib/features/email/domain/usecases/download_attachment_for_web_interactor.dart b/lib/features/email/domain/usecases/download_attachment_for_web_interactor.dart index 9e7b820887..3715d76478 100644 --- a/lib/features/email/domain/usecases/download_attachment_for_web_interactor.dart +++ b/lib/features/email/domain/usecases/download_attachment_for_web_interactor.dart @@ -72,7 +72,7 @@ class DownloadAttachmentForWebInteractor { } catch (exception) { yield Left( DownloadAttachmentForWebFailure( - attachmentBlobId: attachment.blobId, + attachment: attachment, taskId: taskId, exception: exception ) diff --git a/lib/features/email/domain/usecases/view_attachment_for_web_interactor.dart b/lib/features/email/domain/usecases/view_attachment_for_web_interactor.dart index f69ff81d82..fc318bf9e3 100644 --- a/lib/features/email/domain/usecases/view_attachment_for_web_interactor.dart +++ b/lib/features/email/domain/usecases/view_attachment_for_web_interactor.dart @@ -34,7 +34,7 @@ class ViewAttachmentForWebInteractor { (failure) { if (failure is DownloadAttachmentForWebFailure) { return Left(ViewAttachmentForWebFailure( - attachmentBlobId: attachment.blobId, + attachment: attachment, taskId: failure.taskId, exception: failure.exception, )); diff --git a/lib/features/email/presentation/controller/single_email_controller.dart b/lib/features/email/presentation/controller/single_email_controller.dart index 3dea9c1c8b..4c81c87ee6 100644 --- a/lib/features/email/presentation/controller/single_email_controller.dart +++ b/lib/features/email/presentation/controller/single_email_controller.dart @@ -20,6 +20,7 @@ import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:jmap_dart_client/jmap/mdn/disposition.dart'; import 'package:jmap_dart_client/jmap/mdn/mdn.dart'; import 'package:mime/mime.dart'; +import 'package:model/email/eml_attachment.dart'; import 'package:model/model.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart'; @@ -28,6 +29,7 @@ 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/composer/presentation/extensions/email_action_type_extension.dart'; import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart'; +import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart'; import 'package:tmail_ui_user/features/email/domain/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/email_print.dart'; @@ -810,7 +812,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { } else { consumeState(Stream.value( Left(DownloadAttachmentForWebFailure( - attachmentBlobId: attachment.blobId, + attachment: attachment, exception: NotFoundSessionException())) )); } @@ -831,7 +833,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin { } else { consumeState(Stream.value( Left(ViewAttachmentForWebFailure( - attachmentBlobId: attachment.blobId, + attachment: attachment, exception: NotFoundSessionException())) )); } @@ -876,12 +878,16 @@ class SingleEmailController extends BaseController with AppLoaderMixin { mailboxDashBoardController.deleteDownloadTask(failure.taskId!); } - _updateAttachmentsViewState(failure.attachmentBlobId, Left(failure)); + if (failure.attachment != null) { + _updateAttachmentsViewState(failure.attachment?.blobId, Left(failure)); + } if (currentOverlayContext != null && currentContext != null) { appToast.showToastErrorMessage( currentOverlayContext!, - AppLocalizations.of(currentContext!).attachment_download_failed); + failure.attachment is EMLAttachment + ? AppLocalizations.of(currentContext!).downloadMessageAsEMLFailed + : AppLocalizations.of(currentContext!).attachment_download_failed); } } @@ -1141,6 +1147,9 @@ class SingleEmailController extends BaseController with AppLoaderMixin { case EmailActionType.printAll: _printEmail(context, presentationEmail); break; + case EmailActionType.downloadMessageAsEML: + _downloadMessageAsEML(presentationEmail); + break; default: break; } @@ -1772,4 +1781,14 @@ class SingleEmailController extends BaseController with AppLoaderMixin { AppLocalizations.of(currentContext!).eventReplyWasSentUnsuccessfully); } } + + void _downloadMessageAsEML(PresentationEmail presentationEmail) { + final emlAttachment = presentationEmail.createEMLAttachment(); + if (emlAttachment.blobId == null) { + consumeState(Stream.value(Left(DownloadAttachmentForWebFailure(exception: NotFoundEmailBlobIdException())))); + return; + } + + downloadAttachmentForWeb(emlAttachment); + } } \ No newline at end of file diff --git a/lib/features/email/presentation/email_view.dart b/lib/features/email/presentation/email_view.dart index 990fd895a9..9555e6f489 100644 --- a/lib/features/email/presentation/email_view.dart +++ b/lib/features/email/presentation/email_view.dart @@ -473,6 +473,8 @@ class EmailView extends GetWidget { EmailActionType.unsubscribe, if (mailboxContain?.isArchive == false) EmailActionType.archiveMessage, + if (PlatformInfo.isWeb && PlatformInfo.isCanvasKit) + EmailActionType.downloadMessageAsEML ]; if (position == null) { diff --git a/lib/features/email/presentation/utils/email_utils.dart b/lib/features/email/presentation/utils/email_utils.dart index 730866beff..79b3a66bb3 100644 --- a/lib/features/email/presentation/utils/email_utils.dart +++ b/lib/features/email/presentation/utils/email_utils.dart @@ -1,6 +1,5 @@ import 'package:collection/collection.dart'; -import 'package:core/utils/app_logger.dart'; import 'package:get/get_utils/src/get_utils/get_utils.dart'; import 'package:core/core.dart'; import 'package:dartz/dartz.dart'; diff --git a/lib/features/thread/data/extensions/email_cache_extension.dart b/lib/features/thread/data/extensions/email_cache_extension.dart index 5d101f04ab..4edb88c50d 100644 --- a/lib/features/thread/data/extensions/email_cache_extension.dart +++ b/lib/features/thread/data/extensions/email_cache_extension.dart @@ -34,7 +34,8 @@ extension EmailCacheExtension on EmailCache { : null, headerCalendarEvent: headerCalendarEvent != null ? Map.fromIterables(headerCalendarEvent!.keys.map((value) => IndividualHeaderIdentifier(value)), headerCalendarEvent!.values) - : null + : null, + blobId: blobId != null ? Id(blobId!) : null, ); } diff --git a/lib/features/thread/data/extensions/email_extension.dart b/lib/features/thread/data/extensions/email_extension.dart index d6a02dd60d..d5c6166bc1 100644 --- a/lib/features/thread/data/extensions/email_extension.dart +++ b/lib/features/thread/data/extensions/email_extension.dart @@ -25,6 +25,7 @@ extension EmailExtension on Email { replyTo: replyTo?.map((emailAddress) => emailAddress.toEmailAddressHiveCache()).toList(), mailboxIds: mailboxIds?.toMapString(), headerCalendarEvent: headerCalendarEvent?.toMapString(), + blobId: blobId?.value, ); } diff --git a/lib/features/thread/data/model/email_cache.dart b/lib/features/thread/data/model/email_cache.dart index 6db80e1d50..6218d7505d 100644 --- a/lib/features/thread/data/model/email_cache.dart +++ b/lib/features/thread/data/model/email_cache.dart @@ -54,6 +54,9 @@ class EmailCache extends HiveObject with EquatableMixin { @HiveField(14) Map? headerCalendarEvent; + @HiveField(15) + final String? blobId; + EmailCache( this.id, { @@ -71,6 +74,7 @@ class EmailCache extends HiveObject with EquatableMixin { this.replyTo, this.mailboxIds, this.headerCalendarEvent, + this.blobId, } ); @@ -91,5 +95,6 @@ class EmailCache extends HiveObject with EquatableMixin { hasAttachment, mailboxIds, headerCalendarEvent, + blobId, ]; } \ No newline at end of file diff --git a/lib/features/thread/domain/constants/thread_constants.dart b/lib/features/thread/domain/constants/thread_constants.dart index b72edf5be0..a5da5b4027 100644 --- a/lib/features/thread/domain/constants/thread_constants.dart +++ b/lib/features/thread/domain/constants/thread_constants.dart @@ -8,6 +8,7 @@ class ThreadConstants { static final defaultLimit = UnsignedInt(maxCountEmails); static final propertiesDefault = Properties({ EmailProperty.id, + EmailProperty.blobId, EmailProperty.subject, EmailProperty.from, EmailProperty.to, @@ -29,6 +30,7 @@ class ThreadConstants { static final propertiesQuickSearch = Properties({ EmailProperty.id, + EmailProperty.blobId, EmailProperty.subject, EmailProperty.from, EmailProperty.to, @@ -53,6 +55,7 @@ class ThreadConstants { static final propertiesGetDetailedEmail = Properties({ EmailProperty.id, + EmailProperty.blobId, EmailProperty.subject, EmailProperty.from, EmailProperty.to, @@ -74,6 +77,7 @@ class ThreadConstants { static final propertiesCalendarEvent = Properties({ EmailProperty.id, + EmailProperty.blobId, EmailProperty.subject, EmailProperty.from, EmailProperty.to, diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index 8b095991f0..81ce7423e4 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2024-04-19T16:31:35.757887", + "@@last_modified": "2024-05-16T11:36:53.008027", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -3719,5 +3719,17 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "downloadMessageAsEML": "Download message as EML", + "@downloadMessageAsEML": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "downloadMessageAsEMLFailed": "Download message as EML failed", + "@downloadMessageAsEMLFailed": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index 176ff8a042..2f8f746615 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -3879,4 +3879,18 @@ class AppLocalizations { name: 'youMayAttendThisMeeting', ); } + + String get downloadMessageAsEML { + return Intl.message( + 'Download message as EML', + name: 'downloadMessageAsEML', + ); + } + + String get downloadMessageAsEMLFailed { + return Intl.message( + 'Download message as EML failed', + name: 'downloadMessageAsEMLFailed', + ); + } } \ No newline at end of file diff --git a/model/lib/email/attachment.dart b/model/lib/email/attachment.dart index dd73c7bad7..9819bdf81b 100644 --- a/model/lib/email/attachment.dart +++ b/model/lib/email/attachment.dart @@ -44,8 +44,8 @@ class Attachment with EquatableMixin { final downloadUri = downloadUriTemplate.expand({ 'accountId' : accountId.id.value, 'blobId' : '${blobId?.value}', - 'name' : '$name', - 'type' : '${type?.mimeType}', + 'name' : name ?? '', + 'type' : type?.mimeType ?? '', }); return Uri.decodeFull(downloadUri); } diff --git a/model/lib/email/email_action_type.dart b/model/lib/email/email_action_type.dart index e9a7b4460f..a125b4c3ae 100644 --- a/model/lib/email/email_action_type.dart +++ b/model/lib/email/email_action_type.dart @@ -27,5 +27,6 @@ enum EmailActionType { unsubscribe, composeFromUnsubscribeMailtoLink, archiveMessage, - printAll + printAll, + downloadMessageAsEML } \ No newline at end of file diff --git a/model/lib/email/email_property.dart b/model/lib/email/email_property.dart index f705555db8..d1981e4043 100644 --- a/model/lib/email/email_property.dart +++ b/model/lib/email/email_property.dart @@ -1,6 +1,7 @@ class EmailProperty { static const String id = 'id'; + static const String blobId = 'blobId'; static const String keywords = 'keywords'; static const String size = 'size'; static const String receivedAt = 'receivedAt'; diff --git a/model/lib/email/eml_attachment.dart b/model/lib/email/eml_attachment.dart new file mode 100644 index 0000000000..08189e14be --- /dev/null +++ b/model/lib/email/eml_attachment.dart @@ -0,0 +1,15 @@ + +import 'package:model/email/attachment.dart'; + +class EMLAttachment extends Attachment { + + EMLAttachment({ + super.partId, + super.blobId, + super.size, + super.name, + super.type, + super.cid, + super.disposition, + }); +} \ No newline at end of file diff --git a/model/lib/email/presentation_email.dart b/model/lib/email/presentation_email.dart index 599aced126..999605dd79 100644 --- a/model/lib/email/presentation_email.dart +++ b/model/lib/email/presentation_email.dart @@ -1,6 +1,7 @@ import 'package:core/presentation/extensions/string_extension.dart'; import 'package:equatable/equatable.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; import 'package:jmap_dart_client/jmap/core/unsigned_int.dart'; import 'package:jmap_dart_client/jmap/core/utc_date.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; @@ -21,6 +22,7 @@ import 'package:model/mailbox/select_mode.dart'; class PresentationEmail with EquatableMixin { final EmailId? id; + final Id? blobId; final Map? keywords; final UnsignedInt? size; final UTCDate? receivedAt; @@ -44,6 +46,7 @@ class PresentationEmail with EquatableMixin { PresentationEmail({ this.id, + this.blobId, this.keywords, this.size, this.receivedAt, @@ -142,6 +145,7 @@ class PresentationEmail with EquatableMixin { @override List get props => [ id, + blobId, keywords, size, receivedAt, diff --git a/model/lib/extensions/email_extension.dart b/model/lib/extensions/email_extension.dart index a7738ebc37..7b7707abde 100644 --- a/model/lib/extensions/email_extension.dart +++ b/model/lib/extensions/email_extension.dart @@ -67,6 +67,7 @@ extension EmailExtension on Email { Email updatedEmail({Map? newKeywords, Map? newMailboxIds}) { return Email( id: id, + blobId: blobId, keywords: newKeywords ?? keywords, size: size, receivedAt: receivedAt, @@ -91,6 +92,7 @@ extension EmailExtension on Email { PresentationEmail toPresentationEmail({SelectMode selectMode = SelectMode.INACTIVE}) { return PresentationEmail( id: id, + blobId: blobId, keywords: keywords, size: size, receivedAt: receivedAt, @@ -113,6 +115,7 @@ extension EmailExtension on Email { Email combineEmail(Email newEmail, Properties updatedProperties) { return Email( id: newEmail.id, + blobId: updatedProperties.contain(EmailProperty.blobId) ? newEmail.blobId : blobId, keywords: updatedProperties.contain(EmailProperty.keywords) ? newEmail.keywords : keywords, size: updatedProperties.contain(EmailProperty.size) ? newEmail.size : size, receivedAt: updatedProperties.contain(EmailProperty.receivedAt) ? newEmail.receivedAt : receivedAt, @@ -171,6 +174,7 @@ extension EmailExtension on Email { ) { return PresentationEmail( id: emailId ?? id, + blobId: blobId, keywords: keywords, size: size, receivedAt: receivedAt, diff --git a/model/lib/extensions/presentation_email_extension.dart b/model/lib/extensions/presentation_email_extension.dart index efab88131c..1ca8061f4b 100644 --- a/model/lib/extensions/presentation_email_extension.dart +++ b/model/lib/extensions/presentation_email_extension.dart @@ -1,14 +1,17 @@ import 'dart:ui'; +import 'package:core/data/constants/constant.dart'; import 'package:core/domain/extensions/datetime_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/utils/app_logger.dart'; +import 'package:http_parser/http_parser.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:dartz/dartz.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:model/email/email_action_type.dart'; +import 'package:model/email/eml_attachment.dart'; import 'package:model/email/presentation_email.dart'; import 'package:model/extensions/email_address_extension.dart'; import 'package:model/extensions/list_email_address_extension.dart'; @@ -44,6 +47,7 @@ extension PresentationEmailExtension on PresentationEmail { PresentationEmail toggleSelect() { return PresentationEmail( id: this.id, + blobId: blobId, keywords: keywords, size: size, receivedAt: receivedAt, @@ -67,6 +71,7 @@ extension PresentationEmailExtension on PresentationEmail { PresentationEmail toSelectedEmail({required SelectMode selectMode}) { return PresentationEmail( id: this.id, + blobId: blobId, keywords: keywords, size: size, receivedAt: receivedAt, @@ -90,6 +95,7 @@ extension PresentationEmailExtension on PresentationEmail { Email toEmail() { return Email( id: this.id, + blobId: blobId, keywords: keywords, size: size, receivedAt: receivedAt, @@ -146,6 +152,7 @@ extension PresentationEmailExtension on PresentationEmail { return PresentationEmail( id: this.id, + blobId: blobId, keywords: keywords, size: size, receivedAt: receivedAt, @@ -182,6 +189,7 @@ extension PresentationEmailExtension on PresentationEmail { PresentationEmail withRouteWeb(Uri routeWeb) { return PresentationEmail( id: this.id, + blobId: blobId, keywords: keywords, size: size, receivedAt: receivedAt, @@ -205,6 +213,7 @@ extension PresentationEmailExtension on PresentationEmail { PresentationEmail updateKeywords(Map? newKeywords) { return PresentationEmail( id: this.id, + blobId: blobId, keywords: newKeywords, size: size, receivedAt: receivedAt, @@ -228,6 +237,7 @@ extension PresentationEmailExtension on PresentationEmail { PresentationEmail syncPresentationEmail({PresentationMailbox? mailboxContain, Uri? routeWeb}) { return PresentationEmail( id: this.id, + blobId: blobId, keywords: keywords, size: size, receivedAt: receivedAt, @@ -262,4 +272,12 @@ extension PresentationEmailExtension on PresentationEmail { return false; } + + EMLAttachment createEMLAttachment() { + return EMLAttachment( + blobId: blobId, + name: getEmailTitle().isEmpty ? '${blobId?.value}.eml' : '${getEmailTitle()}.eml', + type: MediaType.parse(Constant.octetStreamMimeType) + ); + } } \ No newline at end of file diff --git a/test/features/email/domain/usecases/view_attachment_for_web_interactor_test.dart b/test/features/email/domain/usecases/view_attachment_for_web_interactor_test.dart index 6b947149c1..ba4fe053c7 100644 --- a/test/features/email/domain/usecases/view_attachment_for_web_interactor_test.dart +++ b/test/features/email/domain/usecases/view_attachment_for_web_interactor_test.dart @@ -55,7 +55,7 @@ void main() { (_) => Stream.value( Left( DownloadAttachmentForWebFailure( - attachmentBlobId: testAttachment.blobId, + attachment: testAttachment, taskId: testDownloadTaskId, exception: testException, ), @@ -75,7 +75,7 @@ void main() { emitsInOrder([ Left( ViewAttachmentForWebFailure( - attachmentBlobId: testAttachment.blobId, + attachment: testAttachment, taskId: testDownloadTaskId, exception: testException, ), diff --git a/test/features/interceptor/authorization_interceptor_test.dart b/test/features/interceptor/authorization_interceptor_test.dart index b614309c53..fcc70b1cc5 100644 --- a/test/features/interceptor/authorization_interceptor_test.dart +++ b/test/features/interceptor/authorization_interceptor_test.dart @@ -59,7 +59,7 @@ void main() { }; final baseOption = BaseOptions(headers: headers); dio = Dio(baseOption) - ..options.baseUrl = baseUrl;; + ..options.baseUrl = baseUrl; authenticationClient = MockAuthenticationClientBase(); tokenOidcCacheManager = MockTokenOidcCacheManager();