From 44fb61026918c235a688dbf61b17de7dfa7c1530 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Sat, 17 Aug 2024 11:04:56 +0800 Subject: [PATCH] fix: support pasting web image on mobile (#5987) * fix: support pasting web image on mobile * fix: permission check will deactive editor focus --- .../copy_and_paste/clipboard_service.dart | 6 +- .../copy_and_paste/custom_paste_command.dart | 1 + .../copy_and_paste/paste_from_image.dart | 75 ++++++++++++++++--- .../custom_mobile_floating_toolbar.dart | 5 +- 4 files changed, 70 insertions(+), 17 deletions(-) diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart index 436de2c601..1dde980f03 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart @@ -100,9 +100,7 @@ class ClipboardService { for (final item in reader.items) { final availableFormats = await item.rawReader!.getAvailableFormats(); - Log.debug( - 'availableFormats: $availableFormats', - ); + Log.info('availableFormats: $availableFormats'); } final plainText = await reader.readValue(Formats.plainText); @@ -115,6 +113,8 @@ class ClipboardService { image = ('jpeg', await reader.readFile(Formats.jpeg)); } else if (reader.canProvide(Formats.gif)) { image = ('gif', await reader.readFile(Formats.gif)); + } else if (reader.canProvide(Formats.webp)) { + image = ('webp', await reader.readFile(Formats.webp)); } return ClipboardServiceData( diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/copy_and_paste/custom_paste_command.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/copy_and_paste/custom_paste_command.dart index 067d6b766c..2e0cef8b8b 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/copy_and_paste/custom_paste_command.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/copy_and_paste/custom_paste_command.dart @@ -76,6 +76,7 @@ CommandShortcutEventHandler _pasteCommandHandler = (editorState) { image.$1, image.$2!, documentId, + selection: selection, ); if (result) { Log.info('Pasted image'); 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 989798dcf2..4ce4f6c405 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 @@ -9,7 +9,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/image/imag import 'package:appflowy/shared/patterns/common_patterns.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/workspace/presentation/widgets/dialogs.dart'; import 'package:appflowy_backend/log.dart'; import 'package:appflowy_editor/appflowy_editor.dart' hide Log; import 'package:cross_file/cross_file.dart'; @@ -23,6 +23,7 @@ extension PasteFromImage on EditorState { 'png', 'jpeg', 'gif', + 'webp', ]; Future dropImages( @@ -64,18 +65,26 @@ extension PasteFromImage on EditorState { Future pasteImage( String format, Uint8List imageBytes, - String documentId, - ) async { - if (!supportedImageFormats.contains(format)) { - return false; - } - + String documentId, { + Selection? selection, + }) async { final context = document.root.context; if (context == null) { return false; } + if (!supportedImageFormats.contains(format)) { + Log.info('unsupported format: $format'); + if (PlatformExtension.isMobile) { + showToastNotification( + context, + message: LocaleKeys.document_imageBlock_error_invalidImageFormat.tr(), + ); + } + return false; + } + final isLocalMode = context.read().isLocalMode; final path = await getIt().getPath(); @@ -105,9 +114,9 @@ extension PasteFromImage on EditorState { final errorMessage = result.$2; if (errorMessage != null && context.mounted) { - showSnackBarMessage( + showToastNotification( context, - errorMessage, + message: errorMessage, ); return false; } @@ -116,7 +125,7 @@ extension PasteFromImage on EditorState { } if (path != null) { - await insertImageNode(path); + await insertImageNode(path, selection: selection); } await File(copyToPath).delete(); @@ -124,13 +133,55 @@ extension PasteFromImage on EditorState { } catch (e) { Log.error('cannot copy image file', e); if (context.mounted) { - showSnackBarMessage( + showToastNotification( context, - LocaleKeys.document_imageBlock_error_invalidImage.tr(), + message: LocaleKeys.document_imageBlock_error_invalidImage.tr(), ); } } return false; } + + Future insertImageNode( + String src, { + Selection? selection, + }) async { + selection ??= this.selection; + if (selection == null || !selection.isCollapsed) { + return; + } + final node = getNodeAtPath(selection.end.path); + if (node == null) { + return; + } + final transaction = this.transaction; + // if the current node is empty paragraph, replace it with image node + if (node.type == ParagraphBlockKeys.type && + (node.delta?.isEmpty ?? false)) { + transaction + ..insertNode( + node.path, + imageNode( + url: src, + ), + ) + ..deleteNode(node); + } else { + transaction.insertNode( + node.path.next, + imageNode( + url: src, + ), + ); + } + + transaction.afterSelection = Selection.collapsed( + Position( + path: node.path.next, + ), + ); + + return apply(transaction); + } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_floating_toolbar/custom_mobile_floating_toolbar.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_floating_toolbar/custom_mobile_floating_toolbar.dart index e3b320a63d..ba170e8d24 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_floating_toolbar/custom_mobile_floating_toolbar.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_floating_toolbar/custom_mobile_floating_toolbar.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; @@ -23,7 +24,7 @@ List buildMobileFloatingToolbarItems( ContextMenuButtonItem( label: LocaleKeys.editor_copy.tr(), onPressed: () { - copyCommand.execute(editorState); + customCopyCommand.execute(editorState); closeToolbar(); }, ), @@ -34,7 +35,7 @@ List buildMobileFloatingToolbarItems( ContextMenuButtonItem( label: LocaleKeys.editor_paste.tr(), onPressed: () { - pasteCommand.execute(editorState); + customPasteCommand.execute(editorState); closeToolbar(); }, ),