TF-3416 Handle open EML attachment in EML previewer on mobile
This commit is contained in:
@@ -6,6 +6,7 @@ import 'package:core/utils/app_logger.dart';
|
|||||||
import 'package:core/utils/html/html_interaction.dart';
|
import 'package:core/utils/html/html_interaction.dart';
|
||||||
import 'package:core/utils/html/html_utils.dart';
|
import 'package:core/utils/html/html_utils.dart';
|
||||||
import 'package:core/utils/platform_info.dart';
|
import 'package:core/utils/platform_info.dart';
|
||||||
|
import 'package:core/utils/preview_eml_file_utils.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
@@ -16,6 +17,7 @@ import 'package:url_launcher/url_launcher_string.dart';
|
|||||||
typedef OnScrollHorizontalEndAction = Function(bool leftDirection);
|
typedef OnScrollHorizontalEndAction = Function(bool leftDirection);
|
||||||
typedef OnLoadWidthHtmlViewerAction = Function(bool isScrollPageViewActivated);
|
typedef OnLoadWidthHtmlViewerAction = Function(bool isScrollPageViewActivated);
|
||||||
typedef OnMailtoDelegateAction = Future<void> Function(Uri? uri);
|
typedef OnMailtoDelegateAction = Future<void> Function(Uri? uri);
|
||||||
|
typedef OnPreviewEMLDelegateAction = Future<void> Function(Uri? uri);
|
||||||
|
|
||||||
class HtmlContentViewer extends StatefulWidget {
|
class HtmlContentViewer extends StatefulWidget {
|
||||||
|
|
||||||
@@ -26,6 +28,7 @@ class HtmlContentViewer extends StatefulWidget {
|
|||||||
final OnLoadWidthHtmlViewerAction? onLoadWidthHtmlViewer;
|
final OnLoadWidthHtmlViewerAction? onLoadWidthHtmlViewer;
|
||||||
final OnMailtoDelegateAction? onMailtoDelegateAction;
|
final OnMailtoDelegateAction? onMailtoDelegateAction;
|
||||||
final OnScrollHorizontalEndAction? onScrollHorizontalEnd;
|
final OnScrollHorizontalEndAction? onScrollHorizontalEnd;
|
||||||
|
final OnPreviewEMLDelegateAction? onPreviewEMLDelegateAction;
|
||||||
|
|
||||||
const HtmlContentViewer({
|
const HtmlContentViewer({
|
||||||
Key? key,
|
Key? key,
|
||||||
@@ -34,7 +37,8 @@ class HtmlContentViewer extends StatefulWidget {
|
|||||||
this.direction,
|
this.direction,
|
||||||
this.onLoadWidthHtmlViewer,
|
this.onLoadWidthHtmlViewer,
|
||||||
this.onMailtoDelegateAction,
|
this.onMailtoDelegateAction,
|
||||||
this.onScrollHorizontalEnd
|
this.onScrollHorizontalEnd,
|
||||||
|
this.onPreviewEMLDelegateAction,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -246,7 +250,7 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
|
|||||||
NavigationAction navigationAction
|
NavigationAction navigationAction
|
||||||
) async {
|
) async {
|
||||||
final url = navigationAction.request.url?.toString();
|
final url = navigationAction.request.url?.toString();
|
||||||
|
log('_HtmlContentViewState::_shouldOverrideUrlLoading: URL = $url');
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
return NavigationActionPolicy.CANCEL;
|
return NavigationActionPolicy.CANCEL;
|
||||||
}
|
}
|
||||||
@@ -256,9 +260,15 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final requestUri = Uri.parse(url);
|
final requestUri = Uri.parse(url);
|
||||||
final mailtoHandler = widget.onMailtoDelegateAction;
|
if (widget.onMailtoDelegateAction != null &&
|
||||||
if (mailtoHandler != null && requestUri.isScheme('mailto')) {
|
requestUri.isScheme('mailto')) {
|
||||||
await mailtoHandler(requestUri);
|
await widget.onMailtoDelegateAction?.call(requestUri);
|
||||||
|
return NavigationActionPolicy.CANCEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget.onPreviewEMLDelegateAction != null &&
|
||||||
|
requestUri.isScheme(PreviewEmlFileUtils.emlPreviewerScheme)) {
|
||||||
|
await widget.onPreviewEMLDelegateAction?.call(requestUri);
|
||||||
return NavigationActionPolicy.CANCEL;
|
return NavigationActionPolicy.CANCEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:html/dom.dart';
|
|||||||
import 'package:html/parser.dart';
|
import 'package:html/parser.dart';
|
||||||
|
|
||||||
class PreviewEmlFileUtils {
|
class PreviewEmlFileUtils {
|
||||||
|
static const String emlPreviewerScheme = 'eml-previewer';
|
||||||
|
|
||||||
Element? _createEmailElement({
|
Element? _createEmailElement({
|
||||||
required String subjectPrefix,
|
required String subjectPrefix,
|
||||||
|
|||||||
@@ -452,11 +452,8 @@ class EmailDataSourceImpl extends EmailDataSource {
|
|||||||
final iconBase64Data = await _fileUtils.convertImageAssetToBase64(
|
final iconBase64Data = await _fileUtils.convertImageAssetToBase64(
|
||||||
attachment.getIcon(_imagePaths));
|
attachment.getIcon(_imagePaths));
|
||||||
|
|
||||||
final link = attachment.isEMLFile && PlatformInfo.isWeb
|
final link = attachment.isEMLFile
|
||||||
? RouteUtils.createUrlWebLocationBar(
|
? _generateEMLAttachmentLink(attachment.blobId?.value)
|
||||||
AppRoutes.emailEMLPreviewer,
|
|
||||||
previewId: attachment.blobId?.value,
|
|
||||||
).toString()
|
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
final previewAttachment = PreviewAttachment(
|
final previewAttachment = PreviewAttachment(
|
||||||
@@ -501,6 +498,21 @@ class EmailDataSourceImpl extends EmailDataSource {
|
|||||||
}).catchError(_exceptionThrower.throwException);
|
}).catchError(_exceptionThrower.throwException);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _generateEMLAttachmentLink(String? blobId) {
|
||||||
|
if (blobId == null) return '';
|
||||||
|
|
||||||
|
if (PlatformInfo.isWeb) {
|
||||||
|
return RouteUtils.createUrlWebLocationBar(
|
||||||
|
AppRoutes.emailEMLPreviewer,
|
||||||
|
previewId: blobId,
|
||||||
|
).toString();
|
||||||
|
} else if (PlatformInfo.isMobile) {
|
||||||
|
return '${PreviewEmlFileUtils.emlPreviewerScheme}://$blobId';
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<String> _transformEmailContent(
|
Future<String> _transformEmailContent(
|
||||||
List<EmailContent> emailContents,
|
List<EmailContent> emailContents,
|
||||||
Map<String, String> mapCidImageDownloadUrl,
|
Map<String, String> mapCidImageDownloadUrl,
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ import 'package:tmail_ui_user/features/email/presentation/model/blob_calendar_ev
|
|||||||
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/email_loaded.dart';
|
import 'package:tmail_ui_user/features/email/presentation/model/email_loaded.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/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';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/widgets/attachment_list/attachment_list_bottom_sheet_builder.dart';
|
import 'package:tmail_ui_user/features/email/presentation/widgets/attachment_list/attachment_list_bottom_sheet_builder.dart';
|
||||||
import 'package:tmail_ui_user/features/email/presentation/widgets/attachment_list/attachment_list_dialog_builder.dart';
|
import 'package:tmail_ui_user/features/email/presentation/widgets/attachment_list/attachment_list_dialog_builder.dart';
|
||||||
@@ -1919,7 +1920,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
if (PlatformInfo.isWeb && PlatformInfo.isCanvasKit && attachment.isPDFFile) {
|
if (PlatformInfo.isWeb && PlatformInfo.isCanvasKit && attachment.isPDFFile) {
|
||||||
previewPDFFileAction(context, attachment);
|
previewPDFFileAction(context, attachment);
|
||||||
} else if (attachment.isEMLFile) {
|
} else if (attachment.isEMLFile) {
|
||||||
previewEMLFileAction(attachment, AppLocalizations.of(context));
|
previewEMLFileAction(attachment.blobId, AppLocalizations.of(context));
|
||||||
} else {
|
} else {
|
||||||
handleDownloadAttachmentAction(context, attachment);
|
handleDownloadAttachmentAction(context, attachment);
|
||||||
}
|
}
|
||||||
@@ -1952,7 +1953,7 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void previewEMLFileAction(Attachment attachment, AppLocalizations appLocalizations) {
|
void previewEMLFileAction(Id? blobId, AppLocalizations appLocalizations) {
|
||||||
SmartDialog.showLoading(
|
SmartDialog.showLoading(
|
||||||
msg: appLocalizations.loadingPleaseWait,
|
msg: appLocalizations.loadingPleaseWait,
|
||||||
maskColor: Colors.black38,
|
maskColor: Colors.black38,
|
||||||
@@ -1963,15 +1964,12 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attachment.blobId == null) {
|
if (blobId == null) {
|
||||||
consumeState(Stream.value(Left(ParseEmailByBlobIdFailure(NotFoundBlobIdException([])))));
|
consumeState(Stream.value(Left(ParseEmailByBlobIdFailure(NotFoundBlobIdException([])))));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
consumeState(_parseEmailByBlobIdInteractor.execute(
|
consumeState(_parseEmailByBlobIdInteractor.execute(accountId!, blobId));
|
||||||
accountId!,
|
|
||||||
attachment.blobId!,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleParseEmailByBlobIdSuccess(ParseEmailByBlobIdSuccess success) {
|
void _handleParseEmailByBlobIdSuccess(ParseEmailByBlobIdSuccess success) {
|
||||||
@@ -2033,30 +2031,51 @@ class SingleEmailController extends BaseController with AppLoaderMixin {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
showModalBottomSheet(
|
showModalSheetToPreviewEMLAttachment(
|
||||||
context: currentContext!,
|
currentContext!,
|
||||||
showDragHandle: true,
|
success.emlPreviewer);
|
||||||
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: (__, ___) => EmailPreviewerDialogView(
|
|
||||||
emlPreviewer: success.emlPreviewer,
|
|
||||||
onMailtoDelegateAction: openMailToLink,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void showModalSheetToPreviewEMLAttachment(
|
||||||
|
BuildContext context,
|
||||||
|
EMLPreviewer emlPreviewer,
|
||||||
|
) {
|
||||||
|
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,
|
||||||
|
onMailtoDelegateAction: openMailToLink,
|
||||||
|
onPreviewEMLDelegateAction: (uri) => _openEMLPreviewer(context, uri),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _openEMLPreviewer(BuildContext context, Uri? uri) async {
|
||||||
|
log('SingleEmailController::_openEMLPreviewer:uri = $uri');
|
||||||
|
if (uri == null) return;
|
||||||
|
|
||||||
|
final blobId = uri.authority;
|
||||||
|
log('SingleEmailController::_openEMLPreviewer:blobId = $blobId');
|
||||||
|
if (blobId.isEmpty) return;
|
||||||
|
|
||||||
|
previewEMLFileAction(Id(blobId), AppLocalizations.of(context));
|
||||||
|
}
|
||||||
|
|
||||||
void handleMailToAttendees(CalendarOrganizer? organizer, List<CalendarAttendee>? attendees) {
|
void handleMailToAttendees(CalendarOrganizer? organizer, List<CalendarAttendee>? attendees) {
|
||||||
final listEmailAddressAttendees = attendees
|
final listEmailAddressAttendees = attendees
|
||||||
?.map((attendee) => EmailAddress(attendee.name?.name, attendee.mailto?.mailAddress.value))
|
?.map((attendee) => EmailAddress(attendee.name?.name, attendee.mailto?.mailAddress.value))
|
||||||
|
|||||||
@@ -9,11 +9,13 @@ class EmailPreviewerDialogView extends StatelessWidget {
|
|||||||
|
|
||||||
final EMLPreviewer emlPreviewer;
|
final EMLPreviewer emlPreviewer;
|
||||||
final OnMailtoDelegateAction onMailtoDelegateAction;
|
final OnMailtoDelegateAction onMailtoDelegateAction;
|
||||||
|
final OnPreviewEMLDelegateAction onPreviewEMLDelegateAction;
|
||||||
|
|
||||||
const EmailPreviewerDialogView({
|
const EmailPreviewerDialogView({
|
||||||
super.key,
|
super.key,
|
||||||
required this.emlPreviewer,
|
required this.emlPreviewer,
|
||||||
required this.onMailtoDelegateAction,
|
required this.onMailtoDelegateAction,
|
||||||
|
required this.onPreviewEMLDelegateAction,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -26,6 +28,7 @@ class EmailPreviewerDialogView extends StatelessWidget {
|
|||||||
initialWidth: context.width,
|
initialWidth: context.width,
|
||||||
direction: AppUtils.getCurrentDirection(context),
|
direction: AppUtils.getCurrentDirection(context),
|
||||||
onMailtoDelegateAction: onMailtoDelegateAction,
|
onMailtoDelegateAction: onMailtoDelegateAction,
|
||||||
|
onPreviewEMLDelegateAction: onPreviewEMLDelegateAction,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user