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

This commit is contained in:
dab246
2025-11-07 01:12:20 +07:00
committed by Dat H. Pham
parent ecd3ac0295
commit 972b20ec16
72 changed files with 2438 additions and 1751 deletions
+2 -1
View File
@@ -16,6 +16,7 @@ import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:model/model.dart'; import 'package:model/model.dart';
import 'package:rule_filter/rule_filter/capability_rule_filter.dart'; import 'package:rule_filter/rule_filter/capability_rule_filter.dart';
import 'package:tmail_ui_user/features/base/before_reconnect_manager.dart'; import 'package:tmail_ui_user/features/base/before_reconnect_manager.dart';
import 'package:tmail_ui_user/features/base/mixin/emit_state_mixin.dart';
import 'package:tmail_ui_user/features/base/mixin/logout_mixin.dart'; import 'package:tmail_ui_user/features/base/mixin/logout_mixin.dart';
import 'package:tmail_ui_user/features/base/mixin/popup_context_menu_action_mixin.dart'; import 'package:tmail_ui_user/features/base/mixin/popup_context_menu_action_mixin.dart';
import 'package:tmail_ui_user/features/caching/caching_manager.dart'; import 'package:tmail_ui_user/features/caching/caching_manager.dart';
@@ -63,7 +64,7 @@ import 'package:tmail_ui_user/main/utils/twake_app_manager.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
abstract class BaseController extends GetxController abstract class BaseController extends GetxController
with PopupContextMenuActionMixin, LogoutMixin { with PopupContextMenuActionMixin, LogoutMixin, EmitStateMixin {
final CachingManager cachingManager = Get.find<CachingManager>(); final CachingManager cachingManager = Get.find<CachingManager>();
final LanguageCacheManager languageCacheManager = Get.find<LanguageCacheManager>(); final LanguageCacheManager languageCacheManager = Get.find<LanguageCacheManager>();
@@ -0,0 +1,12 @@
import 'package:core/presentation/state/failure.dart';
import 'package:dartz/dartz.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart';
mixin EmitStateMixin {
void emitFailure({
required BaseController controller,
required FeatureFailure failure,
}) {
controller.consumeState(Stream.value(Left(failure)));
}
}
@@ -15,6 +15,7 @@ import 'package:tmail_ui_user/features/composer/presentation/extensions/handle_e
import 'package:tmail_ui_user/features/composer/presentation/extensions/handle_recipients_collapsed_extensions.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/handle_recipients_collapsed_extensions.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/handle_keyboard_shortcut_actions_extension.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/handle_keyboard_shortcut_actions_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/mark_as_important_extension.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/mark_as_important_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/preview_upload_file_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/remove_draggable_email_address_between_recipient_fields_extension.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/remove_draggable_email_address_between_recipient_fields_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/prefix_recipient_state.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/prefix_recipient_state.dart';
import 'package:tmail_ui_user/features/composer/presentation/styles/composer_style.dart'; import 'package:tmail_ui_user/features/composer/presentation/styles/composer_style.dart';
@@ -70,6 +71,22 @@ class ComposerView extends GetWidget<ComposerController> {
} }
}); });
final attachmentWidget = Obx(() {
if (controller.uploadController.listUploadAttachments.isNotEmpty) {
return AttachmentComposerWidget(
listFileUploaded: controller.uploadController.listUploadAttachments,
isCollapsed: controller.isAttachmentCollapsed,
onDeleteAttachmentAction: controller.deleteAttachmentUploaded,
onPreviewAttachmentAction: (id) =>
controller.previewUploadFile(context, id),
onToggleExpandAttachmentAction: (isCollapsed) =>
controller.isAttachmentCollapsed = isCollapsed,
);
} else {
return const SizedBox.shrink();
}
});
final bodyWidget = ResponsiveWidget( final bodyWidget = ResponsiveWidget(
responsiveUtils: controller.responsiveUtils, responsiveUtils: controller.responsiveUtils,
mobile: MobileResponsiveContainerView( mobile: MobileResponsiveContainerView(
@@ -251,18 +268,7 @@ class ComposerView extends GetWidget<ComposerController> {
} }
), ),
), ),
Obx(() { attachmentWidget,
if (controller.uploadController.listUploadAttachments.isNotEmpty) {
return AttachmentComposerWidget(
listFileUploaded: controller.uploadController.listUploadAttachments,
isCollapsed: controller.isAttachmentCollapsed,
onDeleteAttachmentAction: controller.deleteAttachmentUploaded,
onToggleExpandAttachmentAction: (isCollapsed) => controller.isAttachmentCollapsed = isCollapsed,
);
} else {
return const SizedBox.shrink();
}
}),
Obx(() { Obx(() {
if (controller.richTextWebController!.isFormattingOptionsEnabled) { if (controller.richTextWebController!.isFormattingOptionsEnabled) {
return ToolbarRichTextWidget( return ToolbarRichTextWidget(
@@ -504,18 +510,7 @@ class ComposerView extends GetWidget<ComposerController> {
} }
), ),
), ),
Obx(() { attachmentWidget,
if (controller.uploadController.listUploadAttachments.isNotEmpty) {
return AttachmentComposerWidget(
listFileUploaded: controller.uploadController.listUploadAttachments,
isCollapsed: controller.isAttachmentCollapsed,
onDeleteAttachmentAction: controller.deleteAttachmentUploaded,
onToggleExpandAttachmentAction: (isCollapsed) => controller.isAttachmentCollapsed = isCollapsed,
);
} else {
return const SizedBox.shrink();
}
}),
Obx(() { Obx(() {
if (controller.richTextWebController!.isFormattingOptionsEnabled) { if (controller.richTextWebController!.isFormattingOptionsEnabled) {
return ToolbarRichTextWidget( return ToolbarRichTextWidget(
@@ -785,18 +780,7 @@ class ComposerView extends GetWidget<ComposerController> {
} }
), ),
), ),
Obx(() { attachmentWidget,
if (controller.uploadController.listUploadAttachments.isNotEmpty) {
return AttachmentComposerWidget(
listFileUploaded: controller.uploadController.listUploadAttachments,
isCollapsed: controller.isAttachmentCollapsed,
onDeleteAttachmentAction: controller.deleteAttachmentUploaded,
onToggleExpandAttachmentAction: (isCollapsed) => controller.isAttachmentCollapsed = isCollapsed,
);
} else {
return const SizedBox.shrink();
}
}),
Obx(() { Obx(() {
if (controller.richTextWebController!.isFormattingOptionsEnabled) { if (controller.richTextWebController!.isFormattingOptionsEnabled) {
return ToolbarRichTextWidget( return ToolbarRichTextWidget(
@@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_download_attachment_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_preview_attachment_extension.dart';
import 'package:tmail_ui_user/features/upload/domain/model/upload_task_id.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
extension PreviewUploadFileExtension on ComposerController {
void previewUploadFile(BuildContext context, UploadTaskId uploadId) {
final uploadFile = uploadController.getUploadFileId(uploadId);
if (uploadFile == null || uploadFile.attachment == null) {
appToast.showToastWarningMessage(
context,
AppLocalizations.of(context).noPreviewAvailable,
);
return;
}
mailboxDashBoardController.previewUploadFile(
context: context,
uploadFile: uploadFile,
isDialogLoadingVisible: true,
onPreviewOrDownloadAction: (attachment, isPreview) {
mailboxDashBoardController.downloadAttachment(
attachment: attachment,
previewerSupported: isPreview,
);
},
);
}
}
@@ -3,7 +3,6 @@ import 'package:core/presentation/views/button/tmail_button_widget.dart';
import 'package:core/presentation/views/text/middle_ellipsis_text.dart'; import 'package:core/presentation/views/text/middle_ellipsis_text.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.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/styles/attachment_item_composer_widget_style.dart'; import 'package:tmail_ui_user/features/composer/presentation/styles/attachment_item_composer_widget_style.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_progress_loading_composer_widget.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_progress_loading_composer_widget.dart';
@@ -11,11 +10,11 @@ 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'; import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_status.dart';
typedef OnDeleteAttachmentAction = void Function(UploadTaskId uploadTaskId); typedef OnDeleteAttachmentAction = void Function(UploadTaskId uploadTaskId);
typedef OnPreviewAttachmentAction = void Function(UploadTaskId uploadTaskId);
class AttachmentItemComposerWidget extends StatelessWidget with AppLoaderMixin { class AttachmentItemComposerWidget extends StatelessWidget with AppLoaderMixin {
final _imagePaths = Get.find<ImagePaths>(); final ImagePaths imagePaths;
final String fileIcon; final String fileIcon;
final String fileName; final String fileName;
final String fileSize; final String fileSize;
@@ -26,10 +25,12 @@ class AttachmentItemComposerWidget extends StatelessWidget with AppLoaderMixin {
final EdgeInsetsGeometry? itemMargin; final EdgeInsetsGeometry? itemMargin;
final EdgeInsetsGeometry? itemPadding; final EdgeInsetsGeometry? itemPadding;
final OnDeleteAttachmentAction? onDeleteAttachmentAction; final OnDeleteAttachmentAction? onDeleteAttachmentAction;
final OnPreviewAttachmentAction? onPreviewAttachmentAction;
final Widget? buttonAction; final Widget? buttonAction;
AttachmentItemComposerWidget({ const AttachmentItemComposerWidget({
super.key, super.key,
required this.imagePaths,
required this.fileIcon, required this.fileIcon,
required this.fileName, required this.fileName,
required this.fileSize, required this.fileSize,
@@ -41,10 +42,65 @@ class AttachmentItemComposerWidget extends StatelessWidget with AppLoaderMixin {
this.itemPadding, this.itemPadding,
this.buttonAction, this.buttonAction,
this.onDeleteAttachmentAction, this.onDeleteAttachmentAction,
this.onPreviewAttachmentAction,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget bodyItem = Row(
children: [
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
SvgPicture.asset(
fileIcon,
width: AttachmentItemComposerWidgetStyle.iconSize,
height: AttachmentItemComposerWidgetStyle.iconSize,
fit: BoxFit.fill
),
const SizedBox(width: AttachmentItemComposerWidgetStyle.space),
Flexible(
child: MiddleEllipsisText(
fileName,
style: AttachmentItemComposerWidgetStyle.labelTextStyle,
)
),
const SizedBox(width: AttachmentItemComposerWidgetStyle.space),
Text(
fileSize,
style: AttachmentItemComposerWidgetStyle.sizeLabelTextStyle
),
],
),
AttachmentProgressLoadingComposerWidget(
uploadStatus: uploadStatus,
percentUploading: percentUploading,
)
],
),
),
const SizedBox(width: AttachmentItemComposerWidgetStyle.space),
TMailButtonWidget.fromIcon(
icon: imagePaths.icCancel,
iconSize: AttachmentItemComposerWidgetStyle.deleteIconSize,
borderRadius: AttachmentItemComposerWidgetStyle.deleteIconRadius,
padding: AttachmentItemComposerWidgetStyle.deleteIconPadding,
iconColor: AttachmentItemComposerWidgetStyle.deleteIconColor,
onTapActionCallback: () => onDeleteAttachmentAction?.call(uploadTaskId),
)
],
);
if (onPreviewAttachmentAction != null) {
bodyItem = InkWell(
onTap: () => onPreviewAttachmentAction!(uploadTaskId),
child: bodyItem,
);
}
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(AttachmentItemComposerWidgetStyle.radius)), borderRadius: const BorderRadius.all(Radius.circular(AttachmentItemComposerWidgetStyle.radius)),
@@ -54,52 +110,7 @@ class AttachmentItemComposerWidget extends StatelessWidget with AppLoaderMixin {
width: AttachmentItemComposerWidgetStyle.width, width: AttachmentItemComposerWidgetStyle.width,
padding: itemPadding ?? AttachmentItemComposerWidgetStyle.padding, padding: itemPadding ?? AttachmentItemComposerWidgetStyle.padding,
margin: itemMargin, margin: itemMargin,
child: Row( child: bodyItem,
children: [
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
SvgPicture.asset(
fileIcon,
width: AttachmentItemComposerWidgetStyle.iconSize,
height: AttachmentItemComposerWidgetStyle.iconSize,
fit: BoxFit.fill
),
const SizedBox(width: AttachmentItemComposerWidgetStyle.space),
Flexible(
child: MiddleEllipsisText(
fileName,
style: AttachmentItemComposerWidgetStyle.labelTextStyle,
)
),
const SizedBox(width: AttachmentItemComposerWidgetStyle.space),
Text(
fileSize,
style: AttachmentItemComposerWidgetStyle.sizeLabelTextStyle
),
],
),
AttachmentProgressLoadingComposerWidget(
uploadStatus: uploadStatus,
percentUploading: percentUploading,
)
],
),
),
const SizedBox(width: AttachmentItemComposerWidgetStyle.space),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icCancel,
iconSize: AttachmentItemComposerWidgetStyle.deleteIconSize,
borderRadius: AttachmentItemComposerWidgetStyle.deleteIconRadius,
padding: AttachmentItemComposerWidgetStyle.deleteIconPadding,
iconColor: AttachmentItemComposerWidgetStyle.deleteIconColor,
onTapActionCallback: () => onDeleteAttachmentAction?.call(uploadTaskId),
)
],
),
); );
} }
} }
@@ -76,6 +76,7 @@ class _MobileAttachmentComposerWidgetState extends State<MobileAttachmentCompose
itemBuilder: (context, index) { itemBuilder: (context, index) {
final file = _listFileDisplayed[index]; final file = _listFileDisplayed[index];
return AttachmentItemComposerWidget( return AttachmentItemComposerWidget(
imagePaths: _imagePaths,
fileIcon: file.getIcon(_imagePaths), fileIcon: file.getIcon(_imagePaths),
fileName: file.fileName, fileName: file.fileName,
fileSize: filesize(file.fileSize), fileSize: filesize(file.fileSize),
@@ -139,6 +140,7 @@ class _MobileAttachmentComposerWidgetState extends State<MobileAttachmentCompose
itemBuilder: (context, index) { itemBuilder: (context, index) {
final file = _listFileDisplayed[index]; final file = _listFileDisplayed[index];
return AttachmentItemComposerWidget( return AttachmentItemComposerWidget(
imagePaths: _imagePaths,
fileIcon: file.getIcon(_imagePaths), fileIcon: file.getIcon(_imagePaths),
fileName: file.fileName, fileName: file.fileName,
fileSize: filesize(file.fileSize), fileSize: filesize(file.fileSize),
@@ -16,6 +16,7 @@ class AttachmentComposerWidget extends StatefulWidget {
final bool isCollapsed; final bool isCollapsed;
final OnDeleteAttachmentAction onDeleteAttachmentAction; final OnDeleteAttachmentAction onDeleteAttachmentAction;
final OnToggleExpandAttachmentAction onToggleExpandAttachmentAction; final OnToggleExpandAttachmentAction onToggleExpandAttachmentAction;
final OnPreviewAttachmentAction onPreviewAttachmentAction;
const AttachmentComposerWidget({ const AttachmentComposerWidget({
super.key, super.key,
@@ -23,6 +24,7 @@ class AttachmentComposerWidget extends StatefulWidget {
required this.isCollapsed, required this.isCollapsed,
required this.onDeleteAttachmentAction, required this.onDeleteAttachmentAction,
required this.onToggleExpandAttachmentAction, required this.onToggleExpandAttachmentAction,
required this.onPreviewAttachmentAction,
}); });
@override @override
@@ -76,6 +78,7 @@ class _AttachmentComposerWidgetState extends State<AttachmentComposerWidget> {
runSpacing: AttachmentComposerWidgetStyle.listItemSpace, runSpacing: AttachmentComposerWidgetStyle.listItemSpace,
children: widget.listFileUploaded children: widget.listFileUploaded
.map((file) => AttachmentItemComposerWidget( .map((file) => AttachmentItemComposerWidget(
imagePaths: _imagePaths,
fileIcon: file.getIcon(_imagePaths), fileIcon: file.getIcon(_imagePaths),
fileName: file.fileName, fileName: file.fileName,
fileSize: filesize(file.fileSize), fileSize: filesize(file.fileSize),
@@ -83,6 +86,7 @@ class _AttachmentComposerWidgetState extends State<AttachmentComposerWidget> {
percentUploading: file.percentUploading, percentUploading: file.percentUploading,
uploadTaskId: file.uploadTaskId, uploadTaskId: file.uploadTaskId,
onDeleteAttachmentAction: widget.onDeleteAttachmentAction, onDeleteAttachmentAction: widget.onDeleteAttachmentAction,
onPreviewAttachmentAction: widget.onPreviewAttachmentAction,
)) ))
.toList(), .toList(),
), ),
@@ -11,7 +11,7 @@ import 'package:model/account/account_request.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';
import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/download_datasource.dart'; import 'package:tmail_ui_user/features/download/data/datasource/download_datasource.dart';
import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart'; import 'package:tmail_ui_user/main/exceptions/exception_thrower.dart';
class DownloadDatasourceImpl extends DownloadDatasource { class DownloadDatasourceImpl extends DownloadDatasource {
@@ -10,12 +10,12 @@ import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/account/account_request.dart'; import 'package:model/account/account_request.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';
import 'package:tmail_ui_user/features/download/data/datasource/download_datasource.dart';
import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.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/html_datasource.dart';
import 'package:tmail_ui_user/features/email/domain/model/preview_email_eml_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/preview_email_eml_request.dart';
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/download_datasource.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart';
class DownloadRepositoryImpl extends DownloadRepository { class DownloadRepositoryImpl extends DownloadRepository {
final DownloadDatasource _downloadDatasource; final DownloadDatasource _downloadDatasource;
@@ -131,4 +131,40 @@ class DownloadRepositoryImpl extends DownloadRepository {
return _emailDataSource[DataSourceType.session]! return _emailDataSource[DataSourceType.session]!
.getPreviewEMLContentInMemory(keyStored); .getPreviewEMLContentInMemory(keyStored);
} }
@override
Future<DownloadedResponse> exportAttachment(
Attachment attachment,
AccountId accountId,
String baseDownloadUrl,
AccountRequest accountRequest,
CancelToken cancelToken,
) {
return _emailDataSource[DataSourceType.network]!.exportAttachment(
attachment,
accountId,
baseDownloadUrl,
accountRequest,
cancelToken,
);
}
@override
Future<DownloadedResponse> exportAllAttachments(
AccountId accountId,
EmailId emailId,
String baseDownloadAllUrl,
String outputFileName,
AccountRequest accountRequest,
CancelToken cancelToken,
) {
return _emailDataSource[DataSourceType.network]!.exportAllAttachments(
accountId,
emailId,
baseDownloadAllUrl,
outputFileName,
accountRequest,
cancelToken: cancelToken,
);
}
} }
@@ -0,0 +1,5 @@
class DownloadAttachmentInteractorIsNull implements Exception {}
class CapabilityDownloadAllNotSupportedException implements Exception {}
class DownloadUrlIsNullException implements Exception {}
@@ -0,0 +1,4 @@
enum DownloadSourceView {
emailView,
composerView;
}
@@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:typed_data'; 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/failure.dart';
import 'package:core/presentation/state/success.dart'; import 'package:core/presentation/state/success.dart';
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart'; import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
@@ -59,4 +60,21 @@ abstract class DownloadRepository {
Future<void> removePreviewEmailEMLContentShared(String keyStored); Future<void> removePreviewEmailEMLContentShared(String keyStored);
Future<EMLPreviewer> getPreviewEMLContentInMemory(String keyStored); Future<EMLPreviewer> getPreviewEMLContentInMemory(String keyStored);
Future<DownloadedResponse> exportAttachment(
Attachment attachment,
AccountId accountId,
String baseDownloadUrl,
AccountRequest accountRequest,
CancelToken cancelToken,
);
Future<DownloadedResponse> exportAllAttachments(
AccountId accountId,
EmailId emailId,
String baseDownloadAllUrl,
String outputFileName,
AccountRequest accountRequest,
CancelToken cancelToken,
);
} }
@@ -0,0 +1,62 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:model/email/attachment.dart';
import 'package:tmail_ui_user/features/download/domain/model/download_source_view.dart';
class DownloadAndGettingHtmlContentFromAttachment extends LoadingState {
DownloadAndGettingHtmlContentFromAttachment({
required this.blobId,
this.sourceView,
});
final Id? blobId;
final DownloadSourceView? sourceView;
@override
List<Object?> get props => [blobId, sourceView];
}
class DownloadAndGetHtmlContentFromAttachmentSuccess extends UIState {
DownloadAndGetHtmlContentFromAttachmentSuccess({
required this.sanitizedHtmlContent,
required this.htmlAttachmentTitle,
required this.attachment,
required this.accountId,
required this.session,
this.sourceView,
});
final String sanitizedHtmlContent;
final String htmlAttachmentTitle;
final Attachment attachment;
final AccountId accountId;
final Session session;
final DownloadSourceView? sourceView;
@override
List<Object?> get props => [
sanitizedHtmlContent,
htmlAttachmentTitle,
attachment,
accountId,
session,
sourceView,
];
}
class DownloadAndGetHtmlContentFromAttachmentFailure extends FeatureFailure {
DownloadAndGetHtmlContentFromAttachmentFailure({
super.exception,
required this.blobId,
this.sourceView,
});
final Id? blobId;
final DownloadSourceView? sourceView;
@override
List<Object?> get props => [blobId, sourceView];
}
@@ -4,6 +4,7 @@ import 'package:core/presentation/state/success.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.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';
import 'package:tmail_ui_user/features/download/domain/model/download_source_view.dart';
class IdleDownloadAttachmentForWeb extends UIState {} class IdleDownloadAttachmentForWeb extends UIState {}
@@ -13,11 +14,24 @@ class StartDownloadAttachmentForWeb extends UIState {
final Attachment attachment; final Attachment attachment;
final CancelToken? cancelToken; final CancelToken? cancelToken;
final bool previewerSupported; final bool previewerSupported;
final DownloadSourceView? sourceView;
StartDownloadAttachmentForWeb(this.taskId, this.attachment, [this.cancelToken, this.previewerSupported = false]); StartDownloadAttachmentForWeb(
this.taskId,
this.attachment, [
this.cancelToken,
this.previewerSupported = false,
this.sourceView,
]);
@override @override
List<Object?> get props => [taskId, attachment, cancelToken, previewerSupported]; List<Object?> get props => [
taskId,
attachment,
cancelToken,
previewerSupported,
sourceView,
];
} }
class DownloadingAttachmentForWeb extends UIState { class DownloadingAttachmentForWeb extends UIState {
@@ -27,22 +41,25 @@ class DownloadingAttachmentForWeb extends UIState {
final double progress; final double progress;
final int downloaded; final int downloaded;
final int total; final int total;
final DownloadSourceView? sourceView;
DownloadingAttachmentForWeb( DownloadingAttachmentForWeb(
this.taskId, this.taskId,
this.attachment, this.attachment,
this.progress, this.progress,
this.downloaded, this.downloaded,
this.total this.total,
this.sourceView,
); );
@override @override
List<Object> get props => [ List<Object?> get props => [
taskId, taskId,
attachment, attachment,
progress, progress,
downloaded, downloaded,
total total,
sourceView,
]; ];
} }
@@ -52,11 +69,24 @@ class DownloadAttachmentForWebSuccess extends UIState {
final Attachment attachment; final Attachment attachment;
final Uint8List bytes; final Uint8List bytes;
final bool previewerSupported; final bool previewerSupported;
final DownloadSourceView? sourceView;
DownloadAttachmentForWebSuccess(this.taskId, this.attachment, this.bytes, this.previewerSupported); DownloadAttachmentForWebSuccess(
this.taskId,
this.attachment,
this.bytes,
this.previewerSupported,
this.sourceView,
);
@override @override
List<Object> get props => [taskId, attachment, bytes, previewerSupported]; List<Object?> get props => [
taskId,
attachment,
bytes,
previewerSupported,
sourceView,
];
} }
class DownloadAttachmentForWebFailure extends FeatureFailure { class DownloadAttachmentForWebFailure extends FeatureFailure {
@@ -64,14 +94,22 @@ class DownloadAttachmentForWebFailure extends FeatureFailure {
final DownloadTaskId? taskId; final DownloadTaskId? taskId;
final Attachment? attachment; final Attachment? attachment;
final CancelToken? cancelToken; final CancelToken? cancelToken;
final DownloadSourceView? sourceView;
DownloadAttachmentForWebFailure({ DownloadAttachmentForWebFailure({
this.attachment, this.attachment,
this.taskId, this.taskId,
this.cancelToken, this.cancelToken,
super.exception this.sourceView,
super.exception,
}); });
@override @override
List<Object?> get props => [attachment, taskId, cancelToken, ...super.props]; List<Object?> get props => [
attachment,
taskId,
cancelToken,
sourceView,
...super.props,
];
} }
@@ -0,0 +1,36 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:model/email/attachment.dart';
class GettingHtmlContentFromUploadFile extends LoadingState {}
class GetHtmlContentFromUploadFileSuccess extends UIState {
GetHtmlContentFromUploadFileSuccess({
required this.sanitizedHtmlContent,
required this.htmlAttachmentTitle,
required this.attachment,
required this.accountId,
required this.session,
});
final String sanitizedHtmlContent;
final String htmlAttachmentTitle;
final Attachment attachment;
final AccountId? accountId;
final Session? session;
@override
List<Object?> get props => [
sanitizedHtmlContent,
htmlAttachmentTitle,
attachment,
accountId,
session,
];
}
class GetHtmlContentFromUploadFileFailure extends FeatureFailure {
GetHtmlContentFromUploadFileFailure({super.exception});
}
@@ -8,8 +8,8 @@ import 'package:jmap_dart_client/jmap/mail/email/email.dart';
class ParsingEmailByBlobId extends LoadingState {} class ParsingEmailByBlobId extends LoadingState {}
class ParseEmailByBlobIdSuccess extends UIState { class ParseEmailByBlobIdSuccess extends UIState {
final AccountId? accountId; final AccountId accountId;
final Session? session; final Session session;
final String ownEmailAddress; final String ownEmailAddress;
final Email email; final Email email;
final Id blobId; final Id blobId;
@@ -3,20 +3,23 @@ import 'package:core/presentation/state/success.dart';
import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class PreviewingEmailFromEmlFile extends LoadingState {} class PreviewingEmailFromEmlFile extends LoadingState {}
class PreviewEmailFromEmlFileSuccess extends UIState { class PreviewEmailFromEmlFileSuccess extends UIState {
final EMLPreviewer emlPreviewer; final EMLPreviewer emlPreviewer;
final AccountId? accountId; final AccountId accountId;
final Session? session; final Session session;
final String ownEmailAddress; final String ownEmailAddress;
final AppLocalizations appLocalizations;
PreviewEmailFromEmlFileSuccess( PreviewEmailFromEmlFileSuccess(
this.emlPreviewer, this.emlPreviewer,
this.accountId, this.accountId,
this.session, this.session,
this.ownEmailAddress, this.ownEmailAddress,
this.appLocalizations,
); );
@override @override
@@ -25,6 +28,7 @@ class PreviewEmailFromEmlFileSuccess extends UIState {
accountId, accountId,
session, session,
ownEmailAddress, ownEmailAddress,
appLocalizations,
]; ];
} }
@@ -13,11 +13,11 @@ import 'package:model/account/authentication_type.dart';
import 'package:model/account/password.dart'; import 'package:model/account/password.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';
import 'package:tmail_ui_user/features/email/domain/state/download_all_attachments_for_web_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/download_all_attachments_for_web_state.dart';
import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart';
import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart';
import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart'; import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
class DownloadAllAttachmentsForWebInteractor { class DownloadAllAttachmentsForWebInteractor {
const DownloadAllAttachmentsForWebInteractor( const DownloadAllAttachmentsForWebInteractor(
@@ -7,14 +7,16 @@ import 'package:core/utils/app_logger.dart';
import 'package:core/utils/string_convert.dart'; import 'package:core/utils/string_convert.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:model/download/download_task_id.dart'; import 'package:model/download/download_task_id.dart';
import 'package:model/email/attachment.dart'; import 'package:model/email/attachment.dart';
import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; import 'package:tmail_ui_user/features/download/domain/model/download_source_view.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_html_content_from_attachment_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/download_attachment_for_web_state.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart'; import 'package:tmail_ui_user/features/download/domain/state/download_and_get_html_content_from_attachment_state.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/download_attachment_for_web_interactor.dart';
class GetHtmlContentFromAttachmentInteractor { class DownloadAndGetHtmlContentFromAttachmentInteractor {
GetHtmlContentFromAttachmentInteractor( DownloadAndGetHtmlContentFromAttachmentInteractor(
this._downloadAttachmentForWebInteractor, this._downloadAttachmentForWebInteractor,
); );
@@ -22,31 +24,41 @@ class GetHtmlContentFromAttachmentInteractor {
Stream<Either<Failure, Success>> execute( Stream<Either<Failure, Success>> execute(
AccountId accountId, AccountId accountId,
Session session,
Attachment attachment, Attachment attachment,
DownloadTaskId taskId, DownloadTaskId taskId,
String baseDownloadUrl, String baseDownloadUrl,
TransformConfiguration transformConfiguration, TransformConfiguration transformConfiguration, {
) async* { DownloadSourceView? sourceView,
}) async* {
try { try {
yield Right(GettingHtmlContentFromAttachment(attachment: attachment)); yield Right(DownloadAndGettingHtmlContentFromAttachment(
blobId: attachment.blobId,
sourceView: sourceView,
));
final downloadState = await _downloadAttachmentForWebInteractor.execute( final downloadState = await _downloadAttachmentForWebInteractor.execute(
taskId, taskId,
attachment, attachment,
accountId, accountId,
baseDownloadUrl, baseDownloadUrl,
sourceView: sourceView,
).last; ).last;
Either<Failure, Success>? sanitizeState; Either<Failure, Success>? sanitizeState;
await downloadState.fold( await downloadState.fold(
(failure) { (failure) {
sanitizeState = Left(GetHtmlContentFromAttachmentFailure( sanitizeState = Left(DownloadAndGetHtmlContentFromAttachmentFailure(
exception: failure is FeatureFailure ? failure.exception : null, exception: failure is FeatureFailure ? failure.exception : null,
attachment: attachment, blobId: attachment.blobId,
sourceView: sourceView,
)); ));
}, },
(success) async { (success) async {
if (success is! DownloadAttachmentForWebSuccess) { if (success is! DownloadAttachmentForWebSuccess) {
sanitizeState = Right(GettingHtmlContentFromAttachment(attachment: attachment)); sanitizeState = Right(DownloadAndGettingHtmlContentFromAttachment(
blobId: attachment.blobId,
sourceView: sourceView,
));
} else { } else {
final htmlContent = StringConvert.decodeFromBytes( final htmlContent = StringConvert.decodeFromBytes(
success.bytes, success.bytes,
@@ -57,6 +69,9 @@ class GetHtmlContentFromAttachmentInteractor {
htmlContent, htmlContent,
transformConfiguration, transformConfiguration,
attachment, attachment,
accountId,
session,
sourceView,
); );
} }
}, },
@@ -65,17 +80,18 @@ class GetHtmlContentFromAttachmentInteractor {
if (sanitizeState != null) { if (sanitizeState != null) {
yield sanitizeState!; yield sanitizeState!;
} else { } else {
yield Left(GetHtmlContentFromAttachmentFailure( yield Left(DownloadAndGetHtmlContentFromAttachmentFailure(
exception: null, exception: null,
attachment: attachment, blobId: attachment.blobId,
sourceView: sourceView,
)); ));
} }
} catch (e) { } catch (e) {
logError('GetHtmlContentFromAttachmentInteractor:exception: $e'); logError('GetHtmlContentFromAttachmentInteractor:exception: $e');
yield Left(GetHtmlContentFromAttachmentFailure( yield Left(DownloadAndGetHtmlContentFromAttachmentFailure(
exception: e, exception: e,
attachment: attachment, blobId: attachment.blobId,
sourceView: sourceView,
)); ));
} }
} }
@@ -84,21 +100,28 @@ class GetHtmlContentFromAttachmentInteractor {
String htmlContent, String htmlContent,
TransformConfiguration transformConfiguration, TransformConfiguration transformConfiguration,
Attachment attachment, Attachment attachment,
AccountId accountId,
Session session,
DownloadSourceView? sourceView,
) async { ) async {
try { try {
final sanitizedHtmlContent = await _downloadAttachmentForWebInteractor final sanitizedHtmlContent = await _downloadAttachmentForWebInteractor
.downloadRepository .downloadRepository
.sanitizeHtmlContent(htmlContent, transformConfiguration); .sanitizeHtmlContent(htmlContent, transformConfiguration);
return Right(GetHtmlContentFromAttachmentSuccess( return Right(DownloadAndGetHtmlContentFromAttachmentSuccess(
sanitizedHtmlContent: sanitizedHtmlContent, sanitizedHtmlContent: sanitizedHtmlContent,
htmlAttachmentTitle: attachment.generateFileName(), htmlAttachmentTitle: attachment.generateFileName(),
attachment: attachment, attachment: attachment,
accountId: accountId,
session: session,
sourceView: sourceView,
)); ));
} catch (e) { } catch (e) {
return Left(GetHtmlContentFromAttachmentFailure( return Left(DownloadAndGetHtmlContentFromAttachmentFailure(
exception: e, exception: e,
attachment: attachment, blobId: attachment.blobId,
sourceView: sourceView,
)); ));
} }
} }
@@ -11,11 +11,12 @@ import 'package:model/account/authentication_type.dart';
import 'package:model/account/password.dart'; import 'package:model/account/password.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';
import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; import 'package:tmail_ui_user/features/download/domain/model/download_source_view.dart';
import 'package:tmail_ui_user/features/download/domain/state/download_attachment_for_web_state.dart';
import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart';
import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart';
import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart'; import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
class DownloadAttachmentForWebInteractor { class DownloadAttachmentForWebInteractor {
final DownloadRepository downloadRepository; final DownloadRepository downloadRepository;
@@ -37,10 +38,20 @@ class DownloadAttachmentForWebInteractor {
StreamController<Either<Failure, Success>>? onReceiveController, StreamController<Either<Failure, Success>>? onReceiveController,
CancelToken? cancelToken, CancelToken? cancelToken,
bool previewerSupported = false, bool previewerSupported = false,
DownloadSourceView? sourceView,
}) async* { }) async* {
try { try {
yield Right<Failure, Success>(StartDownloadAttachmentForWeb(taskId, attachment, cancelToken, previewerSupported)); final loadingState = Right<Failure, Success>(
onReceiveController?.add(Right(StartDownloadAttachmentForWeb(taskId, attachment, cancelToken, previewerSupported))); StartDownloadAttachmentForWeb(
taskId,
attachment,
cancelToken,
previewerSupported,
sourceView,
),
);
yield loadingState;
onReceiveController?.add(loadingState);
final currentAccount = await _accountRepository.getCurrentAccount(); final currentAccount = await _accountRepository.getCurrentAccount();
AccountRequest? accountRequest; AccountRequest? accountRequest;
@@ -72,6 +83,7 @@ class DownloadAttachmentForWebInteractor {
attachment, attachment,
bytesDownloaded, bytesDownloaded,
previewerSupported, previewerSupported,
sourceView,
) )
); );
} catch (exception) { } catch (exception) {
@@ -79,8 +91,9 @@ class DownloadAttachmentForWebInteractor {
DownloadAttachmentForWebFailure( DownloadAttachmentForWebFailure(
attachment: attachment, attachment: attachment,
taskId: taskId, taskId: taskId,
exception: exception, sourceView: sourceView,
cancelToken: cancelToken, cancelToken: cancelToken,
exception: exception,
) )
); );
} }
@@ -9,31 +9,31 @@ import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/account/account_request.dart'; import 'package:model/account/account_request.dart';
import 'package:model/account/authentication_type.dart'; import 'package:model/account/authentication_type.dart';
import 'package:model/account/password.dart'; import 'package:model/account/password.dart';
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
import 'package:tmail_ui_user/features/email/domain/state/export_all_attachments_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/export_all_attachments_state.dart';
import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart';
import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart';
import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart';
class ExportAllAttachmentsInteractor { class ExportAllAttachmentsInteractor {
const ExportAllAttachmentsInteractor( const ExportAllAttachmentsInteractor(
this._emailRepository, this._downloadRepository,
this._accountRepository, this._accountRepository,
this._authenticationOIDCRepository, this._authenticationOIDCRepository,
this.credentialRepository, this._credentialRepository,
); );
final EmailRepository _emailRepository; final DownloadRepository _downloadRepository;
final AccountRepository _accountRepository; final AccountRepository _accountRepository;
final AuthenticationOIDCRepository _authenticationOIDCRepository; final AuthenticationOIDCRepository _authenticationOIDCRepository;
final CredentialRepository credentialRepository; final CredentialRepository _credentialRepository;
Stream<Either<Failure, Success>> execute( Stream<Either<Failure, Success>> execute(
AccountId accountId, AccountId accountId,
EmailId emailId, EmailId emailId,
String baseDownloadAllUrl, String baseDownloadAllUrl,
String outputFileName, String outputFileName,
{CancelToken? cancelToken} CancelToken cancelToken,
) async* { ) async* {
try { try {
yield Right(ExportingAllAttachments()); yield Right(ExportingAllAttachments());
@@ -43,20 +43,20 @@ class ExportAllAttachmentsInteractor {
final tokenOidc = await _authenticationOIDCRepository.getStoredTokenOIDC(currentAccount.id); final tokenOidc = await _authenticationOIDCRepository.getStoredTokenOIDC(currentAccount.id);
accountRequest = AccountRequest.withOidc(token: tokenOidc); accountRequest = AccountRequest.withOidc(token: tokenOidc);
} else { } else {
final authenticationInfoCache = await credentialRepository.getAuthenticationInfoStored(); final authenticationInfoCache = await _credentialRepository.getAuthenticationInfoStored();
accountRequest = AccountRequest.withBasic( accountRequest = AccountRequest.withBasic(
userName: UserName(authenticationInfoCache.username), userName: UserName(authenticationInfoCache.username),
password: Password(authenticationInfoCache.password), password: Password(authenticationInfoCache.password),
); );
} }
final result = await _emailRepository.exportAllAttachments( final result = await _downloadRepository.exportAllAttachments(
accountId, accountId,
emailId, emailId,
baseDownloadAllUrl, baseDownloadAllUrl,
outputFileName, outputFileName,
accountRequest, accountRequest,
cancelToken: cancelToken, cancelToken,
); );
yield Right(ExportAllAttachmentsSuccess(result)); yield Right(ExportAllAttachmentsSuccess(result));
@@ -6,21 +6,21 @@ import 'package:dio/dio.dart';
import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/user_name.dart'; import 'package:jmap_dart_client/jmap/core/user_name.dart';
import 'package:model/model.dart'; import 'package:model/model.dart';
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
import 'package:tmail_ui_user/features/email/domain/state/export_attachment_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/export_attachment_state.dart';
import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart';
import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart';
import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart';
class ExportAttachmentInteractor { class ExportAttachmentInteractor {
final EmailRepository emailRepository; final DownloadRepository _downloadRepository;
final CredentialRepository credentialRepository; final CredentialRepository _credentialRepository;
final AccountRepository _accountRepository; final AccountRepository _accountRepository;
final AuthenticationOIDCRepository _authenticationOIDCRepository; final AuthenticationOIDCRepository _authenticationOIDCRepository;
ExportAttachmentInteractor( ExportAttachmentInteractor(
this.emailRepository, this._downloadRepository,
this.credentialRepository, this._credentialRepository,
this._accountRepository, this._accountRepository,
this._authenticationOIDCRepository, this._authenticationOIDCRepository,
); );
@@ -40,14 +40,14 @@ class ExportAttachmentInteractor {
final tokenOidc = await _authenticationOIDCRepository.getStoredTokenOIDC(currentAccount.id); final tokenOidc = await _authenticationOIDCRepository.getStoredTokenOIDC(currentAccount.id);
accountRequest = AccountRequest.withOidc(token: tokenOidc); accountRequest = AccountRequest.withOidc(token: tokenOidc);
} else { } else {
final authenticationInfoCache = await credentialRepository.getAuthenticationInfoStored(); final authenticationInfoCache = await _credentialRepository.getAuthenticationInfoStored();
accountRequest = AccountRequest.withBasic( accountRequest = AccountRequest.withBasic(
userName: UserName(authenticationInfoCache.username), userName: UserName(authenticationInfoCache.username),
password: Password(authenticationInfoCache.password), password: Password(authenticationInfoCache.password),
); );
} }
final downloadedResponse = await emailRepository.exportAttachment( final downloadedResponse = await _downloadRepository.exportAttachment(
attachment, attachment,
accountId, accountId,
baseDownloadUrl, baseDownloadUrl,
@@ -0,0 +1,57 @@
import 'dart:async';
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:core/presentation/utils/html_transformer/dom/sanitize_hyper_link_tag_in_html_transformers.dart';
import 'package:core/presentation/utils/html_transformer/text/standardize_html_sanitizing_transformers.dart';
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
import 'package:core/utils/string_convert.dart';
import 'package:dartz/dartz.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:tmail_ui_user/features/download/domain/state/get_html_content_from_upload_file_state.dart';
import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart';
class GetHtmlContentFromUploadFileInteractor {
final DownloadRepository _downloadRepository;
GetHtmlContentFromUploadFileInteractor(this._downloadRepository);
Stream<Either<Failure, Success>> execute({
required UploadFileState uploadFile,
required Session? session,
required AccountId? accountId,
}) async* {
try {
yield Right(GettingHtmlContentFromUploadFile());
final htmlContent = StringConvert.decodeFromBytes(
uploadFile.file!.bytes!,
charset: uploadFile.attachment!.charset,
isHtml: true,
);
final sanitizedHtmlContent =
await _downloadRepository.sanitizeHtmlContent(
htmlContent,
TransformConfiguration.create(
customDomTransformers: [SanitizeHyperLinkTagInHtmlTransformer()],
customTextTransformers: [
const StandardizeHtmlSanitizingTransformers()
],
),
);
yield Right(GetHtmlContentFromUploadFileSuccess(
sanitizedHtmlContent: sanitizedHtmlContent,
htmlAttachmentTitle: uploadFile.attachment!.generateFileName(),
attachment: uploadFile.attachment!,
accountId: accountId,
session: session,
));
} catch (e) {
yield Left(GetHtmlContentFromUploadFileFailure(exception: e));
}
}
}
@@ -1,8 +1,8 @@
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:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_preview_email_eml_content_shared_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/get_preview_email_eml_content_shared_state.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart'; import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
class GetPreviewEmailEMLContentSharedInteractor { class GetPreviewEmailEMLContentSharedInteractor {
final DownloadRepository _downloadRepository; final DownloadRepository _downloadRepository;
@@ -1,8 +1,8 @@
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:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_preview_eml_content_in_memory_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/get_preview_eml_content_in_memory_state.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart'; import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
class GetPreviewEmlContentInMemoryInteractor { class GetPreviewEmlContentInMemoryInteractor {
final DownloadRepository _downloadRepository; final DownloadRepository _downloadRepository;
@@ -1,9 +1,9 @@
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:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:tmail_ui_user/features/email/domain/state/move_preview_eml_content_from_persistent_to_memory_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/move_preview_eml_content_from_persistent_to_memory_state.dart';
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart'; import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
class MovePreviewEmlContentFromPersistentToMemoryInteractor { class MovePreviewEmlContentFromPersistentToMemoryInteractor {
final DownloadRepository _downloadRepository; final DownloadRepository _downloadRepository;
@@ -4,8 +4,8 @@ import 'package:dartz/dartz.dart';
import 'package:jmap_dart_client/jmap/account_id.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/id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:tmail_ui_user/features/email/domain/state/parse_email_by_blob_id_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/parse_email_by_blob_id_state.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart'; import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
class ParseEmailByBlobIdInteractor { class ParseEmailByBlobIdInteractor {
final DownloadRepository _downloadRepository; final DownloadRepository _downloadRepository;
@@ -3,9 +3,9 @@ import 'package:core/presentation/state/success.dart';
import 'package:core/utils/platform_info.dart'; import 'package:core/utils/platform_info.dart';
import 'package:dartz/dartz.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/model/preview_email_eml_request.dart';
import 'package:tmail_ui_user/features/email/domain/state/preview_email_from_eml_file_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/preview_email_from_eml_file_state.dart';
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart'; import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
class PreviewEmailFromEmlFileInteractor { class PreviewEmailFromEmlFileInteractor {
final DownloadRepository _downloadRepository; final DownloadRepository _downloadRepository;
@@ -43,6 +43,7 @@ class PreviewEmailFromEmlFileInteractor {
previewEmailEMLRequest.accountId, previewEmailEMLRequest.accountId,
previewEmailEMLRequest.session, previewEmailEMLRequest.session,
previewEmailEMLRequest.ownEmailAddress, previewEmailEMLRequest.ownEmailAddress,
previewEmailEMLRequest.appLocalizations,
), ),
); );
} catch (e) { } catch (e) {
@@ -1,8 +1,8 @@
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:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:tmail_ui_user/features/email/domain/state/remove_preview_email_eml_content_shared_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/remove_preview_email_eml_content_shared_state.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart'; import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
class RemovePreviewEmailEmlContentSharedInteractor { class RemovePreviewEmailEmlContentSharedInteractor {
final DownloadRepository _downloadRepository; final DownloadRepository _downloadRepository;
@@ -3,6 +3,22 @@ import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/interactors_bindings.dart'; import 'package:tmail_ui_user/features/base/interactors_bindings.dart';
import 'package:tmail_ui_user/features/caching/utils/local_storage_manager.dart'; import 'package:tmail_ui_user/features/caching/utils/local_storage_manager.dart';
import 'package:tmail_ui_user/features/caching/utils/session_storage_manager.dart'; import 'package:tmail_ui_user/features/caching/utils/session_storage_manager.dart';
import 'package:tmail_ui_user/features/download/data/datasource/download_datasource.dart';
import 'package:tmail_ui_user/features/download/data/datasource_impl/download_datasource_impl.dart';
import 'package:tmail_ui_user/features/download/data/repository/download_repository_impl.dart';
import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/download_all_attachments_for_web_interactor.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/download_and_get_html_content_from_attachment_interactor.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/download_attachment_for_web_interactor.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/export_all_attachments_interactor.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/export_attachment_interactor.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/get_html_content_from_upload_file_interactor.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/get_preview_email_eml_content_shared_interactor.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/get_preview_eml_content_in_memory_interactor.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/move_preview_eml_content_from_persistent_to_memory_interactor.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/parse_email_by_blob_id_interactor.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/preview_email_from_eml_file_interactor.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/remove_preview_email_eml_content_shared_interactor.dart';
import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.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/html_datasource.dart';
import 'package:tmail_ui_user/features/email/data/datasource_impl/email_datasource_impl.dart'; import 'package:tmail_ui_user/features/email/data/datasource_impl/email_datasource_impl.dart';
@@ -11,22 +27,9 @@ import 'package:tmail_ui_user/features/email/data/datasource_impl/email_session_
import 'package:tmail_ui_user/features/email/data/datasource_impl/html_datasource_impl.dart'; import 'package:tmail_ui_user/features/email/data/datasource_impl/html_datasource_impl.dart';
import 'package:tmail_ui_user/features/email/data/local/html_analyzer.dart'; import 'package:tmail_ui_user/features/email/data/local/html_analyzer.dart';
import 'package:tmail_ui_user/features/email/data/network/email_api.dart'; import 'package:tmail_ui_user/features/email/data/network/email_api.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/download_all_attachments_for_web_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_html_content_from_attachment_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_preview_email_eml_content_shared_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_preview_eml_content_in_memory_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/move_preview_eml_content_from_persistent_to_memory_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/parse_email_by_blob_id_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/preview_email_from_eml_file_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/remove_preview_email_eml_content_shared_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart';
import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart';
import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart'; import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource/download_datasource.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/data/datasource_impl/download_datasource_impl.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/data/repository/download_repository_impl.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart';
import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart'; import 'package:tmail_ui_user/main/exceptions/cache_exception_thrower.dart';
import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart'; import 'package:tmail_ui_user/main/exceptions/remote_exception_thrower.dart';
@@ -84,6 +87,22 @@ class DownloadInteractorBindings extends InteractorsBindings {
Get.find<CredentialRepository>(), Get.find<CredentialRepository>(),
), ),
); );
Get.lazyPut(
() => ExportAttachmentInteractor(
Get.find<DownloadRepository>(),
Get.find<CredentialRepository>(),
Get.find<AccountRepository>(),
Get.find<AuthenticationOIDCRepository>(),
),
);
Get.lazyPut(
() => ExportAllAttachmentsInteractor(
Get.find<DownloadRepository>(),
Get.find<AccountRepository>(),
Get.find<AuthenticationOIDCRepository>(),
Get.find<CredentialRepository>(),
),
);
Get.lazyPut( Get.lazyPut(
() => ParseEmailByBlobIdInteractor(Get.find<DownloadRepository>()), () => ParseEmailByBlobIdInteractor(Get.find<DownloadRepository>()),
); );
@@ -91,7 +110,7 @@ class DownloadInteractorBindings extends InteractorsBindings {
() => PreviewEmailFromEmlFileInteractor(Get.find<DownloadRepository>()), () => PreviewEmailFromEmlFileInteractor(Get.find<DownloadRepository>()),
); );
Get.lazyPut( Get.lazyPut(
() => GetHtmlContentFromAttachmentInteractor( () => DownloadAndGetHtmlContentFromAttachmentInteractor(
Get.find<DownloadAttachmentForWebInteractor>(), Get.find<DownloadAttachmentForWebInteractor>(),
), ),
); );
@@ -116,6 +135,11 @@ class DownloadInteractorBindings extends InteractorsBindings {
Get.find<DownloadRepository>(), Get.find<DownloadRepository>(),
), ),
); );
Get.lazyPut(
() => GetHtmlContentFromUploadFileInteractor(
Get.find<DownloadRepository>(),
),
);
} }
@override @override
@@ -0,0 +1,294 @@
import 'dart:async';
import 'package:core/data/network/download/download_manager.dart';
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/print_utils.dart';
import 'package:dartz/dartz.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:model/download/download_task_id.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/download/domain/model/download_source_view.dart';
import 'package:tmail_ui_user/features/download/domain/state/download_all_attachments_for_web_state.dart';
import 'package:tmail_ui_user/features/download/domain/state/download_and_get_html_content_from_attachment_state.dart';
import 'package:tmail_ui_user/features/download/domain/state/download_attachment_for_web_state.dart';
import 'package:tmail_ui_user/features/download/domain/state/export_all_attachments_state.dart';
import 'package:tmail_ui_user/features/download/domain/state/export_attachment_state.dart';
import 'package:tmail_ui_user/features/download/domain/state/get_html_content_from_upload_file_state.dart';
import 'package:tmail_ui_user/features/download/domain/state/parse_email_by_blob_id_state.dart';
import 'package:tmail_ui_user/features/download/domain/state/preview_email_from_eml_file_state.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/download_all_attachments_for_web_interactor.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/download_and_get_html_content_from_attachment_interactor.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/download_attachment_for_web_interactor.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/export_all_attachments_interactor.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/export_attachment_interactor.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/get_html_content_from_upload_file_interactor.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/parse_email_by_blob_id_interactor.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/preview_email_from_eml_file_interactor.dart';
import 'package:tmail_ui_user/features/download/presentation/extensions/download_attachment_download_controller_extension.dart';
import 'package:tmail_ui_user/features/download/presentation/extensions/preview_attachment_download_controller_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/download/download_task_state.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
typedef UpdateDownloadTaskStateCallback = DownloadTaskState Function(DownloadTaskState currentState);
class DownloadController extends BaseController {
final DownloadManager downloadManager;
final PrintUtils printUtils;
final DownloadAttachmentForWebInteractor downloadAttachmentForWebInteractor;
final DownloadAllAttachmentsForWebInteractor
downloadAllAttachmentsForWebInteractor;
final ParseEmailByBlobIdInteractor parseEmailByBlobIdInteractor;
final PreviewEmailFromEmlFileInteractor previewEmailFromEmlFileInteractor;
final DownloadAndGetHtmlContentFromAttachmentInteractor
downloadAndGetHtmlContentFromAttachmentInteractor;
final GetHtmlContentFromUploadFileInteractor
getHtmlContentFromUploadFileInteractor;
final ExportAttachmentInteractor exportAttachmentInteractor;
final ExportAllAttachmentsInteractor exportAllAttachmentsInteractor;
DownloadController(
this.downloadManager,
this.printUtils,
this.downloadAttachmentForWebInteractor,
this.downloadAllAttachmentsForWebInteractor,
this.parseEmailByBlobIdInteractor,
this.previewEmailFromEmlFileInteractor,
this.downloadAndGetHtmlContentFromAttachmentInteractor,
this.getHtmlContentFromUploadFileInteractor,
this.exportAttachmentInteractor,
this.exportAllAttachmentsInteractor,
);
final listDownloadTaskState = RxList<DownloadTaskState>();
final hideDownloadTaskbar = RxBool(false);
final downloadUIAction = Rxn<DownloadUIAction>();
final downloadProgressStateController =
StreamController<Either<Failure, Success>>.broadcast();
StreamSubscription<Either<Failure, Success>>?
_downloadProgressStateSubscription;
@override
void onInit() {
super.onInit();
_registerDownloadProgressState();
}
void _registerDownloadProgressState() {
_downloadProgressStateSubscription = downloadProgressStateController.stream
.listen(_onDownloadProgressStateChanged);
}
void _onDownloadProgressStateChanged(Either<Failure, Success> state) {
state.fold((_) => null, (success) {
if (success is StartDownloadAttachmentForWeb) {
_handleStartSingleDownload(success);
} else if (success is DownloadingAttachmentForWeb) {
_updateDownloadProgress(
success.taskId,
success.progress,
success.downloaded,
success.total,
);
} else if (success is StartDownloadAllAttachmentsForWeb) {
_handleStartAllDownload(success);
} else if (success is DownloadingAllAttachmentsForWeb) {
_updateDownloadProgress(
success.taskId,
success.progress,
success.downloaded,
success.total,
);
}
});
}
void _handleStartSingleDownload(StartDownloadAttachmentForWeb success) {
if (success.previewerSupported) return;
addDownloadTask(
DownloadTaskState(
taskId: success.taskId,
attachment: success.attachment,
onCancel: success.cancelToken?.cancel,
),
);
if (_hasValidContext) {
appToast.showToastMessage(
currentOverlayContext!,
AppLocalizations.of(currentContext!).your_download_has_started,
leadingSVGIconColor: AppColor.primaryColor,
leadingSVGIcon: imagePaths.icDownload,
);
}
}
void _handleStartAllDownload(StartDownloadAllAttachmentsForWeb success) {
addDownloadTask(
DownloadTaskState(
taskId: success.taskId,
attachment: success.attachment,
onCancel: () => success.cancelToken?.cancel(),
),
);
if (_hasValidContext) {
appToast.showToastSuccessMessage(
currentOverlayContext!,
AppLocalizations.of(currentContext!).creatingAnArchiveForDownloading,
leadingSVGIconColor: Colors.white,
leadingSVGIcon: imagePaths.icDownloadAll,
);
}
}
void _updateDownloadProgress(
DownloadTaskId taskId,
double progress,
int downloaded,
int total,
) {
final percent = progress.round();
log('$runtimeType::_updateDownloadProgress(): $percent%');
updateDownloadTaskByTaskId(taskId, (currentTask) {
return currentTask.copyWith(
progress: progress,
downloaded: downloaded,
total: total,
);
});
}
bool get _hasValidContext =>
currentOverlayContext != null && currentContext != null;
bool get notEmptyListDownloadTask => listDownloadTaskState.isNotEmpty;
void addDownloadTask(DownloadTaskState task) {
log('DownloadController::addDownloadTask(): ${task.taskId}');
listDownloadTaskState.add(task);
hideDownloadTaskbar.value = false;
}
void updateDownloadTaskByTaskId(
DownloadTaskId downloadTaskId,
UpdateDownloadTaskStateCallback updateDownloadTaskCallback,
) {
final matchIndex = listDownloadTaskState
.indexWhere((task) => task.taskId == downloadTaskId);
if (matchIndex >= 0) {
listDownloadTaskState[matchIndex] = updateDownloadTaskCallback(listDownloadTaskState[matchIndex]);
listDownloadTaskState.refresh();
}
}
void deleteDownloadTask(DownloadTaskId taskId) {
log('DownloadController::deleteDownloadTask(): $taskId');
final matchIndex = listDownloadTaskState
.indexWhere((task) => task.taskId == taskId);
if (matchIndex >= 0) {
listDownloadTaskState.removeAt(matchIndex);
listDownloadTaskState.refresh();
}
if (listDownloadTaskState.isEmpty) {
hideDownloadTaskbar.value = true;
}
}
void pushDownloadUIAction(DownloadUIAction action) {
downloadUIAction.value = action;
}
void clearDownloadUIAction() {
downloadUIAction.value = null;
}
@override
void handleSuccessViewState(Success success) {
if (success is DownloadAttachmentForWebSuccess) {
handleDownloadAttachmentForWebSuccess(success);
} else if (success is StartDownloadAttachmentForWeb &&
success.sourceView == DownloadSourceView.emailView) {
pushDownloadUIAction(UpdateAttachmentsViewStateAction(
success.attachment.blobId,
Right<Failure, Success>(success),
));
} else if (success is DownloadingAttachmentForWeb &&
success.sourceView == DownloadSourceView.emailView) {
pushDownloadUIAction(UpdateAttachmentsViewStateAction(
success.attachment.blobId,
Right<Failure, Success>(success),
));
} else if (success is DownloadAllAttachmentsForWebSuccess) {
deleteDownloadTask(success.taskId);
} else if (success is ParseEmailByBlobIdSuccess) {
handleParseEmailByBlobIdSuccess(
context: currentContext,
accountId: success.accountId,
session: success.session,
ownEmailAddress: success.ownEmailAddress,
blobId: success.blobId,
email: success.email,
);
} else if (success is PreviewEmailFromEmlFileSuccess) {
handlePreviewEmailFromEmlFileSuccess(success);
} else if (success is DownloadAndGetHtmlContentFromAttachmentSuccess) {
handleDownloadAndGetHtmlContentFromAttachmentSuccess(success);
} else if (success is DownloadAndGettingHtmlContentFromAttachment &&
success.sourceView == DownloadSourceView.emailView) {
pushDownloadUIAction(
UpdateAttachmentsViewStateAction(
success.blobId,
Right<Failure, Success>(success),
),
);
} else if (success is ExportAttachmentSuccess) {
exportAttachmentSuccessAction(success.downloadedResponse);
} else if (success is ExportAllAttachmentsSuccess) {
exportAllAttachmentsSuccessAction(success.downloadedResponse.filePath);
} else if (success is GetHtmlContentFromUploadFileSuccess) {
handleGetHtmlContentFromUploadFileSuccess(success);
} else {
super.handleSuccessViewState(success);
}
}
@override
void handleFailureViewState(Failure failure) {
if (failure is DownloadAllAttachmentsForWebFailure) {
downloadAllAttachmentsForWebFailure(failureState: failure);
} else if (failure is DownloadAttachmentForWebFailure) {
downloadAttachmentForWebFailureAction(failureState: failure);
} else if (failure is ParseEmailByBlobIdFailure) {
handleParseEmailByBlobIdFailure(failure);
} else if (failure is PreviewEmailFromEmlFileFailure) {
handlePreviewEmailFromEMLFileFailure(failure);
} else if (failure is GetHtmlContentFromUploadFileFailure ||
failure is DownloadAndGetHtmlContentFromAttachmentFailure) {
handlePreviewHtmlFileFailure(failureState: failure);
} else if (failure is ExportAttachmentFailure) {
exportAttachmentFailureAction(failure);
} else if (failure is ExportAllAttachmentsFailure) {
exportAllAttachmentsFailureAction(failure);
} else {
super.handleFailureViewState(failure);
}
}
@override
void onClose() {
_downloadProgressStateSubscription?.cancel();
_downloadProgressStateSubscription = null;
downloadProgressStateController.close();
super.onClose();
}
}
@@ -0,0 +1,631 @@
import 'dart:async';
import 'dart:io';
import 'package:core/data/network/download/downloaded_response.dart';
import 'package:core/domain/exceptions/download_file_exception.dart';
import 'package:core/presentation/extensions/media_type_extension.dart';
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:core/presentation/views/dialog/downloading_file_dialog_builder.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/platform_info.dart';
import 'package:dartz/dartz.dart';
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_file_dialog/flutter_file_dialog.dart';
import 'package:get/get.dart';
import 'package:http_parser/http_parser.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/download/download_task_id.dart';
import 'package:model/email/attachment.dart';
import 'package:model/email/eml_attachment.dart';
import 'package:model/email/presentation_email.dart';
import 'package:model/extensions/presentation_email_extension.dart';
import 'package:model/extensions/session_extension.dart';
import 'package:open_file/open_file.dart' as open_file;
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:tmail_ui_user/features/download/domain/exceptions/download_attachment_exceptions.dart';
import 'package:tmail_ui_user/features/download/domain/model/download_source_view.dart';
import 'package:tmail_ui_user/features/download/domain/state/download_all_attachments_for_web_state.dart';
import 'package:tmail_ui_user/features/download/domain/state/download_attachment_for_web_state.dart';
import 'package:tmail_ui_user/features/download/domain/state/export_all_attachments_state.dart';
import 'package:tmail_ui_user/features/download/domain/state/export_attachment_state.dart';
import 'package:tmail_ui_user/features/download/presentation/controllers/download_controller.dart';
import 'package:tmail_ui_user/features/download/presentation/extensions/preview_attachment_download_controller_extension.dart';
import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/attachment_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/attachment_item_widget.dart';
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
typedef OnDownloadWebFileAction = void Function(String name, Uint8List bytes);
extension DownloadAttachmentDownloadControllerExtension on DownloadController {
void downloadAttachment({
required Attachment attachment,
required AccountId? accountId,
required Session? session,
bool previewerSupported = false,
bool showBottomDownloadProgressBar = false,
DownloadSourceView? sourceView,
}) {
if (PlatformInfo.isWeb) {
downloadAttachmentForWeb(
attachment: attachment,
accountId: accountId,
session: session,
previewerSupported: previewerSupported,
onReceiveController: showBottomDownloadProgressBar
? downloadProgressStateController
: null,
sourceView: sourceView,
);
} else if (PlatformInfo.isMobile) {
exportAttachment(
attachment: attachment,
accountId: accountId,
session: session,
);
} else {
log('$runtimeType::downloadAttachment: THE PLATFORM IS SUPPORTED');
}
}
Future<void> downloadAllAttachments({
required String outputFileName,
required EmailId? emailId,
required AccountId? accountId,
required Session? session,
bool showBottomDownloadProgressBar = false,
}) async {
if (PlatformInfo.isWeb) {
downloadAllAttachmentsForWeb(
outputFileName: outputFileName,
emailId: emailId,
session: session,
accountId: accountId,
onReceiveController: showBottomDownloadProgressBar
? downloadProgressStateController
: null,
);
} else if (PlatformInfo.isMobile) {
exportAllAttachments(
outputFileName: outputFileName,
emailId: emailId,
session: session,
accountId: accountId,
);
} else {
log('$runtimeType::downloadAllAttachments: THE PLATFORM IS SUPPORTED');
}
}
void exportAttachment({
required Attachment attachment,
required AccountId? accountId,
required Session? session,
}) {
final cancelToken = CancelToken();
showDownloadingFileDialog(
attachmentName: attachment.name ?? '',
cancelToken: cancelToken,
);
if (session == null) {
emitFailure(
controller: this,
failure: ExportAttachmentFailure(NotFoundSessionException()),
);
return;
}
if (accountId == null) {
emitFailure(
controller: this,
failure: ExportAttachmentFailure(NotFoundAccountIdException()),
);
return;
}
final baseDownloadUrl = session.getSafetyDownloadUrl(
jmapUrl: dynamicUrlInterceptors.jmapUrl,
);
consumeState(
exportAttachmentInteractor.execute(
attachment,
accountId,
baseDownloadUrl,
cancelToken,
),
);
}
void showDownloadingFileDialog({
required String attachmentName,
required CancelToken cancelToken,
}) {
Get.dialog(
PointerInterceptor(
child: Builder(
builder: (context) {
final appLocalizations = AppLocalizations.of(context);
return (DownloadingFileDialogBuilder()
..key(const Key('downloading_file_dialog'))
..title(appLocalizations.preparing_to_export)
..content(appLocalizations.downloading_file(attachmentName))
..actionText(appLocalizations.cancel)
..addCancelDownloadActionClick(() {
cancelToken.cancel([
appLocalizations.user_cancel_download_file,
]);
popBack();
}))
.build();
},
),
),
barrierDismissible: false,
);
}
void downloadAttachmentForWeb({
required Attachment attachment,
required AccountId? accountId,
required Session? session,
bool previewerSupported = false,
StreamController<Either<Failure, Success>>? onReceiveController,
DownloadSourceView? sourceView,
}) {
if (session == null) {
emitFailure(
controller: this,
failure: DownloadAttachmentForWebFailure(
attachment: attachment,
exception: NotFoundSessionException(),
sourceView: sourceView,
),
);
return;
}
if (accountId == null) {
emitFailure(
controller: this,
failure: DownloadAttachmentForWebFailure(
attachment: attachment,
exception: NotFoundAccountIdException(),
sourceView: sourceView,
),
);
return;
}
final generateTaskId = DownloadTaskId(uuid.v4());
final baseDownloadUrl = session.getSafetyDownloadUrl(
jmapUrl: dynamicUrlInterceptors.jmapUrl,
);
final cancelToken = CancelToken();
consumeState(downloadAttachmentForWebInteractor.execute(
generateTaskId,
attachment,
accountId,
baseDownloadUrl,
onReceiveController: onReceiveController,
cancelToken: cancelToken,
previewerSupported: previewerSupported,
sourceView: sourceView,
));
}
void exportAttachmentSuccessAction(DownloadedResponse downloadedResponse) {
popBack();
_openDownloadedPreviewWorkGroupDocument(
filePath: downloadedResponse.filePath,
mediaType: downloadedResponse.mediaType,
);
}
Future<void> _openDownloadedPreviewWorkGroupDocument({
required String filePath,
MediaType? mediaType,
}) async {
if (mediaType == null) {
await _saveFileToStorage(filePath);
return;
}
final openResult = await open_file.OpenFile.open(
filePath,
type: Platform.isAndroid ? mediaType.mimeType : null,
// "xdg" is default value
linuxDesktopName:
Platform.isIOS ? mediaType.getDocumentUti().value ?? 'xdg' : 'xdg',
);
if (openResult.type != open_file.ResultType.done) {
await _saveFileToStorage(filePath);
}
}
Future<void> _saveFileToStorage(String filePath) async {
final params = SaveFileDialogParams(sourceFilePath: filePath);
await FlutterFileDialog.saveFile(params: params);
}
void exportAllAttachmentsSuccessAction(String filePath) {
popBack();
_saveFileToStorage(filePath);
}
void exportAllAttachmentsFailureAction(dynamic exception) {
if (exception is CancelDownloadFileException) {
if (currentOverlayContext != null && currentContext != null) {
appToast.showToastWarningMessage(
currentOverlayContext!,
AppLocalizations.of(currentContext!).user_cancel_download_file,
);
}
return;
}
popBack();
if (currentOverlayContext != null && currentContext != null) {
appToast.showToastErrorMessage(
currentOverlayContext!,
AppLocalizations.of(currentContext!).attachment_download_failed,
);
}
}
void exportAttachmentFailureAction(dynamic exception) {
if (exception is CancelDownloadFileException) {
if (currentOverlayContext != null && currentContext != null) {
appToast.showToastWarningMessage(
currentOverlayContext!,
AppLocalizations.of(currentContext!).user_cancel_download_file,
);
}
return;
}
if (Get.isDialogOpen == true) {
popBack();
}
if (currentOverlayContext != null && currentContext != null) {
appToast.showToastErrorMessage(
currentOverlayContext!,
AppLocalizations.of(currentContext!).attachment_download_failed,
);
}
}
void downloadAttachmentForWebFailureAction({
required DownloadAttachmentForWebFailure failureState,
}) {
closeDialogLoading();
if (failureState.taskId != null) {
deleteDownloadTask(failureState.taskId!);
}
if (failureState.attachment != null &&
failureState.sourceView == DownloadSourceView.emailView) {
pushDownloadUIAction(
UpdateAttachmentsViewStateAction(
failureState.attachment?.blobId,
Left<Failure, Success>(failureState),
),
);
}
if (currentOverlayContext == null || currentContext == null) return;
final appLocalizations = AppLocalizations.of(currentContext!);
String message = appLocalizations.attachment_download_failed;
if (failureState.attachment is EMLAttachment) {
message = appLocalizations.downloadMessageAsEMLFailed;
} else if (failureState.cancelToken?.isCancelled == true) {
message = appLocalizations.downloadAttachmentHasBeenCancelled;
}
appToast.showToastErrorMessage(currentOverlayContext!, message);
}
void downloadAllAttachmentsForWebFailure({
required DownloadAllAttachmentsForWebFailure failureState,
}) {
deleteDownloadTask(failureState.taskId);
if (currentOverlayContext == null || currentContext == null) return;
final appLocalizations = AppLocalizations.of(currentContext!);
final message = failureState.cancelToken?.isCancelled == true
? appLocalizations.downloadAttachmentHasBeenCancelled
: appLocalizations.attachment_download_failed;
appToast.showToastErrorMessage(currentOverlayContext!, message);
}
void downloadFileWeb({
required String fileName,
required Uint8List fileBytes,
}) {
downloadManager.createAnchorElementDownloadFileWeb(
fileBytes,
fileName,
);
}
void downloadAttachmentInEMLPreview({
required OnDownloadAttachmentFileAction onDownloadAction,
required Uri? uri,
}) {
if (uri == null) return;
final attachment = EmailUtils.parsingAttachmentByUri(uri);
if (attachment == null) return;
onDownloadAction(attachment);
}
void exportAllAttachments({
required String outputFileName,
required EmailId? emailId,
required AccountId? accountId,
required Session? session,
}) {
final cancelToken = CancelToken();
showDownloadingFileDialog(
attachmentName: outputFileName,
cancelToken: cancelToken,
);
if (session == null) {
emitFailure(
controller: this,
failure: ExportAllAttachmentsFailure(
exception: NotFoundSessionException(),
),
);
return;
}
if (accountId == null) {
emitFailure(
controller: this,
failure: ExportAllAttachmentsFailure(
exception: NotFoundAccountIdException(),
),
);
return;
}
if (emailId == null) {
emitFailure(
controller: this,
failure: ExportAllAttachmentsFailure(
exception: NotFoundEmailException(),
),
);
return;
}
final downloadAllSupported = session.isDownloadAllSupported(accountId);
if (!downloadAllSupported) {
emitFailure(
controller: this,
failure: ExportAllAttachmentsFailure(
exception: CapabilityDownloadAllNotSupportedException(),
),
);
return;
}
final baseDownloadAllUrl =
session.getDownloadAllCapability(accountId)!.endpoint!;
consumeState(
exportAllAttachmentsInteractor.execute(
accountId,
emailId,
baseDownloadAllUrl,
outputFileName,
cancelToken,
),
);
}
void downloadAllAttachmentsForWeb({
required String outputFileName,
required EmailId? emailId,
required AccountId? accountId,
required Session? session,
bool previewerSupported = false,
StreamController<Either<Failure, Success>>? onReceiveController,
}) {
final taskId = DownloadTaskId(uuid.v4());
if (session == null) {
emitFailure(
controller: this,
failure: DownloadAllAttachmentsForWebFailure(
exception: NotFoundSessionException(),
taskId: taskId,
),
);
return;
}
if (accountId == null) {
emitFailure(
controller: this,
failure: DownloadAllAttachmentsForWebFailure(
exception: NotFoundAccountIdException(),
taskId: taskId,
),
);
return;
}
if (emailId == null) {
emitFailure(
controller: this,
failure: DownloadAllAttachmentsForWebFailure(
exception: NotFoundEmailException(),
taskId: taskId,
),
);
return;
}
final downloadAllSupported = session.isDownloadAllSupported(accountId);
if (!downloadAllSupported) {
emitFailure(
controller: this,
failure: DownloadAllAttachmentsForWebFailure(
exception: CapabilityDownloadAllNotSupportedException(),
taskId: taskId,
),
);
return;
}
final baseDownloadAllUrl =
session.getDownloadAllCapability(accountId)!.endpoint!;
final downloadAttachment = Attachment(
name: outputFileName,
type: MediaType('application', 'zip'),
);
final cancelToken = CancelToken();
consumeState(
downloadAllAttachmentsForWebInteractor.execute(
accountId,
emailId,
baseDownloadAllUrl,
downloadAttachment,
taskId,
onReceiveController: onReceiveController,
cancelToken: cancelToken,
),
);
}
void downloadMessageAsEML({
required PresentationEmail presentationEmail,
required AccountId? accountId,
required Session? session,
bool showBottomDownloadProgressBar = false,
}) {
if (session == null) {
emitFailure(
controller: this,
failure: DownloadAttachmentForWebFailure(
exception: NotFoundSessionException(),
),
);
return;
}
if (accountId == null) {
emitFailure(
controller: this,
failure: DownloadAttachmentForWebFailure(
exception: NotFoundAccountIdException(),
),
);
return;
}
final emlAttachment = presentationEmail.createEMLAttachment();
if (emlAttachment.blobId == null) {
emitFailure(
controller: this,
failure: DownloadAttachmentForWebFailure(
exception: NotFoundEmailBlobIdException(),
),
);
return;
}
final generateTaskId = DownloadTaskId(uuid.v4());
final baseDownloadUrl = session.getSafetyDownloadUrl(
jmapUrl: dynamicUrlInterceptors.jmapUrl,
);
final cancelToken = CancelToken();
consumeState(
downloadAttachmentForWebInteractor.execute(
generateTaskId,
emlAttachment,
accountId,
baseDownloadUrl,
onReceiveController: showBottomDownloadProgressBar
? downloadProgressStateController
: null,
cancelToken: cancelToken,
previewerSupported: false,
),
);
}
void handleDownloadAttachmentForWebSuccess(
DownloadAttachmentForWebSuccess success,
) {
closeDialogLoading();
if (success.sourceView == DownloadSourceView.emailView) {
pushDownloadUIAction(UpdateAttachmentsViewStateAction(
success.attachment.blobId,
Right<Failure, Success>(success),
));
}
deleteDownloadTask(success.taskId);
if (!success.previewerSupported) {
downloadFileWeb(
fileBytes: success.bytes,
fileName: success.attachment.generateFileName(),
);
return;
}
if (success.attachment.isImage) {
previewImageFile(
fileName: success.attachment.generateFileName(),
imageBytes: success.bytes,
context: currentContext,
onDownloadWebFileAction: (name, bytes) => downloadFileWeb(
fileName: name,
fileBytes: bytes,
),
);
} else if (success.attachment.isText || success.attachment.isJson) {
previewPlainTextFile(
fileName: success.attachment.generateFileName(),
fileBytes: success.bytes,
context: currentContext,
onDownloadWebFileAction: (name, bytes) => downloadFileWeb(
fileName: name,
fileBytes: bytes,
),
);
}
}
}
@@ -0,0 +1,763 @@
import 'package:core/domain/exceptions/web_session_exception.dart';
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:core/presentation/utils/html_transformer/dom/sanitize_hyper_link_tag_in_html_transformers.dart';
import 'package:core/presentation/utils/html_transformer/text/standardize_html_sanitizing_transformers.dart';
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
import 'package:core/presentation/views/html_viewer/html_content_viewer_widget.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/html/html_utils.dart';
import 'package:core/utils/platform_info.dart';
import 'package:dartz/dartz.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:get/get_navigation/src/dialog/dialog_route.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/download/download_task_id.dart';
import 'package:model/email/attachment.dart';
import 'package:model/extensions/session_extension.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:tmail_ui_user/features/download/domain/exceptions/download_attachment_exceptions.dart';
import 'package:tmail_ui_user/features/download/domain/model/download_source_view.dart';
import 'package:tmail_ui_user/features/download/domain/state/download_and_get_html_content_from_attachment_state.dart';
import 'package:tmail_ui_user/features/download/domain/state/get_html_content_from_upload_file_state.dart';
import 'package:tmail_ui_user/features/download/domain/state/parse_email_by_blob_id_state.dart';
import 'package:tmail_ui_user/features/download/domain/state/preview_email_from_eml_file_state.dart';
import 'package:tmail_ui_user/features/download/presentation/controllers/download_controller.dart';
import 'package:tmail_ui_user/features/download/presentation/extensions/download_attachment_download_controller_extension.dart';
import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart';
import 'package:tmail_ui_user/features/email/domain/model/preview_email_eml_request.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/attachment_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/attachment_item_widget.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/html_attachment_previewer.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/pdf_viewer/pdf_viewer.dart';
import 'package:tmail_ui_user/features/email_previewer/email_previewer_dialog_view.dart';
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart';
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
import 'package:tmail_ui_user/main/routes/route_utils.dart';
import 'package:twake_previewer_flutter/core/constants/supported_charset.dart';
import 'package:twake_previewer_flutter/core/previewer_options/options/previewer_state.dart';
import 'package:twake_previewer_flutter/core/previewer_options/options/top_bar_options.dart';
import 'package:twake_previewer_flutter/core/previewer_options/previewer_options.dart';
import 'package:twake_previewer_flutter/twake_image_previewer/twake_image_previewer.dart';
import 'package:twake_previewer_flutter/twake_plain_text_previewer/twake_plain_text_previewer.dart';
typedef OnPreviewOrDownloadAttachmentAction = void Function(
Attachment attachment,
bool isPreviewSupported,
);
extension PreviewAttachmentDownloadControllerExtension on DownloadController {
void previewAttachment({
required BuildContext context,
required Attachment attachment,
required AccountId? accountId,
required Session? session,
required String ownEmailAddress,
required OnPreviewOrDownloadAttachmentAction onPreviewOrDownloadAction,
bool isDialogLoadingVisible = false,
DownloadSourceView? sourceView,
}) {
final appLocalizations = AppLocalizations.of(context);
if (PlatformInfo.isWeb && attachment.isPDFFile) {
_previewPDFFile(
context: context,
attachment: attachment,
accountId: accountId,
session: session,
);
} else if (attachment.isEMLFile) {
_preparePreviewEMLFile(
appLocalizations: appLocalizations,
accountId: accountId,
session: session,
ownEmailAddress: ownEmailAddress,
blobId: attachment.blobId,
);
} else if (attachment.isHTMLFile) {
_preparePreviewHtmlFile(
appLocalizations: appLocalizations,
session: session,
accountId: accountId,
attachment: attachment,
isDialogLoadingVisible: isDialogLoadingVisible,
sourceView: sourceView,
);
} else {
if (isDialogLoadingVisible) {
showDialogLoading(appLocalizations);
}
onPreviewOrDownloadAction(
attachment,
attachment.isPreviewSupported,
);
}
}
void previewUploadFile({
required BuildContext context,
required UploadFileState uploadFile,
required AccountId? accountId,
required Session? session,
required String ownEmailAddress,
required OnPreviewOrDownloadAttachmentAction onPreviewOrDownloadAction,
bool isDialogLoadingVisible = false,
}) {
final attachment = uploadFile.attachment;
final appLocalizations = AppLocalizations.of(context);
if (attachment == null) {
showPreviewNotAvailableToastMessage(context);
return;
}
if (PlatformInfo.isWeb && attachment.isPDFFile == true) {
_previewPDFFile(
context: context,
attachment: attachment,
accountId: accountId,
session: session,
);
return;
}
if (attachment.isEMLFile == true) {
_preparePreviewEMLFile(
appLocalizations: appLocalizations,
accountId: accountId,
session: session,
ownEmailAddress: ownEmailAddress,
blobId: attachment.blobId,
);
return;
}
if (attachment.isHTMLFile == true) {
if (uploadFile.file?.bytes != null) {
_preparePreviewHtmlFileByUploadFile(
appLocalizations: appLocalizations,
uploadFile: uploadFile,
accountId: accountId,
session: session,
isDialogLoadingVisible: isDialogLoadingVisible,
);
} else {
_preparePreviewHtmlFile(
appLocalizations: appLocalizations,
attachment: attachment,
accountId: accountId,
session: session,
isDialogLoadingVisible: isDialogLoadingVisible,
);
}
return;
}
if (attachment.isImage == true && uploadFile.file?.bytes != null) {
previewImageFile(
fileName: attachment.generateFileName(),
imageBytes: uploadFile.file!.bytes!,
context: context,
onDownloadWebFileAction: (fileName, fileBytes) => downloadFileWeb(
fileName: fileName,
fileBytes: fileBytes,
),
);
return;
}
if ((attachment.isText == true || attachment.isJson == true) &&
uploadFile.file?.bytes != null) {
previewPlainTextFile(
fileName: attachment.generateFileName(),
fileBytes: uploadFile.file!.bytes!,
context: context,
onDownloadWebFileAction: (fileName, fileBytes) => downloadFileWeb(
fileName: fileName,
fileBytes: fileBytes,
),
);
return;
}
if (uploadFile.file?.bytes != null) {
downloadFileWeb(
fileName: attachment.generateFileName(),
fileBytes: uploadFile.file!.bytes!,
);
return;
}
if (isDialogLoadingVisible) {
showDialogLoading(appLocalizations);
}
onPreviewOrDownloadAction(attachment, attachment.isPreviewSupported);
}
Future<void> _previewPDFFile({
required BuildContext context,
required Attachment attachment,
required AccountId? accountId,
required Session? session,
}) async {
final downloadUrl = session?.getSafetyDownloadUrl(
jmapUrl: dynamicUrlInterceptors.jmapUrl,
);
if (accountId == null || session == null || downloadUrl == null) {
showPreviewNotAvailableToastMessage(context);
return;
}
await Get.generalDialog(
barrierColor: Colors.black.withValues(alpha: 0.8),
pageBuilder: (_, __, ___) {
return PointerInterceptor(
child: PDFViewer(
attachment: attachment,
accountId: accountId,
downloadUrl: downloadUrl,
downloadAction: (bytes, name) =>
downloadFileWeb(fileName: name, fileBytes: bytes),
printAction: printUtils.printPDFFile,
),
);
},
);
}
void _preparePreviewEMLFile({
required AppLocalizations appLocalizations,
required AccountId? accountId,
required Session? session,
required String ownEmailAddress,
required Id? blobId,
}) {
showDialogLoading(appLocalizations);
if (session == null) {
emitFailure(
controller: this,
failure: ParseEmailByBlobIdFailure(NotFoundSessionException()),
);
return;
}
if (accountId == null) {
emitFailure(
controller: this,
failure: ParseEmailByBlobIdFailure(NotFoundAccountIdException()),
);
return;
}
if (blobId == null) {
emitFailure(
controller: this,
failure: ParseEmailByBlobIdFailure(NotFoundBlobIdException([])),
);
return;
}
consumeState(
parseEmailByBlobIdInteractor.execute(
accountId,
session,
ownEmailAddress,
blobId,
),
);
}
void _preparePreviewHtmlFile({
required AppLocalizations appLocalizations,
required Session? session,
required AccountId? accountId,
required Attachment attachment,
bool isDialogLoadingVisible = false,
DownloadSourceView? sourceView,
}) {
if (isDialogLoadingVisible) {
showDialogLoading(appLocalizations);
}
final downloadUrl = session?.getSafetyDownloadUrl(
jmapUrl: dynamicUrlInterceptors.jmapUrl,
);
final blobId = attachment.blobId;
if (session == null) {
emitFailure(
controller: this,
failure: DownloadAndGetHtmlContentFromAttachmentFailure(
exception: NotFoundSessionException(),
blobId: blobId,
sourceView: sourceView,
),
);
return;
}
if (accountId == null) {
emitFailure(
controller: this,
failure: DownloadAndGetHtmlContentFromAttachmentFailure(
exception: NotFoundAccountIdException(),
blobId: blobId,
sourceView: sourceView,
),
);
return;
}
if (blobId == null) {
emitFailure(
controller: this,
failure: DownloadAndGetHtmlContentFromAttachmentFailure(
exception: NotFoundBlobIdException([]),
blobId: blobId,
sourceView: sourceView,
),
);
return;
}
if (downloadUrl == null) {
emitFailure(
controller: this,
failure: DownloadAndGetHtmlContentFromAttachmentFailure(
exception: DownloadUrlIsNullException(),
blobId: blobId,
sourceView: sourceView,
),
);
return;
}
consumeState(
downloadAndGetHtmlContentFromAttachmentInteractor.execute(
accountId,
session,
attachment,
DownloadTaskId(blobId.value),
downloadUrl,
TransformConfiguration.create(
customDomTransformers: [SanitizeHyperLinkTagInHtmlTransformer()],
customTextTransformers: [
const StandardizeHtmlSanitizingTransformers()
],
),
sourceView: sourceView,
),
);
}
void _preparePreviewHtmlFileByUploadFile({
required AppLocalizations appLocalizations,
required UploadFileState uploadFile,
required AccountId? accountId,
required Session? session,
bool isDialogLoadingVisible = false,
}) {
if (isDialogLoadingVisible) {
showDialogLoading(appLocalizations);
}
consumeState(
getHtmlContentFromUploadFileInteractor.execute(
uploadFile: uploadFile,
accountId: accountId,
session: session,
),
);
}
void handleParseEmailByBlobIdSuccess({
required BuildContext? context,
required AccountId accountId,
required Session session,
required String ownEmailAddress,
required Id blobId,
required Email email,
}) {
if (context == null) {
emitFailure(
controller: this,
failure: PreviewEmailFromEmlFileFailure(NotFoundContextException()),
);
return;
}
final downloadUrl = session.getDownloadUrl(
jmapUrl: dynamicUrlInterceptors.jmapUrl,
);
if (downloadUrl.isEmpty) {
emitFailure(
controller: this,
failure: PreviewEmailFromEmlFileFailure(DownloadUrlIsNullException()),
);
return;
}
consumeState(
previewEmailFromEmlFileInteractor.execute(
PreviewEmailEMLRequest(
accountId: accountId,
session: session,
ownEmailAddress: ownEmailAddress,
blobId: blobId,
email: email,
locale: Localizations.localeOf(context),
appLocalizations: AppLocalizations.of(context),
baseDownloadUrl: downloadUrl,
),
),
);
}
void handleParseEmailByBlobIdFailure(ParseEmailByBlobIdFailure failure) {
closeDialogLoading();
toastManager.showMessageFailure(failure);
}
void handlePreviewEmailFromEMLFileFailure(
PreviewEmailFromEmlFileFailure failure,
) {
closeDialogLoading();
toastManager.showMessageFailure(failure);
}
void handleGetHtmlContentFromUploadFileSuccess(
GetHtmlContentFromUploadFileSuccess success,
) {
closeDialogLoading();
previewHtmlFile(
attachment: success.attachment,
title: success.htmlAttachmentTitle,
content: success.sanitizedHtmlContent,
openMailToLink: (uri) async {
if (uri == null) return;
pushDownloadUIAction(OpenComposerFromMailtoLinkAction(uri));
},
onDownloadAction: (attachment) => downloadAttachment(
attachment: attachment,
accountId: success.accountId,
session: success.session,
),
);
}
void handleDownloadAndGetHtmlContentFromAttachmentSuccess(
DownloadAndGetHtmlContentFromAttachmentSuccess success,
) {
if (success.sourceView == DownloadSourceView.emailView) {
pushDownloadUIAction(
UpdateAttachmentsViewStateAction(
success.attachment.blobId,
Right<Failure, Success>(success),
),
);
}
closeDialogLoading();
previewHtmlFile(
attachment: success.attachment,
title: success.htmlAttachmentTitle,
content: success.sanitizedHtmlContent,
openMailToLink: (uri) async {
if (uri == null) return;
pushDownloadUIAction(OpenComposerFromMailtoLinkAction(uri));
},
onDownloadAction: (attachment) => downloadAttachment(
attachment: attachment,
accountId: success.accountId,
session: success.session,
),
);
}
void handlePreviewEmailFromEmlFileSuccess(
PreviewEmailFromEmlFileSuccess success,
) {
previewEMLFile(
emlPreviewer: success.emlPreviewer,
context: currentContext,
onMailtoAction: (uri) async {
if (uri == null) return;
pushDownloadUIAction(OpenComposerFromMailtoLinkAction(uri));
},
onDownloadAction: (uri) async => downloadAttachmentInEMLPreview(
uri: uri,
onDownloadAction: (attachment) => downloadAttachment(
attachment: attachment,
accountId: success.accountId,
session: success.session,
)),
onPreviewAction: (uri) async => openEMLPreviewer(
accountId: success.accountId,
session: success.session,
ownEmailAddress: success.ownEmailAddress,
uri: uri,
appLocalizations: success.appLocalizations,
),
);
}
void previewEMLFile({
required EMLPreviewer emlPreviewer,
required BuildContext? context,
required OnMailtoDelegateAction onMailtoAction,
required OnDownloadAttachmentDelegateAction onDownloadAction,
required OnPreviewEMLDelegateAction onPreviewAction,
}) {
closeDialogLoading();
if (PlatformInfo.isWeb) {
bool isOpen = HtmlUtils.openNewWindowByUrl(
RouteUtils.createUrlWebLocationBar(
AppRoutes.emailEMLPreviewer,
previewId: emlPreviewer.id,
).toString(),
);
if (!isOpen) {
toastManager.showMessageFailure(
PreviewEmailFromEmlFileFailure(CannotOpenNewWindowException()),
);
}
} else if (PlatformInfo.isMobile) {
if (context == null) {
toastManager.showMessageFailure(
PreviewEmailFromEmlFileFailure(NotFoundContextException()),
);
return;
}
if (PlatformInfo.isAndroid) {
showModalSheetToPreviewEMLAttachment(
context: context,
emlPreviewer: emlPreviewer,
onMailtoAction: onMailtoAction,
onDownloadAction: onDownloadAction,
onPreviewAction: onPreviewAction,
);
} else if (PlatformInfo.isIOS) {
showDialogToPreviewEMLAttachment(
context: context,
emlPreviewer: emlPreviewer,
onMailtoAction: onMailtoAction,
onDownloadAction: onDownloadAction,
onPreviewAction: onPreviewAction,
);
}
}
}
void showModalSheetToPreviewEMLAttachment({
required BuildContext context,
required EMLPreviewer emlPreviewer,
required OnMailtoDelegateAction onMailtoAction,
required OnDownloadAttachmentDelegateAction onDownloadAction,
required OnPreviewEMLDelegateAction onPreviewAction,
}) {
showModalBottomSheet(
context: context,
showDragHandle: true,
useSafeArea: true,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0),
topRight: Radius.circular(20.0),
),
),
builder: (_) {
return DraggableScrollableSheet(
initialChildSize: 1.0,
builder: (context, ___) => EmailPreviewerDialogView(
emlPreviewer: emlPreviewer,
imagePaths: imagePaths,
onMailtoDelegateAction: onMailtoAction,
onPreviewEMLDelegateAction: onPreviewAction,
onDownloadAttachmentDelegateAction: onDownloadAction,
),
);
},
);
}
void showDialogToPreviewEMLAttachment({
required BuildContext context,
required EMLPreviewer emlPreviewer,
required OnMailtoDelegateAction onMailtoAction,
required OnDownloadAttachmentDelegateAction onDownloadAction,
required OnPreviewEMLDelegateAction onPreviewAction,
}) {
Get.dialog(
EmailPreviewerDialogView(
emlPreviewer: emlPreviewer,
imagePaths: imagePaths,
onMailtoDelegateAction: onMailtoAction,
onPreviewEMLDelegateAction: onPreviewAction,
onDownloadAttachmentDelegateAction: onDownloadAction,
),
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
);
}
void handlePreviewHtmlFileFailure({required Failure failureState}) {
if (failureState is DownloadAndGetHtmlContentFromAttachmentFailure &&
failureState.sourceView == DownloadSourceView.emailView) {
pushDownloadUIAction(
UpdateAttachmentsViewStateAction(
failureState.blobId,
Left<Failure, Success>(failureState),
),
);
}
closeDialogLoading();
if (currentOverlayContext != null && currentContext != null) {
appToast.showToastErrorMessage(
currentOverlayContext!,
AppLocalizations.of(currentContext!)
.thisHtmlAttachmentCannotBePreviewed,
);
}
}
void previewImageFile({
required String fileName,
required Uint8List imageBytes,
required BuildContext? context,
required OnDownloadWebFileAction onDownloadWebFileAction,
}) {
log('$runtimeType::previewImageFile: Attachment fileName is $fileName');
if (context == null) return;
Navigator.of(context).push(
GetDialogRoute(
pageBuilder: (context, _, __) => PointerInterceptor(
child: TwakeImagePreviewer(
bytes: imageBytes,
zoomable: true,
previewerOptions: const PreviewerOptions(
previewerState: PreviewerState.success,
),
topBarOptions: TopBarOptions(
title: fileName,
onClose: () => Navigator.maybePop(context),
onDownload: () => onDownloadWebFileAction(fileName, imageBytes),
),
),
),
barrierDismissible: false,
),
);
}
void previewPlainTextFile({
required String fileName,
required Uint8List fileBytes,
required BuildContext? context,
required OnDownloadWebFileAction onDownloadWebFileAction,
}) {
log('$runtimeType::previewPlainTextFile: Attachment fileName is $fileName');
if (context == null) return;
Navigator.of(context).push(
GetDialogRoute(
pageBuilder: (context, _, __) => PointerInterceptor(
child: TwakePlainTextPreviewer(
supportedCharset: SupportedCharset.utf8,
bytes: fileBytes,
previewerOptions: PreviewerOptions(
previewerState: PreviewerState.success,
width: context.width * 0.8,
),
topBarOptions: TopBarOptions(
title: fileName,
onClose: () => Navigator.maybePop(context),
onDownload: () => onDownloadWebFileAction(fileName, fileBytes),
),
),
),
barrierDismissible: false,
),
);
}
void previewHtmlFile({
required Attachment attachment,
required String title,
required String content,
required OnMailtoDelegateAction openMailToLink,
required OnDownloadAttachmentFileAction onDownloadAction,
}) {
Get.dialog(
HtmlAttachmentPreviewer(
title: title,
htmlContent: content,
mailToClicked: openMailToLink,
downloadAttachmentClicked: () => onDownloadAction(attachment),
responsiveUtils: responsiveUtils,
),
);
}
Future<void> openEMLPreviewer({
required AppLocalizations appLocalizations,
required Uri? uri,
required AccountId? accountId,
required Session? session,
required String ownEmailAddress,
}) async {
if (uri == null) return;
final blobId = uri.path;
if (blobId.isEmpty) return;
_preparePreviewEMLFile(
appLocalizations: appLocalizations,
accountId: accountId,
session: session,
ownEmailAddress: ownEmailAddress,
blobId: Id(blobId),
);
}
void showDialogLoading(AppLocalizations appLocalizations) {
SmartDialog.showLoading(
msg: appLocalizations.loadingPleaseWait,
maskColor: Colors.black38,
);
}
void closeDialogLoading() {
if (SmartDialog.checkExist()) {
SmartDialog.dismiss();
}
}
void showPreviewNotAvailableToastMessage(BuildContext context) {
appToast.showToastErrorMessage(
context,
AppLocalizations.of(context).noPreviewAvailable,
);
}
}
+15 -8
View File
@@ -57,11 +57,12 @@ import 'package:tmail_ui_user/features/base/mixin/handle_error_mixin.dart';
import 'package:tmail_ui_user/features/base/mixin/mail_api_mixin.dart'; import 'package:tmail_ui_user/features/base/mixin/mail_api_mixin.dart';
import 'package:tmail_ui_user/features/composer/domain/exceptions/set_method_exception.dart'; import 'package:tmail_ui_user/features/composer/domain/exceptions/set_method_exception.dart';
import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart'; import 'package:tmail_ui_user/features/composer/domain/model/email_request.dart';
import 'package:tmail_ui_user/features/download/domain/model/download_source_view.dart';
import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart'; import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart';
import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_to_mailbox_request.dart';
import 'package:tmail_ui_user/features/email/domain/model/restore_deleted_message_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/restore_deleted_message_request.dart';
import 'package:tmail_ui_user/features/email/domain/state/download_all_attachments_for_web_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/download_all_attachments_for_web_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/download_attachment_for_web_state.dart';
import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart'; import 'package:tmail_ui_user/features/mailbox/domain/model/create_new_mailbox_request.dart';
import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart'; import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart';
import 'package:tmail_ui_user/main/error/capability_validator.dart'; import 'package:tmail_ui_user/main/error/capability_validator.dart';
@@ -343,12 +344,18 @@ class EmailAPI with HandleSetErrorMixin, MailAPIMixin {
progress = (downloaded / total) * 100; progress = (downloaded / total) * 100;
} }
log('EmailAPI::downloadFileForWeb(): progress = ${progress.round()}%'); log('EmailAPI::downloadFileForWeb(): progress = ${progress.round()}%');
onReceiveController?.add(Right(DownloadingAttachmentForWeb( onReceiveController?.add(
taskId, Right(
attachment, DownloadingAttachmentForWeb(
progress, taskId,
downloaded, attachment,
total))); progress,
downloaded,
total,
DownloadSourceView.emailView,
),
),
);
} }
); );
@@ -1,7 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:core/data/model/source_type/data_source_type.dart'; import 'package:core/data/model/source_type/data_source_type.dart';
import 'package:core/data/network/download/downloaded_response.dart';
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart'; import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
import 'package:core/utils/app_logger.dart'; import 'package:core/utils/app_logger.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
@@ -14,8 +13,6 @@ 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/session/session.dart';
import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; import 'package:jmap_dart_client/jmap/core/state.dart' as jmap;
import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/account/account_request.dart';
import 'package:model/email/attachment.dart';
import 'package:model/email/email_content.dart'; import 'package:model/email/email_content.dart';
import 'package:model/email/mark_star_action.dart'; import 'package:model/email/mark_star_action.dart';
import 'package:model/email/read_actions.dart'; import 'package:model/email/read_actions.dart';
@@ -135,22 +132,6 @@ class EmailRepositoryImpl extends EmailRepository {
return result; return result;
} }
@override
Future<DownloadedResponse> exportAttachment(
Attachment attachment,
AccountId accountId,
String baseDownloadUrl,
AccountRequest accountRequest,
CancelToken cancelToken
) {
return emailDataSource[DataSourceType.network]!.exportAttachment(
attachment,
accountId,
baseDownloadUrl,
accountRequest,
cancelToken);
}
@override @override
Future<({ Future<({
List<EmailId> emailIdsSuccess, List<EmailId> emailIdsSuccess,
@@ -494,22 +475,6 @@ class EmailRepositoryImpl extends EmailRepository {
return _printFileDataSource.printEmail(emailPrint); return _printFileDataSource.printEmail(emailPrint);
} }
@override
Future<DownloadedResponse> exportAllAttachments(
AccountId accountId,
EmailId emailId,
String baseDownloadAllUrl,
String outputFileName,
AccountRequest accountRequest, {
CancelToken? cancelToken,
}) => emailDataSource[DataSourceType.network]!.exportAllAttachments(
accountId,
emailId,
baseDownloadAllUrl,
outputFileName,
accountRequest,
);
@override @override
Future<String> generateEntireMessageAsDocument(ViewEntireMessageRequest entireMessageRequest) { Future<String> generateEntireMessageAsDocument(ViewEntireMessageRequest entireMessageRequest) {
return emailDataSource[DataSourceType.local]!.generateEntireMessageAsDocument(entireMessageRequest); return emailDataSource[DataSourceType.local]!.generateEntireMessageAsDocument(entireMessageRequest);
@@ -1 +0,0 @@
class DownloadAttachmentInteractorIsNull implements Exception {}
@@ -1,6 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:core/data/network/download/downloaded_response.dart';
import 'package:core/presentation/utils/html_transformer/transform_configuration.dart'; import 'package:core/presentation/utils/html_transformer/transform_configuration.dart';
import 'package:dio/dio.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.dart';
@@ -12,8 +11,6 @@ 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/session/session.dart';
import 'package:jmap_dart_client/jmap/core/state.dart' as jmap; import 'package:jmap_dart_client/jmap/core/state.dart' as jmap;
import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/account/account_request.dart';
import 'package:model/email/attachment.dart';
import 'package:model/email/email_content.dart'; import 'package:model/email/email_content.dart';
import 'package:model/email/mark_star_action.dart'; import 'package:model/email/mark_star_action.dart';
import 'package:model/email/read_actions.dart'; import 'package:model/email/read_actions.dart';
@@ -52,14 +49,6 @@ abstract class EmailRepository {
ReadActions readActions, ReadActions readActions,
); );
Future<DownloadedResponse> exportAttachment(
Attachment attachment,
AccountId accountId,
String baseDownloadUrl,
AccountRequest accountRequest,
CancelToken cancelToken
);
Future<({ Future<({
List<EmailId> emailIdsSuccess, List<EmailId> emailIdsSuccess,
Map<Id, SetError> mapErrors, Map<Id, SetError> mapErrors,
@@ -170,14 +159,5 @@ abstract class EmailRepository {
Future<void> printEmail(EmailPrint emailPrint); Future<void> printEmail(EmailPrint emailPrint);
Future<DownloadedResponse> exportAllAttachments(
AccountId accountId,
EmailId emailId,
String baseDownloadAllUrl,
String outputFileName,
AccountRequest accountRequest,
{CancelToken? cancelToken}
);
Future<String> generateEntireMessageAsDocument(ViewEntireMessageRequest entireMessageRequest); Future<String> generateEntireMessageAsDocument(ViewEntireMessageRequest entireMessageRequest);
} }
@@ -1,40 +0,0 @@
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:model/email/attachment.dart';
class GettingHtmlContentFromAttachment extends LoadingState {
GettingHtmlContentFromAttachment({required this.attachment});
final Attachment attachment;
@override
List<Object?> get props => [attachment];
}
class GetHtmlContentFromAttachmentSuccess extends UIState {
GetHtmlContentFromAttachmentSuccess({
required this.sanitizedHtmlContent,
required this.htmlAttachmentTitle,
required this.attachment,
});
final String sanitizedHtmlContent;
final String htmlAttachmentTitle;
final Attachment attachment;
@override
List<Object?> get props => [
sanitizedHtmlContent,
htmlAttachmentTitle,
attachment,
];
}
class GetHtmlContentFromAttachmentFailure extends FeatureFailure {
GetHtmlContentFromAttachmentFailure({super.exception, required this.attachment});
final Attachment attachment;
@override
List<Object?> get props => [attachment];
}
@@ -1,7 +1,5 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/export_all_attachments_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/export_attachment_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_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/mark_as_star_email_interactor.dart';
@@ -22,12 +20,10 @@ class EmailBindings extends Bindings {
Get.put(SingleEmailController( Get.put(SingleEmailController(
Get.find<GetEmailContentInteractor>(), Get.find<GetEmailContentInteractor>(),
Get.find<MarkAsEmailReadInteractor>(), Get.find<MarkAsEmailReadInteractor>(),
Get.find<ExportAttachmentInteractor>(),
Get.find<MarkAsStarEmailInteractor>(), Get.find<MarkAsStarEmailInteractor>(),
Get.find<GetAllIdentitiesInteractor>(), Get.find<GetAllIdentitiesInteractor>(),
Get.find<StoreOpenedEmailInteractor>(), Get.find<StoreOpenedEmailInteractor>(),
Get.find<PrintEmailInteractor>(), Get.find<PrintEmailInteractor>(),
Get.find<ExportAllAttachmentsInteractor>(),
currentEmailId: currentEmailId, currentEmailId: currentEmailId,
), tag: tag); ), tag: tag);
} }
@@ -16,8 +16,6 @@ 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/network/email_api.dart';
import 'package:tmail_ui_user/features/email/data/repository/email_repository_impl.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/repository/email_repository.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/export_all_attachments_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/export_attachment_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_entire_message_as_document_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_entire_message_as_document_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_stored_email_state_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_stored_email_state_interactor.dart';
@@ -26,9 +24,6 @@ import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_star_email_
import 'package:tmail_ui_user/features/email/domain/usecases/move_to_mailbox_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/print_email_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/print_email_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/store_opened_email_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/store_opened_email_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/repository/account_repository.dart';
import 'package:tmail_ui_user/features/login/domain/repository/authentication_oidc_repository.dart';
import 'package:tmail_ui_user/features/login/domain/repository/credential_repository.dart';
import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/mailbox_datasource.dart';
import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource/state_datasource.dart';
import 'package:tmail_ui_user/features/mailbox/data/datasource_impl/mailbox_cache_datasource_impl.dart'; import 'package:tmail_ui_user/features/mailbox/data/datasource_impl/mailbox_cache_datasource_impl.dart';
@@ -111,23 +106,12 @@ class EmailInteractorBindings extends InteractorsBindings {
void bindingsInteractor() { void bindingsInteractor() {
Get.lazyPut(() => GetEmailContentInteractor(Get.find<EmailRepository>())); Get.lazyPut(() => GetEmailContentInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => MarkAsEmailReadInteractor(Get.find<EmailRepository>())); Get.lazyPut(() => MarkAsEmailReadInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => ExportAttachmentInteractor(
Get.find<EmailRepository>(),
Get.find<CredentialRepository>(),
Get.find<AccountRepository>(),
Get.find<AuthenticationOIDCRepository>(),
));
Get.lazyPut(() => MoveToMailboxInteractor(Get.find<EmailRepository>())); Get.lazyPut(() => MoveToMailboxInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => MarkAsStarEmailInteractor(Get.find<EmailRepository>())); Get.lazyPut(() => MarkAsStarEmailInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => GetStoredEmailStateInteractor(Get.find<EmailRepository>())); Get.lazyPut(() => GetStoredEmailStateInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => StoreOpenedEmailInteractor(Get.find<EmailRepository>())); Get.lazyPut(() => StoreOpenedEmailInteractor(Get.find<EmailRepository>()));
Get.lazyPut(() => PrintEmailInteractor(Get.find<EmailRepository>())); Get.lazyPut(() => PrintEmailInteractor(Get.find<EmailRepository>()));
IdentityInteractorsBindings().dependencies(); IdentityInteractorsBindings().dependencies();
Get.lazyPut(() => ExportAllAttachmentsInteractor(
Get.find<EmailRepository>(),
Get.find<AccountRepository>(),
Get.find<AuthenticationOIDCRepository>(),
Get.find<CredentialRepository>()));
if (PlatformInfo.isIOS) { if (PlatformInfo.isIOS) {
Get.lazyPut(() => GetEntireMessageAsDocumentInteractor( Get.lazyPut(() => GetEntireMessageAsDocumentInteractor(
Get.find<EmailRepository>(), Get.find<EmailRepository>(),
@@ -1,13 +1,10 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:core/core.dart'; import 'package:core/core.dart';
import 'package:core/presentation/utils/html_transformer/text/new_line_transformer.dart'; import 'package:core/presentation/utils/html_transformer/text/new_line_transformer.dart';
import 'package:core/presentation/utils/html_transformer/text/sanitize_autolink_unescape_html_transformer.dart'; import 'package:core/presentation/utils/html_transformer/text/sanitize_autolink_unescape_html_transformer.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_file_dialog/flutter_file_dialog.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/account_id.dart';
@@ -26,12 +23,12 @@ 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:model/error_type_handler/unknown_uri_exception.dart'; import 'package:model/error_type_handler/unknown_uri_exception.dart';
import 'package:model/model.dart'; import 'package:model/model.dart';
import 'package:open_file/open_file.dart' as open_file;
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart'; 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/base/mixin/message_dialog_action_manager.dart'; import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_manager.dart';
import 'package:tmail_ui_user/features/base/state/button_state.dart'; import 'package:tmail_ui_user/features/base/state/button_state.dart';
import 'package:tmail_ui_user/features/download/domain/model/download_source_view.dart';
import 'package:tmail_ui_user/features/download/domain/state/download_attachment_for_web_state.dart';
import 'package:tmail_ui_user/features/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/event_action.dart'; import 'package:tmail_ui_user/features/email/domain/model/event_action.dart';
@@ -43,9 +40,6 @@ import 'package:tmail_ui_user/features/email/domain/state/calendar_event_counter
import 'package:tmail_ui_user/features/email/domain/state/calendar_event_maybe_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/calendar_event_maybe_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/calendar_event_reject_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/calendar_event_reject_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/calendar_event_reply_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/calendar_event_reply_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/export_all_attachments_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/export_attachment_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/get_email_content_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_entire_message_as_document_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/get_entire_message_as_document_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_read_state.dart';
@@ -57,8 +51,6 @@ import 'package:tmail_ui_user/features/email/domain/state/unsubscribe_email_stat
import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_accept_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_accept_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_counter_accept_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_counter_accept_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_reject_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_reject_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/export_all_attachments_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/export_attachment_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_entire_message_as_document_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_entire_message_as_document_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart';
@@ -71,7 +63,6 @@ import 'package:tmail_ui_user/features/email/domain/usecases/store_opened_email_
import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart'; import 'package:tmail_ui_user/features/email/presentation/action/email_ui_action.dart';
import 'package:tmail_ui_user/features/email/presentation/bindings/calendar_event_interactor_bindings.dart'; import 'package:tmail_ui_user/features/email/presentation/bindings/calendar_event_interactor_bindings.dart';
import 'package:tmail_ui_user/features/email/presentation/bindings/mdn_interactor_bindings.dart'; import 'package:tmail_ui_user/features/email/presentation/bindings/mdn_interactor_bindings.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/attachment_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/calendar_attendee_extension.dart'; import 'package:tmail_ui_user/features/email/presentation/extensions/calendar_attendee_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/calendar_organizer_extension.dart'; import 'package:tmail_ui_user/features/email/presentation/extensions/calendar_organizer_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/handle_mail_action_by_shortcut_action_extension.dart'; import 'package:tmail_ui_user/features/email/presentation/extensions/handle_mail_action_by_shortcut_action_extension.dart';
@@ -84,12 +75,11 @@ import 'package:tmail_ui_user/features/email/presentation/model/email_unsubscrib
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart';
import 'package:tmail_ui_user/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart'; import 'package:tmail_ui_user/features/email/presentation/utils/email_action_reactor/email_action_reactor.dart';
import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart'; import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart';
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart'; import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/action/mailbox_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/action/mailbox_ui_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_download_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_download_attachment_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_preview_attachment_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_preview_attachment_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/update_current_emails_flags_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/update_current_emails_flags_extension.dart';
@@ -121,12 +111,10 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
final GetEmailContentInteractor _getEmailContentInteractor; final GetEmailContentInteractor _getEmailContentInteractor;
final MarkAsEmailReadInteractor _markAsEmailReadInteractor; final MarkAsEmailReadInteractor _markAsEmailReadInteractor;
final ExportAttachmentInteractor _exportAttachmentInteractor;
final MarkAsStarEmailInteractor _markAsStarEmailInteractor; final MarkAsStarEmailInteractor _markAsStarEmailInteractor;
final GetAllIdentitiesInteractor _getAllIdentitiesInteractor; final GetAllIdentitiesInteractor _getAllIdentitiesInteractor;
final StoreOpenedEmailInteractor _storeOpenedEmailInteractor; final StoreOpenedEmailInteractor _storeOpenedEmailInteractor;
final PrintEmailInteractor _printEmailInteractor; final PrintEmailInteractor _printEmailInteractor;
final ExportAllAttachmentsInteractor _exportAllAttachmentsInteractor;
final EmailId? _currentEmailId; final EmailId? _currentEmailId;
CreateNewEmailRuleFilterInteractor? _createNewEmailRuleFilterInteractor; CreateNewEmailRuleFilterInteractor? _createNewEmailRuleFilterInteractor;
@@ -193,12 +181,10 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
SingleEmailController( SingleEmailController(
this._getEmailContentInteractor, this._getEmailContentInteractor,
this._markAsEmailReadInteractor, this._markAsEmailReadInteractor,
this._exportAttachmentInteractor,
this._markAsStarEmailInteractor, this._markAsStarEmailInteractor,
this._getAllIdentitiesInteractor, this._getAllIdentitiesInteractor,
this._storeOpenedEmailInteractor, this._storeOpenedEmailInteractor,
this._printEmailInteractor, this._printEmailInteractor, {
this._exportAllAttachmentsInteractor, {
EmailId? currentEmailId, EmailId? currentEmailId,
}) : _currentEmailId = currentEmailId; }) : _currentEmailId = currentEmailId;
@@ -236,10 +222,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
_getEmailContentOfflineSuccess(success); _getEmailContentOfflineSuccess(success);
} else if (success is MarkAsEmailReadSuccess) { } else if (success is MarkAsEmailReadSuccess) {
_handleMarkAsEmailReadCompleted(success); _handleMarkAsEmailReadCompleted(success);
} else if (success is ExportAttachmentSuccess) {
_exportAttachmentSuccessAction(success);
} else if (success is ExportAllAttachmentsSuccess) {
_exportAllAttachmentsSuccessAction(success);
} else if (success is MarkAsStarEmailSuccess) { } else if (success is MarkAsStarEmailSuccess) {
_markAsEmailStarSuccess(success); _markAsEmailStarSuccess(success);
} else if (success is GetAllIdentitiesSuccess) { } else if (success is GetAllIdentitiesSuccess) {
@@ -267,10 +249,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
void handleFailureViewState(Failure failure) { void handleFailureViewState(Failure failure) {
if (failure is MarkAsEmailReadFailure) { if (failure is MarkAsEmailReadFailure) {
_handleMarkAsEmailReadFailure(failure); _handleMarkAsEmailReadFailure(failure);
} else if (failure is ExportAttachmentFailure) {
_exportAttachmentFailureAction(failure);
} else if (failure is ExportAllAttachmentsFailure) {
_exportAllAttachmentsFailureAction(failure);
} else if (failure is ParseCalendarEventFailure) { } else if (failure is ParseCalendarEventFailure) {
_handleParseCalendarEventFailure(failure); _handleParseCalendarEventFailure(failure);
} else if (failure is GetEmailContentFailure) { } else if (failure is GetEmailContentFailure) {
@@ -697,173 +675,6 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
_threadDetailController?.markCollapsedEmailReadSuccess(success); _threadDetailController?.markCollapsedEmailReadSuccess(success);
} }
void exportAttachment(Attachment attachment) {
final cancelToken = CancelToken();
_showDownloadingFileDialog(attachment.name ?? '', cancelToken: cancelToken);
_exportAttachmentAction(attachment, cancelToken);
}
void _showDownloadingFileDialog(String attachmentName, {CancelToken? cancelToken}) {
if (cancelToken != null) {
Get.dialog(
PointerInterceptor(
child: Builder(
builder: (context) {
final appLocalizations = AppLocalizations.of(context);
return (DownloadingFileDialogBuilder()
..key(const Key('downloading_file_dialog'))
..title(appLocalizations.preparing_to_export)
..content(appLocalizations.downloading_file(attachmentName))
..actionText(appLocalizations.cancel)
..addCancelDownloadActionClick(() {
cancelToken.cancel([
appLocalizations.user_cancel_download_file,
]);
popBack();
}))
.build();
},
),
),
barrierDismissible: false,
);
} else {
Get.dialog(
PointerInterceptor(
child: Builder(
builder: (context) {
final appLocalizations = AppLocalizations.of(context);
return (DownloadingFileDialogBuilder()
..key(const Key('downloading_file_for_web_dialog'))
..title(appLocalizations.preparing_to_save)
..content(
appLocalizations.downloading_file(attachmentName),
))
.build();
},
),
),
);
}
}
void _exportAttachmentAction(Attachment attachment, CancelToken cancelToken) {
if (accountId != null && session != null) {
try {
final baseDownloadUrl = session!.getDownloadUrl(
jmapUrl: dynamicUrlInterceptors.jmapUrl,
);
consumeState(_exportAttachmentInteractor.execute(
attachment,
accountId!,
baseDownloadUrl,
cancelToken,
));
} catch (e) {
logError('SingleEmailController::_exportAttachmentAction(): $e');
consumeState(Stream.value(Left(ExportAttachmentFailure(e))));
}
} else {
consumeState(Stream.value(Left(ExportAttachmentFailure(NotFoundAccountIdException()))));
}
}
void exportAllAttachments(String outputFileName) {
final cancelToken = CancelToken();
_showDownloadingFileDialog(outputFileName, cancelToken: cancelToken);
_exportAllAttachmentsAction(outputFileName, cancelToken);
}
void _exportAllAttachmentsAction(String outputFileName, CancelToken cancelToken) {
if (accountId == null || session == null) {
consumeState(Stream.value(Left(ExportAllAttachmentsFailure(
exception: NotFoundSessionException(),
))));
return;
}
final downloadAllSupported = session!.isDownloadAllSupported(accountId);
if (!downloadAllSupported) {
consumeState(Stream.value(Left(ExportAllAttachmentsFailure())));
return;
}
final baseDownloadAllUrl = session!.getDownloadAllCapability(accountId)!.endpoint!;
consumeState(_exportAllAttachmentsInteractor.execute(
accountId!,
currentEmail!.id!,
baseDownloadAllUrl,
outputFileName,
cancelToken: cancelToken,
));
}
void _exportAttachmentFailureAction(ExportAttachmentFailure failure) {
if (failure.exception is! CancelDownloadFileException) {
if (Get.isDialogOpen == true) {
popBack();
}
if (currentOverlayContext != null && currentContext != null) {
appToast.showToastErrorMessage(
currentOverlayContext!,
AppLocalizations.of(currentContext!).attachment_download_failed);
}
}
}
void _exportAttachmentSuccessAction(ExportAttachmentSuccess success) async {
popBack();
_openDownloadedPreviewWorkGroupDocument(success.downloadedResponse);
}
void _exportAllAttachmentsFailureAction(ExportAllAttachmentsFailure failure) {
if (failure.exception is! CancelDownloadFileException) {
popBack();
if (currentOverlayContext != null && currentContext != null) {
appToast.showToastErrorMessage(
currentOverlayContext!,
AppLocalizations.of(currentContext!).attachment_download_failed);
}
}
}
void _exportAllAttachmentsSuccessAction(ExportAllAttachmentsSuccess success) {
popBack();
_saveFileToStorage(success.downloadedResponse.filePath);
}
void _openDownloadedPreviewWorkGroupDocument(DownloadedResponse downloadedResponse) async {
log('SingleEmailController::_openDownloadedPreviewWorkGroupDocument(): $downloadedResponse');
final filePath = downloadedResponse.filePath;
final mediaType = downloadedResponse.mediaType;
if (mediaType == null) {
_saveFileToStorage(filePath);
return;
}
final openResult = await open_file.OpenFile.open(
filePath,
type: Platform.isAndroid ? mediaType.mimeType : null,
// "xdg" is default value
linuxDesktopName: Platform.isIOS
? mediaType.getDocumentUti().value ?? 'xdg'
: 'xdg',
);
if (openResult.type != open_file.ResultType.done) {
logError('SingleEmailController::_openDownloadedPreviewWorkGroupDocument(): no preview available');
_saveFileToStorage(filePath);
}
}
Future<void> _saveFileToStorage(String filePath) async {
final params = SaveFileDialogParams(sourceFilePath: filePath);
await FlutterFileDialog.saveFile(params: params);
}
bool isDownloadAllSupported() { bool isDownloadAllSupported() {
return session?.isDownloadAllSupported(accountId) ?? false; return session?.isDownloadAllSupported(accountId) ?? false;
} }
@@ -1059,7 +870,10 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
_printEmail(presentationEmail); _printEmail(presentationEmail);
break; break;
case EmailActionType.downloadMessageAsEML: case EmailActionType.downloadMessageAsEML:
mailboxDashBoardController.downloadMessageAsEML(presentationEmail); mailboxDashBoardController.downloadMessageAsEML(
presentationEmail: presentationEmail,
showBottomDownloadProgressBar: true,
);
break; break;
case EmailActionType.editAsNewEmail: case EmailActionType.editAsNewEmail:
_editAsNewEmail(presentationEmail); _editAsNewEmail(presentationEmail);
@@ -1100,8 +914,10 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
); );
} }
Future<void> openMailToLink(Uri? uri) => void openMailToLink(Uri? uri) {
mailboxDashBoardController.openMailToLink(uri); if (uri == null) return;
mailboxDashBoardController.openComposerFromMailToLink(uri);
}
void deleteEmailPermanently(PresentationEmail email) { void deleteEmailPermanently(PresentationEmail email) {
emailActionReactor.deleteEmailPermanently( emailActionReactor.deleteEmailPermanently(
@@ -1618,68 +1434,36 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
Attachment attachment, Attachment attachment,
{bool previewerSupported = false} {bool previewerSupported = false}
) { ) {
if (PlatformInfo.isWeb) { mailboxDashBoardController.downloadAttachment(
mailboxDashBoardController.downloadAttachmentForWeb( attachment: attachment,
attachment: attachment, previewerSupported: previewerSupported,
previewerSupported: previewerSupported, showBottomDownloadProgressBar: true,
); sourceView: DownloadSourceView.emailView,
} else if (PlatformInfo.isMobile) { );
exportAttachment(attachment);
} else {
log('$runtimeType::handleDownloadAttachmentAction: THE PLATFORM IS SUPPORTED');
}
} }
void handleDownloadAllAttachmentsAction(String outputFileName) { void handleDownloadAllAttachmentsAction(String outputFileName) {
if (PlatformInfo.isWeb) { mailboxDashBoardController.downloadAllAttachments(
mailboxDashBoardController.downloadAllAttachmentsForWeb( outputFileName: outputFileName,
outputFileName: outputFileName, emailId: currentEmail?.id,
currentEmail: currentEmail, showBottomDownloadProgressBar: true,
); );
} else if (PlatformInfo.isMobile) {
exportAllAttachments(outputFileName);
} else {
log('EmailView::handleDownloadAllAttachmentsAction: THE PLATFORM IS SUPPORTED');
}
} }
void handleViewAttachmentAction(BuildContext context, Attachment attachment) { void handleViewAttachmentAction(BuildContext context, Attachment attachment) {
mailboxDashBoardController.previewAttachmentAction( mailboxDashBoardController.previewAttachment(
context: context, context: context,
attachment: attachment, attachment: attachment,
onDownloadAttachment: (attachment) { sourceView: DownloadSourceView.emailView,
onPreviewOrDownloadAction: (attachment, isPreview) {
handleDownloadAttachmentAction( handleDownloadAttachmentAction(
attachment, attachment,
previewerSupported: attachment.isPreviewSupported, previewerSupported: isPreview,
); );
}, },
); );
} }
Future<void> _openEMLPreviewer(BuildContext context, Uri? uri) async {
log('SingleEmailController::_openEMLPreviewer:uri = $uri');
if (uri == null) return;
final blobId = uri.path;
log('SingleEmailController::_openEMLPreviewer:blobId = $blobId');
if (blobId.isEmpty) return;
mailboxDashBoardController.previewEMLFileAction(
appLocalizations: AppLocalizations.of(context),
blobId: Id(blobId),
);
}
Future<void> _downloadAttachmentInEMLPreview(Uri? uri) async {
log('SingleEmailController::_downloadAttachmentInEMLPreview:uri = $uri');
if (uri == null) return;
final attachment = EmailUtils.parsingAttachmentByUri(uri);
if (attachment == null) return;
handleDownloadAttachmentAction(attachment);
}
void handleMailToAttendees(CalendarOrganizer? organizer, List<CalendarAttendee>? attendees) { void handleMailToAttendees(CalendarOrganizer? organizer, List<CalendarAttendee>? attendees) {
List<EmailAddress> listEmailAddressAttendees = []; List<EmailAddress> listEmailAddressAttendees = [];
@@ -1745,9 +1529,8 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
content: success.messageDocument, content: success.messageDocument,
), ),
imagePaths: imagePaths, imagePaths: imagePaths,
onMailtoAction: openMailToLink, onMailtoAction: (uri) async => openMailToLink(uri),
onPreviewAction: (uri) => _openEMLPreviewer(context, uri), onDownloadFileAction: handleDownloadAttachmentAction,
onDownloadAction: _downloadAttachmentInEMLPreview,
); );
}, },
onFailure: (_) { onFailure: (_) {
@@ -336,7 +336,7 @@ class EmailView extends GetWidget<SingleEmailController> {
Obx(() => CalendarEventDetailWidget( Obx(() => CalendarEventDetailWidget(
calendarEvent: calendarEvent, calendarEvent: calendarEvent,
emailContent: controller.currentEmailLoaded.value?.htmlContent ?? '', emailContent: controller.currentEmailLoaded.value?.htmlContent ?? '',
onMailtoDelegateAction: controller.openMailToLink, onMailtoDelegateAction: (uri) async => controller.openMailToLink(uri),
presentationEmail: controller.currentEmail, presentationEmail: controller.currentEmail,
scrollController: scrollController, scrollController: scrollController,
isInsideThreadDetailView: isInsideThreadDetailView, isInsideThreadDetailView: isInsideThreadDetailView,
@@ -385,7 +385,7 @@ class EmailView extends GetWidget<SingleEmailController> {
contentPadding: 0, contentPadding: 0,
useDefaultFontStyle: true, useDefaultFontStyle: true,
maxHtmlContentHeight: ConstantsUI.htmlContentMaxHeight, maxHtmlContentHeight: ConstantsUI.htmlContentMaxHeight,
onMailtoDelegateAction: controller.openMailToLink, onMailtoDelegateAction: (uri) async => controller.openMailToLink(uri),
onHtmlContentClippedAction: controller.onHtmlContentClippedAction, onHtmlContentClippedAction: controller.onHtmlContentClippedAction,
onScrollHorizontalEnd: controller.onScrollHorizontalEnd, onScrollHorizontalEnd: controller.onScrollHorizontalEnd,
keepAlive: isInsideThreadDetailView, keepAlive: isInsideThreadDetailView,
@@ -423,7 +423,7 @@ class EmailView extends GetWidget<SingleEmailController> {
direction: AppUtils.getCurrentDirection(context), direction: AppUtils.getCurrentDirection(context),
contentPadding: 0, contentPadding: 0,
useDefaultFontStyle: true, useDefaultFontStyle: true,
onMailtoDelegateAction: controller.openMailToLink, onMailtoDelegateAction: (uri) async => controller.openMailToLink(uri),
keepAlive: isInsideThreadDetailView, keepAlive: isInsideThreadDetailView,
enableQuoteToggle: true, enableQuoteToggle: true,
onScrollHorizontalEnd: controller.onScrollHorizontalEnd, onScrollHorizontalEnd: controller.onScrollHorizontalEnd,
@@ -474,8 +474,7 @@ class EmailView extends GetWidget<SingleEmailController> {
), ),
onTapShowAllAttachmentFile: controller.showAllAttachmentsAction, onTapShowAllAttachmentFile: controller.showAllAttachmentsAction,
onTapHideAllAttachments: controller.hideAllAttachmentsAction, onTapHideAllAttachments: controller.hideAllAttachmentsAction,
showDownloadAllAttachmentsButton: showDownloadAllAttachmentsButton: controller.downloadAllButtonIsEnabled(),
controller.downloadAllButtonIsEnabled(),
onTapDownloadAllButton: () => onTapDownloadAllButton: () =>
controller.handleDownloadAllAttachmentsAction( controller.handleDownloadAllAttachmentsAction(
'TwakeMail-${DateTime.now()}', 'TwakeMail-${DateTime.now()}',
@@ -1,444 +0,0 @@
import 'dart:typed_data';
import 'package:core/core.dart';
import 'package:dartz/dartz.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/download/download_task_id.dart';
import 'package:model/email/attachment.dart';
import 'package:model/extensions/session_extension.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart';
import 'package:tmail_ui_user/features/email/domain/model/preview_email_eml_request.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_html_content_from_attachment_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/parse_email_by_blob_id_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/preview_email_from_eml_file_state.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_html_content_from_attachment_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/parse_email_by_blob_id_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/preview_email_from_eml_file_interactor.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/attachment_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/pdf_viewer/pdf_viewer.dart';
import 'package:tmail_ui_user/features/email_previewer/email_previewer_dialog_view.dart';
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
import 'package:tmail_ui_user/main/routes/route_utils.dart';
import 'package:tmail_ui_user/main/utils/toast_manager.dart';
typedef OnDownloadAttachmentAction = void Function(Attachment attachment);
mixin PreviewAttachmentMixin {
final DownloadManager _downloadManager = Get.find<DownloadManager>();
final PrintUtils _printUtils = Get.find<PrintUtils>();
final AppToast _appToast = Get.find<AppToast>();
final ToastManager _toastManager = Get.find<ToastManager>();
final DynamicUrlInterceptors _dynamicUrlInterceptors =
Get.find<DynamicUrlInterceptors>();
void previewAttachmentAction({
required BuildContext context,
required Attachment attachment,
required AccountId? accountId,
required Session? session,
required String ownEmailAddress,
required BaseController controller,
required ParseEmailByBlobIdInteractor parseEmailInteractor,
required GetHtmlContentFromAttachmentInteractor getHtmlInteractor,
required OnDownloadAttachmentAction onDownloadAttachment,
}) {
if (PlatformInfo.isWeb && attachment.isPDFFile) {
_previewPDFFile(
context: context,
attachment: attachment,
accountId: accountId,
session: session,
);
} else if (attachment.isEMLFile) {
previewEMLFileAction(
appLocalizations: AppLocalizations.of(context),
accountId: accountId,
session: session,
ownEmailAddress: ownEmailAddress,
blobId: attachment.blobId,
controller: controller,
parseEmailByBlobIdInteractor: parseEmailInteractor,
);
} else if (attachment.isHTMLFile) {
_previewHtmlFileAction(
session: session,
accountId: accountId,
attachment: attachment,
controller: controller,
getHtmlInteractor: getHtmlInteractor,
);
} else {
onDownloadAttachment(attachment);
}
}
Future<void> _previewPDFFile({
required BuildContext context,
required Attachment attachment,
required AccountId? accountId,
required Session? session,
}) async {
if (accountId == null || session == null) {
_appToast.showToastErrorMessage(
context,
AppLocalizations.of(context).noPreviewAvailable,
);
return;
}
try {
final downloadUrl = session.getDownloadUrl(
jmapUrl: _dynamicUrlInterceptors.jmapUrl,
);
await Get.generalDialog(
barrierColor: Colors.black.withValues(alpha: 0.8),
pageBuilder: (_, __, ___) {
return PointerInterceptor(
child: PDFViewer(
attachment: attachment,
accountId: accountId,
downloadUrl: downloadUrl,
downloadAction: _downloadPDFFile,
printAction: _printPDFFile,
),
);
},
);
} catch (e) {
if (context.mounted) {
_appToast.showToastErrorMessage(
context,
AppLocalizations.of(context).noPreviewAvailable,
);
}
}
}
void _downloadPDFFile(Uint8List bytes, String fileName) {
_downloadManager.createAnchorElementDownloadFileWeb(bytes, fileName);
}
Future<void> _printPDFFile(Uint8List bytes, String fileName) async {
await _printUtils.printPDFFile(bytes, fileName);
}
void previewEMLFileAction({
required AppLocalizations appLocalizations,
required AccountId? accountId,
required Session? session,
required String ownEmailAddress,
required Id? blobId,
required ParseEmailByBlobIdInteractor parseEmailByBlobIdInteractor,
required BaseController controller,
}) {
SmartDialog.showLoading(
msg: appLocalizations.loadingPleaseWait,
maskColor: Colors.black38,
);
if (session == null) {
_emitFailure(
controller: controller,
failure: ParseEmailByBlobIdFailure(NotFoundSessionException()),
);
return;
}
if (accountId == null) {
_emitFailure(
controller: controller,
failure: ParseEmailByBlobIdFailure(NotFoundAccountIdException()),
);
return;
}
if (blobId == null) {
_emitFailure(
controller: controller,
failure: ParseEmailByBlobIdFailure(NotFoundBlobIdException([])),
);
return;
}
controller.consumeState(
parseEmailByBlobIdInteractor.execute(
accountId,
session,
ownEmailAddress,
blobId,
),
);
}
({
bool canDownloadAttachment,
AccountId? accountId,
String? downloadUrl,
Id? blobId,
}) evaluateAttachment({
required Session? session,
required AccountId? accountId,
required Attachment attachment,
}) {
String? downloadUrl;
try {
downloadUrl = session?.getDownloadUrl(
jmapUrl: _dynamicUrlInterceptors.jmapUrl,
);
} catch (e) {
downloadUrl = null;
}
final blobId = attachment.blobId;
if (accountId == null || downloadUrl == null || blobId == null) {
return (
canDownloadAttachment: false,
accountId: accountId,
downloadUrl: downloadUrl,
blobId: blobId,
);
}
return (
canDownloadAttachment: true,
accountId: accountId,
downloadUrl: downloadUrl,
blobId: blobId,
);
}
void _previewHtmlFileAction({
required Session? session,
required AccountId? accountId,
required Attachment attachment,
required BaseController controller,
required GetHtmlContentFromAttachmentInteractor getHtmlInteractor,
}) {
final attachmentEvaluation = evaluateAttachment(
accountId: accountId,
session: session,
attachment: attachment,
);
if (!attachmentEvaluation.canDownloadAttachment) {
_emitFailure(
controller: controller,
failure: GetHtmlContentFromAttachmentFailure(
exception: null,
attachment: attachment,
),
);
return;
}
controller.consumeState(
getHtmlInteractor.execute(
attachmentEvaluation.accountId!,
attachment,
DownloadTaskId(attachmentEvaluation.blobId!.value),
attachmentEvaluation.downloadUrl!,
TransformConfiguration.create(
customDomTransformers: [SanitizeHyperLinkTagInHtmlTransformer()],
customTextTransformers: [
const StandardizeHtmlSanitizingTransformers()
],
),
),
);
}
void _emitFailure({
required BaseController controller,
required FeatureFailure failure,
}) {
controller.consumeState(Stream.value(Left(failure)));
}
void handleParseEmailByBlobIdSuccess({
required BuildContext? context,
required AccountId? accountId,
required Session? session,
required String ownEmailAddress,
required Id blobId,
required Email email,
required BaseController controller,
required PreviewEmailFromEmlFileInteractor previewInteractor,
}) {
if (session == null) {
_emitFailure(
controller: controller,
failure: PreviewEmailFromEmlFileFailure(NotFoundSessionException()),
);
return;
}
if (accountId == null) {
_emitFailure(
controller: controller,
failure: PreviewEmailFromEmlFileFailure(NotFoundAccountIdException()),
);
return;
}
if (context == null) {
_emitFailure(
controller: controller,
failure: PreviewEmailFromEmlFileFailure(NotFoundContextException()),
);
return;
}
try {
controller.consumeState(previewInteractor.execute(
PreviewEmailEMLRequest(
accountId: accountId,
session: session,
ownEmailAddress: ownEmailAddress,
blobId: blobId,
email: email,
locale: Localizations.localeOf(context),
appLocalizations: AppLocalizations.of(context),
baseDownloadUrl: session.getDownloadUrl(
jmapUrl: _dynamicUrlInterceptors.jmapUrl,
),
),
));
} catch (e) {
_emitFailure(
controller: controller,
failure: PreviewEmailFromEmlFileFailure(e),
);
}
}
void handleParseEmailByBlobIdFailure(ParseEmailByBlobIdFailure failure) {
SmartDialog.dismiss();
_toastManager.showMessageFailure(failure);
}
void handlePreviewEmailFromEMLFileFailure(
PreviewEmailFromEmlFileFailure failure,
) {
SmartDialog.dismiss();
_toastManager.showMessageFailure(failure);
}
void handlePreviewEmailFromEMLFileSuccess({
required EMLPreviewer emlPreviewer,
required BuildContext? context,
required ImagePaths imagePaths,
required OnMailtoDelegateAction onMailtoAction,
required OnDownloadAttachmentDelegateAction onDownloadAction,
required OnPreviewEMLDelegateAction onPreviewAction,
}) {
SmartDialog.dismiss();
if (PlatformInfo.isWeb) {
bool isOpen = HtmlUtils.openNewWindowByUrl(
RouteUtils.createUrlWebLocationBar(
AppRoutes.emailEMLPreviewer,
previewId: emlPreviewer.id,
).toString(),
);
if (!isOpen) {
_toastManager.showMessageFailure(
PreviewEmailFromEmlFileFailure(CannotOpenNewWindowException()),
);
}
} else if (PlatformInfo.isMobile) {
if (context == null) {
_toastManager.showMessageFailure(
PreviewEmailFromEmlFileFailure(NotFoundContextException()),
);
return;
}
if (PlatformInfo.isAndroid) {
showModalSheetToPreviewEMLAttachment(
context: context,
emlPreviewer: emlPreviewer,
imagePaths: imagePaths,
onMailtoAction: onMailtoAction,
onDownloadAction: onDownloadAction,
onPreviewAction: onPreviewAction,
);
} else if (PlatformInfo.isIOS) {
showDialogToPreviewEMLAttachment(
context: context,
emlPreviewer: emlPreviewer,
imagePaths: imagePaths,
onMailtoAction: onMailtoAction,
onDownloadAction: onDownloadAction,
onPreviewAction: onPreviewAction,
);
}
}
}
void showModalSheetToPreviewEMLAttachment({
required BuildContext context,
required EMLPreviewer emlPreviewer,
required ImagePaths imagePaths,
required OnMailtoDelegateAction onMailtoAction,
required OnDownloadAttachmentDelegateAction onDownloadAction,
required OnPreviewEMLDelegateAction onPreviewAction,
}) {
showModalBottomSheet(
context: context,
showDragHandle: true,
useSafeArea: true,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0),
topRight: Radius.circular(20.0),
),
),
builder: (_) {
return DraggableScrollableSheet(
initialChildSize: 1.0,
builder: (context, ___) => EmailPreviewerDialogView(
emlPreviewer: emlPreviewer,
imagePaths: imagePaths,
onMailtoDelegateAction: onMailtoAction,
onPreviewEMLDelegateAction: onPreviewAction,
onDownloadAttachmentDelegateAction: onDownloadAction,
),
);
},
);
}
void showDialogToPreviewEMLAttachment({
required BuildContext context,
required EMLPreviewer emlPreviewer,
required ImagePaths imagePaths,
required OnMailtoDelegateAction onMailtoAction,
required OnDownloadAttachmentDelegateAction onDownloadAction,
required OnPreviewEMLDelegateAction onPreviewAction,
}) {
Get.dialog(
EmailPreviewerDialogView(
emlPreviewer: emlPreviewer,
imagePaths: imagePaths,
onMailtoDelegateAction: onMailtoAction,
onPreviewEMLDelegateAction: onPreviewAction,
onDownloadAttachmentDelegateAction: onDownloadAction,
),
barrierColor: AppColor.colorDefaultCupertinoActionSheet,
);
}
}
@@ -14,8 +14,8 @@ import 'package:jmap_dart_client/jmap/core/session/session.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/mail/email/email_address.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
import 'package:model/email/attachment.dart'; import 'package:model/email/attachment.dart';
import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/download_attachment_for_web_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_html_content_from_attachment_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/download_and_get_html_content_from_attachment_state.dart';
import 'package:tmail_ui_user/features/email/presentation/model/email_unsubscribe.dart'; import 'package:tmail_ui_user/features/email/presentation/model/email_unsubscribe.dart';
import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart'; import 'package:tmail_ui_user/features/thread/domain/constants/thread_constants.dart';
import 'package:tmail_ui_user/main/error/capability_validator.dart'; import 'package:tmail_ui_user/main/error/capability_validator.dart';
@@ -83,11 +83,11 @@ class EmailUtils {
return state?.fold( return state?.fold(
(failure) { (failure) {
return failure is DownloadAttachmentForWebFailure return failure is DownloadAttachmentForWebFailure
|| failure is GetHtmlContentFromAttachmentFailure; || failure is DownloadAndGetHtmlContentFromAttachmentFailure;
}, },
(success) { (success) {
return success is DownloadAttachmentForWebSuccess return success is DownloadAttachmentForWebSuccess
|| success is GetHtmlContentFromAttachmentSuccess || success is DownloadAndGetHtmlContentFromAttachmentSuccess
|| success is IdleDownloadAttachmentForWeb; || success is IdleDownloadAttachmentForWeb;
}) ?? false; }) ?? false;
} }
@@ -13,9 +13,9 @@ import 'package:get/get.dart';
import 'package:jmap_dart_client/jmap/account_id.dart'; import 'package:jmap_dart_client/jmap/account_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';
import 'package:tmail_ui_user/features/email/domain/exceptions/download_attachment_exceptions.dart'; import 'package:tmail_ui_user/features/download/domain/exceptions/download_attachment_exceptions.dart';
import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/download_attachment_for_web_state.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart'; import 'package:tmail_ui_user/features/download/domain/usecase/download_attachment_for_web_interactor.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart';
import 'package:twake_previewer_flutter/core/previewer_options/options/loading_options.dart'; import 'package:twake_previewer_flutter/core/previewer_options/options/loading_options.dart';
@@ -2,7 +2,7 @@
import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:percent_indicator/linear_percent_indicator.dart'; import 'package:percent_indicator/linear_percent_indicator.dart';
import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/download_attachment_for_web_state.dart';
class DownloadAttachmentLoadingBar extends StatelessWidget { class DownloadAttachmentLoadingBar extends StatelessWidget {
@@ -1,14 +1,14 @@
import 'package:core/data/network/download/download_manager.dart'; import 'package:core/data/network/download/download_manager.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart'; import 'package:tmail_ui_user/features/download/domain/usecase/download_attachment_for_web_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_preview_email_eml_content_shared_interactor.dart'; import 'package:tmail_ui_user/features/download/domain/usecase/get_preview_email_eml_content_shared_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_preview_eml_content_in_memory_interactor.dart'; import 'package:tmail_ui_user/features/download/domain/usecase/get_preview_eml_content_in_memory_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/move_preview_eml_content_from_persistent_to_memory_interactor.dart'; import 'package:tmail_ui_user/features/download/domain/usecase/move_preview_eml_content_from_persistent_to_memory_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/parse_email_by_blob_id_interactor.dart'; import 'package:tmail_ui_user/features/download/domain/usecase/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/download/domain/usecase/preview_email_from_eml_file_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/remove_preview_email_eml_content_shared_interactor.dart'; import 'package:tmail_ui_user/features/download/domain/usecase/remove_preview_email_eml_content_shared_interactor.dart';
import 'package:tmail_ui_user/features/email_previewer/email_previewer_controller.dart'; import 'package:tmail_ui_user/features/email_previewer/email_previewer_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/bindings/download_interactor_bindings.dart'; import 'package:tmail_ui_user/features/download/presentation/bindings/download_interactor_bindings.dart';
class EmailPreviewerBindings extends Bindings { class EmailPreviewerBindings extends Bindings {
@override @override
@@ -20,18 +20,18 @@ import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dar
import 'package:tmail_ui_user/features/caching/exceptions/local_storage_exception.dart'; import 'package:tmail_ui_user/features/caching/exceptions/local_storage_exception.dart';
import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart'; import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart';
import 'package:tmail_ui_user/features/email/domain/model/preview_email_eml_request.dart'; import 'package:tmail_ui_user/features/email/domain/model/preview_email_eml_request.dart';
import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/download_attachment_for_web_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_preview_email_eml_content_shared_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/get_preview_email_eml_content_shared_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_preview_eml_content_in_memory_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/get_preview_eml_content_in_memory_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/parse_email_by_blob_id_state.dart'; import 'package:tmail_ui_user/features/download/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/download/domain/state/preview_email_from_eml_file_state.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart'; import 'package:tmail_ui_user/features/download/domain/usecase/download_attachment_for_web_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_preview_email_eml_content_shared_interactor.dart'; import 'package:tmail_ui_user/features/download/domain/usecase/get_preview_email_eml_content_shared_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_preview_eml_content_in_memory_interactor.dart'; import 'package:tmail_ui_user/features/download/domain/usecase/get_preview_eml_content_in_memory_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/move_preview_eml_content_from_persistent_to_memory_interactor.dart'; import 'package:tmail_ui_user/features/download/domain/usecase/move_preview_eml_content_from_persistent_to_memory_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/parse_email_by_blob_id_interactor.dart'; import 'package:tmail_ui_user/features/download/domain/usecase/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/download/domain/usecase/preview_email_from_eml_file_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/remove_preview_email_eml_content_shared_interactor.dart'; import 'package:tmail_ui_user/features/download/domain/usecase/remove_preview_email_eml_content_shared_interactor.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/attachment_extension.dart'; import 'package:tmail_ui_user/features/email/presentation/extensions/attachment_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart';
import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart'; import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart';
@@ -3,9 +3,9 @@ import 'package:core/presentation/views/html_viewer/html_content_viewer_on_web_w
import 'package:core/presentation/views/loading/cupertino_loading_widget.dart'; import 'package:core/presentation/views/loading/cupertino_loading_widget.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_preview_email_eml_content_shared_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/get_preview_email_eml_content_shared_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_preview_eml_content_in_memory_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/get_preview_eml_content_in_memory_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/preview_email_from_eml_file_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/preview_email_from_eml_file_state.dart';
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart';
import 'package:tmail_ui_user/features/email_previewer/download_attachment_loading_bar.dart'; import 'package:tmail_ui_user/features/email_previewer/download_attachment_loading_bar.dart';
import 'package:tmail_ui_user/features/email_previewer/email_previewer_controller.dart'; import 'package:tmail_ui_user/features/email_previewer/email_previewer_controller.dart';
@@ -2,7 +2,6 @@ import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart'; import 'package:core/presentation/state/success.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:jmap_dart_client/jmap/core/id.dart'; import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:model/email/attachment.dart';
import 'package:tmail_ui_user/features/base/action/ui_action.dart'; import 'package:tmail_ui_user/features/base/action/ui_action.dart';
class DownloadUIAction extends UIAction { class DownloadUIAction extends UIAction {
@@ -24,19 +23,10 @@ class UpdateAttachmentsViewStateAction extends DownloadUIAction {
List<Object?> get props => [blobId, viewState]; List<Object?> get props => [blobId, viewState];
} }
class DownloadAttachmentsQuicklyAction extends DownloadUIAction { class OpenComposerFromMailtoLinkAction extends DownloadUIAction {
DownloadAttachmentsQuicklyAction(this.attachment); OpenComposerFromMailtoLinkAction(this.uri);
final Attachment attachment; final Uri uri;
@override
List<Object?> get props => [attachment];
}
class OpenMailtoLinkFromPreviewAttachmentAction extends DownloadUIAction {
OpenMailtoLinkFromPreviewAttachmentAction(this.uri);
final Uri? uri;
@override @override
List<Object?> get props => [uri]; List<Object?> get props => [uri];
@@ -17,6 +17,8 @@ import 'package:tmail_ui_user/features/composer/data/repository/contact_reposito
import 'package:tmail_ui_user/features/composer/domain/repository/contact_repository.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'; import 'package:tmail_ui_user/features/composer/domain/usecases/send_email_interactor.dart';
import 'package:tmail_ui_user/features/composer/presentation/manager/composer_manager.dart'; import 'package:tmail_ui_user/features/composer/presentation/manager/composer_manager.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/export_all_attachments_interactor.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/export_attachment_interactor.dart';
import 'package:tmail_ui_user/features/email/data/datasource/email_datasource.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/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/print_file_datasource.dart';
@@ -32,15 +34,16 @@ import 'package:tmail_ui_user/features/email/data/repository/email_repository_im
import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart'; import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/delete_email_permanently_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/delete_email_permanently_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/delete_multiple_emails_permanently_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/delete_multiple_emails_permanently_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/download_all_attachments_for_web_interactor.dart'; import 'package:tmail_ui_user/features/download/domain/usecase/download_all_attachments_for_web_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart'; import 'package:tmail_ui_user/features/download/domain/usecase/download_attachment_for_web_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_html_content_from_attachment_interactor.dart'; import 'package:tmail_ui_user/features/download/domain/usecase/download_and_get_html_content_from_attachment_interactor.dart';
import 'package:tmail_ui_user/features/download/domain/usecase/get_html_content_from_upload_file_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_restored_deleted_message_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_restored_deleted_message_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_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/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/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/download/domain/usecase/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/download/domain/usecase/preview_email_from_eml_file_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/restore_deleted_message_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/restore_deleted_message_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/unsubscribe_email_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/unsubscribe_email_interactor.dart';
import 'package:tmail_ui_user/features/email/presentation/bindings/email_bindings.dart'; import 'package:tmail_ui_user/features/email/presentation/bindings/email_bindings.dart';
@@ -99,10 +102,10 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_re
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_email_sort_order_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_email_sort_order_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_last_time_dismissed_spam_reported_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_last_time_dismissed_spam_reported_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_spam_report_state_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_spam_report_state_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/bindings/download_interactor_bindings.dart'; import 'package:tmail_ui_user/features/download/presentation/bindings/download_interactor_bindings.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/download/download_controller.dart'; import 'package:tmail_ui_user/features/download/presentation/controllers/download_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart';
@@ -175,12 +178,16 @@ class MailboxDashBoardBindings extends BaseBindings {
Get.find<GetAppGridLinagraEcosystemInteractor>(), Get.find<GetAppGridLinagraEcosystemInteractor>(),
)); ));
Get.put(DownloadController( Get.put(DownloadController(
Get.find<DownloadManager>(),
Get.find<PrintUtils>(),
Get.find<DownloadAttachmentForWebInteractor>(), Get.find<DownloadAttachmentForWebInteractor>(),
Get.find<DownloadAllAttachmentsForWebInteractor>(), Get.find<DownloadAllAttachmentsForWebInteractor>(),
Get.find<DownloadManager>(),
Get.find<ParseEmailByBlobIdInteractor>(), Get.find<ParseEmailByBlobIdInteractor>(),
Get.find<PreviewEmailFromEmlFileInteractor>(), Get.find<PreviewEmailFromEmlFileInteractor>(),
Get.find<GetHtmlContentFromAttachmentInteractor>(), Get.find<DownloadAndGetHtmlContentFromAttachmentInteractor>(),
Get.find<GetHtmlContentFromUploadFileInteractor>(),
Get.find<ExportAttachmentInteractor>(),
Get.find<ExportAllAttachmentsInteractor>(),
)); ));
Get.put(SearchController( Get.put(SearchController(
Get.find<QuickSearchEmailInteractor>(), Get.find<QuickSearchEmailInteractor>(),
@@ -1,617 +0,0 @@
import 'dart:async';
import 'package:core/data/network/config/dynamic_url_interceptors.dart';
import 'package:core/data/network/download/download_manager.dart';
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/state/failure.dart';
import 'package:core/presentation/state/success.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/platform_info.dart';
import 'package:dartz/dartz.dart';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get/get_navigation/src/dialog/dialog_route.dart';
import 'package:http_parser/http_parser.dart';
import 'package:jmap_dart_client/jmap/account_id.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/core/session/session.dart';
import 'package:model/download/download_task_id.dart';
import 'package:model/email/attachment.dart';
import 'package:model/email/eml_attachment.dart';
import 'package:model/email/presentation_email.dart';
import 'package:model/extensions/presentation_email_extension.dart';
import 'package:model/extensions/session_extension.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:tmail_ui_user/features/base/base_controller.dart';
import 'package:tmail_ui_user/features/email/domain/exceptions/email_exceptions.dart';
import 'package:tmail_ui_user/features/email/domain/state/download_all_attachments_for_web_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/download_attachment_for_web_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/get_html_content_from_attachment_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/parse_email_by_blob_id_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/preview_email_from_eml_file_state.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/download_all_attachments_for_web_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/download_attachment_for_web_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_html_content_from_attachment_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/parse_email_by_blob_id_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/preview_email_from_eml_file_interactor.dart';
import 'package:tmail_ui_user/features/email/presentation/extensions/attachment_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/mixin/preview_attachment_mixin.dart';
import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/html_attachment_previewer.dart';
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/download/download_task_state.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
import 'package:twake_previewer_flutter/core/constants/supported_charset.dart';
import 'package:twake_previewer_flutter/core/previewer_options/options/previewer_state.dart';
import 'package:twake_previewer_flutter/core/previewer_options/options/top_bar_options.dart';
import 'package:twake_previewer_flutter/core/previewer_options/previewer_options.dart';
import 'package:twake_previewer_flutter/twake_image_previewer/twake_image_previewer.dart';
import 'package:twake_previewer_flutter/twake_plain_text_previewer/twake_plain_text_previewer.dart';
import 'package:uuid/uuid.dart';
typedef UpdateDownloadTaskStateCallback = DownloadTaskState Function(DownloadTaskState currentState);
class DownloadController extends BaseController with PreviewAttachmentMixin {
final DownloadAttachmentForWebInteractor _downloadAttachmentForWebInteractor;
final DownloadAllAttachmentsForWebInteractor
_downloadAllAttachmentsForWebInteractor;
final DownloadManager _downloadManager;
final ParseEmailByBlobIdInteractor parseEmailByBlobIdInteractor;
final PreviewEmailFromEmlFileInteractor previewEmailFromEmlFileInteractor;
final GetHtmlContentFromAttachmentInteractor
getHtmlContentFromAttachmentInteractor;
DownloadController(
this._downloadAttachmentForWebInteractor,
this._downloadAllAttachmentsForWebInteractor,
this._downloadManager,
this.parseEmailByBlobIdInteractor,
this.previewEmailFromEmlFileInteractor,
this.getHtmlContentFromAttachmentInteractor,
);
final listDownloadTaskState = RxList<DownloadTaskState>();
final hideDownloadTaskbar = RxBool(false);
final downloadUIAction = Rxn<DownloadUIAction>();
final _downloadProgressStateController =
StreamController<Either<Failure, Success>>.broadcast();
StreamSubscription<Either<Failure, Success>>?
_downloadProgressStateSubscription;
@override
void onInit() {
super.onInit();
_registerDownloadProgressState();
}
void _registerDownloadProgressState() {
_downloadProgressStateSubscription = _downloadProgressStateController.stream
.listen(_onDownloadProgressStateChanged);
}
void _onDownloadProgressStateChanged(Either<Failure, Success> state) {
state.fold((_) => null, (success) {
if (success is StartDownloadAttachmentForWeb) {
_handleStartSingleDownload(success);
} else if (success is DownloadingAttachmentForWeb) {
_updateDownloadProgress(
success.taskId,
success.progress,
success.downloaded,
success.total,
);
} else if (success is StartDownloadAllAttachmentsForWeb) {
_handleStartAllDownload(success);
} else if (success is DownloadingAllAttachmentsForWeb) {
_updateDownloadProgress(
success.taskId,
success.progress,
success.downloaded,
success.total,
);
}
});
}
void _handleStartSingleDownload(StartDownloadAttachmentForWeb success) {
if (success.previewerSupported) return;
addDownloadTask(
DownloadTaskState(
taskId: success.taskId,
attachment: success.attachment,
onCancel: success.cancelToken?.cancel,
),
);
if (_hasValidContext) {
appToast.showToastMessage(
currentOverlayContext!,
AppLocalizations.of(currentContext!).your_download_has_started,
leadingSVGIconColor: AppColor.primaryColor,
leadingSVGIcon: imagePaths.icDownload,
);
}
}
void _handleStartAllDownload(StartDownloadAllAttachmentsForWeb success) {
addDownloadTask(
DownloadTaskState(
taskId: success.taskId,
attachment: success.attachment,
onCancel: () => success.cancelToken?.cancel(),
),
);
if (_hasValidContext) {
appToast.showToastSuccessMessage(
currentOverlayContext!,
AppLocalizations.of(currentContext!).creatingAnArchiveForDownloading,
leadingSVGIconColor: Colors.white,
leadingSVGIcon: imagePaths.icDownloadAll,
);
}
}
void _updateDownloadProgress(
DownloadTaskId taskId,
double progress,
int downloaded,
int total,
) {
final percent = progress.round();
log('$runtimeType::_updateDownloadProgress(): $percent%');
updateDownloadTaskByTaskId(taskId, (currentTask) {
return currentTask.copyWith(
progress: progress,
downloaded: downloaded,
total: total,
);
});
}
bool get _hasValidContext =>
currentOverlayContext != null && currentContext != null;
bool get notEmptyListDownloadTask => listDownloadTaskState.isNotEmpty;
void addDownloadTask(DownloadTaskState task) {
log('DownloadController::addDownloadTask(): ${task.taskId}');
listDownloadTaskState.add(task);
hideDownloadTaskbar.value = false;
}
void updateDownloadTaskByTaskId(
DownloadTaskId downloadTaskId,
UpdateDownloadTaskStateCallback updateDownloadTaskCallback,
) {
final matchIndex = listDownloadTaskState
.indexWhere((task) => task.taskId == downloadTaskId);
if (matchIndex >= 0) {
listDownloadTaskState[matchIndex] = updateDownloadTaskCallback(listDownloadTaskState[matchIndex]);
listDownloadTaskState.refresh();
}
}
void deleteDownloadTask(DownloadTaskId taskId) {
log('DownloadController::deleteDownloadTask(): $taskId');
final matchIndex = listDownloadTaskState
.indexWhere((task) => task.taskId == taskId);
if (matchIndex >= 0) {
listDownloadTaskState.removeAt(matchIndex);
listDownloadTaskState.refresh();
}
if (listDownloadTaskState.isEmpty) {
hideDownloadTaskbar.value = true;
}
}
void downloadAttachmentForWeb({
required Attachment attachment,
required AccountId? accountId,
required Session? session,
bool previewerSupported = false,
}) {
if (accountId == null || session == null) {
consumeState(Stream.value(
Left(DownloadAttachmentForWebFailure(
attachment: attachment,
exception: NotFoundSessionException(),
)),
));
return;
}
final generateTaskId = DownloadTaskId(uuid.v4());
try {
final baseDownloadUrl = session.getDownloadUrl(
jmapUrl: dynamicUrlInterceptors.jmapUrl,
);
final cancelToken = CancelToken();
consumeState(_downloadAttachmentForWebInteractor.execute(
generateTaskId,
attachment,
accountId,
baseDownloadUrl,
onReceiveController: _downloadProgressStateController,
cancelToken: cancelToken,
previewerSupported: previewerSupported,
));
} catch (e) {
consumeState(Stream.value(
Left(DownloadAttachmentForWebFailure(
attachment: attachment,
taskId: generateTaskId,
exception: e,
)),
));
}
}
void downloadAllAttachmentsForWeb({
required String outputFileName,
required PresentationEmail? currentEmail,
required AccountId? accountId,
required Session? session,
bool previewerSupported = false,
}) {
final taskId = DownloadTaskId(uuid.v4());
if (accountId == null || session == null) {
consumeState(Stream.value(
Left(DownloadAllAttachmentsForWebFailure(
exception: NotFoundSessionException(),
taskId: taskId,
)),
));
return;
}
final downloadAllSupported = session.isDownloadAllSupported(accountId);
final emailId = currentEmail?.id;
if (!downloadAllSupported || emailId == null) {
consumeState(Stream.value(
Left(DownloadAllAttachmentsForWebFailure(taskId: taskId)),
));
return;
}
final baseDownloadAllUrl = session.getDownloadAllCapability(accountId)!.endpoint!;
final downloadAttachment = Attachment(
name: outputFileName,
type: MediaType('application', 'zip'),
);
final cancelToken = CancelToken();
consumeState(_downloadAllAttachmentsForWebInteractor.execute(
accountId,
emailId,
baseDownloadAllUrl,
downloadAttachment,
taskId,
onReceiveController: _downloadProgressStateController,
cancelToken: cancelToken,
));
}
void downloadMessageAsEML({
required PresentationEmail presentationEmail,
required AccountId? accountId,
required Session? session,
}) {
if (accountId == null || session == null) return;
final emlAttachment = presentationEmail.createEMLAttachment();
if (emlAttachment.blobId == null) {
consumeState(Stream.value(
Left(DownloadAttachmentForWebFailure(
exception: NotFoundEmailBlobIdException(),
)),
));
return;
}
final generateTaskId = DownloadTaskId(const Uuid().v4());
try {
final baseDownloadUrl = session.getDownloadUrl(
jmapUrl: getBinding<DynamicUrlInterceptors>()?.jmapUrl,
);
final cancelToken = CancelToken();
consumeState(_downloadAttachmentForWebInteractor.execute(
generateTaskId,
emlAttachment,
accountId,
baseDownloadUrl,
onReceiveController: _downloadProgressStateController,
cancelToken: cancelToken,
previewerSupported: false,
));
} catch (e) {
consumeState(Stream.value(Left(DownloadAttachmentForWebFailure(
attachment: emlAttachment,
taskId: generateTaskId,
exception: e,
))));
}
}
void _pushDownloadUIAction(DownloadUIAction action) {
downloadUIAction.value = action;
}
void _handleDownloadAttachmentForWebSuccess(
DownloadAttachmentForWebSuccess success,
) {
_pushDownloadUIAction(UpdateAttachmentsViewStateAction(
success.attachment.blobId,
Right<Failure, Success>(success),
));
deleteDownloadTask(success.taskId);
if (!success.previewerSupported) {
_downloadManager.createAnchorElementDownloadFileWeb(
success.bytes,
success.attachment.generateFileName(),
);
return;
}
if (success.attachment.isImage) {
_previewImageFile(attachment: success.attachment, bytes: success.bytes);
} else if (success.attachment.isText || success.attachment.isJson) {
_previewTextPlainFile(
attachment: success.attachment,
bytes: success.bytes,
);
}
}
void _downloadAttachmentQuickly(Attachment attachment) {
if (PlatformInfo.isWeb) {
_pushDownloadUIAction(DownloadAttachmentsQuicklyAction(attachment));
}
}
void _previewImageFile({
required Uint8List bytes,
required Attachment attachment,
}) {
if (currentContext == null) return;
Navigator.of(currentContext!).push(GetDialogRoute(
pageBuilder: (context, _, __) => PointerInterceptor(
child: TwakeImagePreviewer(
bytes: bytes,
zoomable: true,
previewerOptions: const PreviewerOptions(
previewerState: PreviewerState.success,
),
topBarOptions: TopBarOptions(
title: attachment.generateFileName(),
onClose: () => Navigator.maybePop(context),
onDownload: currentContext == null
? null
: () => _downloadAttachmentQuickly(attachment),
),
),
),
barrierDismissible: false,
));
}
void _previewTextPlainFile({
required Uint8List bytes,
required Attachment attachment,
}) {
if (currentContext == null) return;
Navigator.of(currentContext!).push(GetDialogRoute(
pageBuilder: (context, _, __) => PointerInterceptor(
child: TwakePlainTextPreviewer(
supportedCharset: SupportedCharset.utf8,
bytes: bytes,
previewerOptions: PreviewerOptions(
previewerState: PreviewerState.success,
width: currentContext == null ? 200 : currentContext!.width * 0.8,
),
topBarOptions: TopBarOptions(
title: attachment.generateFileName(),
onClose: () => Navigator.maybePop(context),
onDownload: currentContext == null
? null
: () => _downloadAttachmentQuickly(attachment),
),
),
),
barrierDismissible: false,
));
}
void clearDownloadUIAction() {
downloadUIAction.value = null;
}
void _downloadAllAttachmentsForWebFailure(
DownloadAllAttachmentsForWebFailure failure,
) {
deleteDownloadTask(failure.taskId);
if (currentOverlayContext == null || currentContext == null) return;
final appLocalizations = AppLocalizations.of(currentContext!);
String message = failure.cancelToken?.isCancelled == true
? appLocalizations.downloadAttachmentHasBeenCancelled
: appLocalizations.attachment_download_failed;
appToast.showToastErrorMessage(currentOverlayContext!, message);
}
void _downloadAttachmentForWebFailureAction(DownloadAttachmentForWebFailure failure) {
if (failure.taskId != null) {
deleteDownloadTask(failure.taskId!);
}
if (failure.attachment != null) {
_pushDownloadUIAction(
UpdateAttachmentsViewStateAction(
failure.attachment?.blobId,
Left<Failure, Success>(failure),
),
);
}
if (currentOverlayContext == null || currentContext == null) return;
final appLocalizations = AppLocalizations.of(currentContext!);
String message = appLocalizations.attachment_download_failed;
if (failure.attachment is EMLAttachment) {
message = appLocalizations.downloadMessageAsEMLFailed;
} else if (failure.cancelToken?.isCancelled == true) {
message = appLocalizations.downloadAttachmentHasBeenCancelled;
}
appToast.showToastErrorMessage(currentOverlayContext!, message);
}
@override
void handleSuccessViewState(Success success) {
if (success is DownloadAttachmentForWebSuccess) {
_handleDownloadAttachmentForWebSuccess(success);
} else if (success is StartDownloadAttachmentForWeb) {
_pushDownloadUIAction(UpdateAttachmentsViewStateAction(
success.attachment.blobId,
Right<Failure, Success>(success),
));
} else if (success is DownloadingAttachmentForWeb) {
_pushDownloadUIAction(UpdateAttachmentsViewStateAction(
success.attachment.blobId,
Right<Failure, Success>(success),
));
} else if (success is DownloadAllAttachmentsForWebSuccess) {
deleteDownloadTask(success.taskId);
} else if (success is ParseEmailByBlobIdSuccess) {
handleParseEmailByBlobIdSuccess(
context: currentContext,
accountId: success.accountId,
session: success.session,
ownEmailAddress: success.ownEmailAddress,
blobId: success.blobId,
email: success.email,
controller: this,
previewInteractor: previewEmailFromEmlFileInteractor,
);
} else if (success is PreviewEmailFromEmlFileSuccess) {
handlePreviewEmailFromEMLFileSuccess(
emlPreviewer: success.emlPreviewer,
context: currentContext,
imagePaths: imagePaths,
onMailtoAction: _openMailtoLink,
onDownloadAction: (uri) async {
if (uri == null) return;
final attachment = EmailUtils.parsingAttachmentByUri(uri);
if (attachment == null) return;
_downloadAttachmentQuickly(attachment);
},
onPreviewAction: (uri) async {
if (currentContext != null && uri?.path.isNotEmpty == true) {
previewEMLFileAction(
appLocalizations: AppLocalizations.of(currentContext!),
accountId: success.accountId,
session: success.session,
ownEmailAddress: success.ownEmailAddress,
blobId: Id(uri!.path),
controller: this,
parseEmailByBlobIdInteractor: parseEmailByBlobIdInteractor,
);
}
},
);
} else if (success is GetHtmlContentFromAttachmentSuccess) {
_pushDownloadUIAction(
UpdateAttachmentsViewStateAction(
success.attachment.blobId,
Right<Failure, Success>(success),
),
);
Get.dialog(HtmlAttachmentPreviewer(
title: success.htmlAttachmentTitle,
htmlContent: success.sanitizedHtmlContent,
mailToClicked: _openMailtoLink,
downloadAttachmentClicked: () =>
_downloadAttachmentQuickly(success.attachment),
responsiveUtils: responsiveUtils,
));
} else if (success is GettingHtmlContentFromAttachment) {
_pushDownloadUIAction(
UpdateAttachmentsViewStateAction(
success.attachment.blobId,
Right<Failure, Success>(success),
),
);
} else {
super.handleSuccessViewState(success);
}
}
@override
void handleFailureViewState(Failure failure) {
if (failure is DownloadAllAttachmentsForWebFailure) {
_downloadAllAttachmentsForWebFailure(failure);
} else if (failure is DownloadAttachmentForWebFailure) {
_downloadAttachmentForWebFailureAction(failure);
} else if (failure is ParseEmailByBlobIdFailure) {
handleParseEmailByBlobIdFailure(failure);
} else if (failure is PreviewEmailFromEmlFileFailure) {
handlePreviewEmailFromEMLFileFailure(failure);
} else if (failure is GetHtmlContentFromAttachmentFailure) {
_handleGetHtmlContentFromAttachmentFailure(failure);
} else {
super.handleFailureViewState(failure);
}
}
void _handleGetHtmlContentFromAttachmentFailure(
GetHtmlContentFromAttachmentFailure failure,
) {
_pushDownloadUIAction(
UpdateAttachmentsViewStateAction(
failure.attachment.blobId,
Left<Failure, Success>(failure),
),
);
if (currentOverlayContext != null && currentContext != null) {
appToast.showToastErrorMessage(
currentOverlayContext!,
AppLocalizations.of(currentContext!)
.thisHtmlAttachmentCannotBePreviewed,
);
}
}
Future<void> _openMailtoLink(Uri? uri) async {
_pushDownloadUIAction(OpenMailtoLinkFromPreviewAttachmentAction(uri));
}
@override
void onClose() {
_downloadProgressStateSubscription?.cancel();
_downloadProgressStateSubscription = null;
_downloadProgressStateController.close();
super.onClose();
}
}
@@ -50,6 +50,7 @@ import 'package:tmail_ui_user/features/composer/presentation/manager/composer_ma
import 'package:tmail_ui_user/features/composer/presentation/model/compose_action_mode.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/compose_action_mode.dart';
import 'package:tmail_ui_user/features/contact/presentation/model/contact_arguments.dart'; import 'package:tmail_ui_user/features/contact/presentation/model/contact_arguments.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/download/presentation/controllers/download_controller.dart';
import 'package:tmail_ui_user/features/email/domain/model/mark_read_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_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/move_to_mailbox_request.dart';
@@ -113,7 +114,6 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_e
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/dashboard_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/download/download_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart' as search; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart' as search;
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/cleanup_recent_search_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/cleanup_recent_search_extension.dart';
@@ -121,7 +121,6 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_action_type_for_email_selection.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_action_type_for_email_selection.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_clear_mailbox_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_clear_mailbox_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_create_new_rule_filter.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_create_new_rule_filter.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_download_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_preferences_setting_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_preferences_setting_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_preview_attachment_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_preview_attachment_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_reactive_obx_variable_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_reactive_obx_variable_extension.dart';
@@ -765,11 +764,8 @@ class MailboxDashBoardController extends ReloadableController
_downloadUIActionWorker = ever( _downloadUIActionWorker = ever(
downloadController.downloadUIAction, downloadController.downloadUIAction,
(action) { (action) {
if (action is DownloadAttachmentsQuicklyAction) { if (action is OpenComposerFromMailtoLinkAction) {
downloadAttachmentForWeb(attachment: action.attachment); openComposerFromMailToLink(action.uri);
downloadController.clearDownloadUIAction();
} else if (action is OpenMailtoLinkFromPreviewAttachmentAction) {
openMailToLink(action.uri);
downloadController.clearDownloadUIAction(); downloadController.clearDownloadUIAction();
} }
}, },
@@ -0,0 +1,47 @@
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
import 'package:model/email/attachment.dart';
import 'package:model/email/presentation_email.dart';
import 'package:tmail_ui_user/features/download/domain/model/download_source_view.dart';
import 'package:tmail_ui_user/features/download/presentation/extensions/download_attachment_download_controller_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
extension HandleDownloadAttachmentExtension on MailboxDashBoardController {
void downloadMessageAsEML({
required PresentationEmail presentationEmail,
bool showBottomDownloadProgressBar = false,
}) =>
downloadController.downloadMessageAsEML(
presentationEmail: presentationEmail,
accountId: accountId.value,
session: sessionCurrent,
showBottomDownloadProgressBar: showBottomDownloadProgressBar,
);
void downloadAttachment({
required Attachment attachment,
bool previewerSupported = false,
bool showBottomDownloadProgressBar = false,
DownloadSourceView? sourceView,
}) =>
downloadController.downloadAttachment(
attachment: attachment,
accountId: accountId.value,
session: sessionCurrent,
previewerSupported: previewerSupported,
showBottomDownloadProgressBar: showBottomDownloadProgressBar,
sourceView: sourceView,
);
void downloadAllAttachments({
required String outputFileName,
required EmailId? emailId,
bool showBottomDownloadProgressBar = false,
}) =>
downloadController.downloadAllAttachments(
outputFileName: outputFileName,
emailId: emailId,
accountId: accountId.value,
session: sessionCurrent,
showBottomDownloadProgressBar: showBottomDownloadProgressBar,
);
}
@@ -1,37 +0,0 @@
import 'package:model/email/attachment.dart';
import 'package:model/email/presentation_email.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
extension HandleDownloadExtension on MailboxDashBoardController {
void downloadMessageAsEML(PresentationEmail presentationEmail) {
downloadController.downloadMessageAsEML(
presentationEmail: presentationEmail,
accountId: accountId.value,
session: sessionCurrent,
);
}
void downloadAttachmentForWeb({
required Attachment attachment,
bool previewerSupported = false,
}) {
downloadController.downloadAttachmentForWeb(
attachment: attachment,
accountId: accountId.value,
session: sessionCurrent,
previewerSupported: previewerSupported,
);
}
void downloadAllAttachmentsForWeb({
required String outputFileName,
PresentationEmail? currentEmail,
}) {
downloadController.downloadAllAttachmentsForWeb(
outputFileName: outputFileName,
currentEmail: currentEmail,
accountId: accountId.value,
session: sessionCurrent,
);
}
}
@@ -2,48 +2,53 @@ import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/views/html_viewer/html_content_viewer_widget.dart'; import 'package:core/presentation/views/html_viewer/html_content_viewer_widget.dart';
import 'package:core/utils/app_logger.dart'; import 'package:core/utils/app_logger.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:model/email/attachment.dart'; import 'package:model/email/attachment.dart';
import 'package:tmail_ui_user/features/email/presentation/mixin/preview_attachment_mixin.dart'; import 'package:tmail_ui_user/features/download/domain/model/download_source_view.dart';
import 'package:tmail_ui_user/features/download/presentation/extensions/download_attachment_download_controller_extension.dart';
import 'package:tmail_ui_user/features/download/presentation/extensions/preview_attachment_download_controller_extension.dart';
import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart'; import 'package:tmail_ui_user/features/email/presentation/model/composer_arguments.dart';
import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart';
import 'package:tmail_ui_user/features/email/presentation/widgets/attachment_item_widget.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/handle_download_attachment_extension.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/open_and_close_composer_extension.dart';
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/routes/route_utils.dart'; import 'package:tmail_ui_user/main/routes/route_utils.dart';
extension HandlePreviewAttachmentExtension on MailboxDashBoardController { extension HandlePreviewAttachmentExtension on MailboxDashBoardController {
void previewAttachmentAction({ void previewAttachment({
required BuildContext context, required BuildContext context,
required Attachment attachment, required Attachment attachment,
required OnDownloadAttachmentAction onDownloadAttachment, required OnPreviewOrDownloadAttachmentAction onPreviewOrDownloadAction,
bool isDialogLoadingVisible = false,
DownloadSourceView? sourceView,
}) => }) =>
downloadController.previewAttachmentAction( downloadController.previewAttachment(
context: context, context: context,
attachment: attachment, attachment: attachment,
accountId: accountId.value, accountId: accountId.value,
session: sessionCurrent, session: sessionCurrent,
ownEmailAddress: ownEmailAddress.value, ownEmailAddress: ownEmailAddress.value,
controller: downloadController, sourceView: sourceView,
parseEmailInteractor: downloadController.parseEmailByBlobIdInteractor, onPreviewOrDownloadAction: onPreviewOrDownloadAction,
getHtmlInteractor: isDialogLoadingVisible: isDialogLoadingVisible,
downloadController.getHtmlContentFromAttachmentInteractor,
onDownloadAttachment: onDownloadAttachment,
); );
void previewEMLFileAction({ void previewUploadFile({
required AppLocalizations appLocalizations, required BuildContext context,
required Id? blobId, required UploadFileState uploadFile,
required OnPreviewOrDownloadAttachmentAction onPreviewOrDownloadAction,
bool isDialogLoadingVisible = false,
}) => }) =>
downloadController.previewEMLFileAction( downloadController.previewUploadFile(
appLocalizations: appLocalizations, context: context,
uploadFile: uploadFile,
accountId: accountId.value, accountId: accountId.value,
session: sessionCurrent, session: sessionCurrent,
ownEmailAddress: ownEmailAddress.value, ownEmailAddress: ownEmailAddress.value,
blobId: blobId, onPreviewOrDownloadAction: onPreviewOrDownloadAction,
controller: downloadController, isDialogLoadingVisible: isDialogLoadingVisible,
parseEmailByBlobIdInteractor:
downloadController.parseEmailByBlobIdInteractor,
); );
void showDialogToPreviewEMLAttachment({ void showDialogToPreviewEMLAttachment({
@@ -51,24 +56,32 @@ extension HandlePreviewAttachmentExtension on MailboxDashBoardController {
required EMLPreviewer emlPreviewer, required EMLPreviewer emlPreviewer,
required ImagePaths imagePaths, required ImagePaths imagePaths,
required OnMailtoDelegateAction onMailtoAction, required OnMailtoDelegateAction onMailtoAction,
required OnDownloadAttachmentDelegateAction onDownloadAction, required OnDownloadAttachmentFileAction onDownloadFileAction,
required OnPreviewEMLDelegateAction onPreviewAction,
}) => }) =>
downloadController.showDialogToPreviewEMLAttachment( downloadController.showDialogToPreviewEMLAttachment(
context: context, context: context,
emlPreviewer: emlPreviewer, emlPreviewer: emlPreviewer,
imagePaths: imagePaths,
onMailtoAction: onMailtoAction, onMailtoAction: onMailtoAction,
onDownloadAction: onDownloadAction, onPreviewAction: (uri) => downloadController.openEMLPreviewer(
onPreviewAction: onPreviewAction, appLocalizations: AppLocalizations.of(context),
uri: uri,
accountId: accountId.value,
session: sessionCurrent,
ownEmailAddress: ownEmailAddress.value,
),
onDownloadAction: (uri) async => downloadController.downloadAttachmentInEMLPreview(
uri: uri,
onDownloadAction: (attachment) => downloadAttachment(
attachment: attachment,
)
),
); );
Future<void> openMailToLink(Uri? uri) async { void openComposerFromMailToLink(Uri uri) {
if (uri == null) return;
final navigationRouter = RouteUtils.generateNavigationRouterFromMailtoLink( final navigationRouter = RouteUtils.generateNavigationRouterFromMailtoLink(
uri.toString(), uri.toString(),
); );
log('$runtimeType::openMailToLink(): ${uri.toString()}'); log('$runtimeType::openComposerFromMailToLink(): ${uri.toString()}');
if (!RouteUtils.canOpenComposerFromNavigationRouter(navigationRouter)) { if (!RouteUtils.canOpenComposerFromNavigationRouter(navigationRouter)) {
return; return;
} }
@@ -267,6 +267,11 @@ class UploadController extends BaseController {
.toList(); .toList();
} }
UploadFileState? getUploadFileId(UploadTaskId id) {
return listUploadAttachments
.firstWhereOrNull((fileState) => fileState.uploadTaskId == id);
}
Set<EmailBodyPart>? generateAttachments() { Set<EmailBodyPart>? generateAttachments() {
if (attachmentsUploaded.isEmpty) { if (attachmentsUploaded.isEmpty) {
return null; return null;
+2 -2
View File
@@ -23,8 +23,8 @@ import 'package:tmail_ui_user/features/email/domain/exceptions/calendar_event_ex
import 'package:tmail_ui_user/features/email/domain/model/move_action.dart'; import 'package:tmail_ui_user/features/email/domain/model/move_action.dart';
import 'package:tmail_ui_user/features/email/domain/state/calendar_event_reply_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/calendar_event_reply_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_star_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/mark_as_email_star_state.dart';
import 'package:tmail_ui_user/features/email/domain/state/parse_email_by_blob_id_state.dart'; import 'package:tmail_ui_user/features/download/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/download/domain/state/preview_email_from_eml_file_state.dart';
import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart'; import 'package:tmail_ui_user/features/home/data/exceptions/session_exceptions.dart';
import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart'; import 'package:tmail_ui_user/features/home/domain/state/get_session_state.dart';
import 'package:tmail_ui_user/features/login/data/network/oidc_error.dart'; import 'package:tmail_ui_user/features/login/data/network/oidc_error.dart';
@@ -40,6 +40,14 @@ extension SessionExtension on Session {
return downloadUrlDecode; return downloadUrlDecode;
} }
String getSafetyDownloadUrl({String? jmapUrl}) {
try {
return getDownloadUrl(jmapUrl: jmapUrl);
} catch(_) {
return '';
}
}
Uri getUploadUri(AccountId accountId, {String? jmapUrl}) { Uri getUploadUri(AccountId accountId, {String? jmapUrl}) {
final Uri uploadUrlValid; final Uri uploadUrlValid;
if (jmapUrl != null) { if (jmapUrl != null) {
@@ -6,9 +6,9 @@ import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:mockito/annotations.dart'; import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:model/extensions/session_extension.dart'; import 'package:model/extensions/session_extension.dart';
import 'package:tmail_ui_user/features/email/domain/state/parse_email_by_blob_id_state.dart'; import 'package:tmail_ui_user/features/download/domain/state/parse_email_by_blob_id_state.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/parse_email_by_blob_id_interactor.dart'; import 'package:tmail_ui_user/features/download/domain/usecase/parse_email_by_blob_id_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/repository/download_repository.dart'; import 'package:tmail_ui_user/features/download/domain/repository/download_repository.dart';
import '../../../../fixtures/account_fixtures.dart'; import '../../../../fixtures/account_fixtures.dart';
import '../../../../fixtures/email_fixtures.dart'; import '../../../../fixtures/email_fixtures.dart';
@@ -29,8 +29,6 @@ import 'package:tmail_ui_user/features/email/domain/state/get_email_content_stat
import 'package:tmail_ui_user/features/email/domain/state/parse_calendar_event_state.dart'; import 'package:tmail_ui_user/features/email/domain/state/parse_calendar_event_state.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_accept_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_accept_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_reject_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/calendar_event_reject_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/export_all_attachments_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/export_attachment_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/get_email_content_interactor.dart';
import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_email_read_interactor.dart'; import 'package:tmail_ui_user/features/email/domain/usecases/mark_as_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/mark_as_star_email_interactor.dart';
@@ -45,7 +43,7 @@ import 'package:tmail_ui_user/features/login/data/network/interceptors/authoriza
import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/delete_authority_oidc_interactor.dart';
import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart'; import 'package:tmail_ui_user/features/login/domain/usecases/delete_credential_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/download/download_controller.dart'; import 'package:tmail_ui_user/features/download/presentation/controllers/download_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart'; import 'package:tmail_ui_user/features/manage_account/data/local/language_cache_manager.dart';
import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart'; import 'package:tmail_ui_user/features/manage_account/domain/usecases/get_all_identities_interactor.dart';
@@ -72,7 +70,6 @@ const fallbackGenerators = {
@GenerateNiceMocks([ @GenerateNiceMocks([
MockSpec<GetEmailContentInteractor>(), MockSpec<GetEmailContentInteractor>(),
MockSpec<MarkAsEmailReadInteractor>(), MockSpec<MarkAsEmailReadInteractor>(),
MockSpec<ExportAttachmentInteractor>(),
MockSpec<MarkAsStarEmailInteractor>(), MockSpec<MarkAsStarEmailInteractor>(),
MockSpec<GetAllIdentitiesInteractor>(), MockSpec<GetAllIdentitiesInteractor>(),
MockSpec<StoreOpenedEmailInteractor>(), MockSpec<StoreOpenedEmailInteractor>(),
@@ -100,7 +97,6 @@ const fallbackGenerators = {
MockSpec<CalendarEventDataSource>(), MockSpec<CalendarEventDataSource>(),
MockSpec<DioClient>(), MockSpec<DioClient>(),
MockSpec<TwakeAppManager>(), MockSpec<TwakeAppManager>(),
MockSpec<ExportAllAttachmentsInteractor>(),
MockSpec<FileUploader>(), MockSpec<FileUploader>(),
]) ])
void main() { void main() {
@@ -108,7 +104,6 @@ void main() {
final getEmailContentInteractor = MockGetEmailContentInteractor(); final getEmailContentInteractor = MockGetEmailContentInteractor();
final markAsEmailReadInteractor = MockMarkAsEmailReadInteractor(); final markAsEmailReadInteractor = MockMarkAsEmailReadInteractor();
final exportAttachmentInteractor = MockExportAttachmentInteractor();
final markAsStarEmailInteractor = MockMarkAsStarEmailInteractor(); final markAsStarEmailInteractor = MockMarkAsStarEmailInteractor();
final getAllIdentitiesInteractor = MockGetAllIdentitiesInteractor(); final getAllIdentitiesInteractor = MockGetAllIdentitiesInteractor();
final storeOpenedEmailInteractor = MockStoreOpenedEmailInteractor(); final storeOpenedEmailInteractor = MockStoreOpenedEmailInteractor();
@@ -132,8 +127,6 @@ void main() {
final mockToastManager = MockToastManager(); final mockToastManager = MockToastManager();
final mockTwakeAppManager = MockTwakeAppManager(); final mockTwakeAppManager = MockTwakeAppManager();
final exportAllAttachmentsInteractor = MockExportAllAttachmentsInteractor();
late SingleEmailController singleEmailController; late SingleEmailController singleEmailController;
final testAccountId = AccountId(Id('123')); final testAccountId = AccountId(Id('123'));
@@ -178,12 +171,10 @@ void main() {
singleEmailController = SingleEmailController( singleEmailController = SingleEmailController(
getEmailContentInteractor, getEmailContentInteractor,
markAsEmailReadInteractor, markAsEmailReadInteractor,
exportAttachmentInteractor,
markAsStarEmailInteractor, markAsStarEmailInteractor,
getAllIdentitiesInteractor, getAllIdentitiesInteractor,
storeOpenedEmailInteractor, storeOpenedEmailInteractor,
printEmailInteractor, printEmailInteractor,
exportAllAttachmentsInteractor,
); );
}); });
@@ -71,7 +71,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_e
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/download/download_controller.dart'; import 'package:tmail_ui_user/features/download/presentation/controllers/download_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart';
@@ -68,7 +68,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_re
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_email_sort_order_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_email_sort_order_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/download/download_controller.dart'; import 'package:tmail_ui_user/features/download/presentation/controllers/download_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart';
@@ -54,7 +54,7 @@ import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/save_re
import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_email_sort_order_interactor.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/domain/usecases/store_email_sort_order_interactor.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/action/download_ui_action.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/app_grid_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/download/download_controller.dart'; import 'package:tmail_ui_user/features/download/presentation/controllers/download_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/search_controller.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/spam_report_controller.dart';