From 33d0e911789b04ee4be67f3e6ba662b431479f85 Mon Sep 17 00:00:00 2001 From: dab246 Date: Tue, 14 Jan 2025 01:27:48 +0700 Subject: [PATCH] TF-3416 Handle preview EML attachment on mobile --- .../data/datasource/email_datasource.dart | 4 +-- .../email_datasource_impl.dart | 6 ++-- .../email_hive_cache_datasource_impl.dart | 4 +-- .../email_local_storage_datasource_impl.dart | 9 +++-- ...email_session_storage_datasource_impl.dart | 9 +++-- .../repository/email_repository_impl.dart | 8 ++--- .../domain/repository/email_repository.dart | 4 +-- ...review_email_eml_content_shared_state.dart | 5 ++- .../preview_email_from_eml_file_state.dart | 5 ++- ...w_email_eml_content_shared_interactor.dart | 2 +- ..._from_persistent_to_memory_interactor.dart | 6 ++-- ...review_email_from_eml_file_interactor.dart | 16 +++++---- .../controller/single_email_controller.dart | 28 ++++++++++++++- .../presentation/model/eml_previewer.dart | 9 +++-- .../email_previewer_controller.dart | 14 ++------ .../email_previewer_dialog_view.dart | 27 ++++++++++++++ .../email_previewer/email_previewer_view.dart | 35 ++++++++----------- 17 files changed, 120 insertions(+), 71 deletions(-) create mode 100644 lib/features/email_previewer/email_previewer_dialog_view.dart diff --git a/lib/features/email/data/datasource/email_datasource.dart b/lib/features/email/data/datasource/email_datasource.dart index ae28c6f77..65d099dbe 100644 --- a/lib/features/email/data/datasource/email_datasource.dart +++ b/lib/features/email/data/datasource/email_datasource.dart @@ -192,13 +192,13 @@ abstract class EmailDataSource { Future generatePreviewEmailEMLContent(PreviewEmailEMLRequest previewEmailEMLRequest); - Future sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer); + Future sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer); Future getPreviewEmailEMLContentShared(String keyStored); Future removePreviewEmailEMLContentShared(String keyStored); - Future storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer); + Future storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer); Future getPreviewEMLContentInMemory(String keyStored); } \ No newline at end of file diff --git a/lib/features/email/data/datasource_impl/email_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_datasource_impl.dart index eca0d4c57..a157345e1 100644 --- a/lib/features/email/data/datasource_impl/email_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_datasource_impl.dart @@ -452,7 +452,7 @@ class EmailDataSourceImpl extends EmailDataSource { final iconBase64Data = await _fileUtils.convertImageAssetToBase64( attachment.getIcon(_imagePaths)); - final link = attachment.isEMLFile + final link = attachment.isEMLFile && PlatformInfo.isWeb ? RouteUtils.createUrlWebLocationBar( AppRoutes.emailEMLPreviewer, previewId: attachment.blobId?.value, @@ -526,7 +526,7 @@ class EmailDataSourceImpl extends EmailDataSource { } @override - Future sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer) { + Future sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer) { throw UnimplementedError(); } @@ -536,7 +536,7 @@ class EmailDataSourceImpl extends EmailDataSource { } @override - Future storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer) { + Future storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer) { throw UnimplementedError(); } diff --git a/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart index afe81c91f..f7cb296ea 100644 --- a/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_hive_cache_datasource_impl.dart @@ -540,7 +540,7 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource { } @override - Future sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer) { + Future sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer) { throw UnimplementedError(); } @@ -555,7 +555,7 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource { } @override - Future storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer) { + Future storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer) { throw UnimplementedError(); } diff --git a/lib/features/email/data/datasource_impl/email_local_storage_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_local_storage_datasource_impl.dart index 37a9d62fa..da7cec82f 100644 --- a/lib/features/email/data/datasource_impl/email_local_storage_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_local_storage_datasource_impl.dart @@ -166,9 +166,12 @@ class EmailLocalStorageDataSourceImpl extends EmailDataSource { } @override - Future sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer) { + Future sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer) { return Future.sync(() async { - return _localStorageManager.save(keyStored, jsonEncode(emlPreviewer.toJson())); + return _localStorageManager.save( + emlPreviewer.id, + jsonEncode(emlPreviewer.toJson()), + ); }).catchError(_exceptionThrower.throwException); } @@ -226,7 +229,7 @@ class EmailLocalStorageDataSourceImpl extends EmailDataSource { } @override - Future storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer) { + Future storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer) { throw UnimplementedError(); } diff --git a/lib/features/email/data/datasource_impl/email_session_storage_datasource_impl.dart b/lib/features/email/data/datasource_impl/email_session_storage_datasource_impl.dart index 568b65403..ba8af33aa 100644 --- a/lib/features/email/data/datasource_impl/email_session_storage_datasource_impl.dart +++ b/lib/features/email/data/datasource_impl/email_session_storage_datasource_impl.dart @@ -166,7 +166,7 @@ class EmailSessionStorageDatasourceImpl extends EmailDataSource { } @override - Future sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer) { + Future sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer) { throw UnimplementedError(); } @@ -221,9 +221,12 @@ class EmailSessionStorageDatasourceImpl extends EmailDataSource { } @override - Future storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer) { + Future storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer) { return Future.sync(() async { - return _sessionStorageManager.save(keyStored, jsonEncode(emlPreviewer.toJson())); + return _sessionStorageManager.save( + emlPreviewer.id, + jsonEncode(emlPreviewer.toJson()), + ); }).catchError(_exceptionThrower.throwException); } diff --git a/lib/features/email/data/repository/email_repository_impl.dart b/lib/features/email/data/repository/email_repository_impl.dart index a2601dd25..d6d09d4a1 100644 --- a/lib/features/email/data/repository/email_repository_impl.dart +++ b/lib/features/email/data/repository/email_repository_impl.dart @@ -497,8 +497,8 @@ class EmailRepositoryImpl extends EmailRepository { } @override - Future sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer) { - return emailDataSource[DataSourceType.local]!.sharePreviewEmailEMLContent(keyStored, emlPreviewer); + Future sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer) { + return emailDataSource[DataSourceType.local]!.sharePreviewEmailEMLContent(emlPreviewer); } @override @@ -512,8 +512,8 @@ class EmailRepositoryImpl extends EmailRepository { } @override - Future storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer) { - return emailDataSource[DataSourceType.session]!.storePreviewEMLContentToSessionStorage(keyStored, emlPreviewer); + Future storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer) { + return emailDataSource[DataSourceType.session]!.storePreviewEMLContentToSessionStorage(emlPreviewer); } @override diff --git a/lib/features/email/domain/repository/email_repository.dart b/lib/features/email/domain/repository/email_repository.dart index e8a3f05db..83dff5b13 100644 --- a/lib/features/email/domain/repository/email_repository.dart +++ b/lib/features/email/domain/repository/email_repository.dart @@ -186,13 +186,13 @@ abstract class EmailRepository { Future generatePreviewEmailEMLContent(PreviewEmailEMLRequest previewEmailEMLRequest); - Future sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer); + Future sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer); Future getPreviewEmailEMLContentShared(String keyStored); Future removePreviewEmailEMLContentShared(String keyStored); - Future storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer); + Future storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer); Future getPreviewEMLContentInMemory(String keyStored); } \ No newline at end of file diff --git a/lib/features/email/domain/state/get_preview_email_eml_content_shared_state.dart b/lib/features/email/domain/state/get_preview_email_eml_content_shared_state.dart index 709437395..e41295abe 100644 --- a/lib/features/email/domain/state/get_preview_email_eml_content_shared_state.dart +++ b/lib/features/email/domain/state/get_preview_email_eml_content_shared_state.dart @@ -6,13 +6,12 @@ class GettingPreviewEmailEMLContentShared extends LoadingState {} class GetPreviewEmailEMLContentSharedSuccess extends UIState { - final String keyStored; final EMLPreviewer emlPreviewer; - GetPreviewEmailEMLContentSharedSuccess(this.keyStored, this.emlPreviewer); + GetPreviewEmailEMLContentSharedSuccess(this.emlPreviewer); @override - List get props => [keyStored, emlPreviewer]; + List get props => [emlPreviewer]; } class GetPreviewEmailEMLContentSharedFailure extends FeatureFailure { diff --git a/lib/features/email/domain/state/preview_email_from_eml_file_state.dart b/lib/features/email/domain/state/preview_email_from_eml_file_state.dart index 715821b20..30abb03aa 100644 --- a/lib/features/email/domain/state/preview_email_from_eml_file_state.dart +++ b/lib/features/email/domain/state/preview_email_from_eml_file_state.dart @@ -6,13 +6,12 @@ class PreviewingEmailFromEmlFile extends LoadingState {} class PreviewEmailFromEmlFileSuccess extends UIState { - final String storeKey; final EMLPreviewer emlPreviewer; - PreviewEmailFromEmlFileSuccess(this.storeKey, this.emlPreviewer); + PreviewEmailFromEmlFileSuccess(this.emlPreviewer); @override - List get props => [storeKey, emlPreviewer]; + List get props => [emlPreviewer]; } class PreviewEmailFromEmlFileFailure extends FeatureFailure { diff --git a/lib/features/email/domain/usecases/get_preview_email_eml_content_shared_interactor.dart b/lib/features/email/domain/usecases/get_preview_email_eml_content_shared_interactor.dart index be7a894e6..5cc7df496 100644 --- a/lib/features/email/domain/usecases/get_preview_email_eml_content_shared_interactor.dart +++ b/lib/features/email/domain/usecases/get_preview_email_eml_content_shared_interactor.dart @@ -16,7 +16,7 @@ class GetPreviewEmailEMLContentSharedInteractor { final previewEMLContent = await _emailRepository .getPreviewEmailEMLContentShared(keyStored); - yield Right(GetPreviewEmailEMLContentSharedSuccess(keyStored, previewEMLContent)); + yield Right(GetPreviewEmailEMLContentSharedSuccess(previewEMLContent)); } catch (e) { yield Left(GetPreviewEmailEMLContentSharedFailure( keyStored: keyStored, diff --git a/lib/features/email/domain/usecases/move_preview_eml_content_from_persistent_to_memory_interactor.dart b/lib/features/email/domain/usecases/move_preview_eml_content_from_persistent_to_memory_interactor.dart index 7cfd99363..97b04ba42 100644 --- a/lib/features/email/domain/usecases/move_preview_eml_content_from_persistent_to_memory_interactor.dart +++ b/lib/features/email/domain/usecases/move_preview_eml_content_from_persistent_to_memory_interactor.dart @@ -10,12 +10,12 @@ class MovePreviewEmlContentFromPersistentToMemoryInteractor { const MovePreviewEmlContentFromPersistentToMemoryInteractor(this._emailRepository); - Stream> execute(String keyStored, EMLPreviewer emlPreviewer) async* { + Stream> execute(EMLPreviewer emlPreviewer) async* { try { yield Right(MovingPreviewEmailEMLContentFromPersistentToMemory()); await Future.wait([ - _emailRepository.removePreviewEmailEMLContentShared(keyStored), - _emailRepository.storePreviewEMLContentToSessionStorage(keyStored, emlPreviewer), + _emailRepository.removePreviewEmailEMLContentShared(emlPreviewer.id), + _emailRepository.storePreviewEMLContentToSessionStorage(emlPreviewer), ]); yield Right(MovePreviewEmailEMLContentFromPersistentToMemorySuccess()); } catch (e) { diff --git a/lib/features/email/domain/usecases/preview_email_from_eml_file_interactor.dart b/lib/features/email/domain/usecases/preview_email_from_eml_file_interactor.dart index c6b536eb5..4676d275b 100644 --- a/lib/features/email/domain/usecases/preview_email_from_eml_file_interactor.dart +++ b/lib/features/email/domain/usecases/preview_email_from_eml_file_interactor.dart @@ -1,5 +1,6 @@ import 'package:core/presentation/state/failure.dart'; import 'package:core/presentation/state/success.dart'; +import 'package:core/utils/platform_info.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/repository/email_repository.dart'; @@ -20,20 +21,21 @@ class PreviewEmailFromEmlFileInteractor { final previewEMLContent = await _emailRepository .generatePreviewEmailEMLContent(previewEmailEMLRequest); - final keyStored = previewEmailEMLRequest.keyStored; - final emlPreviewer = EMLPreviewer( + id: previewEmailEMLRequest.keyStored, title: previewEmailEMLRequest.title, content: previewEMLContent, ); - if (previewEmailEMLRequest.isShared) { - await _emailRepository.sharePreviewEmailEMLContent(keyStored, emlPreviewer); - } else { - await _emailRepository.storePreviewEMLContentToSessionStorage(keyStored, emlPreviewer); + if (PlatformInfo.isWeb) { + if (previewEmailEMLRequest.isShared) { + await _emailRepository.sharePreviewEmailEMLContent(emlPreviewer); + } else { + await _emailRepository.storePreviewEMLContentToSessionStorage(emlPreviewer); + } } - yield Right(PreviewEmailFromEmlFileSuccess(keyStored, emlPreviewer)); + yield Right(PreviewEmailFromEmlFileSuccess(emlPreviewer)); } catch (e) { yield Left(PreviewEmailFromEmlFileFailure(e)); } diff --git a/lib/features/email/presentation/controller/single_email_controller.dart b/lib/features/email/presentation/controller/single_email_controller.dart index 607f98096..0659f6732 100644 --- a/lib/features/email/presentation/controller/single_email_controller.dart +++ b/lib/features/email/presentation/controller/single_email_controller.dart @@ -99,6 +99,7 @@ import 'package:tmail_ui_user/features/email/presentation/widgets/attachment_lis import 'package:tmail_ui_user/features/email/presentation/widgets/email_address_bottom_sheet_builder.dart'; import 'package:tmail_ui_user/features/email/presentation/widgets/email_address_dialog_builder.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/presentation/action/mailbox_ui_action.dart'; import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart'; @@ -2019,13 +2020,38 @@ class SingleEmailController extends BaseController with AppLoaderMixin { bool isOpen = HtmlUtils.openNewWindowByUrl( RouteUtils.createUrlWebLocationBar( AppRoutes.emailEMLPreviewer, - previewId: success.storeKey + previewId: success.emlPreviewer.id ).toString(), ); if (!isOpen) { toastManager.showMessageFailure(PreviewEmailFromEmlFileFailure(CannotOpenNewWindowException())); } + } else if (PlatformInfo.isMobile) { + if (currentContext == null) { + toastManager.showMessageFailure(PreviewEmailFromEmlFileFailure(NotFoundContextException())); + return; + } + + showModalBottomSheet( + context: currentContext!, + 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: (__, ___) => + EmailPreviewerDialogView(emlPreviewer: success.emlPreviewer) + ); + }, + ); } } diff --git a/lib/features/email/presentation/model/eml_previewer.dart b/lib/features/email/presentation/model/eml_previewer.dart index d358906a8..e2b7c6ce8 100644 --- a/lib/features/email/presentation/model/eml_previewer.dart +++ b/lib/features/email/presentation/model/eml_previewer.dart @@ -6,15 +6,20 @@ part 'eml_previewer.g.dart'; @JsonSerializable(explicitToJson: true, includeIfNull: false) class EMLPreviewer with EquatableMixin { + final String id; final String title; final String content; - EMLPreviewer({required this.title, required this.content}); + EMLPreviewer({ + required this.id, + required this.title, + required this.content, + }); factory EMLPreviewer.fromJson(Map json) => _$EMLPreviewerFromJson(json); Map toJson() => _$EMLPreviewerToJson(this); @override - List get props => [title, content]; + List get props => [id, title, content]; } \ No newline at end of file diff --git a/lib/features/email_previewer/email_previewer_controller.dart b/lib/features/email_previewer/email_previewer_controller.dart index 3f900a81a..bb8ac0832 100644 --- a/lib/features/email_previewer/email_previewer_controller.dart +++ b/lib/features/email_previewer/email_previewer_controller.dart @@ -88,9 +88,7 @@ class EmailPreviewerController extends ReloadableController { } else if (success is GetPreviewEmailEMLContentSharedSuccess) { emlContentViewState.value = Right(success); _updateWindowBrowserTitle(success.emlPreviewer.title); - _movePreviewEmlContentFromPersistentToMemory( - success.keyStored, - success.emlPreviewer); + _movePreviewEmlContentFromPersistentToMemory(success.emlPreviewer); } else if (success is ParseEmailByBlobIdSuccess) { _handleParseEmailByBlobIdSuccess(success); } else if (success is PreviewEmailFromEmlFileSuccess) { @@ -162,15 +160,9 @@ class EmailPreviewerController extends ReloadableController { } } - void _movePreviewEmlContentFromPersistentToMemory( - String keyStored, - EMLPreviewer emlPreviewer, - ) { + void _movePreviewEmlContentFromPersistentToMemory(EMLPreviewer emlPreviewer) { consumeState( - _movePreviewEmlContentFromPersistentToMemoryInteractor.execute( - keyStored, - emlPreviewer, - ), + _movePreviewEmlContentFromPersistentToMemoryInteractor.execute(emlPreviewer), ); } diff --git a/lib/features/email_previewer/email_previewer_dialog_view.dart b/lib/features/email_previewer/email_previewer_dialog_view.dart new file mode 100644 index 000000000..7a7d95101 --- /dev/null +++ b/lib/features/email_previewer/email_previewer_dialog_view.dart @@ -0,0 +1,27 @@ + +import 'package:core/presentation/views/html_viewer/html_content_viewer_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/email/presentation/model/eml_previewer.dart'; +import 'package:tmail_ui_user/main/utils/app_utils.dart'; + +class EmailPreviewerDialogView extends StatelessWidget { + + final EMLPreviewer emlPreviewer; + + const EmailPreviewerDialogView({super.key, required this.emlPreviewer}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: SingleChildScrollView( + child: HtmlContentViewer( + contentHtml: emlPreviewer.content, + initialWidth: context.width, + direction: AppUtils.getCurrentDirection(context), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/features/email_previewer/email_previewer_view.dart b/lib/features/email_previewer/email_previewer_view.dart index 9920daaf4..d5df2aa68 100644 --- a/lib/features/email_previewer/email_previewer_view.dart +++ b/lib/features/email_previewer/email_previewer_view.dart @@ -6,6 +6,7 @@ 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/email/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/email/presentation/model/eml_previewer.dart'; import 'package:tmail_ui_user/features/email_previewer/email_previewer_controller.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/utils/app_utils.dart'; @@ -26,29 +27,11 @@ class EmailPreviewerView extends GetWidget { ), (success) { if (success is GetPreviewEmailEMLContentSharedSuccess) { - return HtmlContentViewerOnWeb( - widthContent: context.width, - heightContent: context.height, - contentHtml: success.emlPreviewer.content, - direction: AppUtils.getCurrentDirection(context), - onClickHyperLinkAction: controller.onClickHyperLink, - ); + return _buildEMLPreviewerWidget(context, success.emlPreviewer); } else if (success is GetPreviewEMLContentInMemorySuccess) { - return HtmlContentViewerOnWeb( - widthContent: context.width, - heightContent: context.height, - contentHtml: success.emlPreviewer.content, - direction: AppUtils.getCurrentDirection(context), - onClickHyperLinkAction: controller.onClickHyperLink, - ); + return _buildEMLPreviewerWidget(context, success.emlPreviewer); } else if (success is PreviewEmailFromEmlFileSuccess) { - return HtmlContentViewerOnWeb( - widthContent: context.width, - heightContent: context.height, - contentHtml: success.emlPreviewer.content, - direction: AppUtils.getCurrentDirection(context), - onClickHyperLinkAction: controller.onClickHyperLink, - ); + return _buildEMLPreviewerWidget(context, success.emlPreviewer); } else { return const Center( child: CupertinoLoadingWidget(), @@ -58,4 +41,14 @@ class EmailPreviewerView extends GetWidget { }), ); } + + Widget _buildEMLPreviewerWidget(BuildContext context, EMLPreviewer emlPreviewer) { + return HtmlContentViewerOnWeb( + widthContent: context.width, + heightContent: context.height, + contentHtml: emlPreviewer.content, + direction: AppUtils.getCurrentDirection(context), + onClickHyperLinkAction: controller.onClickHyperLink, + ); + } } \ No newline at end of file