From e63a8f7d7fb5f68aa937d51ff84f6c5a56ddb552 Mon Sep 17 00:00:00 2001 From: dab246 Date: Fri, 8 Sep 2023 11:41:35 +0700 Subject: [PATCH] TF-2116 Insert image at mouse cursor position (cherry picked from commit cb347e5077eddb1366daf706aa87eb2291f71aab) --- .../state/download_image_as_base64_state.dart | 17 ++++++++-- .../domain/state/upload_attachment_state.dart | 30 +++++++++++++++--- .../download_image_as_base64_interactor.dart | 31 ++++++++++++------- .../upload_attachment_interactor.dart | 15 +++++++-- .../presentation/composer_controller.dart | 26 +++++++++++----- .../rich_text_mobile_tablet_controller.dart | 14 +++++++-- .../domain/state/attachment_upload_state.dart | 17 ++++++++-- .../controller/upload_controller.dart | 29 ++++++++++++----- .../upload_attachment_extension.dart | 5 +-- .../presentation/model/upload_file_state.dart | 29 +++++++++++------ .../model/upload_file_state_list.dart | 4 +++ 11 files changed, 164 insertions(+), 53 deletions(-) diff --git a/lib/features/composer/domain/state/download_image_as_base64_state.dart b/lib/features/composer/domain/state/download_image_as_base64_state.dart index 25997c285..af801f1a2 100644 --- a/lib/features/composer/domain/state/download_image_as_base64_state.dart +++ b/lib/features/composer/domain/state/download_image_as_base64_state.dart @@ -10,11 +10,24 @@ class DownloadImageAsBase64Success extends UIState { final String base64Uri; final String cid; final FileInfo fileInfo; + final bool fromFileShared; - DownloadImageAsBase64Success(this.base64Uri, this.cid, this.fileInfo); + DownloadImageAsBase64Success( + this.base64Uri, + this.cid, + this.fileInfo, + { + this.fromFileShared = false + } + ); @override - List get props => [base64Uri, cid, fileInfo]; + List get props => [ + base64Uri, + cid, + fileInfo, + fromFileShared, + ]; } class DownloadImageAsBase64Failure extends FeatureFailure { diff --git a/lib/features/composer/domain/state/upload_attachment_state.dart b/lib/features/composer/domain/state/upload_attachment_state.dart index 3c67a365b..c58d04d11 100644 --- a/lib/features/composer/domain/state/upload_attachment_state.dart +++ b/lib/features/composer/domain/state/upload_attachment_state.dart @@ -7,18 +7,40 @@ class UploadAttachmentSuccess extends UIState { final UploadAttachment uploadAttachment; final bool isInline; + final bool fromFileShared; - UploadAttachmentSuccess(this.uploadAttachment, {this.isInline = false}); + UploadAttachmentSuccess( + this.uploadAttachment, + { + this.isInline = false, + this.fromFileShared = false, + } + ); @override - List get props => [uploadAttachment, isInline]; + List get props => [ + uploadAttachment, + isInline, + fromFileShared, + ]; } class UploadAttachmentFailure extends FeatureFailure { final bool isInline; + final bool fromFileShared; - UploadAttachmentFailure(dynamic exception, {this.isInline = false}) : super(exception: exception); + UploadAttachmentFailure( + dynamic exception, + { + this.isInline = false, + this.fromFileShared = false, + } + ) : super(exception: exception); @override - List get props => [isInline, ...super.props]; + List get props => [ + isInline, + fromFileShared, + ...super.props + ]; } \ No newline at end of file diff --git a/lib/features/composer/domain/usecases/download_image_as_base64_interactor.dart b/lib/features/composer/domain/usecases/download_image_as_base64_interactor.dart index 3f37fb5bf..b3fb5d0ac 100644 --- a/lib/features/composer/domain/usecases/download_image_as_base64_interactor.dart +++ b/lib/features/composer/domain/usecases/download_image_as_base64_interactor.dart @@ -10,24 +10,31 @@ class DownloadImageAsBase64Interactor { DownloadImageAsBase64Interactor(this._composerRepository); Stream> execute( - String url, - String cid, - FileInfo fileInfo, - { - double? maxWidth, - bool? compress - } + String url, + String cid, + FileInfo fileInfo, + { + double? maxWidth, + bool? compress, + bool fromFileShared = false, + } ) async* { try { yield Right(DownloadingImageAsBase64()); final result = await _composerRepository.downloadImageAsBase64( - url, + url, + cid, + fileInfo, + maxWidth: maxWidth, + compress: compress + ); + if (result?.isNotEmpty == true) { + yield Right(DownloadImageAsBase64Success( + result!, cid, fileInfo, - maxWidth: maxWidth, - compress: compress); - if (result?.isNotEmpty == true) { - yield Right(DownloadImageAsBase64Success(result!, cid, fileInfo)); + fromFileShared: fromFileShared + )); } else { yield Left(DownloadImageAsBase64Failure(null)); } diff --git a/lib/features/composer/domain/usecases/upload_attachment_interactor.dart b/lib/features/composer/domain/usecases/upload_attachment_interactor.dart index 52e3a45d5..6abdcf801 100644 --- a/lib/features/composer/domain/usecases/upload_attachment_interactor.dart +++ b/lib/features/composer/domain/usecases/upload_attachment_interactor.dart @@ -15,7 +15,8 @@ class UploadAttachmentInteractor { FileInfo fileInfo, Uri uploadUri, { CancelToken? cancelToken, - bool isInline = false + bool isInline = false, + bool fromFileShared = false, }) async* { try { final uploadAttachment = await _composerRepository.uploadAttachment( @@ -23,9 +24,17 @@ class UploadAttachmentInteractor { uploadUri, cancelToken: cancelToken ); - yield Right(UploadAttachmentSuccess(uploadAttachment, isInline: isInline)); + yield Right(UploadAttachmentSuccess( + uploadAttachment, + isInline: isInline, + fromFileShared: fromFileShared + )); } catch (e) { - yield Left(UploadAttachmentFailure(e, isInline: isInline)); + yield Left(UploadAttachmentFailure( + e, + isInline: isInline, + fromFileShared: fromFileShared + )); } } } \ No newline at end of file diff --git a/lib/features/composer/presentation/composer_controller.dart b/lib/features/composer/presentation/composer_controller.dart index a5f161d5a..d31cd3b69 100644 --- a/lib/features/composer/presentation/composer_controller.dart +++ b/lib/features/composer/presentation/composer_controller.dart @@ -311,7 +311,10 @@ class ComposerController extends BaseController { ImageSource.local, fileInfo: success.fileInfo, cid: success.cid, - base64Uri: success.base64Uri)); + base64Uri: success.base64Uri + ), + fromFileShare: success.fromFileShared + ); } maxWithEditor = null; } @@ -1235,7 +1238,7 @@ class ComposerController extends BaseController { if (listImageSharedMediaFile.isNotEmpty) { final listInlineImage = covertListSharedMediaFileToInlineImage(listSharedMediaFile); for (var e in listInlineImage) { - _uploadInlineAttachmentsAction(e.fileInfo!); + _uploadInlineAttachmentsAction(e.fileInfo!, fromFileShared: true); } } if (listFileAttachmentSharedMediaFile.isNotEmpty) { @@ -1681,13 +1684,18 @@ class ComposerController extends BaseController { } } - void _uploadInlineAttachmentsAction(FileInfo pickedFile) async { + void _uploadInlineAttachmentsAction(FileInfo pickedFile, {bool fromFileShared = false}) async { if (uploadController.hasEnoughMaxAttachmentSize(listFiles: [pickedFile])) { final session = mailboxDashBoardController.sessionCurrent; final accountId = mailboxDashBoardController.accountId.value; if (session != null && accountId != null) { final uploadUri = session.getUploadUri(accountId, jmapUrl: _dynamicUrlInterceptors.jmapUrl); - uploadController.uploadFileAction(pickedFile, uploadUri, isInline: true); + uploadController.uploadFileAction( + pickedFile, + uploadUri, + isInline: true, + fromFileShared: fromFileShared + ); } } else { if (currentContext != null) { @@ -1713,10 +1721,12 @@ class ComposerController extends BaseController { final imageUrl = uploadState.attachment.getDownloadUrl(baseDownloadUrl, accountId); log('ComposerController::_handleUploadInlineSuccess(): imageUrl: $imageUrl'); consumeState(_downloadImageAsBase64Interactor.execute( - imageUrl, - uploadState.attachment.cid!, - uploadState.fileInfo, - maxWidth: maxWithEditor)); + imageUrl, + uploadState.attachment.cid!, + uploadState.fileInfo, + maxWidth: maxWithEditor, + fromFileShared: uploadState.fromFileShared, + )); } } diff --git a/lib/features/composer/presentation/controller/rich_text_mobile_tablet_controller.dart b/lib/features/composer/presentation/controller/rich_text_mobile_tablet_controller.dart index ccd247bc1..43a0fa912 100644 --- a/lib/features/composer/presentation/controller/rich_text_mobile_tablet_controller.dart +++ b/lib/features/composer/presentation/controller/rich_text_mobile_tablet_controller.dart @@ -9,12 +9,20 @@ import 'package:tmail_ui_user/features/composer/presentation/model/inline_image. class RichTextMobileTabletController extends BaseRichTextController { HtmlEditorApi? htmlEditorApi; - void insertImage(InlineImage image, {double? maxWithEditor}) async { - log('RichTextMobileTabletController::insertImage(): $image | maxWithEditor: $maxWithEditor'); + void insertImage( + InlineImage image, + { + double? maxWithEditor, + bool fromFileShare = false + } + ) async { + log('RichTextMobileTabletController::insertImage(): $image | maxWithEditor: $maxWithEditor | $fromFileShare'); if (image.source == ImageSource.network) { htmlEditorApi?.insertImageLink(image.link!); } else { - await htmlEditorApi?.moveCursorAtLastNode(); + if (fromFileShare) { + await htmlEditorApi?.moveCursorAtLastNode(); + } await htmlEditorApi?.insertHtml(image.base64Uri ?? ''); } } diff --git a/lib/features/upload/domain/state/attachment_upload_state.dart b/lib/features/upload/domain/state/attachment_upload_state.dart index 5880651d8..89fad3396 100644 --- a/lib/features/upload/domain/state/attachment_upload_state.dart +++ b/lib/features/upload/domain/state/attachment_upload_state.dart @@ -31,11 +31,24 @@ class SuccessAttachmentUploadState extends Success { final UploadTaskId uploadId; final Attachment attachment; final FileInfo fileInfo; + final bool fromFileShared; - SuccessAttachmentUploadState(this.uploadId, this.attachment, this.fileInfo); + SuccessAttachmentUploadState( + this.uploadId, + this.attachment, + this.fileInfo, + { + this.fromFileShared = false + } + ); @override - List get props => [uploadId, attachment, fileInfo]; + List get props => [ + uploadId, + attachment, + fileInfo, + fromFileShared, + ]; } class ErrorAttachmentUploadState extends Failure { diff --git a/lib/features/upload/presentation/controller/upload_controller.dart b/lib/features/upload/presentation/controller/upload_controller.dart index c842e2af9..01fc51863 100644 --- a/lib/features/upload/presentation/controller/upload_controller.dart +++ b/lib/features/upload/presentation/controller/upload_controller.dart @@ -157,8 +157,13 @@ class UploadController extends BaseController { } else if (success is SuccessAttachmentUploadState) { log('UploadController::_handleProgressUploadInlineImageStateStream():succeed[${success.uploadId}]'); final inlineAttachment = success.attachment.toAttachmentWithDisposition( - disposition: ContentDisposition.inline, - cid: _uuid.v1()); + disposition: ContentDisposition.inline, + cid: _uuid.v1() + ); + + final uploadFileState = _uploadingStateInlineFiles.getUploadFileStateById(success.uploadId); + log('UploadController::_handleProgressUploadInlineImageStateStream:uploadId: ${uploadFileState?.uploadTaskId} | fromFileShared: ${uploadFileState?.fromFileShared}'); + _uploadingStateInlineFiles.updateElementByUploadTaskId( success.uploadId, (currentState) { @@ -173,7 +178,9 @@ class UploadController extends BaseController { final newUploadSuccess = SuccessAttachmentUploadState( success.uploadId, inlineAttachment, - success.fileInfo); + success.fileInfo, + fromFileShared: uploadFileState?.fromFileShared ?? false + ); _handleUploadInlineAttachmentsSuccess(newUploadSuccess); } } @@ -202,13 +209,21 @@ class UploadController extends BaseController { }); } - Future uploadFileAction(FileInfo uploadFile, Uri uploadUri, {bool isInline = false}) { - log('UploadController::_uploadFile():fileName: ${uploadFile.fileName}'); + Future uploadFileAction( + FileInfo uploadFile, + Uri uploadUri, + { + bool isInline = false, + bool fromFileShared = false, + } + ) { + log('UploadController::_uploadFile():fileName: ${uploadFile.fileName} | isInline: $isInline | fromFileShared: $fromFileShared'); consumeState(_uploadAttachmentInteractor.execute( uploadFile, uploadUri, cancelToken: CancelToken(), - isInline: isInline + isInline: isInline, + fromFileShared: fromFileShared )); return Future.value(); } @@ -368,7 +383,7 @@ class UploadController extends BaseController { super.handleSuccessViewState(success); if (success is UploadAttachmentSuccess) { if (success.isInline) { - _uploadingStateInlineFiles.add(success.uploadAttachment.toUploadFileState()); + _uploadingStateInlineFiles.add(success.uploadAttachment.toUploadFileState(fromFileShared: success.fromFileShared)); await _progressUploadInlineImageStateStreamGroup.add(success.uploadAttachment.progressState); } else { _uploadingStateFiles.add(success.uploadAttachment.toUploadFileState()); diff --git a/lib/features/upload/presentation/extensions/upload_attachment_extension.dart b/lib/features/upload/presentation/extensions/upload_attachment_extension.dart index b03f846b9..4b45c9427 100644 --- a/lib/features/upload/presentation/extensions/upload_attachment_extension.dart +++ b/lib/features/upload/presentation/extensions/upload_attachment_extension.dart @@ -4,11 +4,12 @@ import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_sta extension UploadAttachmentExtension on UploadAttachment { - UploadFileState toUploadFileState() { + UploadFileState toUploadFileState({bool fromFileShared = false}) { return UploadFileState( uploadTaskId, file: fileInfo, - cancelToken: cancelToken + cancelToken: cancelToken, + fromFileShared: fromFileShared, ); } } \ No newline at end of file diff --git a/lib/features/upload/presentation/model/upload_file_state.dart b/lib/features/upload/presentation/model/upload_file_state.dart index 17174f6d9..8b4f67c3a 100644 --- a/lib/features/upload/presentation/model/upload_file_state.dart +++ b/lib/features/upload/presentation/model/upload_file_state.dart @@ -18,14 +18,19 @@ class UploadFileState with EquatableMixin { final int uploadingProgress; final Attachment? attachment; final CancelToken? cancelToken; + final bool fromFileShared; - UploadFileState(this.uploadTaskId, { - this.file, - this.uploadStatus = UploadFileStatus.waiting, - this.uploadingProgress = 0, - this.attachment, - this.cancelToken, - }); + UploadFileState( + this.uploadTaskId, + { + this.file, + this.uploadStatus = UploadFileStatus.waiting, + this.uploadingProgress = 0, + this.attachment, + this.cancelToken, + this.fromFileShared = false, + } + ); UploadFileState copyWith({ UploadTaskId? uploadTaskId, @@ -33,7 +38,8 @@ class UploadFileState with EquatableMixin { UploadFileStatus? uploadStatus, int? uploadingProgress, Attachment? attachment, - CancelToken? cancelToken + CancelToken? cancelToken, + bool? fromFileShared, }) { return UploadFileState( uploadTaskId ?? this.uploadTaskId, @@ -41,7 +47,8 @@ class UploadFileState with EquatableMixin { uploadStatus: uploadStatus ?? this.uploadStatus, uploadingProgress: uploadingProgress ?? this.uploadingProgress, attachment: attachment ?? this.attachment, - cancelToken: cancelToken ?? this.cancelToken + cancelToken: cancelToken ?? this.cancelToken, + fromFileShared: fromFileShared ?? this.fromFileShared ); } @@ -96,6 +103,8 @@ class UploadFileState with EquatableMixin { file, uploadStatus, uploadingProgress, - attachment + attachment, + cancelToken, + fromFileShared, ]; } diff --git a/lib/features/upload/presentation/model/upload_file_state_list.dart b/lib/features/upload/presentation/model/upload_file_state_list.dart index 940a8087c..9529455db 100644 --- a/lib/features/upload/presentation/model/upload_file_state_list.dart +++ b/lib/features/upload/presentation/model/upload_file_state_list.dart @@ -66,4 +66,8 @@ class UploadFileStateList { _uploadingStateFiles.remove(fileState); } } + + UploadFileState? getUploadFileStateById(UploadTaskId uploadTaskId) { + return _uploadingStateFiles.firstWhereOrNull((fileState) => fileState?.uploadTaskId == uploadTaskId); + } } \ No newline at end of file