TF-3416 Handle preview EML attachment on mobile

This commit is contained in:
dab246
2025-01-14 01:27:48 +07:00
committed by Dat H. Pham
parent aa7fded1ff
commit 33d0e91178
17 changed files with 120 additions and 71 deletions
@@ -192,13 +192,13 @@ abstract class EmailDataSource {
Future<String> generatePreviewEmailEMLContent(PreviewEmailEMLRequest previewEmailEMLRequest); Future<String> generatePreviewEmailEMLContent(PreviewEmailEMLRequest previewEmailEMLRequest);
Future<void> sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer); Future<void> sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer);
Future<EMLPreviewer> getPreviewEmailEMLContentShared(String keyStored); Future<EMLPreviewer> getPreviewEmailEMLContentShared(String keyStored);
Future<void> removePreviewEmailEMLContentShared(String keyStored); Future<void> removePreviewEmailEMLContentShared(String keyStored);
Future<void> storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer); Future<void> storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer);
Future<EMLPreviewer> getPreviewEMLContentInMemory(String keyStored); Future<EMLPreviewer> getPreviewEMLContentInMemory(String keyStored);
} }
@@ -452,7 +452,7 @@ class EmailDataSourceImpl extends EmailDataSource {
final iconBase64Data = await _fileUtils.convertImageAssetToBase64( final iconBase64Data = await _fileUtils.convertImageAssetToBase64(
attachment.getIcon(_imagePaths)); attachment.getIcon(_imagePaths));
final link = attachment.isEMLFile final link = attachment.isEMLFile && PlatformInfo.isWeb
? RouteUtils.createUrlWebLocationBar( ? RouteUtils.createUrlWebLocationBar(
AppRoutes.emailEMLPreviewer, AppRoutes.emailEMLPreviewer,
previewId: attachment.blobId?.value, previewId: attachment.blobId?.value,
@@ -526,7 +526,7 @@ class EmailDataSourceImpl extends EmailDataSource {
} }
@override @override
Future<void> sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer) { Future<void> sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer) {
throw UnimplementedError(); throw UnimplementedError();
} }
@@ -536,7 +536,7 @@ class EmailDataSourceImpl extends EmailDataSource {
} }
@override @override
Future<void> storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer) { Future<void> storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer) {
throw UnimplementedError(); throw UnimplementedError();
} }
@@ -540,7 +540,7 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
} }
@override @override
Future<void> sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer) { Future<void> sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer) {
throw UnimplementedError(); throw UnimplementedError();
} }
@@ -555,7 +555,7 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
} }
@override @override
Future<void> storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer) { Future<void> storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer) {
throw UnimplementedError(); throw UnimplementedError();
} }
@@ -166,9 +166,12 @@ class EmailLocalStorageDataSourceImpl extends EmailDataSource {
} }
@override @override
Future<void> sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer) { Future<void> sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer) {
return Future.sync(() async { return Future.sync(() async {
return _localStorageManager.save(keyStored, jsonEncode(emlPreviewer.toJson())); return _localStorageManager.save(
emlPreviewer.id,
jsonEncode(emlPreviewer.toJson()),
);
}).catchError(_exceptionThrower.throwException); }).catchError(_exceptionThrower.throwException);
} }
@@ -226,7 +229,7 @@ class EmailLocalStorageDataSourceImpl extends EmailDataSource {
} }
@override @override
Future<void> storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer) { Future<void> storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer) {
throw UnimplementedError(); throw UnimplementedError();
} }
@@ -166,7 +166,7 @@ class EmailSessionStorageDatasourceImpl extends EmailDataSource {
} }
@override @override
Future<void> sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer) { Future<void> sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer) {
throw UnimplementedError(); throw UnimplementedError();
} }
@@ -221,9 +221,12 @@ class EmailSessionStorageDatasourceImpl extends EmailDataSource {
} }
@override @override
Future<void> storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer) { Future<void> storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer) {
return Future.sync(() async { return Future.sync(() async {
return _sessionStorageManager.save(keyStored, jsonEncode(emlPreviewer.toJson())); return _sessionStorageManager.save(
emlPreviewer.id,
jsonEncode(emlPreviewer.toJson()),
);
}).catchError(_exceptionThrower.throwException); }).catchError(_exceptionThrower.throwException);
} }
@@ -497,8 +497,8 @@ class EmailRepositoryImpl extends EmailRepository {
} }
@override @override
Future<void> sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer) { Future<void> sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer) {
return emailDataSource[DataSourceType.local]!.sharePreviewEmailEMLContent(keyStored, emlPreviewer); return emailDataSource[DataSourceType.local]!.sharePreviewEmailEMLContent(emlPreviewer);
} }
@override @override
@@ -512,8 +512,8 @@ class EmailRepositoryImpl extends EmailRepository {
} }
@override @override
Future<void> storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer) { Future<void> storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer) {
return emailDataSource[DataSourceType.session]!.storePreviewEMLContentToSessionStorage(keyStored, emlPreviewer); return emailDataSource[DataSourceType.session]!.storePreviewEMLContentToSessionStorage(emlPreviewer);
} }
@override @override
@@ -186,13 +186,13 @@ abstract class EmailRepository {
Future<String> generatePreviewEmailEMLContent(PreviewEmailEMLRequest previewEmailEMLRequest); Future<String> generatePreviewEmailEMLContent(PreviewEmailEMLRequest previewEmailEMLRequest);
Future<void> sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer); Future<void> sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer);
Future<EMLPreviewer> getPreviewEmailEMLContentShared(String keyStored); Future<EMLPreviewer> getPreviewEmailEMLContentShared(String keyStored);
Future<void> removePreviewEmailEMLContentShared(String keyStored); Future<void> removePreviewEmailEMLContentShared(String keyStored);
Future<void> storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer); Future<void> storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer);
Future<EMLPreviewer> getPreviewEMLContentInMemory(String keyStored); Future<EMLPreviewer> getPreviewEMLContentInMemory(String keyStored);
} }
@@ -6,13 +6,12 @@ class GettingPreviewEmailEMLContentShared extends LoadingState {}
class GetPreviewEmailEMLContentSharedSuccess extends UIState { class GetPreviewEmailEMLContentSharedSuccess extends UIState {
final String keyStored;
final EMLPreviewer emlPreviewer; final EMLPreviewer emlPreviewer;
GetPreviewEmailEMLContentSharedSuccess(this.keyStored, this.emlPreviewer); GetPreviewEmailEMLContentSharedSuccess(this.emlPreviewer);
@override @override
List<Object?> get props => [keyStored, emlPreviewer]; List<Object?> get props => [emlPreviewer];
} }
class GetPreviewEmailEMLContentSharedFailure extends FeatureFailure { class GetPreviewEmailEMLContentSharedFailure extends FeatureFailure {
@@ -6,13 +6,12 @@ class PreviewingEmailFromEmlFile extends LoadingState {}
class PreviewEmailFromEmlFileSuccess extends UIState { class PreviewEmailFromEmlFileSuccess extends UIState {
final String storeKey;
final EMLPreviewer emlPreviewer; final EMLPreviewer emlPreviewer;
PreviewEmailFromEmlFileSuccess(this.storeKey, this.emlPreviewer); PreviewEmailFromEmlFileSuccess(this.emlPreviewer);
@override @override
List<Object?> get props => [storeKey, emlPreviewer]; List<Object?> get props => [emlPreviewer];
} }
class PreviewEmailFromEmlFileFailure extends FeatureFailure { class PreviewEmailFromEmlFileFailure extends FeatureFailure {
@@ -16,7 +16,7 @@ class GetPreviewEmailEMLContentSharedInteractor {
final previewEMLContent = await _emailRepository final previewEMLContent = await _emailRepository
.getPreviewEmailEMLContentShared(keyStored); .getPreviewEmailEMLContentShared(keyStored);
yield Right<Failure, Success>(GetPreviewEmailEMLContentSharedSuccess(keyStored, previewEMLContent)); yield Right<Failure, Success>(GetPreviewEmailEMLContentSharedSuccess(previewEMLContent));
} catch (e) { } catch (e) {
yield Left<Failure, Success>(GetPreviewEmailEMLContentSharedFailure( yield Left<Failure, Success>(GetPreviewEmailEMLContentSharedFailure(
keyStored: keyStored, keyStored: keyStored,
@@ -10,12 +10,12 @@ class MovePreviewEmlContentFromPersistentToMemoryInteractor {
const MovePreviewEmlContentFromPersistentToMemoryInteractor(this._emailRepository); const MovePreviewEmlContentFromPersistentToMemoryInteractor(this._emailRepository);
Stream<Either<Failure, Success>> execute(String keyStored, EMLPreviewer emlPreviewer) async* { Stream<Either<Failure, Success>> execute(EMLPreviewer emlPreviewer) async* {
try { try {
yield Right<Failure, Success>(MovingPreviewEmailEMLContentFromPersistentToMemory()); yield Right<Failure, Success>(MovingPreviewEmailEMLContentFromPersistentToMemory());
await Future.wait([ await Future.wait([
_emailRepository.removePreviewEmailEMLContentShared(keyStored), _emailRepository.removePreviewEmailEMLContentShared(emlPreviewer.id),
_emailRepository.storePreviewEMLContentToSessionStorage(keyStored, emlPreviewer), _emailRepository.storePreviewEMLContentToSessionStorage(emlPreviewer),
]); ]);
yield Right<Failure, Success>(MovePreviewEmailEMLContentFromPersistentToMemorySuccess()); yield Right<Failure, Success>(MovePreviewEmailEMLContentFromPersistentToMemorySuccess());
} catch (e) { } catch (e) {
@@ -1,5 +1,6 @@
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/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/repository/email_repository.dart'; import 'package:tmail_ui_user/features/email/domain/repository/email_repository.dart';
@@ -20,20 +21,21 @@ class PreviewEmailFromEmlFileInteractor {
final previewEMLContent = await _emailRepository final previewEMLContent = await _emailRepository
.generatePreviewEmailEMLContent(previewEmailEMLRequest); .generatePreviewEmailEMLContent(previewEmailEMLRequest);
final keyStored = previewEmailEMLRequest.keyStored;
final emlPreviewer = EMLPreviewer( final emlPreviewer = EMLPreviewer(
id: previewEmailEMLRequest.keyStored,
title: previewEmailEMLRequest.title, title: previewEmailEMLRequest.title,
content: previewEMLContent, content: previewEMLContent,
); );
if (previewEmailEMLRequest.isShared) { if (PlatformInfo.isWeb) {
await _emailRepository.sharePreviewEmailEMLContent(keyStored, emlPreviewer); if (previewEmailEMLRequest.isShared) {
} else { await _emailRepository.sharePreviewEmailEMLContent(emlPreviewer);
await _emailRepository.storePreviewEMLContentToSessionStorage(keyStored, emlPreviewer); } else {
await _emailRepository.storePreviewEMLContentToSessionStorage(emlPreviewer);
}
} }
yield Right<Failure, Success>(PreviewEmailFromEmlFileSuccess(keyStored, emlPreviewer)); yield Right<Failure, Success>(PreviewEmailFromEmlFileSuccess(emlPreviewer));
} catch (e) { } catch (e) {
yield Left<Failure, Success>(PreviewEmailFromEmlFileFailure(e)); yield Left<Failure, Success>(PreviewEmailFromEmlFileFailure(e));
} }
@@ -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_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/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/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/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/action/mailbox_ui_action.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.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( bool isOpen = HtmlUtils.openNewWindowByUrl(
RouteUtils.createUrlWebLocationBar( RouteUtils.createUrlWebLocationBar(
AppRoutes.emailEMLPreviewer, AppRoutes.emailEMLPreviewer,
previewId: success.storeKey previewId: success.emlPreviewer.id
).toString(), ).toString(),
); );
if (!isOpen) { if (!isOpen) {
toastManager.showMessageFailure(PreviewEmailFromEmlFileFailure(CannotOpenNewWindowException())); 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)
);
},
);
} }
} }
@@ -6,15 +6,20 @@ part 'eml_previewer.g.dart';
@JsonSerializable(explicitToJson: true, includeIfNull: false) @JsonSerializable(explicitToJson: true, includeIfNull: false)
class EMLPreviewer with EquatableMixin { class EMLPreviewer with EquatableMixin {
final String id;
final String title; final String title;
final String content; final String content;
EMLPreviewer({required this.title, required this.content}); EMLPreviewer({
required this.id,
required this.title,
required this.content,
});
factory EMLPreviewer.fromJson(Map<String, dynamic> json) => _$EMLPreviewerFromJson(json); factory EMLPreviewer.fromJson(Map<String, dynamic> json) => _$EMLPreviewerFromJson(json);
Map<String, dynamic> toJson() => _$EMLPreviewerToJson(this); Map<String, dynamic> toJson() => _$EMLPreviewerToJson(this);
@override @override
List<Object?> get props => [title, content]; List<Object?> get props => [id, title, content];
} }
@@ -88,9 +88,7 @@ class EmailPreviewerController extends ReloadableController {
} else if (success is GetPreviewEmailEMLContentSharedSuccess) { } else if (success is GetPreviewEmailEMLContentSharedSuccess) {
emlContentViewState.value = Right(success); emlContentViewState.value = Right(success);
_updateWindowBrowserTitle(success.emlPreviewer.title); _updateWindowBrowserTitle(success.emlPreviewer.title);
_movePreviewEmlContentFromPersistentToMemory( _movePreviewEmlContentFromPersistentToMemory(success.emlPreviewer);
success.keyStored,
success.emlPreviewer);
} else if (success is ParseEmailByBlobIdSuccess) { } else if (success is ParseEmailByBlobIdSuccess) {
_handleParseEmailByBlobIdSuccess(success); _handleParseEmailByBlobIdSuccess(success);
} else if (success is PreviewEmailFromEmlFileSuccess) { } else if (success is PreviewEmailFromEmlFileSuccess) {
@@ -162,15 +160,9 @@ class EmailPreviewerController extends ReloadableController {
} }
} }
void _movePreviewEmlContentFromPersistentToMemory( void _movePreviewEmlContentFromPersistentToMemory(EMLPreviewer emlPreviewer) {
String keyStored,
EMLPreviewer emlPreviewer,
) {
consumeState( consumeState(
_movePreviewEmlContentFromPersistentToMemoryInteractor.execute( _movePreviewEmlContentFromPersistentToMemoryInteractor.execute(emlPreviewer),
keyStored,
emlPreviewer,
),
); );
} }
@@ -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),
),
),
);
}
}
@@ -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_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/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/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/features/email_previewer/email_previewer_controller.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/utils/app_utils.dart'; import 'package:tmail_ui_user/main/utils/app_utils.dart';
@@ -26,29 +27,11 @@ class EmailPreviewerView extends GetWidget<EmailPreviewerController> {
), ),
(success) { (success) {
if (success is GetPreviewEmailEMLContentSharedSuccess) { if (success is GetPreviewEmailEMLContentSharedSuccess) {
return HtmlContentViewerOnWeb( return _buildEMLPreviewerWidget(context, success.emlPreviewer);
widthContent: context.width,
heightContent: context.height,
contentHtml: success.emlPreviewer.content,
direction: AppUtils.getCurrentDirection(context),
onClickHyperLinkAction: controller.onClickHyperLink,
);
} else if (success is GetPreviewEMLContentInMemorySuccess) { } else if (success is GetPreviewEMLContentInMemorySuccess) {
return HtmlContentViewerOnWeb( return _buildEMLPreviewerWidget(context, success.emlPreviewer);
widthContent: context.width,
heightContent: context.height,
contentHtml: success.emlPreviewer.content,
direction: AppUtils.getCurrentDirection(context),
onClickHyperLinkAction: controller.onClickHyperLink,
);
} else if (success is PreviewEmailFromEmlFileSuccess) { } else if (success is PreviewEmailFromEmlFileSuccess) {
return HtmlContentViewerOnWeb( return _buildEMLPreviewerWidget(context, success.emlPreviewer);
widthContent: context.width,
heightContent: context.height,
contentHtml: success.emlPreviewer.content,
direction: AppUtils.getCurrentDirection(context),
onClickHyperLinkAction: controller.onClickHyperLink,
);
} else { } else {
return const Center( return const Center(
child: CupertinoLoadingWidget(), child: CupertinoLoadingWidget(),
@@ -58,4 +41,14 @@ class EmailPreviewerView extends GetWidget<EmailPreviewerController> {
}), }),
); );
} }
Widget _buildEMLPreviewerWidget(BuildContext context, EMLPreviewer emlPreviewer) {
return HtmlContentViewerOnWeb(
widthContent: context.width,
heightContent: context.height,
contentHtml: emlPreviewer.content,
direction: AppUtils.getCurrentDirection(context),
onClickHyperLinkAction: controller.onClickHyperLink,
);
}
} }