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(); final PrintUtils _printUtils = Get.find(); final AppToast _appToast = Get.find(); final ToastManager _toastManager = Get.find(); final DynamicUrlInterceptors _dynamicUrlInterceptors = Get.find(); 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 _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 _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, ); } }