diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_fr-CA.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_fr-CA.dart index 7dd3517ac5..a7239232ed 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_fr-CA.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_fr-CA.dart @@ -22,21 +22,21 @@ class MessageLookup extends MessageLookupByLibrary { final messages = _notInlinedMessages(_notInlinedMessages); static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage(""), - "bulletedList": MessageLookupByLibrary.simpleMessage(""), - "checkbox": MessageLookupByLibrary.simpleMessage(""), - "embedCode": MessageLookupByLibrary.simpleMessage(""), - "heading1": MessageLookupByLibrary.simpleMessage(""), - "heading2": MessageLookupByLibrary.simpleMessage(""), - "heading3": MessageLookupByLibrary.simpleMessage(""), - "highlight": MessageLookupByLibrary.simpleMessage(""), - "image": MessageLookupByLibrary.simpleMessage(""), - "italic": MessageLookupByLibrary.simpleMessage(""), - "link": MessageLookupByLibrary.simpleMessage(""), - "numberedList": MessageLookupByLibrary.simpleMessage(""), - "quote": MessageLookupByLibrary.simpleMessage(""), - "strikethrough": MessageLookupByLibrary.simpleMessage(""), - "text": MessageLookupByLibrary.simpleMessage(""), - "underline": MessageLookupByLibrary.simpleMessage("") + "bold": MessageLookupByLibrary.simpleMessage("gras"), + "bulletedList": MessageLookupByLibrary.simpleMessage("liste à puces"), + "checkbox": MessageLookupByLibrary.simpleMessage("case à cocher"), + "embedCode": MessageLookupByLibrary.simpleMessage("incorporer Code"), + "heading1": MessageLookupByLibrary.simpleMessage("en-tête1"), + "heading2": MessageLookupByLibrary.simpleMessage("en-tête2"), + "heading3": MessageLookupByLibrary.simpleMessage("en-tête3"), + "highlight": MessageLookupByLibrary.simpleMessage("mettre en évidence"), + "image": MessageLookupByLibrary.simpleMessage("l’image"), + "italic": MessageLookupByLibrary.simpleMessage("italique"), + "link": MessageLookupByLibrary.simpleMessage("lien"), + "numberedList": MessageLookupByLibrary.simpleMessage("liste numérotée"), + "quote": MessageLookupByLibrary.simpleMessage("citation"), + "strikethrough": MessageLookupByLibrary.simpleMessage("barré"), + "text": MessageLookupByLibrary.simpleMessage("texte"), + "underline": MessageLookupByLibrary.simpleMessage("souligner") }; } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_hu-HU.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_hu-HU.dart index ac9acad543..44a54b5478 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_hu-HU.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_hu-HU.dart @@ -22,21 +22,21 @@ class MessageLookup extends MessageLookupByLibrary { final messages = _notInlinedMessages(_notInlinedMessages); static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage(""), - "bulletedList": MessageLookupByLibrary.simpleMessage(""), - "checkbox": MessageLookupByLibrary.simpleMessage(""), - "embedCode": MessageLookupByLibrary.simpleMessage(""), - "heading1": MessageLookupByLibrary.simpleMessage(""), - "heading2": MessageLookupByLibrary.simpleMessage(""), - "heading3": MessageLookupByLibrary.simpleMessage(""), - "highlight": MessageLookupByLibrary.simpleMessage(""), - "image": MessageLookupByLibrary.simpleMessage(""), - "italic": MessageLookupByLibrary.simpleMessage(""), - "link": MessageLookupByLibrary.simpleMessage(""), - "numberedList": MessageLookupByLibrary.simpleMessage(""), - "quote": MessageLookupByLibrary.simpleMessage(""), - "strikethrough": MessageLookupByLibrary.simpleMessage(""), - "text": MessageLookupByLibrary.simpleMessage(""), - "underline": MessageLookupByLibrary.simpleMessage("") + "bold": MessageLookupByLibrary.simpleMessage("bátor"), + "bulletedList": MessageLookupByLibrary.simpleMessage("pontozott lista"), + "checkbox": MessageLookupByLibrary.simpleMessage("jelölőnégyzetet"), + "embedCode": MessageLookupByLibrary.simpleMessage("Beágyazás"), + "heading1": MessageLookupByLibrary.simpleMessage("címsor1"), + "heading2": MessageLookupByLibrary.simpleMessage("címsor2"), + "heading3": MessageLookupByLibrary.simpleMessage("címsor3"), + "highlight": MessageLookupByLibrary.simpleMessage("Kiemel"), + "image": MessageLookupByLibrary.simpleMessage("kép"), + "italic": MessageLookupByLibrary.simpleMessage("dőlt"), + "link": MessageLookupByLibrary.simpleMessage("link"), + "numberedList": MessageLookupByLibrary.simpleMessage("számozottLista"), + "quote": MessageLookupByLibrary.simpleMessage("idézet"), + "strikethrough": MessageLookupByLibrary.simpleMessage("áthúzott"), + "text": MessageLookupByLibrary.simpleMessage("szöveg"), + "underline": MessageLookupByLibrary.simpleMessage("aláhúzás") }; } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_id-ID.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_id-ID.dart index e594bacdec..cda97336d4 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_id-ID.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_id-ID.dart @@ -22,21 +22,21 @@ class MessageLookup extends MessageLookupByLibrary { final messages = _notInlinedMessages(_notInlinedMessages); static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage(""), - "bulletedList": MessageLookupByLibrary.simpleMessage(""), - "checkbox": MessageLookupByLibrary.simpleMessage(""), - "embedCode": MessageLookupByLibrary.simpleMessage(""), - "heading1": MessageLookupByLibrary.simpleMessage(""), - "heading2": MessageLookupByLibrary.simpleMessage(""), - "heading3": MessageLookupByLibrary.simpleMessage(""), - "highlight": MessageLookupByLibrary.simpleMessage(""), - "image": MessageLookupByLibrary.simpleMessage(""), - "italic": MessageLookupByLibrary.simpleMessage(""), - "link": MessageLookupByLibrary.simpleMessage(""), - "numberedList": MessageLookupByLibrary.simpleMessage(""), - "quote": MessageLookupByLibrary.simpleMessage(""), - "strikethrough": MessageLookupByLibrary.simpleMessage(""), - "text": MessageLookupByLibrary.simpleMessage(""), - "underline": MessageLookupByLibrary.simpleMessage("") + "bold": MessageLookupByLibrary.simpleMessage("berani"), + "bulletedList": MessageLookupByLibrary.simpleMessage("daftar berpoin"), + "checkbox": MessageLookupByLibrary.simpleMessage("kotak centang"), + "embedCode": MessageLookupByLibrary.simpleMessage("menyematkan Kode"), + "heading1": MessageLookupByLibrary.simpleMessage("pos1"), + "heading2": MessageLookupByLibrary.simpleMessage("pos2"), + "heading3": MessageLookupByLibrary.simpleMessage("pos3"), + "highlight": MessageLookupByLibrary.simpleMessage("menyorot"), + "image": MessageLookupByLibrary.simpleMessage("gambar"), + "italic": MessageLookupByLibrary.simpleMessage("miring"), + "link": MessageLookupByLibrary.simpleMessage("tautan"), + "numberedList": MessageLookupByLibrary.simpleMessage("daftar bernomor"), + "quote": MessageLookupByLibrary.simpleMessage("mengutip"), + "strikethrough": MessageLookupByLibrary.simpleMessage("coret"), + "text": MessageLookupByLibrary.simpleMessage("teks"), + "underline": MessageLookupByLibrary.simpleMessage("menggarisbawahi") }; } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_pt-PT.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_pt-PT.dart index 19a14c2509..d2d2781580 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_pt-PT.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_pt-PT.dart @@ -22,21 +22,22 @@ class MessageLookup extends MessageLookupByLibrary { final messages = _notInlinedMessages(_notInlinedMessages); static Map _notInlinedMessages(_) => { - "bold": MessageLookupByLibrary.simpleMessage(""), - "bulletedList": MessageLookupByLibrary.simpleMessage(""), - "checkbox": MessageLookupByLibrary.simpleMessage(""), - "embedCode": MessageLookupByLibrary.simpleMessage(""), - "heading1": MessageLookupByLibrary.simpleMessage(""), - "heading2": MessageLookupByLibrary.simpleMessage(""), - "heading3": MessageLookupByLibrary.simpleMessage(""), - "highlight": MessageLookupByLibrary.simpleMessage(""), - "image": MessageLookupByLibrary.simpleMessage(""), - "italic": MessageLookupByLibrary.simpleMessage(""), - "link": MessageLookupByLibrary.simpleMessage(""), - "numberedList": MessageLookupByLibrary.simpleMessage(""), - "quote": MessageLookupByLibrary.simpleMessage(""), - "strikethrough": MessageLookupByLibrary.simpleMessage(""), - "text": MessageLookupByLibrary.simpleMessage(""), - "underline": MessageLookupByLibrary.simpleMessage("") + "bold": MessageLookupByLibrary.simpleMessage("negrito"), + "bulletedList": + MessageLookupByLibrary.simpleMessage("lista com marcadores"), + "checkbox": MessageLookupByLibrary.simpleMessage("caixa de seleção"), + "embedCode": MessageLookupByLibrary.simpleMessage("Código embutido"), + "heading1": MessageLookupByLibrary.simpleMessage("Cabeçallho 1"), + "heading2": MessageLookupByLibrary.simpleMessage("Cabeçallho 2"), + "heading3": MessageLookupByLibrary.simpleMessage("Cabeçallho 3"), + "highlight": MessageLookupByLibrary.simpleMessage("realçar"), + "image": MessageLookupByLibrary.simpleMessage("imagem"), + "italic": MessageLookupByLibrary.simpleMessage("itálico"), + "link": MessageLookupByLibrary.simpleMessage("link"), + "numberedList": MessageLookupByLibrary.simpleMessage("lista numerada"), + "quote": MessageLookupByLibrary.simpleMessage("citar"), + "strikethrough": MessageLookupByLibrary.simpleMessage("tachado"), + "text": MessageLookupByLibrary.simpleMessage("texto"), + "underline": MessageLookupByLibrary.simpleMessage("sublinhado") }; } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/markdown_syntax_to_styled_text.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/markdown_syntax_to_styled_text.dart index d0f93fdd73..760d7e0b6c 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/markdown_syntax_to_styled_text.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/markdown_syntax_to_styled_text.dart @@ -1,4 +1,3 @@ -import "dart:math"; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/src/extensions/text_node_extensions.dart'; import 'package:appflowy_editor/src/service/default_text_operations/format_rich_text_style.dart'; @@ -49,7 +48,7 @@ ShortcutEventHandler backquoteToCodeHandler = (editorState, event) { .substring(selection.start.offset, selection.end.offset); // toggle code style when selected some text - if (selectionText.length > 0) { + if (selectionText.isNotEmpty) { formatEmbedCode(editorState); return KeyEventResult.handled; } @@ -125,6 +124,69 @@ ShortcutEventHandler backquoteToCodeHandler = (editorState, event) { return KeyEventResult.handled; }; +// convert ~~abc~~ to strikethrough abc. +ShortcutEventHandler doubleTildeToStrikethrough = (editorState, event) { + final selectionService = editorState.service.selectionService; + final selection = selectionService.currentSelection.value; + final textNodes = selectionService.currentSelectedNodes.whereType(); + if (selection == null || !selection.isSingle || textNodes.length != 1) { + return KeyEventResult.ignored; + } + + final textNode = textNodes.first; + final text = textNode.toRawString().substring(0, selection.end.offset); + + // make sure the last two characters are ~~. + if (text.length < 2 || text[selection.end.offset - 1] != '~') { + return KeyEventResult.ignored; + } + + // find all the index of `~`. + final tildeIndexes = []; + for (var i = 0; i < text.length; i++) { + if (text[i] == '~') { + tildeIndexes.add(i); + } + } + + if (tildeIndexes.length < 3) { + return KeyEventResult.ignored; + } + + // make sure the second to last and third to last tildes are connected. + final thirdToLastTildeIndex = tildeIndexes[tildeIndexes.length - 3]; + final secondToLastTildeIndex = tildeIndexes[tildeIndexes.length - 2]; + final lastTildeIndex = tildeIndexes[tildeIndexes.length - 1]; + if (secondToLastTildeIndex != thirdToLastTildeIndex + 1 || + lastTildeIndex == secondToLastTildeIndex + 1) { + return KeyEventResult.ignored; + } + + // delete the last three tildes. + // update the style of the text surround by `~~ ~~` to strikethrough. + // and update the cursor position. + TransactionBuilder(editorState) + ..deleteText(textNode, lastTildeIndex, 1) + ..deleteText(textNode, thirdToLastTildeIndex, 2) + ..formatText( + textNode, + thirdToLastTildeIndex, + selection.end.offset - thirdToLastTildeIndex - 2, + { + BuiltInAttributeKey.strikethrough: true, + }, + ) + ..afterSelection = Selection.collapsed( + Position( + path: textNode.path, + offset: selection.end.offset - 3, + ), + ) + ..commit(); + + return KeyEventResult.handled; +}; + /// To create a link, enclose the link text in brackets (e.g., [link text]). /// Then, immediately follow it with the URL in parentheses (e.g., (https://example.com)). ShortcutEventHandler markdownLinkToLinkHandler = (editorState, event) { diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart index 569942f635..21e3e90b0d 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart @@ -257,6 +257,11 @@ List builtInShortcutEvents = [ command: 'backquote', handler: backquoteToCodeHandler, ), + ShortcutEvent( + key: 'Double tilde to strikethrough', + command: 'shift+tilde', + handler: doubleTildeToStrikethrough, + ), ShortcutEvent( key: 'Markdown link to link', command: 'shift+parenthesis right', diff --git a/frontend/app_flowy/packages/appflowy_editor/test/infra/test_raw_key_event.dart b/frontend/app_flowy/packages/appflowy_editor/test/infra/test_raw_key_event.dart index 5ad8ddfe3e..81fb46dff9 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/infra/test_raw_key_event.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/infra/test_raw_key_event.dart @@ -139,6 +139,9 @@ extension on LogicalKeyboardKey { if (this == LogicalKeyboardKey.keyZ) { return PhysicalKeyboardKey.keyZ; } + if (this == LogicalKeyboardKey.tilde) { + return PhysicalKeyboardKey.backquote; + } throw UnimplementedError(); } } diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/markdown_syntax_to_styled_text_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/markdown_syntax_to_styled_text_test.dart index d11a955643..d0ca407ea1 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/markdown_syntax_to_styled_text_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/markdown_syntax_to_styled_text_test.dart @@ -150,5 +150,111 @@ void main() async { expect(textNode.toRawString(), text); }); }); + + group('convert double tilde to strikethrough', () { + Future insertTilde( + EditorWidgetTester editor, { + int repeat = 1, + }) async { + for (var i = 0; i < repeat; i++) { + await editor.pressLogicKey( + LogicalKeyboardKey.tilde, + isShiftPressed: true, + ); + } + } + + testWidgets('~~AppFlowy~~ to strikethrough 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; + for (var i = 0; i < text.length; i++) { + await editor.insertText(textNode, text[i], i); + } + await insertTilde(editor); + final allStrikethrough = textNode.allSatisfyStrikethroughInSelection( + Selection.single( + path: [0], + startOffset: 0, + endOffset: textNode.toRawString().length, + ), + ); + expect(allStrikethrough, true); + expect(textNode.toRawString(), 'AppFlowy'); + }); + + testWidgets('App~~Flowy~~ to strikethrough AppFlowy', (tester) async { + const text = 'App~~Flowy~'; + 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 insertTilde(editor); + final allStrikethrough = textNode.allSatisfyStrikethroughInSelection( + Selection.single( + path: [0], + startOffset: 3, + endOffset: textNode.toRawString().length, + ), + ); + expect(allStrikethrough, true); + expect(textNode.toRawString(), 'AppFlowy'); + }); + + testWidgets('~~~AppFlowy~~ to bold ~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; + for (var i = 0; i < text.length; i++) { + await editor.insertText(textNode, text[i], i); + } + await insertTilde(editor); + final allStrikethrough = textNode.allSatisfyStrikethroughInSelection( + Selection.single( + path: [0], + startOffset: 1, + endOffset: textNode.toRawString().length, + ), + ); + expect(allStrikethrough, true); + expect(textNode.toRawString(), '~AppFlowy'); + }); + + testWidgets('~~~~ nothing changes', (tester) async { + const text = '~~~'; + 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 insertTilde(editor); + final allStrikethrough = textNode.allSatisfyStrikethroughInSelection( + Selection.single( + path: [0], + startOffset: 0, + endOffset: textNode.toRawString().length, + ), + ); + expect(allStrikethrough, false); + expect(textNode.toRawString(), text); + }); + }); }); }