diff --git a/frontend/appflowy_flutter/lib/plugins/document/application/doc_bloc.dart b/frontend/appflowy_flutter/lib/plugins/document/application/doc_bloc.dart index e85a6567f8..4bcbbbaafe 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/application/doc_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/application/doc_bloc.dart @@ -12,7 +12,7 @@ import 'package:appflowy/workspace/application/view/view_listener.dart'; import 'package:appflowy_backend/protobuf/flowy-document/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; -import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pbserver.dart'; +import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'; import 'package:appflowy_editor/appflowy_editor.dart' show EditorState, @@ -54,6 +54,12 @@ class DocumentBloc extends Bloc { StreamSubscription? _subscription; + bool get isLocalMode { + final userProfilePB = state.userProfilePB; + final type = userProfilePB?.authenticator ?? AuthenticatorPB.Local; + return type == AuthenticatorPB.Local; + } + @override Future close() async { await _documentListener.stop(); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/copy_and_paste/paste_from_image.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/copy_and_paste/paste_from_image.dart index 5d0919b18c..602852e238 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/copy_and_paste/paste_from_image.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/copy_and_paste/paste_from_image.dart @@ -1,12 +1,18 @@ import 'dart:io'; import 'dart:typed_data'; +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/document/application/doc_bloc.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/image/image_util.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/workspace/application/settings/application_data_storage.dart'; +import 'package:appflowy/workspace/presentation/home/toast.dart'; import 'package:appflowy_backend/log.dart'; import 'package:appflowy_editor/appflowy_editor.dart' hide Log; +import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/uuid.dart'; import 'package:path/path.dart' as p; +import 'package:provider/provider.dart'; extension PasteFromImage on EditorState { static final supportedImageFormats = [ @@ -20,11 +26,20 @@ extension PasteFromImage on EditorState { return false; } + final context = document.root.context; + + if (context == null) { + return false; + } + + final isLocalMode = context.read().isLocalMode; + final path = await getIt().getPath(); final imagePath = p.join( path, 'images', ); + try { // create the directory if not exists final directory = Directory(imagePath); @@ -33,14 +48,52 @@ extension PasteFromImage on EditorState { } final copyToPath = p.join( imagePath, - '${uuid()}.$format', + 'tmp_${uuid()}.$format', ); await File(copyToPath).writeAsBytes(imageBytes); - await insertImageNode(copyToPath); + final String? path; + + if (context.mounted) { + showSnackBarMessage( + context, + LocaleKeys.document_imageBlock_imageIsUploading.tr(), + ); + } + + if (isLocalMode) { + path = await saveImageToLocalStorage(copyToPath); + } else { + final result = await saveImageToCloudStorage(copyToPath); + + final errorMessage = result.$2; + + if (errorMessage != null && context.mounted) { + showSnackBarMessage( + context, + errorMessage, + ); + return false; + } + + path = result.$1; + } + + if (path != null) { + await insertImageNode(path); + } + + await File(copyToPath).delete(); return true; } catch (e) { Log.error('cannot copy image file', e); + if (context.mounted) { + showSnackBarMessage( + context, + LocaleKeys.document_imageBlock_error_invalidImage.tr(), + ); + } } + return false; } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/document_header_node_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/document_header_node_widget.dart index 784a7f246c..78e458f7ab 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/document_header_node_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/document_header_node_widget.dart @@ -13,7 +13,6 @@ import 'package:appflowy/plugins/document/presentation/editor_style.dart'; import 'package:appflowy/shared/appflowy_network_image.dart'; import 'package:appflowy/workspace/application/view/view_listener.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; -import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'; import 'package:appflowy_editor/appflowy_editor.dart' hide UploadImageMenu; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -629,9 +628,7 @@ class DocumentCoverState extends State { } bool _isLocalMode() { - final userProfilePB = context.read().state.userProfilePB; - final type = userProfilePB?.authenticator ?? AuthenticatorPB.Local; - return type == AuthenticatorPB.Local; + return context.read().isLocalMode; } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/image_placeholder.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/image_placeholder.dart index 9c6a9d6fc4..8b1a1237e1 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/image_placeholder.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/image_placeholder.dart @@ -9,11 +9,9 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/image/cust import 'package:appflowy/plugins/document/presentation/editor_plugins/image/image_util.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu.dart'; import 'package:appflowy/startup/startup.dart'; -import 'package:appflowy/util/file_extension.dart'; import 'package:appflowy/workspace/application/settings/application_data_storage.dart'; import 'package:appflowy/workspace/presentation/home/toast.dart'; import 'package:appflowy_backend/log.dart'; -import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'; import 'package:appflowy_editor/appflowy_editor.dart' hide Log, UploadImageMenu; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -194,24 +192,9 @@ class ImagePlaceholderState extends State { } Future insertLocalImage(String? url) async { - if (url == null || url.isEmpty) { - controller.close(); - return; - } + controller.close(); - final size = url.fileSize; - if (size == null || size > 10 * 1024 * 1024) { - controller.close(); - setState(() { - showLoading = false; - this.errorMessage = - LocaleKeys.document_imageBlock_uploadImageErrorImageSizeTooBig.tr(); - }); - // show error - showSnackBarMessage( - context, - LocaleKeys.document_imageBlock_uploadImageErrorImageSizeTooBig.tr(), - ); + if (url == null || url.isEmpty) { return; } @@ -223,6 +206,7 @@ class ImagePlaceholderState extends State { // if the user is using local authenticator, we need to save the image to local storage if (_isLocalMode()) { + // don't limit the image size for local mode. path = await saveImageToLocalStorage(url); } else { // else we should save the image to cloud storage @@ -313,8 +297,6 @@ class ImagePlaceholderState extends State { } bool _isLocalMode() { - final userProfilePB = context.read().state.userProfilePB; - final type = userProfilePB?.authenticator ?? AuthenticatorPB.Local; - return type == AuthenticatorPB.Local; + return context.read().isLocalMode; } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/image_util.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/image_util.dart index c03ce0b5ae..352a6c878e 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/image_util.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/image_util.dart @@ -1,10 +1,13 @@ import 'dart:io'; +import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/document/application/prelude.dart'; import 'package:appflowy/shared/custom_image_cache_manager.dart'; import 'package:appflowy/startup/startup.dart'; +import 'package:appflowy/util/file_extension.dart'; import 'package:appflowy/workspace/application/settings/application_data_storage.dart'; import 'package:appflowy_backend/log.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/uuid.dart'; import 'package:path/path.dart' as p; @@ -37,6 +40,14 @@ Future saveImageToLocalStorage(String localImagePath) async { Future<(String? path, String? errorMessage)> saveImageToCloudStorage( String localImagePath, ) async { + final size = localImagePath.fileSize; + if (size == null || size > 10 * 1024 * 1024) { + // 10MB + return ( + null, + LocaleKeys.document_imageBlock_uploadImageErrorImageSizeTooBig.tr(), + ); + } final documentService = DocumentService(); final result = await documentService.uploadFile( localFilePath: localImagePath,