Ver código fonte

fix: could not insert space sometimes on the web

Lucas.Xu 2 anos atrás
pai
commit
0a49a18280

+ 34 - 0
frontend/app_flowy/packages/appflowy_editor/lib/src/commands/edit_text.dart

@@ -0,0 +1,34 @@
+import 'dart:async';
+
+import 'package:appflowy_editor/src/commands/text_command_infra.dart';
+import 'package:appflowy_editor/src/document/node.dart';
+import 'package:appflowy_editor/src/document/path.dart';
+import 'package:appflowy_editor/src/editor_state.dart';
+import 'package:appflowy_editor/src/operation/transaction_builder.dart';
+import 'package:flutter/widgets.dart';
+
+Future<void> insertContextInText(
+  EditorState editorState,
+  int index,
+  String content, {
+  Path? path,
+  TextNode? textNode,
+}) async {
+  final result = getTextNodeToBeFormatted(
+    editorState,
+    path: path,
+    textNode: textNode,
+  );
+
+  final completer = Completer<void>();
+
+  TransactionBuilder(editorState)
+    ..insertText(result, index, content)
+    ..commit();
+
+  WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
+    completer.complete();
+  });
+
+  return completer.future;
+}

+ 1 - 0
frontend/app_flowy/packages/appflowy_editor/lib/src/commands/format_built_in_text.dart

@@ -1,4 +1,5 @@
 import 'package:appflowy_editor/src/commands/format_text.dart';
+import 'package:appflowy_editor/src/commands/text_command_infra.dart';
 import 'package:appflowy_editor/src/document/attributes.dart';
 import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart';
 import 'package:appflowy_editor/src/document/node.dart';

+ 2 - 38
frontend/app_flowy/packages/appflowy_editor/lib/src/commands/format_text.dart

@@ -1,5 +1,6 @@
 import 'dart:async';
 
+import 'package:appflowy_editor/src/commands/text_command_infra.dart';
 import 'package:appflowy_editor/src/document/attributes.dart';
 import 'package:appflowy_editor/src/document/node.dart';
 import 'package:appflowy_editor/src/document/path.dart';
@@ -45,7 +46,7 @@ Future<void> updateTextNodeDeltaAttributes(
     path: path,
     textNode: textNode,
   );
-  final newSelection = _getSelection(editorState, selection: selection);
+  final newSelection = getSelection(editorState, selection: selection);
 
   final completer = Completer<void>();
 
@@ -64,40 +65,3 @@ Future<void> updateTextNodeDeltaAttributes(
 
   return completer.future;
 }
-
-// get formatted [TextNode]
-TextNode getTextNodeToBeFormatted(
-  EditorState editorState, {
-  Path? path,
-  TextNode? textNode,
-}) {
-  assert(!(path != null && textNode != null));
-  assert(!(path == null && textNode == null));
-
-  TextNode result;
-  if (textNode != null) {
-    result = textNode;
-  } else if (path != null) {
-    result = editorState.document.nodeAtPath(path) as TextNode;
-  } else {
-    throw Exception('path and textNode cannot be null at the same time');
-  }
-  return result;
-}
-
-Selection _getSelection(
-  EditorState editorState, {
-  Selection? selection,
-}) {
-  final currentSelection =
-      editorState.service.selectionService.currentSelection.value;
-  Selection result;
-  if (selection != null) {
-    result = selection;
-  } else if (currentSelection != null) {
-    result = currentSelection;
-  } else {
-    throw Exception('path and textNode cannot be null at the same time');
-  }
-  return result;
-}

+ 43 - 0
frontend/app_flowy/packages/appflowy_editor/lib/src/commands/text_command_infra.dart

@@ -0,0 +1,43 @@
+import 'package:appflowy_editor/src/document/node.dart';
+import 'package:appflowy_editor/src/document/path.dart';
+import 'package:appflowy_editor/src/document/selection.dart';
+import 'package:appflowy_editor/src/editor_state.dart';
+
+// get formatted [TextNode]
+TextNode getTextNodeToBeFormatted(
+  EditorState editorState, {
+  Path? path,
+  TextNode? textNode,
+}) {
+  final currentSelection =
+      editorState.service.selectionService.currentSelection.value;
+  TextNode result;
+  if (textNode != null) {
+    result = textNode;
+  } else if (path != null) {
+    result = editorState.document.nodeAtPath(path) as TextNode;
+  } else if (currentSelection != null && currentSelection.isCollapsed) {
+    result = editorState.document.nodeAtPath(currentSelection.start.path)
+        as TextNode;
+  } else {
+    throw Exception('path and textNode cannot be null at the same time');
+  }
+  return result;
+}
+
+Selection getSelection(
+  EditorState editorState, {
+  Selection? selection,
+}) {
+  final currentSelection =
+      editorState.service.selectionService.currentSelection.value;
+  Selection result;
+  if (selection != null) {
+    result = selection;
+  } else if (currentSelection != null) {
+    result = currentSelection;
+  } else {
+    throw Exception('path and textNode cannot be null at the same time');
+  }
+  return result;
+}

+ 21 - 0
frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/space_on_web_handler.dart

@@ -0,0 +1,21 @@
+import 'package:appflowy_editor/appflowy_editor.dart';
+import 'package:appflowy_editor/src/commands/edit_text.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+
+ShortcutEventHandler spaceOnWebHandler = (editorState, event) {
+  final selection = editorState.service.selectionService.currentSelection.value;
+  final textNodes = editorState.service.selectionService.currentSelectedNodes
+      .whereType<TextNode>()
+      .toList(growable: false);
+  if (selection == null ||
+      !selection.isCollapsed ||
+      !kIsWeb ||
+      textNodes.length != 1) {
+    return KeyEventResult.ignored;
+  }
+
+  insertContextInText(editorState, selection.startIndex, ' ');
+
+  return KeyEventResult.handled;
+};

+ 12 - 0
frontend/app_flowy/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart

@@ -9,9 +9,11 @@ import 'package:appflowy_editor/src/service/internal_key_event_handlers/redo_und
 import 'package:appflowy_editor/src/service/internal_key_event_handlers/select_all_handler.dart';
 import 'package:appflowy_editor/src/service/internal_key_event_handlers/slash_handler.dart';
 import 'package:appflowy_editor/src/service/internal_key_event_handlers/format_style_handler.dart';
+import 'package:appflowy_editor/src/service/internal_key_event_handlers/space_on_web_handler.dart';
 import 'package:appflowy_editor/src/service/internal_key_event_handlers/tab_handler.dart';
 import 'package:appflowy_editor/src/service/internal_key_event_handlers/whitespace_handler.dart';
 import 'package:appflowy_editor/src/service/shortcut_event/shortcut_event.dart';
+import 'package:flutter/foundation.dart';
 
 //
 List<ShortcutEvent> builtInShortcutEvents = [
@@ -249,4 +251,14 @@ List<ShortcutEvent> builtInShortcutEvents = [
     command: 'tab',
     handler: tabHandler,
   ),
+  // https://github.com/flutter/flutter/issues/104944
+  // Workaround: Using space editing on the web platform often results in errors,
+  //  so adding a shortcut event to handle the space input instead of using the
+  //  `input_service`.
+  if (kIsWeb)
+    ShortcutEvent(
+      key: 'Space on the Web',
+      command: 'space',
+      handler: spaceOnWebHandler,
+    ),
 ];

+ 45 - 0
frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/space_on_web_handler_test.dart

@@ -0,0 +1,45 @@
+import 'package:appflowy_editor/appflowy_editor.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+import '../../infra/test_editor.dart';
+
+void main() async {
+  setUpAll(() {
+    TestWidgetsFlutterBinding.ensureInitialized();
+  });
+
+  group('space_on_web_handler.dart', () {
+    testWidgets('Presses space key on web', (tester) async {
+      if (!kIsWeb) return;
+      const count = 10;
+      const text = 'Welcome to Appflowy 😁';
+      final editor = tester.editor;
+      for (var i = 0; i < count; i++) {
+        editor.insertTextNode(text);
+      }
+      await editor.startTesting();
+
+      for (var i = 0; i < count; i++) {
+        await editor.updateSelection(
+          Selection.single(path: [i], startOffset: 1),
+        );
+        await editor.pressLogicKey(LogicalKeyboardKey.space);
+        expect(
+          (editor.nodeAtPath([i]) as TextNode).toRawString(),
+          'W elcome to Appflowy 😁',
+        );
+      }
+      for (var i = 0; i < count; i++) {
+        await editor.updateSelection(
+          Selection.single(path: [i], startOffset: text.length + 1),
+        );
+        await editor.pressLogicKey(LogicalKeyboardKey.space);
+        expect(
+          (editor.nodeAtPath([i]) as TextNode).toRawString(),
+          'W elcome to Appflowy 😁 ',
+        );
+      }
+    });
+  });
+}