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<void> sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer);
Future<void> sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer);
Future<EMLPreviewer> getPreviewEmailEMLContentShared(String keyStored);
Future<void> removePreviewEmailEMLContentShared(String keyStored);
Future<void> storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer);
Future<void> storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer);
Future<EMLPreviewer> getPreviewEMLContentInMemory(String keyStored);
}
@@ -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<void> sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer) {
Future<void> sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer) {
throw UnimplementedError();
}
@@ -536,7 +536,7 @@ class EmailDataSourceImpl extends EmailDataSource {
}
@override
Future<void> storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer) {
Future<void> storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer) {
throw UnimplementedError();
}
@@ -540,7 +540,7 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
}
@override
Future<void> sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer) {
Future<void> sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer) {
throw UnimplementedError();
}
@@ -555,7 +555,7 @@ class EmailHiveCacheDataSourceImpl extends EmailDataSource {
}
@override
Future<void> storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer) {
Future<void> storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer) {
throw UnimplementedError();
}
@@ -166,9 +166,12 @@ class EmailLocalStorageDataSourceImpl extends EmailDataSource {
}
@override
Future<void> sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer) {
Future<void> 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<void> storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer) {
Future<void> storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer) {
throw UnimplementedError();
}
@@ -166,7 +166,7 @@ class EmailSessionStorageDatasourceImpl extends EmailDataSource {
}
@override
Future<void> sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer) {
Future<void> sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer) {
throw UnimplementedError();
}
@@ -221,9 +221,12 @@ class EmailSessionStorageDatasourceImpl extends EmailDataSource {
}
@override
Future<void> storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer) {
Future<void> 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);
}
@@ -497,8 +497,8 @@ class EmailRepositoryImpl extends EmailRepository {
}
@override
Future<void> sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer) {
return emailDataSource[DataSourceType.local]!.sharePreviewEmailEMLContent(keyStored, emlPreviewer);
Future<void> sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer) {
return emailDataSource[DataSourceType.local]!.sharePreviewEmailEMLContent(emlPreviewer);
}
@override
@@ -512,8 +512,8 @@ class EmailRepositoryImpl extends EmailRepository {
}
@override
Future<void> storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer) {
return emailDataSource[DataSourceType.session]!.storePreviewEMLContentToSessionStorage(keyStored, emlPreviewer);
Future<void> storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer) {
return emailDataSource[DataSourceType.session]!.storePreviewEMLContentToSessionStorage(emlPreviewer);
}
@override
@@ -186,13 +186,13 @@ abstract class EmailRepository {
Future<String> generatePreviewEmailEMLContent(PreviewEmailEMLRequest previewEmailEMLRequest);
Future<void> sharePreviewEmailEMLContent(String keyStored, EMLPreviewer emlPreviewer);
Future<void> sharePreviewEmailEMLContent(EMLPreviewer emlPreviewer);
Future<EMLPreviewer> getPreviewEmailEMLContentShared(String keyStored);
Future<void> removePreviewEmailEMLContentShared(String keyStored);
Future<void> storePreviewEMLContentToSessionStorage(String keyStored, EMLPreviewer emlPreviewer);
Future<void> storePreviewEMLContentToSessionStorage(EMLPreviewer emlPreviewer);
Future<EMLPreviewer> getPreviewEMLContentInMemory(String keyStored);
}
@@ -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<Object?> get props => [keyStored, emlPreviewer];
List<Object?> get props => [emlPreviewer];
}
class GetPreviewEmailEMLContentSharedFailure extends FeatureFailure {
@@ -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<Object?> get props => [storeKey, emlPreviewer];
List<Object?> get props => [emlPreviewer];
}
class PreviewEmailFromEmlFileFailure extends FeatureFailure {
@@ -16,7 +16,7 @@ class GetPreviewEmailEMLContentSharedInteractor {
final previewEMLContent = await _emailRepository
.getPreviewEmailEMLContentShared(keyStored);
yield Right<Failure, Success>(GetPreviewEmailEMLContentSharedSuccess(keyStored, previewEMLContent));
yield Right<Failure, Success>(GetPreviewEmailEMLContentSharedSuccess(previewEMLContent));
} catch (e) {
yield Left<Failure, Success>(GetPreviewEmailEMLContentSharedFailure(
keyStored: keyStored,
@@ -10,12 +10,12 @@ class MovePreviewEmlContentFromPersistentToMemoryInteractor {
const MovePreviewEmlContentFromPersistentToMemoryInteractor(this._emailRepository);
Stream<Either<Failure, Success>> execute(String keyStored, EMLPreviewer emlPreviewer) async* {
Stream<Either<Failure, Success>> execute(EMLPreviewer emlPreviewer) async* {
try {
yield Right<Failure, Success>(MovingPreviewEmailEMLContentFromPersistentToMemory());
await Future.wait([
_emailRepository.removePreviewEmailEMLContentShared(keyStored),
_emailRepository.storePreviewEMLContentToSessionStorage(keyStored, emlPreviewer),
_emailRepository.removePreviewEmailEMLContentShared(emlPreviewer.id),
_emailRepository.storePreviewEMLContentToSessionStorage(emlPreviewer),
]);
yield Right<Failure, Success>(MovePreviewEmailEMLContentFromPersistentToMemorySuccess());
} catch (e) {
@@ -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<Failure, Success>(PreviewEmailFromEmlFileSuccess(keyStored, emlPreviewer));
yield Right<Failure, Success>(PreviewEmailFromEmlFileSuccess(emlPreviewer));
} catch (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_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)
);
},
);
}
}
@@ -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<String, dynamic> json) => _$EMLPreviewerFromJson(json);
Map<String, dynamic> toJson() => _$EMLPreviewerToJson(this);
@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) {
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),
);
}
@@ -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_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<EmailPreviewerController> {
),
(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<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,
);
}
}