TF-825 Download email as EML file (#2854)

This commit is contained in:
Dat Vu
2024-05-16 15:50:01 +07:00
committed by GitHub
parent 175f285e51
commit ca4cf68f87
29 changed files with 153 additions and 26 deletions
+1
View File
@@ -3,4 +3,5 @@ class Constant {
static const contentTypeHeaderDefault = 'application/json'; static const contentTypeHeaderDefault = 'application/json';
static const pdfMimeType = 'application/pdf'; static const pdfMimeType = 'application/pdf';
static const textHtmlMimeType = 'text/html'; static const textHtmlMimeType = 'text/html';
static const octetStreamMimeType = 'application/octet-stream';
} }
@@ -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<void> onUpgrade(int oldVersion, int newVersion) async {
if (oldVersion > 0 && oldVersion < newVersion && newVersion == 11) {
await _cachingManager.clearEmailCacheAndAllStateCache();
}
}
}
@@ -1,4 +1,4 @@
class CacheVersion { class CacheVersion {
static const int hiveDBVersion = 10; static const int hiveDBVersion = 11;
} }
@@ -7,6 +7,7 @@ import 'package:core/utils/platform_info.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart' as path_provider; 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_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/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/caching_manager.dart';
import 'package:tmail_ui_user/features/caching/config/cache_version.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 UpgradeHiveDatabaseStepsV7(cachingManager).onUpgrade(oldVersion, newVersion);
await UpgradeHiveDatabaseStepsV10(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<void> initializeEncryptionKey() async { Future<void> initializeEncryptionKey() async {
@@ -133,6 +133,8 @@ extension EmailActionTypeExtension on EmailActionType {
return imagePaths.icUnsubscribe; return imagePaths.icUnsubscribe;
case EmailActionType.archiveMessage: case EmailActionType.archiveMessage:
return imagePaths.icMailboxArchived; return imagePaths.icMailboxArchived;
case EmailActionType.downloadMessageAsEML:
return imagePaths.icDownloadAttachment;
default: default:
return ''; return '';
} }
@@ -152,6 +154,8 @@ extension EmailActionTypeExtension on EmailActionType {
return AppLocalizations.of(context).unsubscribe; return AppLocalizations.of(context).unsubscribe;
case EmailActionType.archiveMessage: case EmailActionType.archiveMessage:
return AppLocalizations.of(context).archiveMessage; return AppLocalizations.of(context).archiveMessage;
case EmailActionType.downloadMessageAsEML:
return AppLocalizations.of(context).downloadMessageAsEML;
default: default:
return ''; return '';
} }
@@ -351,12 +351,12 @@ class EmailAPI with HandleSetErrorMixin {
headers: headerParam, headers: headerParam,
responseType: ResponseType.bytes), responseType: ResponseType.bytes),
onReceiveProgress: (downloaded, total) { onReceiveProgress: (downloaded, total) {
log('DownloadClient::downloadFileForWeb(): downloaded = $downloaded | total: $total'); log('EmailAPI::downloadFileForWeb(): downloaded = $downloaded | total: $total');
double progress = 0; double progress = 0;
if (downloaded > 0 && total > downloaded) { if (downloaded > 0 && total >= downloaded) {
progress = (downloaded / total) * 100; progress = (downloaded / total) * 100;
} }
log('DownloadClient::downloadFileForWeb(): progress = ${progress.round()}%'); log('EmailAPI::downloadFileForWeb(): progress = ${progress.round()}%');
onReceiveController.add(Right(DownloadingAttachmentForWeb( onReceiveController.add(Right(DownloadingAttachmentForWeb(
taskId, taskId,
attachment, attachment,
@@ -4,4 +4,6 @@ class NotFoundEmailContentException implements Exception {}
class EmptyEmailContentException implements Exception {} class EmptyEmailContentException implements Exception {}
class NotFoundEmailRecoveryActionException implements Exception {} class NotFoundEmailRecoveryActionException implements Exception {}
class NotFoundEmailBlobIdException implements Exception {}
@@ -1,7 +1,6 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.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/download/download_task_id.dart';
import 'package:model/email/attachment.dart'; import 'package:model/email/attachment.dart';
@@ -59,14 +58,14 @@ class DownloadAttachmentForWebSuccess extends UIState {
class DownloadAttachmentForWebFailure extends FeatureFailure { class DownloadAttachmentForWebFailure extends FeatureFailure {
final DownloadTaskId? taskId; final DownloadTaskId? taskId;
final Id? attachmentBlobId; final Attachment? attachment;
DownloadAttachmentForWebFailure({ DownloadAttachmentForWebFailure({
required this.attachmentBlobId, this.attachment,
this.taskId, this.taskId,
dynamic exception dynamic exception
}) : super(exception: exception); }) : super(exception: exception);
@override @override
List<Object?> get props => [taskId, ...super.props]; List<Object?> get props => [attachment, taskId, ...super.props];
} }
@@ -22,7 +22,7 @@ class ViewAttachmentForWebSuccess extends DownloadAttachmentForWebSuccess {
class ViewAttachmentForWebFailure extends DownloadAttachmentForWebFailure { class ViewAttachmentForWebFailure extends DownloadAttachmentForWebFailure {
ViewAttachmentForWebFailure({ ViewAttachmentForWebFailure({
required super.attachmentBlobId, required super.attachment,
super.taskId, super.taskId,
super.exception, super.exception,
}); });
@@ -72,7 +72,7 @@ class DownloadAttachmentForWebInteractor {
} catch (exception) { } catch (exception) {
yield Left<Failure, Success>( yield Left<Failure, Success>(
DownloadAttachmentForWebFailure( DownloadAttachmentForWebFailure(
attachmentBlobId: attachment.blobId, attachment: attachment,
taskId: taskId, taskId: taskId,
exception: exception exception: exception
) )
@@ -34,7 +34,7 @@ class ViewAttachmentForWebInteractor {
(failure) { (failure) {
if (failure is DownloadAttachmentForWebFailure) { if (failure is DownloadAttachmentForWebFailure) {
return Left(ViewAttachmentForWebFailure( return Left(ViewAttachmentForWebFailure(
attachmentBlobId: attachment.blobId, attachment: attachment,
taskId: failure.taskId, taskId: failure.taskId,
exception: failure.exception, exception: failure.exception,
)); ));
@@ -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/disposition.dart';
import 'package:jmap_dart_client/jmap/mdn/mdn.dart'; import 'package:jmap_dart_client/jmap/mdn/mdn.dart';
import 'package:mime/mime.dart'; import 'package:mime/mime.dart';
import 'package:model/email/eml_attachment.dart';
import 'package:model/model.dart'; import 'package:model/model.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:pointer_interceptor/pointer_interceptor.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/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/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/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/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/detailed_email.dart';
import 'package:tmail_ui_user/features/email/domain/model/email_print.dart'; import 'package:tmail_ui_user/features/email/domain/model/email_print.dart';
@@ -810,7 +812,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
} else { } else {
consumeState(Stream.value( consumeState(Stream.value(
Left(DownloadAttachmentForWebFailure( Left(DownloadAttachmentForWebFailure(
attachmentBlobId: attachment.blobId, attachment: attachment,
exception: NotFoundSessionException())) exception: NotFoundSessionException()))
)); ));
} }
@@ -831,7 +833,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
} else { } else {
consumeState(Stream.value( consumeState(Stream.value(
Left(ViewAttachmentForWebFailure( Left(ViewAttachmentForWebFailure(
attachmentBlobId: attachment.blobId, attachment: attachment,
exception: NotFoundSessionException())) exception: NotFoundSessionException()))
)); ));
} }
@@ -876,12 +878,16 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
mailboxDashBoardController.deleteDownloadTask(failure.taskId!); mailboxDashBoardController.deleteDownloadTask(failure.taskId!);
} }
_updateAttachmentsViewState(failure.attachmentBlobId, Left(failure)); if (failure.attachment != null) {
_updateAttachmentsViewState(failure.attachment?.blobId, Left(failure));
}
if (currentOverlayContext != null && currentContext != null) { if (currentOverlayContext != null && currentContext != null) {
appToast.showToastErrorMessage( appToast.showToastErrorMessage(
currentOverlayContext!, 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: case EmailActionType.printAll:
_printEmail(context, presentationEmail); _printEmail(context, presentationEmail);
break; break;
case EmailActionType.downloadMessageAsEML:
_downloadMessageAsEML(presentationEmail);
break;
default: default:
break; break;
} }
@@ -1772,4 +1781,14 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
AppLocalizations.of(currentContext!).eventReplyWasSentUnsuccessfully); 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);
}
} }
@@ -473,6 +473,8 @@ class EmailView extends GetWidget<SingleEmailController> {
EmailActionType.unsubscribe, EmailActionType.unsubscribe,
if (mailboxContain?.isArchive == false) if (mailboxContain?.isArchive == false)
EmailActionType.archiveMessage, EmailActionType.archiveMessage,
if (PlatformInfo.isWeb && PlatformInfo.isCanvasKit)
EmailActionType.downloadMessageAsEML
]; ];
if (position == null) { if (position == null) {
@@ -1,6 +1,5 @@
import 'package:collection/collection.dart'; 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:get/get_utils/src/get_utils/get_utils.dart';
import 'package:core/core.dart'; import 'package:core/core.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
@@ -34,7 +34,8 @@ extension EmailCacheExtension on EmailCache {
: null, : null,
headerCalendarEvent: headerCalendarEvent != null headerCalendarEvent: headerCalendarEvent != null
? Map.fromIterables(headerCalendarEvent!.keys.map((value) => IndividualHeaderIdentifier(value)), headerCalendarEvent!.values) ? Map.fromIterables(headerCalendarEvent!.keys.map((value) => IndividualHeaderIdentifier(value)), headerCalendarEvent!.values)
: null : null,
blobId: blobId != null ? Id(blobId!) : null,
); );
} }
@@ -25,6 +25,7 @@ extension EmailExtension on Email {
replyTo: replyTo?.map((emailAddress) => emailAddress.toEmailAddressHiveCache()).toList(), replyTo: replyTo?.map((emailAddress) => emailAddress.toEmailAddressHiveCache()).toList(),
mailboxIds: mailboxIds?.toMapString(), mailboxIds: mailboxIds?.toMapString(),
headerCalendarEvent: headerCalendarEvent?.toMapString(), headerCalendarEvent: headerCalendarEvent?.toMapString(),
blobId: blobId?.value,
); );
} }
@@ -54,6 +54,9 @@ class EmailCache extends HiveObject with EquatableMixin {
@HiveField(14) @HiveField(14)
Map<String, String?>? headerCalendarEvent; Map<String, String?>? headerCalendarEvent;
@HiveField(15)
final String? blobId;
EmailCache( EmailCache(
this.id, this.id,
{ {
@@ -71,6 +74,7 @@ class EmailCache extends HiveObject with EquatableMixin {
this.replyTo, this.replyTo,
this.mailboxIds, this.mailboxIds,
this.headerCalendarEvent, this.headerCalendarEvent,
this.blobId,
} }
); );
@@ -91,5 +95,6 @@ class EmailCache extends HiveObject with EquatableMixin {
hasAttachment, hasAttachment,
mailboxIds, mailboxIds,
headerCalendarEvent, headerCalendarEvent,
blobId,
]; ];
} }
@@ -8,6 +8,7 @@ class ThreadConstants {
static final defaultLimit = UnsignedInt(maxCountEmails); static final defaultLimit = UnsignedInt(maxCountEmails);
static final propertiesDefault = Properties({ static final propertiesDefault = Properties({
EmailProperty.id, EmailProperty.id,
EmailProperty.blobId,
EmailProperty.subject, EmailProperty.subject,
EmailProperty.from, EmailProperty.from,
EmailProperty.to, EmailProperty.to,
@@ -29,6 +30,7 @@ class ThreadConstants {
static final propertiesQuickSearch = Properties({ static final propertiesQuickSearch = Properties({
EmailProperty.id, EmailProperty.id,
EmailProperty.blobId,
EmailProperty.subject, EmailProperty.subject,
EmailProperty.from, EmailProperty.from,
EmailProperty.to, EmailProperty.to,
@@ -53,6 +55,7 @@ class ThreadConstants {
static final propertiesGetDetailedEmail = Properties({ static final propertiesGetDetailedEmail = Properties({
EmailProperty.id, EmailProperty.id,
EmailProperty.blobId,
EmailProperty.subject, EmailProperty.subject,
EmailProperty.from, EmailProperty.from,
EmailProperty.to, EmailProperty.to,
@@ -74,6 +77,7 @@ class ThreadConstants {
static final propertiesCalendarEvent = Properties({ static final propertiesCalendarEvent = Properties({
EmailProperty.id, EmailProperty.id,
EmailProperty.blobId,
EmailProperty.subject, EmailProperty.subject,
EmailProperty.from, EmailProperty.from,
EmailProperty.to, EmailProperty.to,
+13 -1
View File
@@ -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": "Initializing data...",
"@initializing_data": { "@initializing_data": {
"type": "text", "type": "text",
@@ -3719,5 +3719,17 @@
"type": "text", "type": "text",
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
},
"downloadMessageAsEML": "Download message as EML",
"@downloadMessageAsEML": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"downloadMessageAsEMLFailed": "Download message as EML failed",
"@downloadMessageAsEMLFailed": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
} }
} }
@@ -3879,4 +3879,18 @@ class AppLocalizations {
name: 'youMayAttendThisMeeting', 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',
);
}
} }
+2 -2
View File
@@ -44,8 +44,8 @@ class Attachment with EquatableMixin {
final downloadUri = downloadUriTemplate.expand({ final downloadUri = downloadUriTemplate.expand({
'accountId' : accountId.id.value, 'accountId' : accountId.id.value,
'blobId' : '${blobId?.value}', 'blobId' : '${blobId?.value}',
'name' : '$name', 'name' : name ?? '',
'type' : '${type?.mimeType}', 'type' : type?.mimeType ?? '',
}); });
return Uri.decodeFull(downloadUri); return Uri.decodeFull(downloadUri);
} }
+2 -1
View File
@@ -27,5 +27,6 @@ enum EmailActionType {
unsubscribe, unsubscribe,
composeFromUnsubscribeMailtoLink, composeFromUnsubscribeMailtoLink,
archiveMessage, archiveMessage,
printAll printAll,
downloadMessageAsEML
} }
+1
View File
@@ -1,6 +1,7 @@
class EmailProperty { class EmailProperty {
static const String id = 'id'; static const String id = 'id';
static const String blobId = 'blobId';
static const String keywords = 'keywords'; static const String keywords = 'keywords';
static const String size = 'size'; static const String size = 'size';
static const String receivedAt = 'receivedAt'; static const String receivedAt = 'receivedAt';
+15
View File
@@ -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,
});
}
+4
View File
@@ -1,6 +1,7 @@
import 'package:core/presentation/extensions/string_extension.dart'; import 'package:core/presentation/extensions/string_extension.dart';
import 'package:equatable/equatable.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/unsigned_int.dart';
import 'package:jmap_dart_client/jmap/core/utc_date.dart'; import 'package:jmap_dart_client/jmap/core/utc_date.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.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 { class PresentationEmail with EquatableMixin {
final EmailId? id; final EmailId? id;
final Id? blobId;
final Map<KeyWordIdentifier, bool>? keywords; final Map<KeyWordIdentifier, bool>? keywords;
final UnsignedInt? size; final UnsignedInt? size;
final UTCDate? receivedAt; final UTCDate? receivedAt;
@@ -44,6 +46,7 @@ class PresentationEmail with EquatableMixin {
PresentationEmail({ PresentationEmail({
this.id, this.id,
this.blobId,
this.keywords, this.keywords,
this.size, this.size,
this.receivedAt, this.receivedAt,
@@ -142,6 +145,7 @@ class PresentationEmail with EquatableMixin {
@override @override
List<Object?> get props => [ List<Object?> get props => [
id, id,
blobId,
keywords, keywords,
size, size,
receivedAt, receivedAt,
@@ -67,6 +67,7 @@ extension EmailExtension on Email {
Email updatedEmail({Map<KeyWordIdentifier, bool>? newKeywords, Map<MailboxId, bool>? newMailboxIds}) { Email updatedEmail({Map<KeyWordIdentifier, bool>? newKeywords, Map<MailboxId, bool>? newMailboxIds}) {
return Email( return Email(
id: id, id: id,
blobId: blobId,
keywords: newKeywords ?? keywords, keywords: newKeywords ?? keywords,
size: size, size: size,
receivedAt: receivedAt, receivedAt: receivedAt,
@@ -91,6 +92,7 @@ extension EmailExtension on Email {
PresentationEmail toPresentationEmail({SelectMode selectMode = SelectMode.INACTIVE}) { PresentationEmail toPresentationEmail({SelectMode selectMode = SelectMode.INACTIVE}) {
return PresentationEmail( return PresentationEmail(
id: id, id: id,
blobId: blobId,
keywords: keywords, keywords: keywords,
size: size, size: size,
receivedAt: receivedAt, receivedAt: receivedAt,
@@ -113,6 +115,7 @@ extension EmailExtension on Email {
Email combineEmail(Email newEmail, Properties updatedProperties) { Email combineEmail(Email newEmail, Properties updatedProperties) {
return Email( return Email(
id: newEmail.id, id: newEmail.id,
blobId: updatedProperties.contain(EmailProperty.blobId) ? newEmail.blobId : blobId,
keywords: updatedProperties.contain(EmailProperty.keywords) ? newEmail.keywords : keywords, keywords: updatedProperties.contain(EmailProperty.keywords) ? newEmail.keywords : keywords,
size: updatedProperties.contain(EmailProperty.size) ? newEmail.size : size, size: updatedProperties.contain(EmailProperty.size) ? newEmail.size : size,
receivedAt: updatedProperties.contain(EmailProperty.receivedAt) ? newEmail.receivedAt : receivedAt, receivedAt: updatedProperties.contain(EmailProperty.receivedAt) ? newEmail.receivedAt : receivedAt,
@@ -171,6 +174,7 @@ extension EmailExtension on Email {
) { ) {
return PresentationEmail( return PresentationEmail(
id: emailId ?? id, id: emailId ?? id,
blobId: blobId,
keywords: keywords, keywords: keywords,
size: size, size: size,
receivedAt: receivedAt, receivedAt: receivedAt,
@@ -1,14 +1,17 @@
import 'dart:ui'; import 'dart:ui';
import 'package:core/data/constants/constant.dart';
import 'package:core/domain/extensions/datetime_extension.dart'; import 'package:core/domain/extensions/datetime_extension.dart';
import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/utils/app_logger.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:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:dartz/dartz.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/email_address.dart';
import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart'; import 'package:jmap_dart_client/jmap/mail/email/keyword_identifier.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/email/email_action_type.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/email/presentation_email.dart';
import 'package:model/extensions/email_address_extension.dart'; import 'package:model/extensions/email_address_extension.dart';
import 'package:model/extensions/list_email_address_extension.dart'; import 'package:model/extensions/list_email_address_extension.dart';
@@ -44,6 +47,7 @@ extension PresentationEmailExtension on PresentationEmail {
PresentationEmail toggleSelect() { PresentationEmail toggleSelect() {
return PresentationEmail( return PresentationEmail(
id: this.id, id: this.id,
blobId: blobId,
keywords: keywords, keywords: keywords,
size: size, size: size,
receivedAt: receivedAt, receivedAt: receivedAt,
@@ -67,6 +71,7 @@ extension PresentationEmailExtension on PresentationEmail {
PresentationEmail toSelectedEmail({required SelectMode selectMode}) { PresentationEmail toSelectedEmail({required SelectMode selectMode}) {
return PresentationEmail( return PresentationEmail(
id: this.id, id: this.id,
blobId: blobId,
keywords: keywords, keywords: keywords,
size: size, size: size,
receivedAt: receivedAt, receivedAt: receivedAt,
@@ -90,6 +95,7 @@ extension PresentationEmailExtension on PresentationEmail {
Email toEmail() { Email toEmail() {
return Email( return Email(
id: this.id, id: this.id,
blobId: blobId,
keywords: keywords, keywords: keywords,
size: size, size: size,
receivedAt: receivedAt, receivedAt: receivedAt,
@@ -146,6 +152,7 @@ extension PresentationEmailExtension on PresentationEmail {
return PresentationEmail( return PresentationEmail(
id: this.id, id: this.id,
blobId: blobId,
keywords: keywords, keywords: keywords,
size: size, size: size,
receivedAt: receivedAt, receivedAt: receivedAt,
@@ -182,6 +189,7 @@ extension PresentationEmailExtension on PresentationEmail {
PresentationEmail withRouteWeb(Uri routeWeb) { PresentationEmail withRouteWeb(Uri routeWeb) {
return PresentationEmail( return PresentationEmail(
id: this.id, id: this.id,
blobId: blobId,
keywords: keywords, keywords: keywords,
size: size, size: size,
receivedAt: receivedAt, receivedAt: receivedAt,
@@ -205,6 +213,7 @@ extension PresentationEmailExtension on PresentationEmail {
PresentationEmail updateKeywords(Map<KeyWordIdentifier, bool>? newKeywords) { PresentationEmail updateKeywords(Map<KeyWordIdentifier, bool>? newKeywords) {
return PresentationEmail( return PresentationEmail(
id: this.id, id: this.id,
blobId: blobId,
keywords: newKeywords, keywords: newKeywords,
size: size, size: size,
receivedAt: receivedAt, receivedAt: receivedAt,
@@ -228,6 +237,7 @@ extension PresentationEmailExtension on PresentationEmail {
PresentationEmail syncPresentationEmail({PresentationMailbox? mailboxContain, Uri? routeWeb}) { PresentationEmail syncPresentationEmail({PresentationMailbox? mailboxContain, Uri? routeWeb}) {
return PresentationEmail( return PresentationEmail(
id: this.id, id: this.id,
blobId: blobId,
keywords: keywords, keywords: keywords,
size: size, size: size,
receivedAt: receivedAt, receivedAt: receivedAt,
@@ -262,4 +272,12 @@ extension PresentationEmailExtension on PresentationEmail {
return false; return false;
} }
EMLAttachment createEMLAttachment() {
return EMLAttachment(
blobId: blobId,
name: getEmailTitle().isEmpty ? '${blobId?.value}.eml' : '${getEmailTitle()}.eml',
type: MediaType.parse(Constant.octetStreamMimeType)
);
}
} }
@@ -55,7 +55,7 @@ void main() {
(_) => Stream.value( (_) => Stream.value(
Left( Left(
DownloadAttachmentForWebFailure( DownloadAttachmentForWebFailure(
attachmentBlobId: testAttachment.blobId, attachment: testAttachment,
taskId: testDownloadTaskId, taskId: testDownloadTaskId,
exception: testException, exception: testException,
), ),
@@ -75,7 +75,7 @@ void main() {
emitsInOrder([ emitsInOrder([
Left( Left(
ViewAttachmentForWebFailure( ViewAttachmentForWebFailure(
attachmentBlobId: testAttachment.blobId, attachment: testAttachment,
taskId: testDownloadTaskId, taskId: testDownloadTaskId,
exception: testException, exception: testException,
), ),
@@ -59,7 +59,7 @@ void main() {
}; };
final baseOption = BaseOptions(headers: headers); final baseOption = BaseOptions(headers: headers);
dio = Dio(baseOption) dio = Dio(baseOption)
..options.baseUrl = baseUrl;; ..options.baseUrl = baseUrl;
authenticationClient = MockAuthenticationClientBase(); authenticationClient = MockAuthenticationClientBase();
tokenOidcCacheManager = MockTokenOidcCacheManager(); tokenOidcCacheManager = MockTokenOidcCacheManager();