diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/whitespace_handler.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/whitespace_handler.dart index 8732c0f0ea..5821733564 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/whitespace_handler.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/whitespace_handler.dart @@ -20,6 +20,8 @@ const _bulletedListSymbols = ['*', '-']; const _checkboxListSymbols = ['[x]', '-[x]']; const _unCheckboxListSymbols = ['[]', '-[]']; +const _quoteSymbols = ['>']; + final _numberRegex = RegExp(r'^(\d+)\.'); ShortcutEventHandler whiteSpaceHandler = (editorState, event) { @@ -49,6 +51,8 @@ ShortcutEventHandler whiteSpaceHandler = (editorState, event) { return _toBulletedList(editorState, textNode); } else if (_countOfSign(text, selection) != 0) { return _toHeadingStyle(editorState, textNode, selection); + } else if (_quoteSymbols.contains(text)) { + return _toQuoteStyle(editorState, textNode); } else if (numberMatch != null) { final matchText = numberMatch.group(0); final numText = numberMatch.group(1); @@ -196,3 +200,22 @@ int _countOfSign(String text, Selection selection) { } return 0; } + +KeyEventResult _toQuoteStyle(EditorState editorState, TextNode textNode) { + if (textNode.subtype == BuiltInAttributeKey.quote) { + return KeyEventResult.ignored; + } + final transaction = editorState.transaction + ..deleteText(textNode, 0, 1) + ..updateNode(textNode, { + BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote, + }) + ..afterSelection = Selection.collapsed( + Position( + path: textNode.path, + offset: 0, + ), + ); + editorState.apply(transaction); + return KeyEventResult.handled; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/white_space_handler_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/white_space_handler_test.dart index f62d977ece..4349dc7a79 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/white_space_handler_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/white_space_handler_test.dart @@ -184,6 +184,10 @@ void main() async { Selection.single(path: [0], startOffset: 0), ); + await editor.insertText(textNode, '>', 0); + await editor.pressLogicKey(LogicalKeyboardKey.space); + expect(textNode.subtype, BuiltInAttributeKey.quote); + await editor.insertText(textNode, '*', 0); await editor.pressLogicKey(LogicalKeyboardKey.space); expect(textNode.subtype, BuiltInAttributeKey.bulletedList); @@ -227,5 +231,65 @@ void main() async { expect(textNode.subtype, null); expect(textNode.toPlainText(), text); }); + + group('convert geater to blockquote', () { + testWidgets('> AppFlowy to blockquote AppFlowy', (tester) async { + const text = 'AppFlowy'; + final editor = tester.editor..insertTextNode(''); + await editor.startTesting(); + await editor.updateSelection( + Selection.single(path: [0], startOffset: 0), + ); + + final textNode = editor.nodeAtPath([0]) as TextNode; + await editor.insertText(textNode, '>', 0); + await editor.pressLogicKey(LogicalKeyboardKey.space); + expect(textNode.subtype, BuiltInAttributeKey.quote); + for (var i = 0; i < text.length; i++) { + await editor.insertText(textNode, text[i], i); + } + expect(textNode.toPlainText(), 'AppFlowy'); + }); + + testWidgets('AppFlowy > nothing changes', (tester) async { + const text = 'AppFlowy >'; + final editor = tester.editor..insertTextNode(''); + await editor.startTesting(); + await editor.updateSelection( + Selection.single(path: [0], startOffset: 0), + ); + + final textNode = editor.nodeAtPath([0]) as TextNode; + for (var i = 0; i < text.length; i++) { + await editor.insertText(textNode, text[i], i); + } + await editor.pressLogicKey(LogicalKeyboardKey.space); + final isQuote = textNode.subtype == BuiltInAttributeKey.quote; + expect(isQuote, false); + expect(textNode.toPlainText(), text); + }); + + testWidgets('> in front of text to blockquote', (tester) async { + const text = 'AppFlowy'; + final editor = tester.editor..insertTextNode(''); + await editor.startTesting(); + await editor.updateSelection( + Selection.single(path: [0], startOffset: 0), + ); + final textNode = editor.nodeAtPath([0]) as TextNode; + for (var i = 0; i < text.length; i++) { + await editor.insertText(textNode, text[i], i); + } + await editor.updateSelection( + Selection.single(path: [0], startOffset: 0), + ); + await editor.insertText(textNode, '>', 0); + await editor.pressLogicKey(LogicalKeyboardKey.space); + + final isQuote = textNode.subtype == BuiltInAttributeKey.quote; + expect(isQuote, true); + expect(textNode.toPlainText(), text); + }); + }); }); }