From 9df11256ccd84d1ecb36fa038b7c04e9a8df9137 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Tue, 27 Sep 2022 17:52:32 +0800 Subject: [PATCH 01/48] feat: implement TeX plugin --- .../example/assets/example.json | 6 + .../appflowy_editor/example/lib/main.dart | 5 +- .../lib/plugin/code_block_node_widget.dart | 2 +- .../lib/plugin/tex_block_node_widget.dart | 189 ++++++++++++++++++ .../appflowy_editor/example/pubspec.yaml | 1 + 5 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/tex_block_node_widget.dart diff --git a/frontend/app_flowy/packages/appflowy_editor/example/assets/example.json b/frontend/app_flowy/packages/appflowy_editor/example/assets/example.json index 2d441d3367..991c03296a 100644 --- a/frontend/app_flowy/packages/appflowy_editor/example/assets/example.json +++ b/frontend/app_flowy/packages/appflowy_editor/example/assets/example.json @@ -9,6 +9,12 @@ "align": "center" } }, + { + "type": "tex", + "attributes": { + "tex": "x = 2" + } + }, { "type": "text", "attributes": { "subtype": "heading", "heading": "h1" }, diff --git a/frontend/app_flowy/packages/appflowy_editor/example/lib/main.dart b/frontend/app_flowy/packages/appflowy_editor/example/lib/main.dart index 744359052f..fecec3d3e0 100644 --- a/frontend/app_flowy/packages/appflowy_editor/example/lib/main.dart +++ b/frontend/app_flowy/packages/appflowy_editor/example/lib/main.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:example/plugin/code_block_node_widget.dart'; +import 'package:example/plugin/tex_block_node_widget.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -119,6 +120,7 @@ class _MyHomePageState extends State { editable: true, customBuilders: { 'text/code_block': CodeBlockNodeWidgetBuilder(), + 'tex': TeXBlockNodeWidgetBuidler(), }, shortcutEvents: [ enterInCodeBlock, @@ -126,7 +128,8 @@ class _MyHomePageState extends State { underscoreToItalic, ], selectionMenuItems: [ - codeBlockItem, + codeBlockMenuItem, + teXBlockMenuItem, ], ), ); diff --git a/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/code_block_node_widget.dart b/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/code_block_node_widget.dart index 3949073756..645e4c0c75 100644 --- a/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/code_block_node_widget.dart +++ b/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/code_block_node_widget.dart @@ -44,7 +44,7 @@ ShortcutEventHandler _ignorekHandler = (editorState, event) { return KeyEventResult.ignored; }; -SelectionMenuItem codeBlockItem = SelectionMenuItem( +SelectionMenuItem codeBlockMenuItem = SelectionMenuItem( name: 'Code Block', icon: const Icon(Icons.abc), keywords: ['code block'], diff --git a/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/tex_block_node_widget.dart b/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/tex_block_node_widget.dart new file mode 100644 index 0000000000..80ad3642ca --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/tex_block_node_widget.dart @@ -0,0 +1,189 @@ +import 'dart:collection'; + +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_math_fork/flutter_math.dart'; + +SelectionMenuItem teXBlockMenuItem = SelectionMenuItem( + name: 'Tex', + icon: const Icon(Icons.text_fields_rounded), + keywords: ['tex, latex, katex'], + handler: (editorState, _, __) { + final selection = + editorState.service.selectionService.currentSelection.value; + final textNodes = editorState.service.selectionService.currentSelectedNodes + .whereType(); + if (selection == null || !selection.isCollapsed || textNodes.isEmpty) { + return; + } + final Path texNodePath; + if (textNodes.first.toRawString().isEmpty) { + texNodePath = selection.end.path; + TransactionBuilder(editorState) + ..insertNode( + selection.end.path, + Node( + type: 'tex', + children: LinkedList(), + attributes: {'tex': ''}, + ), + ) + ..deleteNode(textNodes.first) + ..afterSelection = selection + ..commit(); + } else { + texNodePath = selection.end.path.next; + TransactionBuilder(editorState) + ..insertNode( + selection.end.path.next, + Node( + type: 'tex', + children: LinkedList(), + attributes: {'tex': ''}, + ), + ) + ..afterSelection = selection + ..commit(); + } + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + final texState = + editorState.document.nodeAtPath(texNodePath)?.key?.currentState; + if (texState != null && texState is __TeXBlockNodeWidgetState) { + texState.showEditingDialog(); + } + }); + }, +); + +class TeXBlockNodeWidgetBuidler extends NodeWidgetBuilder { + @override + Widget build(NodeWidgetContext context) { + return _TeXBlockNodeWidget( + key: context.node.key, + node: context.node, + editorState: context.editorState, + ); + } + + @override + NodeValidator get nodeValidator => (node) { + return node.attributes['tex'] is String; + }; +} + +class _TeXBlockNodeWidget extends StatefulWidget { + const _TeXBlockNodeWidget({ + Key? key, + required this.node, + required this.editorState, + }) : super(key: key); + + final Node node; + final EditorState editorState; + + @override + State<_TeXBlockNodeWidget> createState() => __TeXBlockNodeWidgetState(); +} + +class __TeXBlockNodeWidgetState extends State<_TeXBlockNodeWidget> { + String get _tex => widget.node.attributes['tex'] as String; + bool _isHover = false; + + @override + Widget build(BuildContext context) { + return InkWell( + onHover: (value) { + setState(() { + _isHover = value; + }); + }, + onTap: () { + showEditingDialog(); + }, + child: Stack( + children: [ + _buildTex(context), + if (_isHover) _buildDeleteButton(context), + ], + ), + ); + } + + Widget _buildTex(BuildContext context) { + return Container( + width: MediaQuery.of(context).size.width, + padding: const EdgeInsets.symmetric(vertical: 20), + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(8.0)), + color: _isHover ? Colors.grey[200] : Colors.transparent, + ), + child: Center( + child: Math.tex( + _tex, + textStyle: const TextStyle(fontSize: 20), + mathStyle: MathStyle.display, + ), + ), + ); + } + + Widget _buildDeleteButton(BuildContext context) { + return Positioned( + top: -5, + right: -5, + child: IconButton( + icon: Icon( + Icons.delete_outline, + color: Colors.blue[400], + size: 16, + ), + onPressed: () { + TransactionBuilder(widget.editorState) + ..deleteNode(widget.node) + ..commit(); + }, + ), + ); + } + + void showEditingDialog() { + showDialog( + context: context, + builder: (context) { + final controller = TextEditingController(text: _tex); + return AlertDialog( + title: const Text('Edit Katex'), + content: TextField( + controller: controller, + maxLines: null, + decoration: const InputDecoration( + border: OutlineInputBorder(), + ), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + Navigator.of(context).pop(); + if (controller.text != _tex) { + TransactionBuilder(widget.editorState) + ..updateNode( + widget.node, + {'tex': controller.text}, + ) + ..commit(); + } + }, + child: const Text('OK'), + ), + ], + ); + }, + ); + } +} diff --git a/frontend/app_flowy/packages/appflowy_editor/example/pubspec.yaml b/frontend/app_flowy/packages/appflowy_editor/example/pubspec.yaml index 9f7b4e805b..fc67a9d933 100644 --- a/frontend/app_flowy/packages/appflowy_editor/example/pubspec.yaml +++ b/frontend/app_flowy/packages/appflowy_editor/example/pubspec.yaml @@ -44,6 +44,7 @@ dependencies: file_picker: ^5.0.1 universal_html: ^2.0.8 highlight: ^0.7.0 + flutter_math_fork: ^0.6.3+1 dev_dependencies: flutter_test: From 3dc64f3d50dcdaa68c1bfcbf91c0d2536bb030f1 Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Sat, 1 Oct 2022 21:13:56 +0800 Subject: [PATCH 02/48] fix: improve look of select option list (#1168) --- .../cell/select_option_cell/extension.dart | 12 +++---- .../select_option_editor.dart | 31 +++++++------------ 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/extension.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/extension.dart index 93b0699462..3f60de4142 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/extension.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/extension.dart @@ -130,14 +130,10 @@ class SelectOptionTagCell extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Flexible( - fit: FlexFit.loose, - flex: 2, - child: SelectOptionTag.fromOption( - context: context, - option: option, - onSelected: () => onSelected(option), - ), + SelectOptionTag.fromOption( + context: context, + option: option, + onSelected: () => onSelected(option), ), const Spacer(), ...children, diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart index bc80798fea..1b683713a3 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart @@ -248,32 +248,25 @@ class _SelectOptionCellState extends State<_SelectOptionCell> { mutex: widget.popoverMutex, child: SizedBox( height: GridSize.typeOptionItemHeight, - child: Row( + child: SelectOptionTagCell( + option: widget.option, + onSelected: (option) { + context + .read() + .add(SelectOptionEditorEvent.selectOption(option.id)); + }, children: [ - Flexible( - fit: FlexFit.loose, - child: SelectOptionTagCell( - option: widget.option, - onSelected: (option) { - context - .read() - .add(SelectOptionEditorEvent.selectOption(option.id)); - }, - children: [ - if (widget.isSelected) - Padding( - padding: const EdgeInsets.only(right: 6), - child: svgWidget("grid/checkmark"), - ), - ], + if (widget.isSelected) + Padding( + padding: const EdgeInsets.only(right: 6), + child: svgWidget("grid/checkmark"), ), - ), FlowyIconButton( width: 30, onPressed: () => _popoverController.show(), iconPadding: const EdgeInsets.fromLTRB(4, 4, 4, 4), icon: svgWidget("editor/details", color: theme.iconColor), - ) + ), ], ), ), From 04a7c8dc6d0efc576b700ecd649bf5e0464c59ab Mon Sep 17 00:00:00 2001 From: Victor Szabo <54809586+vszabo2@users.noreply.github.com> Date: Sun, 2 Oct 2022 05:15:53 -0700 Subject: [PATCH 03/48] fix(scripts): add libkeybinder3 to Dockerfile (#1204) --- frontend/scripts/docker-buildfiles/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/scripts/docker-buildfiles/Dockerfile b/frontend/scripts/docker-buildfiles/Dockerfile index a15a31d2e3..4c1ec1e3ea 100644 --- a/frontend/scripts/docker-buildfiles/Dockerfile +++ b/frontend/scripts/docker-buildfiles/Dockerfile @@ -47,7 +47,7 @@ RUN pacman -Syy && \ RUN xdg-user-dirs-update COPY --from=builder /usr/sbin/yay /usr/sbin/yay -RUN yay -S --noconfirm gtk3 +RUN yay -S --noconfirm gtk3 libkeybinder3 ARG user=appflowy ARG uid=1000 From 2d1793c5ff312d2e2d9973652b38cb91920fc5c9 Mon Sep 17 00:00:00 2001 From: Luke Greenwood Date: Sun, 2 Oct 2022 13:19:17 +0100 Subject: [PATCH 04/48] fix: Theme toggle not updating UI (#1205) --- .../presentation/settings/widgets/settings_appearance_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app_flowy/lib/workspace/presentation/settings/widgets/settings_appearance_view.dart b/frontend/app_flowy/lib/workspace/presentation/settings/widgets/settings_appearance_view.dart index ea885fc86a..54077135b0 100644 --- a/frontend/app_flowy/lib/workspace/presentation/settings/widgets/settings_appearance_view.dart +++ b/frontend/app_flowy/lib/workspace/presentation/settings/widgets/settings_appearance_view.dart @@ -13,7 +13,7 @@ class SettingsAppearanceView extends StatelessWidget { @override Widget build(BuildContext context) { - final theme = context.read(); + final theme = context.watch(); return SingleChildScrollView( child: Column( From 7b8aa78e95ace16970b62ea14e0bc20befe51381 Mon Sep 17 00:00:00 2001 From: Alessio Marinelli Date: Sun, 2 Oct 2022 14:21:00 +0200 Subject: [PATCH 05/48] feat: Added fr_FR AppFlowy editor translation (#1201) --- .../appflowy_editor/lib/l10n/intl_fr_FR.arb | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_fr_FR.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_fr_FR.arb index 051676b02c..0e63189506 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_fr_FR.arb +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_fr_FR.arb @@ -1,35 +1,35 @@ { "@@locale": "fr-FR", - "bold": "", + "bold": "Gras", "@bold": {}, - "bulletedList": "", + "bulletedList": "List à puces", "@bulletedList": {}, - "checkbox": "", + "checkbox": "Case à cocher", "@checkbox": {}, - "embedCode": "", + "embedCode": "Incorporer code", "@embedCode": {}, - "heading1": "", + "heading1": "Titre 1", "@heading1": {}, - "heading2": "", + "heading2": "Titre 2", "@heading2": {}, - "heading3": "", + "heading3": "Titre 3", "@heading3": {}, - "highlight": "", + "highlight": "Surligné", "@highlight": {}, - "image": "", + "image": "Image", "@image": {}, - "italic": "", + "italic": "Italique", "@italic": {}, - "link": "", + "link": "Lien", "@link": {}, - "numberedList": "", + "numberedList": "Liste numérotée", "@numberedList": {}, - "quote": "", + "quote": "Citation", "@quote": {}, - "strikethrough": "", + "strikethrough": "Barré", "@strikethrough": {}, - "text": "", + "text": "Texte", "@text": {}, - "underline": "", + "underline": "Souligné", "@underline": {} } \ No newline at end of file From 35fbf99fb12c71888caa327bcd4dbb0ee2e91e30 Mon Sep 17 00:00:00 2001 From: Alessio Marinelli Date: Sun, 2 Oct 2022 14:25:06 +0200 Subject: [PATCH 06/48] feat: Added it_IT translation (#1200) --- .../appflowy_editor/lib/l10n/intl_it_IT.arb | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_it_IT.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_it_IT.arb index 908b80f275..e645959f7b 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_it_IT.arb +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_it_IT.arb @@ -1,35 +1,35 @@ { "@@locale": "it-IT", - "bold": "", + "bold": "Grassetto", "@bold": {}, - "bulletedList": "", + "bulletedList": "Elenco puntato", "@bulletedList": {}, - "checkbox": "", + "checkbox": "Casella di spunta", "@checkbox": {}, - "embedCode": "", + "embedCode": "Incorpora codice", "@embedCode": {}, - "heading1": "", + "heading1": "H1", "@heading1": {}, - "heading2": "", + "heading2": "H2", "@heading2": {}, - "heading3": "", + "heading3": "H3", "@heading3": {}, - "highlight": "", + "highlight": "Evidenzia", "@highlight": {}, - "image": "", + "image": "Immagine", "@image": {}, - "italic": "", + "italic": "Corsivo", "@italic": {}, - "link": "", + "link": "Collegamento", "@link": {}, - "numberedList": "", + "numberedList": "Elenco numerato", "@numberedList": {}, - "quote": "", + "quote": "Cita", "@quote": {}, - "strikethrough": "", + "strikethrough": "Barrato", "@strikethrough": {}, - "text": "", + "text": "Testo", "@text": {}, - "underline": "", + "underline": "Sottolineato", "@underline": {} } \ No newline at end of file From 1eaf97b669059dbc4613e792d60a299a6627ea41 Mon Sep 17 00:00:00 2001 From: PapuleX <32964290+PapuleX@users.noreply.github.com> Date: Mon, 3 Oct 2022 03:45:45 +0200 Subject: [PATCH 07/48] feat: Add Czech language (#1207) --- .../appflowy_editor/lib/l10n/intl_cz_CZ.arb | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_cz_CZ.arb diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_cz_CZ.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_cz_CZ.arb new file mode 100644 index 0000000000..2b1ea7b0bf --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_cz_CZ.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "cs-CZ", + "bold": "Tučně", + "@bold": {}, + "bulletedList": "Odrážkový seznam", + "@bulletedList": {}, + "checkbox": "Zaškrtávací políčko", + "@checkbox": {}, + "embedCode": "Vložit kód", + "@embedCode": {}, + "heading1": "Nadpis 1", + "@heading1": {}, + "heading2": "Nadpis 2", + "@heading2": {}, + "heading3": "Nadpis 3", + "@heading3": {}, + "highlight": "Zvýraznění", + "@highlight": {}, + "image": "Obrázek", + "@image": {}, + "italic": "Kurzíva", + "@italic": {}, + "link": "Odkaz", + "@link": {}, + "numberedList": "Číslovaný seznam", + "@numberedList": {}, + "quote": "Citace", + "@quote": {}, + "strikethrough": "Přeškrtnutí", + "@strikethrough": {}, + "text": "Text", + "@text": {}, + "underline": "Podtržení", + "@underline": {} +} \ No newline at end of file From 285231a8454e83cc518f95078ce114699cd571df Mon Sep 17 00:00:00 2001 From: Mehdi Bertul Date: Mon, 3 Oct 2022 02:46:45 +0100 Subject: [PATCH 08/48] fix : added French translations (#1203) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix : added French translations As usually, I worked on translating the new English UI strings to French. * fix : "fixed the translation" Co-authored-by: Noé Landré <68849024+DevNono@users.noreply.github.com> * fix : "fixed the translation" Co-authored-by: Noé Landré <68849024+DevNono@users.noreply.github.com> Co-authored-by: Noé Landré <68849024+DevNono@users.noreply.github.com> --- .../app_flowy/assets/translations/fr-FR.json | 40 ++++++++++++++----- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/frontend/app_flowy/assets/translations/fr-FR.json b/frontend/app_flowy/assets/translations/fr-FR.json index a35f854523..2de9e63704 100644 --- a/frontend/app_flowy/assets/translations/fr-FR.json +++ b/frontend/app_flowy/assets/translations/fr-FR.json @@ -94,7 +94,20 @@ }, "tooltip": { "lightMode": "Passer en mode clair", - "darkMode": "Passer en mode sombre" + "darkMode": "Passer en mode sombre", + "openAsPage": "Ouvrir en tant que page", + "addNewRow": "Ajouter une ligne", + "openMenu": "Cliquer pour ouvrir le menu" + }, + "sideBar": { + "closeSidebar": "Fermer le menu latéral", + "openSidebar": "Ouvrir le menu latéral" + }, + "notifications": { + "export": { + "markdown": "Note exportée en Markdown", + "path": "Documents/flowy" + } }, "contactsPage": { "title": "Contacts", @@ -123,7 +136,7 @@ "failedMsg": "Assurez-vous d'avoir terminé le processus de connexion dans votre navigateur." }, "google": { - "title": "CONNEXION GOOGLE", + "title": "CONNEXION VIA GOOGLE", "instruction1": "Pour importer vos contacts Google, vous devez autoriser cette application à l'aide de votre navigateur web.", "instruction2": "Copiez ce code dans votre presse-papiers en cliquant sur l'icône ou en sélectionnant le texte:", "instruction3": "Accédez au lien suivant dans votre navigateur web et saisissez le code ci-dessus:", @@ -135,6 +148,7 @@ "menu": { "appearance": "Apparence", "language": "Langue", + "user": "Utilisateur", "open": "Ouvrir les paramètres" }, "appearance": { @@ -142,15 +156,12 @@ "darkLabel": "Mode sombre" } }, - "sideBar": { - "openSidebar": "Open sidebar", - "closeSidebar": "Close sidebar" - }, "grid": { "settings": { "filter": "Filtrer", "sortBy": "Filtrer par", - "Properties": "Propriétés" + "Properties": "Propriétés", + "group": "Groupe" }, "field": { "hide": "Cacher", @@ -179,13 +190,17 @@ "addSelectOption": "Ajouter une option", "optionTitle": "Options", "addOption": "Ajouter une option", - "editProperty": "Modifier la propriété" + "editProperty": "Modifier la propriété", + "newColumn": "Nouvelle colonne", + "deleteFieldPromptMessage": "Vous voulez supprimer cette propriété ?" }, "row": { "duplicate": "Dupliquer", "delete": "Supprimer", "textPlaceholder": "Vide", - "copyProperty": "Copie de la propriété dans le presse-papiers" + "copyProperty": "Copie de la propriété dans le presse-papiers", + "count": "Nombre", + "newRow": "Nouvelle ligne" }, "selectOption": { "create": "Créer", @@ -211,5 +226,10 @@ "timeHintTextInTwelveHour": "01:00 PM", "timeHintTextInTwentyFourHour": "13:00" } + }, + "board": { + "column": { + "create_new_card": "Nouveau" + } } -} \ No newline at end of file +} From cff24581a4c11dbd2bf09c072488f4da7982829e Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Mon, 3 Oct 2022 09:56:45 +0800 Subject: [PATCH 09/48] feat: use appflowy styled toggle widget in date settings (#1192) * feat: use appflowy styled toggle widgets in date settings * feat: some date cell UI improvements --- .../widgets/cell/date_cell/date_editor.dart | 24 +++++++++++++++---- .../widgets/header/type_option/date.dart | 11 ++++++--- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_editor.dart index 63917da8f9..4018c0acb6 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_editor.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_editor.dart @@ -1,6 +1,8 @@ import 'package:app_flowy/generated/locale_keys.g.dart'; import 'package:app_flowy/plugins/grid/application/cell/date_cal_bloc.dart'; import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart'; +import 'package:app_flowy/workspace/presentation/widgets/toggle/toggle.dart'; +import 'package:app_flowy/workspace/presentation/widgets/toggle/toggle_style.dart'; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:dartz/dartz.dart' show Either; import 'package:easy_localization/easy_localization.dart'; @@ -167,6 +169,18 @@ class _CellCalendarWidgetState extends State<_CellCalendarWidget> { rightChevronMargin: EdgeInsets.zero, rightChevronIcon: svgWidget("home/arrow_right"), ), + daysOfWeekStyle: DaysOfWeekStyle( + dowTextFormatter: (date, locale) => + DateFormat.E(locale).format(date).toUpperCase(), + weekdayStyle: TextStyle( + fontSize: 13, + color: theme.shader3, + ), + weekendStyle: TextStyle( + fontSize: 13, + color: theme.shader3, + ), + ), calendarStyle: CalendarStyle( selectedDecoration: BoxDecoration( color: theme.main1, @@ -230,11 +244,13 @@ class _IncludeTimeButton extends StatelessWidget { FlowyText.medium(LocaleKeys.grid_field_includeTime.tr(), fontSize: 14), const Spacer(), - Switch( + Toggle( value: includeTime, - onChanged: (newValue) => context + onChanged: (value) => context .read() - .add(DateCalEvent.setIncludeTime(newValue)), + .add(DateCalEvent.setIncludeTime(!value)), + style: ToggleStyle.big(theme), + padding: EdgeInsets.zero, ), ], ), @@ -350,7 +366,7 @@ class _DateTypeOptionButton extends StatelessWidget { offset: const Offset(20, 0), constraints: BoxConstraints.loose(const Size(140, 100)), child: FlowyButton( - text: FlowyText.medium(title, fontSize: 12), + text: FlowyText.medium(title, fontSize: 14), hoverColor: theme.hover, margin: kMargin, rightIcon: svgWidget("grid/more", color: theme.iconColor), diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/date.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/date.dart index 1cebba9911..954c933511 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/date.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/date.dart @@ -1,5 +1,7 @@ import 'package:app_flowy/plugins/grid/application/field/type_option/date_bloc.dart'; import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart'; +import 'package:app_flowy/workspace/presentation/widgets/toggle/toggle.dart'; +import 'package:app_flowy/workspace/presentation/widgets/toggle/toggle_style.dart'; import 'package:easy_localization/easy_localization.dart' hide DateFormat; import 'package:app_flowy/generated/locale_keys.g.dart'; import 'package:flowy_infra/image.dart'; @@ -161,6 +163,7 @@ class _IncludeTimeButton extends StatelessWidget { @override Widget build(BuildContext context) { + final theme = context.watch(); return BlocSelector( selector: (state) => state.typeOption.includeTime, builder: (context, includeTime) { @@ -173,13 +176,15 @@ class _IncludeTimeButton extends StatelessWidget { FlowyText.medium(LocaleKeys.grid_field_includeTime.tr(), fontSize: 12), const Spacer(), - Switch( + Toggle( value: includeTime, - onChanged: (newValue) { + onChanged: (value) { context .read() - .add(DateTypeOptionEvent.includeTime(newValue)); + .add(DateTypeOptionEvent.includeTime(!value)); }, + style: ToggleStyle.big(theme), + padding: EdgeInsets.zero, ), ], ), From 2331b328d80a62379ae0030dd3d6877f5d2a4b7c Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Mon, 3 Oct 2022 09:58:13 +0800 Subject: [PATCH 10/48] feat: elide sidebar text (#1026) --- .../presentation/home/menu/app/header/header.dart | 1 + .../workspace/presentation/home/menu/app/section/item.dart | 2 +- .../lib/workspace/presentation/home/menu/menu_user.dart | 7 ++++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/presentation/home/menu/app/header/header.dart b/frontend/app_flowy/lib/workspace/presentation/home/menu/app/header/header.dart index 727b18762b..5724262aa5 100644 --- a/frontend/app_flowy/lib/workspace/presentation/home/menu/app/header/header.dart +++ b/frontend/app_flowy/lib/workspace/presentation/home/menu/app/header/header.dart @@ -99,6 +99,7 @@ class MenuAppHeader extends StatelessWidget { app.name, fontSize: 12, color: theme.textColor, + overflow: TextOverflow.ellipsis, ), ), ), diff --git a/frontend/app_flowy/lib/workspace/presentation/home/menu/app/section/item.dart b/frontend/app_flowy/lib/workspace/presentation/home/menu/app/section/item.dart index 083f7b55db..195c82adcd 100644 --- a/frontend/app_flowy/lib/workspace/presentation/home/menu/app/section/item.dart +++ b/frontend/app_flowy/lib/workspace/presentation/home/menu/app/section/item.dart @@ -82,7 +82,7 @@ class ViewSectionItem extends StatelessWidget { child: FlowyText.regular( state.view.name, fontSize: 12, - overflow: TextOverflow.clip, + overflow: TextOverflow.ellipsis, ), ), ]; diff --git a/frontend/app_flowy/lib/workspace/presentation/home/menu/menu_user.dart b/frontend/app_flowy/lib/workspace/presentation/home/menu/menu_user.dart index 399bbd1f89..12a3114f42 100644 --- a/frontend/app_flowy/lib/workspace/presentation/home/menu/menu_user.dart +++ b/frontend/app_flowy/lib/workspace/presentation/home/menu/menu_user.dart @@ -28,8 +28,9 @@ class MenuUser extends StatelessWidget { children: [ _renderAvatar(context), const HSpace(10), - _renderUserName(context), - const Spacer(), + Expanded( + child: _renderUserName(context), + ), _renderSettingsButton(context), //ToDo: when the user is allowed to create another workspace, //we get the below block back @@ -63,7 +64,7 @@ class MenuUser extends StatelessWidget { if (name.isEmpty) { name = context.read().state.userProfile.email; } - return FlowyText(name, fontSize: 12); + return FlowyText(name, fontSize: 12, overflow: TextOverflow.ellipsis); } Widget _renderSettingsButton(BuildContext context) { From 6f4ab9a55d05a2ed719bd3f27d51e329b730d4c4 Mon Sep 17 00:00:00 2001 From: citricacid2 <61765149+citricacid2@users.noreply.github.com> Date: Mon, 3 Oct 2022 00:00:32 -0400 Subject: [PATCH 11/48] feat: added markdown links --- .../markdown_syntax_to_styled_text.dart | 55 +++++++++++++++++++ .../built_in_shortcut_events.dart | 5 ++ 2 files changed, 60 insertions(+) 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 4b74145ec0..d0f93fdd73 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 @@ -124,3 +124,58 @@ ShortcutEventHandler backquoteToCodeHandler = (editorState, event) { 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) { + 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; + } + + // find all of the indexs for important characters + final textNode = textNodes.first; + final text = textNode.toRawString(); + final firstOpeningBracket = text.indexOf('['); + final firstClosingBracket = text.indexOf(']'); + + // use regex to validate the format of the link + // note: this enforces that the link has http or https + final regexp = RegExp(r'\[([\w\s\d]+)\]\(((?:\/|https?:\/\/)[\w\d./?=#]+)$'); + final match = regexp.firstMatch(text); + if (match == null) { + return KeyEventResult.ignored; + } + + // extract the text and the url of the link + final linkText = match.group(1); + final linkUrl = match.group(2); + + // Delete the initial opening bracket, + // update the href attribute of the text surrounded by [ ] to the url, + // delete everything after the text, + // and update the cursor position. + TransactionBuilder(editorState) + ..deleteText(textNode, firstOpeningBracket, 1) + ..formatText( + textNode, + firstOpeningBracket, + firstClosingBracket - firstOpeningBracket - 1, + { + BuiltInAttributeKey.href: linkUrl, + }, + ) + ..deleteText(textNode, firstClosingBracket - 1, + selection.end.offset - firstClosingBracket) + ..afterSelection = Selection.collapsed( + Position( + path: textNode.path, + offset: firstOpeningBracket + linkText!.length, + ), + ) + ..commit(); + + return KeyEventResult.handled; +}; 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 827b057700..569942f635 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: 'Markdown link to link', + command: 'shift+parenthesis right', + handler: markdownLinkToLinkHandler, + ), // 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 From 3e620ed3358be9f1e6e63d2daae7c9d6290a1343 Mon Sep 17 00:00:00 2001 From: citricacid2 <61765149+citricacid2@users.noreply.github.com> Date: Mon, 3 Oct 2022 17:00:17 -0400 Subject: [PATCH 12/48] feat: added numbered list to the slash menu --- .../src/render/selection_menu/selection_menu_service.dart | 8 ++++++++ .../default_text_operations/format_rich_text_style.dart | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_service.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_service.dart index 4fbc480dfd..4da824db80 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_service.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_service.dart @@ -169,6 +169,14 @@ final List _defaultSelectionMenuItems = [ insertBulletedListAfterSelection(editorState); }, ), + SelectionMenuItem( + name: () => AppFlowyEditorLocalizations.current.numberedList, + icon: _selectionMenuIcon('number'), + keywords: ['numbered list', 'list', 'ordered list'], + handler: (editorState, _, __) { + insertNumberedListAfterSelection(editorState); + }, + ), SelectionMenuItem( name: () => AppFlowyEditorLocalizations.current.checkbox, icon: _selectionMenuIcon('checkbox'), diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart index 053d9e542a..adf45bec65 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart @@ -34,6 +34,13 @@ void insertBulletedListAfterSelection(EditorState editorState) { }); } +void insertNumberedListAfterSelection(EditorState editorState) { + insertTextNodeAfterSelection(editorState, { + BuiltInAttributeKey.subtype: BuiltInAttributeKey.numberList, + BuiltInAttributeKey.number: 1, + }); +} + bool insertTextNodeAfterSelection( EditorState editorState, Attributes attributes) { final selection = editorState.service.selectionService.currentSelection.value; From be5123ca21ffc402af594b0e0ee0ac45d1821f47 Mon Sep 17 00:00:00 2001 From: Alexandre Moreau Date: Sun, 2 Oct 2022 17:30:42 +0200 Subject: [PATCH 13/48] feat: implement doubleTildeToStrikethrough ShortcutEventHandler --- .../markdown_syntax_to_styled_text.dart | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) 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 4b74145ec0..95725cb4a1 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 @@ -124,3 +124,66 @@ 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; +}; From b52dfe12c2ffe1d12cb9da54bb766e101c75a0b8 Mon Sep 17 00:00:00 2001 From: Alexandre Moreau Date: Sun, 2 Oct 2022 17:32:09 +0200 Subject: [PATCH 14/48] feat: add doubleTildeToStrikethrough ShortcutEventHandler to built-in ShortcutEvents --- .../src/service/shortcut_event/built_in_shortcut_events.dart | 5 +++++ 1 file changed, 5 insertions(+) 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 827b057700..d4f5118096 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, + ), // 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 From d6cae56d7447faf4ff5185c68504bd406ba2ac06 Mon Sep 17 00:00:00 2001 From: Alexandre Moreau Date: Sun, 2 Oct 2022 17:41:28 +0200 Subject: [PATCH 15/48] test: add tests for double tilde to strikethrough --- .../test/infra/test_raw_key_event.dart | 3 + .../markdown_syntax_to_styled_text_test.dart | 106 ++++++++++++++++++ 2 files changed, 109 insertions(+) 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); + }); + }); }); } From c403e083cc16594b6b52fc565fe395572e727d96 Mon Sep 17 00:00:00 2001 From: Ankur Date: Tue, 4 Oct 2022 17:59:38 +0530 Subject: [PATCH 16/48] feat: Added id-ID translation (#1217) Signed-off-by: ankur12-1610 Signed-off-by: ankur12-1610 --- .../appflowy_editor/lib/l10n/intl_id_ID.arb | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_id_ID.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_id_ID.arb index cadebb36aa..6b3f6a0a6d 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_id_ID.arb +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_id_ID.arb @@ -1,35 +1,35 @@ { "@@locale": "id-ID", - "bold": "", + "bold": "berani", "@bold": {}, - "bulletedList": "", + "bulletedList": "daftar berpoin", "@bulletedList": {}, - "checkbox": "", + "checkbox": "kotak centang", "@checkbox": {}, - "embedCode": "", + "embedCode": "menyematkan Kode", "@embedCode": {}, - "heading1": "", + "heading1": "pos1", "@heading1": {}, - "heading2": "", + "heading2": "pos2", "@heading2": {}, - "heading3": "", + "heading3": "pos3", "@heading3": {}, - "highlight": "", + "highlight": "menyorot", "@highlight": {}, - "image": "", + "image": "gambar", "@image": {}, - "italic": "", + "italic": "miring", "@italic": {}, - "link": "", + "link": "tautan", "@link": {}, - "numberedList": "", + "numberedList": "daftar bernomor", "@numberedList": {}, - "quote": "", + "quote": "mengutip", "@quote": {}, - "strikethrough": "", + "strikethrough": "coret", "@strikethrough": {}, - "text": "", + "text": "teks", "@text": {}, - "underline": "", + "underline": "menggarisbawahi", "@underline": {} } \ No newline at end of file From 069a23c96b51a5a959fdc9e97345d46d1a731824 Mon Sep 17 00:00:00 2001 From: Ankur Date: Tue, 4 Oct 2022 18:00:06 +0530 Subject: [PATCH 17/48] feat: Added hu-HU translation (#1214) Signed-off-by: ankur12-1610 Signed-off-by: ankur12-1610 --- .../appflowy_editor/lib/l10n/intl_hu_HU.arb | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_hu_HU.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_hu_HU.arb index 8b55ed3096..f96b3b0ec3 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_hu_HU.arb +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_hu_HU.arb @@ -1,35 +1,35 @@ { "@@locale": "hu-HU", - "bold": "", + "bold": "bátor", "@bold": {}, - "bulletedList": "", + "bulletedList": "pontozott lista", "@bulletedList": {}, - "checkbox": "", + "checkbox": "jelölőnégyzetet", "@checkbox": {}, - "embedCode": "", + "embedCode": "Beágyazás", "@embedCode": {}, - "heading1": "", + "heading1": "címsor1", "@heading1": {}, - "heading2": "", + "heading2": "címsor2", "@heading2": {}, - "heading3": "", + "heading3": "címsor3", "@heading3": {}, - "highlight": "", + "highlight": "Kiemel", "@highlight": {}, - "image": "", + "image": "kép", "@image": {}, - "italic": "", + "italic": "dőlt", "@italic": {}, - "link": "", + "link": "link", "@link": {}, - "numberedList": "", + "numberedList": "számozottLista", "@numberedList": {}, - "quote": "", + "quote": "idézet", "@quote": {}, - "strikethrough": "", + "strikethrough": "áthúzott", "@strikethrough": {}, - "text": "", + "text": "szöveg", "@text": {}, - "underline": "", + "underline": "aláhúzás", "@underline": {} } \ No newline at end of file From 176668049c932e5bd01c54d4bd4b5de582a8f502 Mon Sep 17 00:00:00 2001 From: Ankur Date: Tue, 4 Oct 2022 18:00:45 +0530 Subject: [PATCH 18/48] feat: Added fr_CA translation (#1212) Signed-off-by: ankur12-1610 Signed-off-by: ankur12-1610 --- .../appflowy_editor/lib/l10n/intl_fr_CA.arb | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_fr_CA.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_fr_CA.arb index 6875d45d72..1ff4ab2f75 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_fr_CA.arb +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_fr_CA.arb @@ -1,35 +1,35 @@ { "@@locale": "fr-CA", - "bold": "", + "bold": "gras", "@bold": {}, - "bulletedList": "", + "bulletedList": "liste à puces", "@bulletedList": {}, - "checkbox": "", + "checkbox": "case à cocher", "@checkbox": {}, - "embedCode": "", + "embedCode": "incorporer Code", "@embedCode": {}, - "heading1": "", + "heading1": "en-tête1", "@heading1": {}, - "heading2": "", + "heading2": "en-tête2", "@heading2": {}, - "heading3": "", + "heading3": "en-tête3", "@heading3": {}, - "highlight": "", + "highlight": "mettre en évidence", "@highlight": {}, - "image": "", + "image": "l’image", "@image": {}, - "italic": "", + "italic": "italique", "@italic": {}, - "link": "", + "link": "lien", "@link": {}, - "numberedList": "", + "numberedList": "liste numérotée", "@numberedList": {}, - "quote": "", + "quote": "citation", "@quote": {}, - "strikethrough": "", + "strikethrough": "barré", "@strikethrough": {}, - "text": "", + "text": "texte", "@text": {}, - "underline": "", + "underline": "souligner", "@underline": {} } \ No newline at end of file From 6ea3ea189e473337e86c454abd6c4d016ca1b38b Mon Sep 17 00:00:00 2001 From: Enzo Lizama Date: Tue, 4 Oct 2022 11:37:40 -0500 Subject: [PATCH 19/48] chore: remove unused imports --- .../example/lib/plugin/code_block_node_widget.dart | 2 +- .../src/render/rich_text/built_in_text_widget.dart | 1 - .../backspace_handler.dart | 1 - .../copy_paste_handler.dart | 1 - .../enter_without_shift_in_text_node_handler.dart | 1 - .../markdown_syntax_to_styled_text.dart | 3 +-- .../test/extensions/path_extensions_test.dart | 1 - .../test/render/rich_text/checkbox_text_test.dart | 2 -- .../render/rich_text/toolbar_rich_text_test.dart | 1 - .../selection_menu/selection_menu_widget_test.dart | 12 ++++++------ .../backspace_handler_test.dart | 1 - .../format_style_handler_test.dart | 1 - .../slash_handler_test.dart | 1 - .../white_space_handler_test.dart | 1 - .../test/service/toolbar_service_test.dart | 1 - 15 files changed, 8 insertions(+), 22 deletions(-) diff --git a/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/code_block_node_widget.dart b/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/code_block_node_widget.dart index 3949073756..8f80990fc4 100644 --- a/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/code_block_node_widget.dart +++ b/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/code_block_node_widget.dart @@ -45,7 +45,7 @@ ShortcutEventHandler _ignorekHandler = (editorState, event) { }; SelectionMenuItem codeBlockItem = SelectionMenuItem( - name: 'Code Block', + name: () => 'Code Block', icon: const Icon(Icons.abc), keywords: ['code block'], handler: (editorState, _, __) { diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/built_in_text_widget.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/built_in_text_widget.dart index f2c8693f87..a0a65d9583 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/built_in_text_widget.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/built_in_text_widget.dart @@ -1,5 +1,4 @@ import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart'; import 'package:flutter/material.dart'; abstract class BuiltInTextWidget extends StatefulWidget { diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/backspace_handler.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/backspace_handler.dart index 3f7a54810f..c9956635ba 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/backspace_handler.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/backspace_handler.dart @@ -3,7 +3,6 @@ import 'package:appflowy_editor/src/service/internal_key_event_handlers/number_l import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/extensions/path_extensions.dart'; // Handle delete text. ShortcutEventHandler deleteTextHandler = (editorState, event) { diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart index e48ec3426d..916ffe317e 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart @@ -1,7 +1,6 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/src/infra/html_converter.dart'; import 'package:appflowy_editor/src/document/node_iterator.dart'; -import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; import 'package:appflowy_editor/src/service/internal_key_event_handlers/number_list_helper.dart'; import 'package:flutter/material.dart'; import 'package:rich_clipboard/rich_clipboard.dart'; diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart index c31b8c3699..2a2d1deedb 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart @@ -3,7 +3,6 @@ import 'dart:collection'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/material.dart'; -import 'package:appflowy_editor/src/extensions/path_extensions.dart'; import './number_list_helper.dart'; /// Handle some cases where enter is pressed and shift is not pressed. 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 4b74145ec0..c46b2322c7 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; } diff --git a/frontend/app_flowy/packages/appflowy_editor/test/extensions/path_extensions_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/extensions/path_extensions_test.dart index c39c0d0e56..5d84690641 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/extensions/path_extensions_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/extensions/path_extensions_test.dart @@ -1,6 +1,5 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:appflowy_editor/src/extensions/path_extensions.dart'; void main() async { setUpAll(() { diff --git a/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/checkbox_text_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/checkbox_text_test.dart index 96cdbbce65..29dfbba136 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/checkbox_text_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/checkbox_text_test.dart @@ -1,6 +1,4 @@ import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; -import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart'; import 'package:appflowy_editor/src/extensions/text_node_extensions.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../infra/test_editor.dart'; diff --git a/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart index 9e8a5c15c9..c9d9ef9e70 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart @@ -1,5 +1,4 @@ import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; import 'package:appflowy_editor/src/extensions/text_node_extensions.dart'; import 'package:appflowy_editor/src/render/toolbar/toolbar_item_widget.dart'; import 'package:appflowy_editor/src/render/toolbar/toolbar_widget.dart'; diff --git a/frontend/app_flowy/packages/appflowy_editor/test/render/selection_menu/selection_menu_widget_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/render/selection_menu/selection_menu_widget_test.dart index 1495105746..2076daefc1 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/render/selection_menu/selection_menu_widget_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/render/selection_menu/selection_menu_widget_test.dart @@ -1,7 +1,6 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/src/render/selection_menu/selection_menu_item_widget.dart'; import 'package:appflowy_editor/src/render/selection_menu/selection_menu_service.dart'; -import 'package:appflowy_editor/src/render/selection_menu/selection_menu_widget.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../infra/test_editor.dart'; @@ -13,8 +12,8 @@ void main() async { group('selection_menu_widget.dart', () { for (var i = 0; i < defaultSelectionMenuItems.length; i += 1) { - testWidgets('Selects number.$i item in selection menu with enter', ( - tester) async { + testWidgets('Selects number.$i item in selection menu with enter', + (tester) async { final editor = await _prepare(tester); for (var j = 0; j < i; j++) { await editor.pressLogicKey(LogicalKeyboardKey.arrowDown); @@ -30,8 +29,8 @@ void main() async { } }); - testWidgets('Selects number.$i item in selection menu with click', ( - tester) async { + testWidgets('Selects number.$i item in selection menu with click', + (tester) async { final editor = await _prepare(tester); await tester.tap(find.byType(SelectionMenuItemWidget).at(i)); @@ -148,7 +147,8 @@ Future _testDefaultSelectionMenuItems( int index, EditorWidgetTester editor) async { expect(editor.documentLength, 4); expect(editor.documentSelection, Selection.single(path: [2], startOffset: 0)); - expect((editor.nodeAtPath([1]) as TextNode).toRawString(), 'Welcome to Appflowy 😁'); + expect((editor.nodeAtPath([1]) as TextNode).toRawString(), + 'Welcome to Appflowy 😁'); final node = editor.nodeAtPath([2]); final item = defaultSelectionMenuItems[index]; final itemName = item.name(); diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/backspace_handler_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/backspace_handler_test.dart index 37ad7bade3..1e423c39e0 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/backspace_handler_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/backspace_handler_test.dart @@ -2,7 +2,6 @@ import 'dart:collection'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/src/render/image/image_node_widget.dart'; -import 'package:appflowy_editor/src/render/rich_text/flowy_rich_text.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:network_image_mock/network_image_mock.dart'; diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart index 7240c186f3..0cb4c71600 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart @@ -8,7 +8,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../infra/test_editor.dart'; -import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; void main() async { setUpAll(() { diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/slash_handler_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/slash_handler_test.dart index 4d5492e620..b3166a46fb 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/slash_handler_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/slash_handler_test.dart @@ -1,7 +1,6 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/src/render/selection_menu/selection_menu_item_widget.dart'; import 'package:appflowy_editor/src/render/selection_menu/selection_menu_service.dart'; -import 'package:appflowy_editor/src/render/selection_menu/selection_menu_widget.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../infra/test_editor.dart'; 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 fd7707a5f5..c0948a93c5 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 @@ -3,7 +3,6 @@ import 'package:appflowy_editor/src/service/internal_key_event_handlers/whitespa import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../infra/test_editor.dart'; -import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; void main() async { setUpAll(() { diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/toolbar_service_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/toolbar_service_test.dart index 5b82a5dea9..1e8f2f1f07 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/service/toolbar_service_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/service/toolbar_service_test.dart @@ -4,7 +4,6 @@ import 'package:appflowy_editor/src/render/toolbar/toolbar_item_widget.dart'; import 'package:appflowy_editor/src/render/toolbar/toolbar_widget.dart'; import 'package:flutter_test/flutter_test.dart'; import '../infra/test_editor.dart'; -import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; void main() async { setUpAll(() { From bf9f6ac13f75a80a061fd93f2d5d4f465cd41b61 Mon Sep 17 00:00:00 2001 From: Enzo Lizama Date: Tue, 4 Oct 2022 11:37:59 -0500 Subject: [PATCH 20/48] chore: small code improvements --- .../lib/src/extensions/attributes_extension.dart | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/attributes_extension.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/attributes_extension.dart index c80753b217..b8970d8225 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/attributes_extension.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/attributes_extension.dart @@ -17,9 +17,9 @@ extension NodeAttributesExtensions on Attributes { return containsKey(BuiltInAttributeKey.quote); } - int? get number { + num? get number { if (containsKey(BuiltInAttributeKey.number) && - this[BuiltInAttributeKey.number] is int) { + this[BuiltInAttributeKey.number] is num) { return this[BuiltInAttributeKey.number]; } return null; @@ -27,7 +27,7 @@ extension NodeAttributesExtensions on Attributes { bool get code { if (containsKey(BuiltInAttributeKey.code) && - this[BuiltInAttributeKey.code] == true) { + this[BuiltInAttributeKey.code] is bool) { return this[BuiltInAttributeKey.code]; } return false; @@ -63,11 +63,14 @@ extension DeltaAttributesExtensions on Attributes { this[BuiltInAttributeKey.strikethrough] == true); } + static const whiteInt = 0XFFFFFFFF; + Color? get color { if (containsKey(BuiltInAttributeKey.color) && this[BuiltInAttributeKey.color] is String) { return Color( - int.parse(this[BuiltInAttributeKey.color]), + // If the parse fails returns white by default + int.tryParse(this[BuiltInAttributeKey.color]) ?? whiteInt, ); } return null; @@ -77,8 +80,7 @@ extension DeltaAttributesExtensions on Attributes { if (containsKey(BuiltInAttributeKey.backgroundColor) && this[BuiltInAttributeKey.backgroundColor] is String) { return Color( - int.parse(this[BuiltInAttributeKey.backgroundColor]), - ); + int.tryParse(this[BuiltInAttributeKey.backgroundColor]) ?? whiteInt); } return null; } From dd15a49d765b0725cd15330777f21604429c201b Mon Sep 17 00:00:00 2001 From: Enzo Lizama Date: Tue, 4 Oct 2022 11:38:34 -0500 Subject: [PATCH 21/48] test: add unit tests for attributes extensions --- .../extensions/attributes_extension_test.dart | 201 ++++++++++++++++++ frontend/app_flowy/pubspec.lock | 2 +- 2 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 frontend/app_flowy/packages/appflowy_editor/test/extensions/attributes_extension_test.dart diff --git a/frontend/app_flowy/packages/appflowy_editor/test/extensions/attributes_extension_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/extensions/attributes_extension_test.dart new file mode 100644 index 0000000000..427e53f32a --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/test/extensions/attributes_extension_test.dart @@ -0,0 +1,201 @@ +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('NodeAttributesExtensions::', () { + test('heading', () { + final Attributes attribute = { + 'subtype': 'heading', + 'heading': 'AppFlowy', + }; + expect(attribute.heading, 'AppFlowy'); + }); + + test('heading - text is not String return null', () { + final Attributes attribute = { + 'subtype': 'heading', + 'heading': 123, + }; + expect(attribute.heading, null); + }); + + test('heading - subtype is not "heading" return null', () { + final Attributes attribute = { + 'subtype': 'code', + 'heading': 'Hello World!', + }; + expect(attribute.heading, null); + }); + + test('quote', () { + final Attributes attribute = { + 'quote': 'quote text', + }; + expect(attribute.quote, true); + }); + + test('number - int', () { + final Attributes attribute = { + 'number': 99, + }; + expect(attribute.number, 99); + }); + + test('number - double', () { + final Attributes attribute = { + 'number': 12.34, + }; + expect(attribute.number, 12.34); + }); + + test('number - return null', () { + final Attributes attribute = { + 'code': 12.34, + }; + expect(attribute.number, null); + }); + + test('code', () { + final Attributes attribute = { + 'code': true, + }; + expect(attribute.code, true); + }); + + test('code - return false', () { + final Attributes attribute = { + 'quote': true, + }; + expect(attribute.code, false); + }); + + test('check', () { + final Attributes attribute = { + 'checkbox': true, + }; + expect(attribute.check, true); + }); + + test('check - return false', () { + final Attributes attribute = { + 'quote': true, + }; + expect(attribute.check, false); + }); + }); + + group('DeltaAttributesExtensions::', () { + test('bold', () { + final Attributes attribute = { + 'bold': true, + }; + expect(attribute.bold, true); + }); + + test('bold - return false', () { + final Attributes attribute = { + 'bold': 123, + }; + expect(attribute.bold, false); + }); + + test('italic', () { + final Attributes attribute = { + 'italic': true, + }; + expect(attribute.italic, true); + }); + + test('italic - return false', () { + final Attributes attribute = { + 'italic': 123, + }; + expect(attribute.italic, false); + }); + + test('underline', () { + final Attributes attribute = { + 'underline': true, + }; + expect(attribute.underline, true); + }); + + test('underline - return false', () { + final Attributes attribute = { + 'underline': 123, + }; + expect(attribute.underline, false); + }); + + test('strikethrough', () { + final Attributes attribute = { + 'strikethrough': true, + }; + expect(attribute.strikethrough, true); + }); + + test('strikethrough - return false', () { + final Attributes attribute = { + 'strikethrough': 123, + }; + expect(attribute.strikethrough, false); + }); + + test('color', () { + final Attributes attribute = { + 'color': '0xff212fff', + }; + expect(attribute.color, const Color(0XFF212FFF)); + }); + + test('color - return null', () { + final Attributes attribute = { + 'color': 123, + }; + expect(attribute.color, null); + }); + + test('color - parse failure return white', () { + final Attributes attribute = { + 'color': 'hello123', + }; + expect(attribute.color, const Color(0XFFFFFFFF)); + }); + + test('backgroundColor', () { + final Attributes attribute = { + 'backgroundColor': '0xff678fff', + }; + expect(attribute.backgroundColor, const Color(0XFF678FFF)); + }); + + test('backgroundColor - return null', () { + final Attributes attribute = { + 'backgroundColor': 123, + }; + expect(attribute.backgroundColor, null); + }); + + test('backgroundColor - parse failure return white', () { + final Attributes attribute = { + 'backgroundColor': 'hello123', + }; + expect(attribute.backgroundColor, const Color(0XFFFFFFFF)); + }); + + test('href', () { + final Attributes attribute = { + 'href': '/app/flowy', + }; + expect(attribute.href, '/app/flowy'); + }); + + test('href - return null', () { + final Attributes attribute = { + 'href': 123, + }; + expect(attribute.href, null); + }); + }); +} diff --git a/frontend/app_flowy/pubspec.lock b/frontend/app_flowy/pubspec.lock index a042baadd7..eaa1ad4900 100644 --- a/frontend/app_flowy/pubspec.lock +++ b/frontend/app_flowy/pubspec.lock @@ -28,7 +28,7 @@ packages: path: "packages/appflowy_board" relative: true source: path - version: "0.0.7" + version: "0.0.8" appflowy_editor: dependency: "direct main" description: From 791494961315e302fe39d2bdddf9c3e9492ec0c3 Mon Sep 17 00:00:00 2001 From: Enzo Lizama Date: Tue, 4 Oct 2022 17:59:20 -0500 Subject: [PATCH 22/48] test: color tests --- .../test/extensions/color_extension_test.dart | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 frontend/app_flowy/packages/appflowy_editor/test/extensions/color_extension_test.dart diff --git a/frontend/app_flowy/packages/appflowy_editor/test/extensions/color_extension_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/extensions/color_extension_test.dart new file mode 100644 index 0000000000..929a5c0378 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/test/extensions/color_extension_test.dart @@ -0,0 +1,40 @@ +import 'package:appflowy_editor/src/extensions/color_extension.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('ColorExtension::', () { + const white = Color(0XFFFFFFFF); + const black = Color(0XFF000000); + const blue = Color(0XFF000FFF); + const blueRgba = 'rgba(0, 15, 255, 255)'; + test('ToRgbaString', () { + expect(blue.toRgbaString(), 'rgba(0, 15, 255, 255)'); + expect(white.toRgbaString(), 'rgba(255, 255, 255, 255)'); + expect(black.toRgbaString(), 'rgba(0, 0, 0, 255)'); + }); + + test('tryFromRgbaString', () { + final color = ColorExtension.tryFromRgbaString(blueRgba); + expect(color, const Color.fromARGB(255, 0, 15, 255)); + }); + + test('tryFromRgbaString - wrong rgba format return null', () { + const wrongRgba = 'abc(1,2,3,4)'; + final color = ColorExtension.tryFromRgbaString(wrongRgba); + expect(color, null); + }); + + test('tryFromRgbaString - wrong length return null', () { + const wrongRgba = 'rgba(0, 15, 255)'; + final color = ColorExtension.tryFromRgbaString(wrongRgba); + expect(color, null); + }); + + test('tryFromRgbaString - wrong values return null', () { + const wrongRgba = 'rgba(-12, 999, 1234, 619)'; + final color = ColorExtension.tryFromRgbaString(wrongRgba); + expect(color, null); + }); + }); +} From 50ebaa3ab8fef7a696230fa6849761e34fc9169f Mon Sep 17 00:00:00 2001 From: Enzo Lizama Date: Tue, 4 Oct 2022 17:59:30 -0500 Subject: [PATCH 23/48] test: object tests --- .../extensions/object_extension_test.dart | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 frontend/app_flowy/packages/appflowy_editor/test/extensions/object_extension_test.dart diff --git a/frontend/app_flowy/packages/appflowy_editor/test/extensions/object_extension_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/extensions/object_extension_test.dart new file mode 100644 index 0000000000..151df9cc31 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/test/extensions/object_extension_test.dart @@ -0,0 +1,20 @@ +import 'dart:io'; + +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:appflowy_editor/src/extensions/object_extensions.dart'; + +void main() { + group('FlowyObjectExtensions::', () { + test('unwrapOrNull', () { + final result = const TextSpan().unwrapOrNull(); + assert(result is TextSpan); + }); + + test('unwrapOrNull - return null', () { + final result = const TextSpan().unwrapOrNull(); + expect(result, null); + }); + }); +} From c0aa90b0b3058a023cecf22aa5638f3fc9b88c53 Mon Sep 17 00:00:00 2001 From: Enzo Lizama Date: Tue, 4 Oct 2022 22:19:38 -0500 Subject: [PATCH 24/48] test: text style tests --- .../extensions/text_style_extension_test.dart | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 frontend/app_flowy/packages/appflowy_editor/test/extensions/text_style_extension_test.dart diff --git a/frontend/app_flowy/packages/appflowy_editor/test/extensions/text_style_extension_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/extensions/text_style_extension_test.dart new file mode 100644 index 0000000000..572178d0f3 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/test/extensions/text_style_extension_test.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:appflowy_editor/src/extensions/text_style_extension.dart'; + +void main() { + group('TextStyleExtensions::', () { + const style = TextStyle( + color: Colors.blue, + backgroundColor: Colors.white, + fontSize: 14, + height: 100, + wordSpacing: 2, + fontWeight: FontWeight.w700, + ); + + const otherStyle = TextStyle( + color: Colors.red, + backgroundColor: Colors.black, + fontSize: 12, + height: 10, + wordSpacing: 1, + ); + test('combine', () { + final result = style.combine(otherStyle); + expect(result.color, Colors.red); + expect(result.backgroundColor, Colors.black); + expect(result.fontSize, 12); + expect(result.height, 10); + expect(result.wordSpacing, 1); + }); + + test('combine - return this', () { + final result = style.combine(null); + expect(result, style); + }); + + test('combine - return null with inherit', () { + final styleCopy = otherStyle.copyWith(inherit: false); + final result = style.combine(styleCopy); + expect(result, styleCopy); + }); + }); +} From 4acce447f24f808a8c4502a10fb078e3193e27bd Mon Sep 17 00:00:00 2001 From: Enzo Lizama Date: Tue, 4 Oct 2022 22:39:55 -0500 Subject: [PATCH 25/48] test: add node extension test --- .../test/extensions/node_extension_test.dart | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 frontend/app_flowy/packages/appflowy_editor/test/extensions/node_extension_test.dart diff --git a/frontend/app_flowy/packages/appflowy_editor/test/extensions/node_extension_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/extensions/node_extension_test.dart new file mode 100644 index 0000000000..7b0d106582 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/test/extensions/node_extension_test.dart @@ -0,0 +1,31 @@ +import 'dart:ui'; + +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:appflowy_editor/src/extensions/node_extensions.dart'; + +class MockNode extends Mock implements Node {} + +void main() { + group('NodeExtensions::', () { + final mockNode = MockNode(); + + final selection = Selection( + start: Position(path: [0, 1]), + end: Position(path: [1, 0]), + ); + + test('rect - renderBox is null', () { + when(mockNode.renderBox).thenReturn(null); + final result = mockNode.rect; + expect(result, Rect.zero); + }); + + // test('inSelection', () { + // when(mockNode.path).thenAnswer((_) => [3, 3]); + // final result = mockNode.inSelection(selection); + // expect(result, true); + // }); + }); +} From b4aceec346a4bb544cf1a6e792bcd79df4481311 Mon Sep 17 00:00:00 2001 From: Enzo Lizama Date: Tue, 4 Oct 2022 22:40:07 -0500 Subject: [PATCH 26/48] test: add mockito --- frontend/app_flowy/packages/appflowy_editor/pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/app_flowy/packages/appflowy_editor/pubspec.yaml b/frontend/app_flowy/packages/appflowy_editor/pubspec.yaml index 76ec0b758d..b41f9243c0 100644 --- a/frontend/app_flowy/packages/appflowy_editor/pubspec.yaml +++ b/frontend/app_flowy/packages/appflowy_editor/pubspec.yaml @@ -32,6 +32,7 @@ dev_dependencies: sdk: flutter flutter_lints: ^2.0.1 network_image_mock: ^2.1.1 + mockito: ^5.3.2 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec From fe524736ec51c8b29ea28180288968b3978643f4 Mon Sep 17 00:00:00 2001 From: Enzo Lizama Date: Tue, 4 Oct 2022 22:40:15 -0500 Subject: [PATCH 27/48] test: add url launcher test --- .../url_launcher_extension_test.dart | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 frontend/app_flowy/packages/appflowy_editor/test/extensions/url_launcher_extension_test.dart diff --git a/frontend/app_flowy/packages/appflowy_editor/test/extensions/url_launcher_extension_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/extensions/url_launcher_extension_test.dart new file mode 100644 index 0000000000..7c8761e5a9 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/test/extensions/url_launcher_extension_test.dart @@ -0,0 +1,22 @@ +import 'package:appflowy_editor/src/extensions/url_launcher_extension.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + // test('safeLaunchUrl with scheme', () async { + // const href = 'https://github.com/AppFlowy-IO'; + // final result = await safeLaunchUrl(href); + // expect(result, true); + // }); + + // test('safeLaunchUrl without scheme', () async { + // const href = 'github.com/AppFlowy-IO'; + // final result = await safeLaunchUrl(href); + // expect(result, true); + // }); + + test('safeLaunchUrl without scheme', () async { + const href = null; + final result = await safeLaunchUrl(href); + expect(result, false); + }); +} From 2501c15a8f1b75dda110cf201fc96a85cd382629 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Wed, 5 Oct 2022 21:07:45 +0800 Subject: [PATCH 28/48] chore: update l10n and fix selection_menu_widget_test --- .../lib/plugin/code_block_node_widget.dart | 2 +- .../l10n/{intl_cz_CZ.arb => intl_cs_CZ.arb} | 0 .../lib/src/l10n/intl/messages_all.dart | 8 ++++ .../lib/src/l10n/intl/messages_cs-CZ.dart | 44 +++++++++++++++++++ .../lib/src/l10n/intl/messages_fr-FR.dart | 32 +++++++------- .../lib/src/l10n/intl/messages_it-IT.dart | 32 +++++++------- .../lib/src/l10n/intl/messages_nl-NL.dart | 43 ++++++++++++++++++ .../lib/src/l10n/intl/messages_pt-BR.dart | 33 +++++++------- .../appflowy_editor/lib/src/l10n/l10n.dart | 2 + .../selection_menu_widget_test.dart | 14 +++--- 10 files changed, 154 insertions(+), 56 deletions(-) rename frontend/app_flowy/packages/appflowy_editor/lib/l10n/{intl_cz_CZ.arb => intl_cs_CZ.arb} (100%) create mode 100644 frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_cs-CZ.dart create mode 100644 frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_nl-NL.dart diff --git a/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/code_block_node_widget.dart b/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/code_block_node_widget.dart index 3949073756..8f80990fc4 100644 --- a/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/code_block_node_widget.dart +++ b/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/code_block_node_widget.dart @@ -45,7 +45,7 @@ ShortcutEventHandler _ignorekHandler = (editorState, event) { }; SelectionMenuItem codeBlockItem = SelectionMenuItem( - name: 'Code Block', + name: () => 'Code Block', icon: const Icon(Icons.abc), keywords: ['code block'], handler: (editorState, _, __) { diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_cz_CZ.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_cs_CZ.arb similarity index 100% rename from frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_cz_CZ.arb rename to frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_cs_CZ.arb diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_all.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_all.dart index 1dc2273626..02e3d46041 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_all.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_all.dart @@ -16,6 +16,7 @@ import 'package:intl/message_lookup_by_library.dart'; import 'package:intl/src/intl_helpers.dart'; import 'messages_ca.dart' as messages_ca; +import 'messages_cs-CZ.dart' as messages_cs_cz; import 'messages_de-DE.dart' as messages_de_de; import 'messages_en.dart' as messages_en; import 'messages_es-VE.dart' as messages_es_ve; @@ -25,6 +26,7 @@ import 'messages_hu-HU.dart' as messages_hu_hu; import 'messages_id-ID.dart' as messages_id_id; import 'messages_it-IT.dart' as messages_it_it; import 'messages_ja-JP.dart' as messages_ja_jp; +import 'messages_nl-NL.dart' as messages_nl_nl; import 'messages_pl-PL.dart' as messages_pl_pl; import 'messages_pt-BR.dart' as messages_pt_br; import 'messages_pt-PT.dart' as messages_pt_pt; @@ -36,6 +38,7 @@ import 'messages_zh-TW.dart' as messages_zh_tw; typedef Future LibraryLoader(); Map _deferredLibraries = { 'ca': () => new Future.value(null), + 'cs_CZ': () => new Future.value(null), 'de_DE': () => new Future.value(null), 'en': () => new Future.value(null), 'es_VE': () => new Future.value(null), @@ -45,6 +48,7 @@ Map _deferredLibraries = { 'id_ID': () => new Future.value(null), 'it_IT': () => new Future.value(null), 'ja_JP': () => new Future.value(null), + 'nl_NL': () => new Future.value(null), 'pl_PL': () => new Future.value(null), 'pt_BR': () => new Future.value(null), 'pt_PT': () => new Future.value(null), @@ -58,6 +62,8 @@ MessageLookupByLibrary? _findExact(String localeName) { switch (localeName) { case 'ca': return messages_ca.messages; + case 'cs_CZ': + return messages_cs_cz.messages; case 'de_DE': return messages_de_de.messages; case 'en': @@ -76,6 +82,8 @@ MessageLookupByLibrary? _findExact(String localeName) { return messages_it_it.messages; case 'ja_JP': return messages_ja_jp.messages; + case 'nl_NL': + return messages_nl_nl.messages; case 'pl_PL': return messages_pl_pl.messages; case 'pt_BR': diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_cs-CZ.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_cs-CZ.dart new file mode 100644 index 0000000000..810fe3888f --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_cs-CZ.dart @@ -0,0 +1,44 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a cs_CZ locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'cs_CZ'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "bold": MessageLookupByLibrary.simpleMessage("Tučně"), + "bulletedList": + MessageLookupByLibrary.simpleMessage("Odrážkový seznam"), + "checkbox": MessageLookupByLibrary.simpleMessage("Zaškrtávací políčko"), + "embedCode": MessageLookupByLibrary.simpleMessage("Vložit kód"), + "heading1": MessageLookupByLibrary.simpleMessage("Nadpis 1"), + "heading2": MessageLookupByLibrary.simpleMessage("Nadpis 2"), + "heading3": MessageLookupByLibrary.simpleMessage("Nadpis 3"), + "highlight": MessageLookupByLibrary.simpleMessage("Zvýraznění"), + "image": MessageLookupByLibrary.simpleMessage("Obrázek"), + "italic": MessageLookupByLibrary.simpleMessage("Kurzíva"), + "link": MessageLookupByLibrary.simpleMessage("Odkaz"), + "numberedList": + MessageLookupByLibrary.simpleMessage("Číslovaný seznam"), + "quote": MessageLookupByLibrary.simpleMessage("Citace"), + "strikethrough": MessageLookupByLibrary.simpleMessage("Přeškrtnutí"), + "text": MessageLookupByLibrary.simpleMessage("Text"), + "underline": MessageLookupByLibrary.simpleMessage("Podtržení") + }; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_fr-FR.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_fr-FR.dart index dbe21e0e02..07e7302033 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_fr-FR.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_fr-FR.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("List à puces"), + "checkbox": MessageLookupByLibrary.simpleMessage("Case à cocher"), + "embedCode": MessageLookupByLibrary.simpleMessage("Incorporer code"), + "heading1": MessageLookupByLibrary.simpleMessage("Titre 1"), + "heading2": MessageLookupByLibrary.simpleMessage("Titre 2"), + "heading3": MessageLookupByLibrary.simpleMessage("Titre 3"), + "highlight": MessageLookupByLibrary.simpleMessage("Surligné"), + "image": MessageLookupByLibrary.simpleMessage("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("Souligné") }; } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_it-IT.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_it-IT.dart index 6717858291..05ee3e1353 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_it-IT.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_it-IT.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("Grassetto"), + "bulletedList": MessageLookupByLibrary.simpleMessage("Elenco puntato"), + "checkbox": MessageLookupByLibrary.simpleMessage("Casella di spunta"), + "embedCode": MessageLookupByLibrary.simpleMessage("Incorpora codice"), + "heading1": MessageLookupByLibrary.simpleMessage("H1"), + "heading2": MessageLookupByLibrary.simpleMessage("H2"), + "heading3": MessageLookupByLibrary.simpleMessage("H3"), + "highlight": MessageLookupByLibrary.simpleMessage("Evidenzia"), + "image": MessageLookupByLibrary.simpleMessage("Immagine"), + "italic": MessageLookupByLibrary.simpleMessage("Corsivo"), + "link": MessageLookupByLibrary.simpleMessage("Collegamento"), + "numberedList": MessageLookupByLibrary.simpleMessage("Elenco numerato"), + "quote": MessageLookupByLibrary.simpleMessage("Cita"), + "strikethrough": MessageLookupByLibrary.simpleMessage("Barrato"), + "text": MessageLookupByLibrary.simpleMessage("Testo"), + "underline": MessageLookupByLibrary.simpleMessage("Sottolineato") }; } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_nl-NL.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_nl-NL.dart new file mode 100644 index 0000000000..eb096b9a7a --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_nl-NL.dart @@ -0,0 +1,43 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a nl_NL locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'nl_NL'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "bold": MessageLookupByLibrary.simpleMessage("Vet"), + "bulletedList": + MessageLookupByLibrary.simpleMessage("Opsommingstekens"), + "checkbox": MessageLookupByLibrary.simpleMessage("Selectievakje"), + "embedCode": MessageLookupByLibrary.simpleMessage("Invoegcode"), + "heading1": MessageLookupByLibrary.simpleMessage("H1"), + "heading2": MessageLookupByLibrary.simpleMessage("H2"), + "heading3": MessageLookupByLibrary.simpleMessage("H3"), + "highlight": MessageLookupByLibrary.simpleMessage("Highlight"), + "image": MessageLookupByLibrary.simpleMessage("Afbeelding"), + "italic": MessageLookupByLibrary.simpleMessage("Cursief"), + "link": MessageLookupByLibrary.simpleMessage(""), + "numberedList": MessageLookupByLibrary.simpleMessage("Nummering"), + "quote": MessageLookupByLibrary.simpleMessage("Quote"), + "strikethrough": MessageLookupByLibrary.simpleMessage("Doorhalen"), + "text": MessageLookupByLibrary.simpleMessage("Tekst"), + "underline": MessageLookupByLibrary.simpleMessage("Onderstrepen") + }; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_pt-BR.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_pt-BR.dart index c7551f9d78..22c53407ac 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_pt-BR.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_pt-BR.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 de marcadores"), + "checkbox": MessageLookupByLibrary.simpleMessage("Caixa de seleção"), + "embedCode": MessageLookupByLibrary.simpleMessage("Código incorporado"), + "heading1": MessageLookupByLibrary.simpleMessage("H1"), + "heading2": MessageLookupByLibrary.simpleMessage("H2"), + "heading3": MessageLookupByLibrary.simpleMessage("H3"), + "highlight": MessageLookupByLibrary.simpleMessage("Destacar"), + "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("Rasurar"), + "text": MessageLookupByLibrary.simpleMessage("Texto"), + "underline": MessageLookupByLibrary.simpleMessage("Sublinhar") }; } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/l10n.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/l10n.dart index ae80f115a1..9ef7ddf4c9 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/l10n.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/l10n.dart @@ -220,6 +220,7 @@ class AppLocalizationDelegate return const [ Locale.fromSubtags(languageCode: 'en'), Locale.fromSubtags(languageCode: 'ca'), + Locale.fromSubtags(languageCode: 'cs', countryCode: 'CZ'), Locale.fromSubtags(languageCode: 'de', countryCode: 'DE'), Locale.fromSubtags(languageCode: 'es', countryCode: 'VE'), Locale.fromSubtags(languageCode: 'fr', countryCode: 'CA'), @@ -228,6 +229,7 @@ class AppLocalizationDelegate Locale.fromSubtags(languageCode: 'id', countryCode: 'ID'), Locale.fromSubtags(languageCode: 'it', countryCode: 'IT'), Locale.fromSubtags(languageCode: 'ja', countryCode: 'JP'), + Locale.fromSubtags(languageCode: 'nl', countryCode: 'NL'), Locale.fromSubtags(languageCode: 'pl', countryCode: 'PL'), Locale.fromSubtags(languageCode: 'pt', countryCode: 'BR'), Locale.fromSubtags(languageCode: 'pt', countryCode: 'PT'), diff --git a/frontend/app_flowy/packages/appflowy_editor/test/render/selection_menu/selection_menu_widget_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/render/selection_menu/selection_menu_widget_test.dart index 1495105746..9f9046ae52 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/render/selection_menu/selection_menu_widget_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/render/selection_menu/selection_menu_widget_test.dart @@ -1,7 +1,6 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/src/render/selection_menu/selection_menu_item_widget.dart'; import 'package:appflowy_editor/src/render/selection_menu/selection_menu_service.dart'; -import 'package:appflowy_editor/src/render/selection_menu/selection_menu_widget.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../infra/test_editor.dart'; @@ -13,8 +12,8 @@ void main() async { group('selection_menu_widget.dart', () { for (var i = 0; i < defaultSelectionMenuItems.length; i += 1) { - testWidgets('Selects number.$i item in selection menu with enter', ( - tester) async { + testWidgets('Selects number.$i item in selection menu with enter', + (tester) async { final editor = await _prepare(tester); for (var j = 0; j < i; j++) { await editor.pressLogicKey(LogicalKeyboardKey.arrowDown); @@ -30,8 +29,8 @@ void main() async { } }); - testWidgets('Selects number.$i item in selection menu with click', ( - tester) async { + testWidgets('Selects number.$i item in selection menu with click', + (tester) async { final editor = await _prepare(tester); await tester.tap(find.byType(SelectionMenuItemWidget).at(i)); @@ -59,7 +58,7 @@ void main() async { await editor.pressLogicKey(LogicalKeyboardKey.backspace); expect( find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNWidgets(4), + findsNWidgets(5), ); await editor.pressLogicKey(LogicalKeyboardKey.keyE); expect( @@ -148,7 +147,8 @@ Future _testDefaultSelectionMenuItems( int index, EditorWidgetTester editor) async { expect(editor.documentLength, 4); expect(editor.documentSelection, Selection.single(path: [2], startOffset: 0)); - expect((editor.nodeAtPath([1]) as TextNode).toRawString(), 'Welcome to Appflowy 😁'); + expect((editor.nodeAtPath([1]) as TextNode).toRawString(), + 'Welcome to Appflowy 😁'); final node = editor.nodeAtPath([2]); final item = defaultSelectionMenuItems[index]; final itemName = item.name(); From 8ee5c6ed25841aab510d82890e991da1c1684cae Mon Sep 17 00:00:00 2001 From: Ishan Pandhare <91841626+Ishanned@users.noreply.github.com> Date: Wed, 5 Oct 2022 18:56:55 +0530 Subject: [PATCH 29/48] added translation for pt_PT (#1219) --- .../appflowy_editor/lib/l10n/intl_pt_PT.arb | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_pt_PT.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_pt_PT.arb index 4e9187f282..9b7386fd46 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_pt_PT.arb +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_pt_PT.arb @@ -1,35 +1,35 @@ { "@@locale": "pt-PT", - "bold": "", + "bold": "negrito", "@bold": {}, - "bulletedList": "", + "bulletedList": "lista com marcadores", "@bulletedList": {}, - "checkbox": "", + "checkbox": "caixa de seleção", "@checkbox": {}, - "embedCode": "", + "embedCode": "Código embutido", "@embedCode": {}, - "heading1": "", + "heading1": "Cabeçallho 1", "@heading1": {}, - "heading2": "", + "heading2": "Cabeçallho 2", "@heading2": {}, - "heading3": "", + "heading3": "Cabeçallho 3", "@heading3": {}, - "highlight": "", + "highlight": "realçar", "@highlight": {}, - "image": "", + "image": "imagem", "@image": {}, - "italic": "", + "italic": "itálico", "@italic": {}, - "link": "", + "link": "link", "@link": {}, - "numberedList": "", + "numberedList": "lista numerada", "@numberedList": {}, - "quote": "", + "quote": "citar", "@quote": {}, - "strikethrough": "", + "strikethrough": "tachado", "@strikethrough": {}, - "text": "", + "text": "texto", "@text": {}, - "underline": "", + "underline": "sublinhado", "@underline": {} } \ No newline at end of file From eaf396630cb71649af81fdba4b8c30443c8adda8 Mon Sep 17 00:00:00 2001 From: Enzo Lizama Date: Wed, 5 Oct 2022 23:06:48 -0500 Subject: [PATCH 30/48] chore: url launcher test --- .../test/extensions/url_launcher_extension_test.dart | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/frontend/app_flowy/packages/appflowy_editor/test/extensions/url_launcher_extension_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/extensions/url_launcher_extension_test.dart index 7c8761e5a9..81b5b51e37 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/extensions/url_launcher_extension_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/extensions/url_launcher_extension_test.dart @@ -2,18 +2,6 @@ import 'package:appflowy_editor/src/extensions/url_launcher_extension.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { - // test('safeLaunchUrl with scheme', () async { - // const href = 'https://github.com/AppFlowy-IO'; - // final result = await safeLaunchUrl(href); - // expect(result, true); - // }); - - // test('safeLaunchUrl without scheme', () async { - // const href = 'github.com/AppFlowy-IO'; - // final result = await safeLaunchUrl(href); - // expect(result, true); - // }); - test('safeLaunchUrl without scheme', () async { const href = null; final result = await safeLaunchUrl(href); From bfe61ea09d6a573c3ce1e7bcb2562269a389e2a2 Mon Sep 17 00:00:00 2001 From: Enzo Lizama Date: Wed, 5 Oct 2022 23:46:34 -0500 Subject: [PATCH 31/48] test: node tests --- .../test/extensions/node_extension_test.dart | 44 +++++++++++++++---- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/frontend/app_flowy/packages/appflowy_editor/test/extensions/node_extension_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/extensions/node_extension_test.dart index 7b0d106582..0e31aa5a96 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/extensions/node_extension_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/extensions/node_extension_test.dart @@ -1,3 +1,4 @@ +import 'dart:collection'; import 'dart:ui'; import 'package:appflowy_editor/appflowy_editor.dart'; @@ -8,12 +9,12 @@ import 'package:appflowy_editor/src/extensions/node_extensions.dart'; class MockNode extends Mock implements Node {} void main() { - group('NodeExtensions::', () { - final mockNode = MockNode(); + final mockNode = MockNode(); + group('NodeExtensions::', () { final selection = Selection( - start: Position(path: [0, 1]), - end: Position(path: [1, 0]), + start: Position(path: [0]), + end: Position(path: [1]), ); test('rect - renderBox is null', () { @@ -22,10 +23,35 @@ void main() { expect(result, Rect.zero); }); - // test('inSelection', () { - // when(mockNode.path).thenAnswer((_) => [3, 3]); - // final result = mockNode.inSelection(selection); - // expect(result, true); - // }); + test('inSelection', () { + // I use an empty implementation instead of mock, because the mocked + // version throws error trying to access the path. + + final subLinkedList = LinkedList() + ..addAll([ + Node(type: 'type', children: LinkedList(), attributes: {}), + Node(type: 'type', children: LinkedList(), attributes: {}), + Node(type: 'type', children: LinkedList(), attributes: {}), + Node(type: 'type', children: LinkedList(), attributes: {}), + Node(type: 'type', children: LinkedList(), attributes: {}), + ]); + + final linkedList = LinkedList() + ..addAll([ + Node( + type: 'type', + children: subLinkedList, + attributes: {}, + ), + ]); + + final node = Node( + type: 'type', + children: linkedList, + attributes: {}, + ); + final result = node.inSelection(selection); + expect(result, false); + }); }); } From 90ac7970bd67f0cd9d5e6622da5533ec8b2dff18 Mon Sep 17 00:00:00 2001 From: Enzo Lizama Date: Wed, 5 Oct 2022 23:49:41 -0500 Subject: [PATCH 32/48] chore: remove dynamic methods --- .../src/extensions/text_node_extensions.dart | 30 +++++++++-------- .../lib/src/render/toolbar/toolbar_item.dart | 32 ++++++++++--------- .../format_rich_text_style.dart | 2 +- .../extensions/text_node_extensions_test.dart | 10 ++++++ .../rich_text/toolbar_rich_text_test.dart | 4 +-- .../format_style_handler_test.dart | 10 +++--- 6 files changed, 51 insertions(+), 37 deletions(-) create mode 100644 frontend/app_flowy/packages/appflowy_editor/test/extensions/text_node_extensions_test.dart diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/text_node_extensions.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/text_node_extensions.dart index d4d7857286..4d8c44b9fa 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/text_node_extensions.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/text_node_extensions.dart @@ -6,7 +6,7 @@ import 'package:appflowy_editor/src/document/text_delta.dart'; import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; extension TextNodeExtension on TextNode { - dynamic getAttributeInSelection(Selection selection, String styleKey) { + T? getAttributeInSelection(Selection selection, String styleKey) { final ops = delta.whereType(); final startOffset = selection.isBackward ? selection.start.offset : selection.end.offset; @@ -29,40 +29,42 @@ extension TextNodeExtension on TextNode { } bool allSatisfyLinkInSelection(Selection selection) => - allSatisfyInSelection(selection, BuiltInAttributeKey.href, (value) { + allSatisfyInSelection(selection, BuiltInAttributeKey.href, (value) { return value != null; }); bool allSatisfyBoldInSelection(Selection selection) => - allSatisfyInSelection(selection, BuiltInAttributeKey.bold, (value) { + allSatisfyInSelection(selection, BuiltInAttributeKey.bold, (value) { return value == true; }); bool allSatisfyItalicInSelection(Selection selection) => - allSatisfyInSelection(selection, BuiltInAttributeKey.italic, (value) { + allSatisfyInSelection(selection, BuiltInAttributeKey.italic, + (value) { return value == true; }); bool allSatisfyUnderlineInSelection(Selection selection) => - allSatisfyInSelection(selection, BuiltInAttributeKey.underline, (value) { + allSatisfyInSelection(selection, BuiltInAttributeKey.underline, + (value) { return value == true; }); bool allSatisfyStrikethroughInSelection(Selection selection) => allSatisfyInSelection(selection, BuiltInAttributeKey.strikethrough, - (value) { + (value) { return value == true; }); bool allSatisfyCodeInSelection(Selection selection) => - allSatisfyInSelection(selection, BuiltInAttributeKey.code, (value) { + allSatisfyInSelection(selection, BuiltInAttributeKey.code, (value) { return value == true; }); bool allSatisfyInSelection( Selection selection, String styleKey, - bool Function(dynamic value) test, + bool Function(T value) test, ) { if (BuiltInAttributeKey.globalStyleKeys.contains(styleKey)) { if (attributes.containsKey(styleKey)) { @@ -127,40 +129,40 @@ extension TextNodesExtension on List { bool allSatisfyBoldInSelection(Selection selection) => allSatisfyInSelection( selection, BuiltInAttributeKey.bold, - (value) => value == true, + (value) => value == true, ); bool allSatisfyItalicInSelection(Selection selection) => allSatisfyInSelection( selection, BuiltInAttributeKey.italic, - (value) => value == true, + (value) => value == true, ); bool allSatisfyUnderlineInSelection(Selection selection) => allSatisfyInSelection( selection, BuiltInAttributeKey.underline, - (value) => value == true, + (value) => value == true, ); bool allSatisfyStrikethroughInSelection(Selection selection) => allSatisfyInSelection( selection, BuiltInAttributeKey.strikethrough, - (value) => value == true, + (value) => value == true, ); bool allSatisfyInSelection( Selection selection, String styleKey, - bool Function(dynamic value) test, + bool Function(T value) test, ) { if (isEmpty) { return false; } if (length == 1) { - return first.allSatisfyInSelection(selection, styleKey, (value) { + return first.allSatisfyInSelection(selection, styleKey, (value) { return test(value); }); } else { diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item.dart index 6407f81cb3..1b531f306b 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item.dart @@ -73,7 +73,7 @@ List defaultToolbarItems = [ highlightCallback: (editorState) => _allSatisfy( editorState, BuiltInAttributeKey.heading, - (value) => value == BuiltInAttributeKey.h1, + (value) => value == BuiltInAttributeKey.h1, ), handler: (editorState, context) => formatHeading(editorState, BuiltInAttributeKey.h1), @@ -90,7 +90,7 @@ List defaultToolbarItems = [ highlightCallback: (editorState) => _allSatisfy( editorState, BuiltInAttributeKey.heading, - (value) => value == BuiltInAttributeKey.h2, + (value) => value == BuiltInAttributeKey.h2, ), handler: (editorState, context) => formatHeading(editorState, BuiltInAttributeKey.h2), @@ -107,7 +107,7 @@ List defaultToolbarItems = [ highlightCallback: (editorState) => _allSatisfy( editorState, BuiltInAttributeKey.heading, - (value) => value == BuiltInAttributeKey.h3, + (value) => value == BuiltInAttributeKey.h3, ), handler: (editorState, context) => formatHeading(editorState, BuiltInAttributeKey.h3), @@ -124,7 +124,7 @@ List defaultToolbarItems = [ highlightCallback: (editorState) => _allSatisfy( editorState, BuiltInAttributeKey.bold, - (value) => value == true, + (value) => value == true, ), handler: (editorState, context) => formatBold(editorState), ), @@ -140,7 +140,7 @@ List defaultToolbarItems = [ highlightCallback: (editorState) => _allSatisfy( editorState, BuiltInAttributeKey.italic, - (value) => value == true, + (value) => value == true, ), handler: (editorState, context) => formatItalic(editorState), ), @@ -156,7 +156,7 @@ List defaultToolbarItems = [ highlightCallback: (editorState) => _allSatisfy( editorState, BuiltInAttributeKey.underline, - (value) => value == true, + (value) => value == true, ), handler: (editorState, context) => formatUnderline(editorState), ), @@ -172,7 +172,7 @@ List defaultToolbarItems = [ highlightCallback: (editorState) => _allSatisfy( editorState, BuiltInAttributeKey.strikethrough, - (value) => value == true, + (value) => value == true, ), handler: (editorState, context) => formatStrikethrough(editorState), ), @@ -188,7 +188,7 @@ List defaultToolbarItems = [ highlightCallback: (editorState) => _allSatisfy( editorState, BuiltInAttributeKey.code, - (value) => value == true, + (value) => value == true, ), handler: (editorState, context) => formatEmbedCode(editorState), ), @@ -204,7 +204,7 @@ List defaultToolbarItems = [ highlightCallback: (editorState) => _allSatisfy( editorState, BuiltInAttributeKey.subtype, - (value) => value == BuiltInAttributeKey.quote, + (value) => value == BuiltInAttributeKey.quote, ), handler: (editorState, context) => formatQuote(editorState), ), @@ -220,7 +220,7 @@ List defaultToolbarItems = [ highlightCallback: (editorState) => _allSatisfy( editorState, BuiltInAttributeKey.subtype, - (value) => value == BuiltInAttributeKey.bulletedList, + (value) => value == BuiltInAttributeKey.bulletedList, ), handler: (editorState, context) => formatBulletedList(editorState), ), @@ -236,7 +236,7 @@ List defaultToolbarItems = [ highlightCallback: (editorState) => _allSatisfy( editorState, BuiltInAttributeKey.href, - (value) => value != null, + (value) => value != null, ), handler: (editorState, context) => showLinkMenu(context, editorState), ), @@ -252,7 +252,7 @@ List defaultToolbarItems = [ highlightCallback: (editorState) => _allSatisfy( editorState, BuiltInAttributeKey.backgroundColor, - (value) => value != null, + (value) => value != null, ), handler: (editorState, context) => formatHighlight( editorState, @@ -284,7 +284,7 @@ ToolbarItemValidator _showInBuiltInTextSelection = (editorState) { bool _allSatisfy( EditorState editorState, String styleKey, - bool Function(dynamic value) test, + bool Function(T value) test, ) { final selection = editorState.service.selectionService.currentSelection.value; return selection != null && @@ -333,8 +333,10 @@ void showLinkMenu( final textNode = node.first as TextNode; String? linkText; if (textNode.allSatisfyLinkInSelection(selection)) { - linkText = - textNode.getAttributeInSelection(selection, BuiltInAttributeKey.href); + linkText = textNode.getAttributeInSelection( + selection, + BuiltInAttributeKey.href, + ); } _linkMenuOverlay = OverlayEntry(builder: (context) { return Positioned( diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart index 053d9e542a..0377b9e37c 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart @@ -188,7 +188,7 @@ bool _allSatisfyInSelection( return false; } - return textNodes.allSatisfyInSelection(selection, styleKey, (value) { + return textNodes.allSatisfyInSelection(selection, styleKey, (value) { return value == matchValue; }); } diff --git a/frontend/app_flowy/packages/appflowy_editor/test/extensions/text_node_extensions_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/extensions/text_node_extensions_test.dart new file mode 100644 index 0000000000..bec92362f4 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/test/extensions/text_node_extensions_test.dart @@ -0,0 +1,10 @@ +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('TextNodeExtension::', () { + test('description', () { + final selecion = Selection.single(path: [0, 1, 2], startOffset: 0); + }); + }); +} diff --git a/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart index c9d9ef9e70..49990b4662 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart @@ -229,7 +229,7 @@ void main() async { node.allSatisfyInSelection( code, BuiltInAttributeKey.code, - (value) { + (value) { return value == true; }, ), @@ -319,7 +319,7 @@ void main() async { node.allSatisfyInSelection( selection, BuiltInAttributeKey.backgroundColor, - (value) { + (value) { return value == blue; }, ), diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart index 0cb4c71600..684e7d21f1 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart @@ -111,7 +111,7 @@ Future _testUpdateTextStyleByCommandX( textNode.allSatisfyInSelection( selection, matchStyle, - (value) { + (value) { return value == matchValue; }, ), @@ -138,7 +138,7 @@ Future _testUpdateTextStyleByCommandX( textNode.allSatisfyInSelection( selection, matchStyle, - (value) { + (value) { return value == matchValue; }, ), @@ -192,7 +192,7 @@ Future _testUpdateTextStyleByCommandX( endOffset: text.length, ), matchStyle, - (value) { + (value) { return value == matchValue; }, ), @@ -266,7 +266,7 @@ Future _testLinkMenuInSingleTextSelection(WidgetTester tester) async { node.allSatisfyInSelection( selection, BuiltInAttributeKey.href, - (value) => value == link, + (value) => value == link, ), true); @@ -303,7 +303,7 @@ Future _testLinkMenuInSingleTextSelection(WidgetTester tester) async { node.allSatisfyInSelection( selection, BuiltInAttributeKey.href, - (value) => value == link, + (value) => value == link, ), false); } From 73a97fdbeabfe69951374beaff38b747da65e885 Mon Sep 17 00:00:00 2001 From: Enzo Lizama Date: Wed, 5 Oct 2022 23:55:01 -0500 Subject: [PATCH 33/48] fix: conflict merge --- .../app_flowy/packages/appflowy_editor/example/lib/main.dart | 2 +- .../example/lib/plugin/tex_block_node_widget.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/app_flowy/packages/appflowy_editor/example/lib/main.dart b/frontend/app_flowy/packages/appflowy_editor/example/lib/main.dart index fecec3d3e0..4283752a03 100644 --- a/frontend/app_flowy/packages/appflowy_editor/example/lib/main.dart +++ b/frontend/app_flowy/packages/appflowy_editor/example/lib/main.dart @@ -128,7 +128,7 @@ class _MyHomePageState extends State { underscoreToItalic, ], selectionMenuItems: [ - codeBlockMenuItem, + codeBlockItem, teXBlockMenuItem, ], ), diff --git a/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/tex_block_node_widget.dart b/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/tex_block_node_widget.dart index 80ad3642ca..e76c3e160b 100644 --- a/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/tex_block_node_widget.dart +++ b/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/tex_block_node_widget.dart @@ -5,7 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_math_fork/flutter_math.dart'; SelectionMenuItem teXBlockMenuItem = SelectionMenuItem( - name: 'Tex', + name: () => 'Tex', icon: const Icon(Icons.text_fields_rounded), keywords: ['tex, latex, katex'], handler: (editorState, _, __) { From 5a5f5eceb57b64ae3a6f2362ae542779fec38c07 Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Thu, 6 Oct 2022 19:33:02 +0800 Subject: [PATCH 34/48] fix: improve look of share button dropdown menu (#1224) --- frontend/app_flowy/lib/plugins/doc/document.dart | 12 ++++++------ .../presentation/widgets/pop_up_action.dart | 4 +--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/frontend/app_flowy/lib/plugins/doc/document.dart b/frontend/app_flowy/lib/plugins/doc/document.dart index d186513acc..b2c75a08fe 100644 --- a/frontend/app_flowy/lib/plugins/doc/document.dart +++ b/frontend/app_flowy/lib/plugins/doc/document.dart @@ -15,6 +15,7 @@ import 'package:clipboard/clipboard.dart'; import 'package:dartz/dartz.dart' as dartz; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/size.dart'; +import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/widget/rounded_button.dart'; import 'package:flowy_sdk/log.dart'; @@ -112,7 +113,6 @@ class DocumentShareButton extends StatelessWidget { @override Widget build(BuildContext context) { - double buttonWidth = 60; return BlocProvider( create: (context) => getIt(param1: view), child: BlocListener( @@ -130,6 +130,7 @@ class DocumentShareButton extends StatelessWidget { }, child: BlocBuilder( builder: (context, state) { + final theme = context.watch(); return ChangeNotifierProvider.value( value: Provider.of(context, listen: true), child: Selector( @@ -137,16 +138,15 @@ class DocumentShareButton extends StatelessWidget { builder: (ctx, _, child) => ConstrainedBox( constraints: const BoxConstraints.expand( height: 30, - // minWidth: buttonWidth, width: 100, ), child: RoundedTextButton( title: LocaleKeys.shareAction_buttonText.tr(), fontSize: 12, borderRadius: Corners.s6Border, - color: Colors.lightBlue, - onPressed: () => _showActionList( - context, Offset(-(buttonWidth / 2), 10)), + color: theme.main1, + onPressed: () => + _showActionList(context, const Offset(0, 10)), ), ), ), @@ -193,7 +193,7 @@ class DocumentShareButton extends StatelessWidget { }); actionList.show( context, - anchorDirection: AnchorDirection.bottomWithCenterAligned, + anchorDirection: AnchorDirection.bottomWithRightAligned, anchorOffset: offset, ); } diff --git a/frontend/app_flowy/lib/workspace/presentation/widgets/pop_up_action.dart b/frontend/app_flowy/lib/workspace/presentation/widgets/pop_up_action.dart index f6aa316f29..2c48cec9d5 100644 --- a/frontend/app_flowy/lib/workspace/presentation/widgets/pop_up_action.dart +++ b/frontend/app_flowy/lib/workspace/presentation/widgets/pop_up_action.dart @@ -97,10 +97,8 @@ class ActionCell extends StatelessWidget { child: SizedBox( height: itemHeight, child: Row( - crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (icon != null) icon, - HSpace(ActionListSizes.itemHPadding), + if (icon != null) ...[icon, HSpace(ActionListSizes.itemHPadding)], FlowyText.medium(action.name, fontSize: 12), ], ), From a4a88959fd0839a1f09b65e2a6c250216266a637 Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Thu, 6 Oct 2022 21:31:52 +0800 Subject: [PATCH 35/48] fix: make sidebar resizing follow cursor more closely (#1202) * fix: make sidebar resizing follow cursor more closely * fix: don't animate sidebar size changes when dragging --- .../workspace/application/home/home_bloc.dart | 44 ++++++++++++++++--- .../presentation/home/home_layout.dart | 3 +- .../presentation/home/home_screen.dart | 21 ++++++--- 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/frontend/app_flowy/lib/workspace/application/home/home_bloc.dart b/frontend/app_flowy/lib/workspace/application/home/home_bloc.dart index 87bac230da..d325fc7705 100644 --- a/frontend/app_flowy/lib/workspace/application/home/home_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/home/home_bloc.dart @@ -1,5 +1,6 @@ import 'package:app_flowy/user/application/user_listener.dart'; import 'package:app_flowy/workspace/application/edit_panel/edit_context.dart'; +import 'package:flowy_infra/time/duration.dart'; import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-error-code/code.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; @@ -50,13 +51,24 @@ class HomeBloc extends Bloc { unauthorized: (_Unauthorized value) { emit(state.copyWith(unauthorized: true)); }, - collapseMenu: (e) { + collapseMenu: (_CollapseMenu e) { emit(state.copyWith(isMenuCollapsed: !state.isMenuCollapsed)); }, - editPanelResized: (e) { - final newOffset = - (state.resizeOffset + e.offset).clamp(-50, 200).toDouble(); - emit(state.copyWith(resizeOffset: newOffset)); + editPanelResizeStart: (_EditPanelResizeStart e) { + emit(state.copyWith( + resizeType: MenuResizeType.drag, + resizeStart: state.resizeOffset, + )); + }, + editPanelResized: (_EditPanelResized e) { + final newPosition = + (e.offset + state.resizeStart).clamp(-50, 200).toDouble(); + if (state.resizeOffset != newPosition) { + emit(state.copyWith(resizeOffset: newPosition)); + } + }, + editPanelResizeEnd: (_EditPanelResizeEnd e) { + emit(state.copyWith(resizeType: MenuResizeType.slide)); }, ); }, @@ -78,6 +90,22 @@ class HomeBloc extends Bloc { } } +enum MenuResizeType { + slide, + drag, +} + +extension MenuResizeTypeExtension on MenuResizeType { + Duration duration() { + switch (this) { + case MenuResizeType.drag: + return 30.milliseconds; + case MenuResizeType.slide: + return 350.milliseconds; + } + } +} + @freezed class HomeEvent with _$HomeEvent { const factory HomeEvent.initial() = _Initial; @@ -91,6 +119,8 @@ class HomeEvent with _$HomeEvent { const factory HomeEvent.unauthorized(String msg) = _Unauthorized; const factory HomeEvent.collapseMenu() = _CollapseMenu; const factory HomeEvent.editPanelResized(double offset) = _EditPanelResized; + const factory HomeEvent.editPanelResizeStart() = _EditPanelResizeStart; + const factory HomeEvent.editPanelResizeEnd() = _EditPanelResizeEnd; } @freezed @@ -103,6 +133,8 @@ class HomeState with _$HomeState { required bool unauthorized, required bool isMenuCollapsed, required double resizeOffset, + required double resizeStart, + required MenuResizeType resizeType, }) = _HomeState; factory HomeState.initial(CurrentWorkspaceSettingPB workspaceSetting) => @@ -114,5 +146,7 @@ class HomeState with _$HomeState { unauthorized: false, isMenuCollapsed: false, resizeOffset: 0, + resizeStart: 0, + resizeType: MenuResizeType.slide, ); } diff --git a/frontend/app_flowy/lib/workspace/presentation/home/home_layout.dart b/frontend/app_flowy/lib/workspace/presentation/home/home_layout.dart index 9a3d5636ab..e515eb9a1b 100644 --- a/frontend/app_flowy/lib/workspace/presentation/home/home_layout.dart +++ b/frontend/app_flowy/lib/workspace/presentation/home/home_layout.dart @@ -2,7 +2,6 @@ import 'dart:io' show Platform; import 'package:app_flowy/workspace/application/home/home_bloc.dart'; import 'package:flowy_infra/size.dart'; -import 'package:flowy_infra/time/duration.dart'; import 'package:flutter/material.dart'; // ignore: import_of_legacy_library_into_null_safe import 'package:sized_context/sized_context.dart'; @@ -44,7 +43,7 @@ class HomeLayout { homePageLOffset = (showMenu && !menuIsDrawer) ? menuWidth : 0.0; menuSpacing = !showMenu && Platform.isMacOS ? 80.0 : 0.0; - animDuration = .35.seconds; + animDuration = homeBlocState.resizeType.duration(); editPanelWidth = HomeSizes.editPanelWidth; homePageROffset = showEditPanel ? editPanelWidth : 0; diff --git a/frontend/app_flowy/lib/workspace/presentation/home/home_screen.dart b/frontend/app_flowy/lib/workspace/presentation/home/home_screen.dart index 1d46ade005..ae4dc7f3b9 100644 --- a/frontend/app_flowy/lib/workspace/presentation/home/home_screen.dart +++ b/frontend/app_flowy/lib/workspace/presentation/home/home_screen.dart @@ -176,11 +176,18 @@ class _HomeScreenState extends State { cursor: SystemMouseCursors.resizeLeftRight, child: GestureDetector( dragStartBehavior: DragStartBehavior.down, - onPanUpdate: ((details) { - context - .read() - .add(HomeEvent.editPanelResized(details.delta.dx)); - }), + onHorizontalDragStart: (details) => context + .read() + .add(const HomeEvent.editPanelResizeStart()), + onHorizontalDragUpdate: (details) => context + .read() + .add(HomeEvent.editPanelResized(details.localPosition.dx)), + onHorizontalDragEnd: (details) => context + .read() + .add(const HomeEvent.editPanelResizeEnd()), + onHorizontalDragCancel: () => context + .read() + .add(const HomeEvent.editPanelResizeEnd()), behavior: HitTestBehavior.translucent, child: SizedBox( width: 10, @@ -208,7 +215,6 @@ class _HomeScreenState extends State { top: 0, animate: true) .animate(layout.animDuration, Curves.easeOut), - homeMenuResizer.positioned(left: layout.homePageLOffset - 5), bubble .positioned( right: 20, @@ -236,6 +242,9 @@ class _HomeScreenState extends State { bottom: 0, animate: true) .animate(layout.animDuration, Curves.easeOut), + homeMenuResizer + .positioned(left: layout.homePageLOffset - 5) + .animate(layout.animDuration, Curves.easeOut), ], ); } From 5860d35d1a51f0cf2722ae6b6bec274d6952097c Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Thu, 6 Oct 2022 21:36:39 +0800 Subject: [PATCH 36/48] fix: improve look of calendar (#1226) --- .../widgets/cell/date_cell/date_editor.dart | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_editor.dart index 4018c0acb6..672e0e648e 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_editor.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_editor.dart @@ -159,6 +159,7 @@ class _CellCalendarWidgetState extends State<_CellCalendarWidget> { focusedDay: state.focusedDay, rowHeight: 40, calendarFormat: state.format, + daysOfWeekHeight: 40, headerStyle: HeaderStyle( formatButtonVisible: false, titleCentered: true, @@ -168,6 +169,7 @@ class _CellCalendarWidgetState extends State<_CellCalendarWidget> { rightChevronPadding: EdgeInsets.zero, rightChevronMargin: EdgeInsets.zero, rightChevronIcon: svgWidget("home/arrow_right"), + headerMargin: const EdgeInsets.only(bottom: 8.0), ), daysOfWeekStyle: DaysOfWeekStyle( dowTextFormatter: (date, locale) => @@ -182,13 +184,31 @@ class _CellCalendarWidgetState extends State<_CellCalendarWidget> { ), ), calendarStyle: CalendarStyle( + cellMargin: const EdgeInsets.all(3), + defaultDecoration: BoxDecoration( + color: theme.surface, + shape: BoxShape.rectangle, + borderRadius: const BorderRadius.all(Radius.circular(6)), + ), selectedDecoration: BoxDecoration( color: theme.main1, - shape: BoxShape.circle, + shape: BoxShape.rectangle, + borderRadius: const BorderRadius.all(Radius.circular(6)), ), todayDecoration: BoxDecoration( color: theme.shader4, - shape: BoxShape.circle, + shape: BoxShape.rectangle, + borderRadius: const BorderRadius.all(Radius.circular(6)), + ), + weekendDecoration: BoxDecoration( + color: theme.surface, + shape: BoxShape.rectangle, + borderRadius: const BorderRadius.all(Radius.circular(6)), + ), + outsideDecoration: BoxDecoration( + color: theme.surface, + shape: BoxShape.rectangle, + borderRadius: const BorderRadius.all(Radius.circular(6)), ), selectedTextStyle: TextStyle( color: theme.surface, From d146bb64812c148c236c49d201cf451bb26e8f13 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Thu, 6 Oct 2022 21:46:18 +0800 Subject: [PATCH 37/48] chore: fix compile error --- .../packages/appflowy_editor/example/assets/example.json | 6 ------ .../example/lib/plugin/tex_block_node_widget.dart | 2 +- .../packages/appflowy_editor/example/macos/Podfile.lock | 2 +- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/frontend/app_flowy/packages/appflowy_editor/example/assets/example.json b/frontend/app_flowy/packages/appflowy_editor/example/assets/example.json index 991c03296a..2d441d3367 100644 --- a/frontend/app_flowy/packages/appflowy_editor/example/assets/example.json +++ b/frontend/app_flowy/packages/appflowy_editor/example/assets/example.json @@ -9,12 +9,6 @@ "align": "center" } }, - { - "type": "tex", - "attributes": { - "tex": "x = 2" - } - }, { "type": "text", "attributes": { "subtype": "heading", "heading": "h1" }, diff --git a/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/tex_block_node_widget.dart b/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/tex_block_node_widget.dart index 80ad3642ca..e76c3e160b 100644 --- a/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/tex_block_node_widget.dart +++ b/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/tex_block_node_widget.dart @@ -5,7 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_math_fork/flutter_math.dart'; SelectionMenuItem teXBlockMenuItem = SelectionMenuItem( - name: 'Tex', + name: () => 'Tex', icon: const Icon(Icons.text_fields_rounded), keywords: ['tex, latex, katex'], handler: (editorState, _, __) { diff --git a/frontend/app_flowy/packages/appflowy_editor/example/macos/Podfile.lock b/frontend/app_flowy/packages/appflowy_editor/example/macos/Podfile.lock index 79143cb186..49a5879fa6 100644 --- a/frontend/app_flowy/packages/appflowy_editor/example/macos/Podfile.lock +++ b/frontend/app_flowy/packages/appflowy_editor/example/macos/Podfile.lock @@ -24,7 +24,7 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos SPEC CHECKSUMS: - FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811 + FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424 path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19 rich_clipboard_macos: 43364b66b9dc69d203eb8dd6d758e2d12e02723c url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3 From 73e81da356809f2f82efac75dc820f8beb011ff6 Mon Sep 17 00:00:00 2001 From: appflowy Date: Thu, 6 Oct 2022 22:26:18 +0800 Subject: [PATCH 38/48] fix: select card text --- frontend/app_flowy/ios/Runner/Info.plist | 2 + .../plugins/board/application/board_bloc.dart | 103 +++++++++++---- .../board/presentation/board_page.dart | 55 ++++---- .../board/presentation/card/board_cell.dart | 4 + .../presentation/card/board_text_cell.dart | 9 +- .../plugins/board/presentation/card/card.dart | 10 ++ .../example/lib/multi_board_list_example.dart | 2 +- .../appflowy_board/lib/src/widgets/board.dart | 13 +- .../lib/src/widgets/board_data.dart | 8 ++ .../src/widgets/board_group/group_data.dart | 11 ++ .../src/widgets/reorder_flex/drag_state.dart | 12 +- .../src/widgets/reorder_flex/drag_target.dart | 18 ++- .../widgets/reorder_flex/reorder_flex.dart | 123 ++++++++++-------- frontend/app_flowy/pubspec.lock | 2 +- 14 files changed, 245 insertions(+), 127 deletions(-) diff --git a/frontend/app_flowy/ios/Runner/Info.plist b/frontend/app_flowy/ios/Runner/Info.plist index 848c264d70..122f6a8cd7 100644 --- a/frontend/app_flowy/ios/Runner/Info.plist +++ b/frontend/app_flowy/ios/Runner/Info.plist @@ -45,5 +45,7 @@ en + CADisableMinimumFrameDurationOnPhone + diff --git a/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart b/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart index afb04e8f38..a02eb0dd4d 100644 --- a/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart +++ b/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart @@ -89,18 +89,30 @@ class BoardBloc extends Bloc { (err) => Log.error(err), ); }, - didCreateRow: (String groupId, RowPB row, int? index) { + didCreateRow: (group, row, int? index) { emit(state.copyWith( editingRow: Some(BoardEditingRow( - columnId: groupId, + group: group, row: row, index: index, )), )); + _groupItemStartEditing(group, row, true); }, - endEditRow: (rowId) { + startEditingRow: (group, row) { + emit(state.copyWith( + editingRow: Some(BoardEditingRow( + group: group, + row: row, + index: null, + )), + )); + _groupItemStartEditing(group, row, true); + }, + endEditingRow: (rowId) { state.editingRow.fold(() => null, (editingRow) { assert(editingRow.row.id == rowId); + _groupItemStartEditing(editingRow.group, editingRow.row, false); emit(state.copyWith(editingRow: none())); }); }, @@ -122,6 +134,24 @@ class BoardBloc extends Bloc { ); } + void _groupItemStartEditing(GroupPB group, RowPB row, bool isEdit) { + final fieldContext = fieldController.getField(group.fieldId); + if (fieldContext == null) { + Log.warn("FieldContext should not be null"); + return; + } + + boardController.enableGroupDragging(!isEdit); + // boardController.updateGroupItem( + // group.groupId, + // GroupItem( + // row: row, + // fieldContext: fieldContext, + // isDraggable: !isEdit, + // ), + // ); + } + void _moveRow(RowPB? fromRow, String columnId, RowPB? toRow) { if (fromRow != null) { _rowService @@ -156,7 +186,7 @@ class BoardBloc extends Bloc { return super.close(); } - void initializeGroups(List groups) { + void initializeGroups(List groupsData) { for (var controller in groupControllers.values) { controller.dispose(); } @@ -164,27 +194,27 @@ class BoardBloc extends Bloc { boardController.clear(); // - List columns = groups + List groups = groupsData .where((group) => fieldController.getField(group.fieldId) != null) .map((group) { return AppFlowyGroupData( id: group.groupId, name: group.desc, - items: _buildRows(group), - customData: BoardCustomData( + items: _buildGroupItems(group), + customData: GroupData( group: group, fieldContext: fieldController.getField(group.fieldId)!, ), ); }).toList(); - boardController.addGroups(columns); + boardController.addGroups(groups); - for (final group in groups) { + for (final group in groupsData) { final delegate = GroupControllerDelegateImpl( controller: boardController, fieldController: fieldController, onNewColumnItem: (groupId, row, index) { - add(BoardEvent.didCreateRow(groupId, row, index)); + add(BoardEvent.didCreateRow(group, row, index)); }, ); final controller = GroupController( @@ -242,10 +272,13 @@ class BoardBloc extends Bloc { ); } - List _buildRows(GroupPB group) { + List _buildGroupItems(GroupPB group) { final items = group.rows.map((row) { final fieldContext = fieldController.getField(group.fieldId); - return BoardColumnItem(row: row, fieldContext: fieldContext!); + return GroupItem( + row: row, + fieldContext: fieldContext!, + ); }).toList(); return [...items]; @@ -270,11 +303,15 @@ class BoardEvent with _$BoardEvent { const factory BoardEvent.createBottomRow(String groupId) = _CreateBottomRow; const factory BoardEvent.createHeaderRow(String groupId) = _CreateHeaderRow; const factory BoardEvent.didCreateRow( - String groupId, + GroupPB group, RowPB row, int? index, ) = _DidCreateRow; - const factory BoardEvent.endEditRow(String rowId) = _EndEditRow; + const factory BoardEvent.startEditingRow( + GroupPB group, + RowPB row, + ) = _StartEditRow; + const factory BoardEvent.endEditingRow(String rowId) = _EndEditRow; const factory BoardEvent.didReceiveError(FlowyError error) = _DidReceiveError; const factory BoardEvent.didReceiveGridUpdate( GridPB grid, @@ -334,14 +371,17 @@ class GridFieldEquatable extends Equatable { UnmodifiableListView get value => UnmodifiableListView(_fields); } -class BoardColumnItem extends AppFlowyGroupItem { +class GroupItem extends AppFlowyGroupItem { final RowPB row; final GridFieldContext fieldContext; - BoardColumnItem({ + GroupItem({ required this.row, required this.fieldContext, - }); + bool draggable = true, + }) { + super.draggable = draggable; + } @override String get id => row.id; @@ -367,10 +407,16 @@ class GroupControllerDelegateImpl extends GroupControllerDelegate { } if (index != null) { - final item = BoardColumnItem(row: row, fieldContext: fieldContext); + final item = GroupItem( + row: row, + fieldContext: fieldContext, + ); controller.insertGroupItem(group.groupId, index, item); } else { - final item = BoardColumnItem(row: row, fieldContext: fieldContext); + final item = GroupItem( + row: row, + fieldContext: fieldContext, + ); controller.addGroupItem(group.groupId, item); } } @@ -389,7 +435,10 @@ class GroupControllerDelegateImpl extends GroupControllerDelegate { } controller.updateGroupItem( group.groupId, - BoardColumnItem(row: row, fieldContext: fieldContext), + GroupItem( + row: row, + fieldContext: fieldContext, + ), ); } @@ -400,7 +449,11 @@ class GroupControllerDelegateImpl extends GroupControllerDelegate { Log.warn("FieldContext should not be null"); return; } - final item = BoardColumnItem(row: row, fieldContext: fieldContext); + final item = GroupItem( + row: row, + fieldContext: fieldContext, + draggable: false, + ); if (index != null) { controller.insertGroupItem(group.groupId, index, item); @@ -412,21 +465,21 @@ class GroupControllerDelegateImpl extends GroupControllerDelegate { } class BoardEditingRow { - String columnId; + GroupPB group; RowPB row; int? index; BoardEditingRow({ - required this.columnId, + required this.group, required this.row, required this.index, }); } -class BoardCustomData { +class GroupData { final GroupPB group; final GridFieldContext fieldContext; - BoardCustomData({ + GroupData({ required this.group, required this.fieldContext, }); diff --git a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart index dd5e619b46..a7f0d90557 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart @@ -83,7 +83,7 @@ class _BoardContentState extends State { @override Widget build(BuildContext context) { return BlocListener( - listener: (context, state) => _handleEditState(state, context), + listener: (context, state) => _handleEditStateChanged(state, context), child: BlocBuilder( buildWhen: (previous, current) => previous.groupIds != current.groupIds, builder: (context, state) { @@ -128,21 +128,14 @@ class _BoardContentState extends State { ); } - void _handleEditState(BoardState state, BuildContext context) { + void _handleEditStateChanged(BoardState state, BuildContext context) { state.editingRow.fold( () => null, (editingRow) { WidgetsBinding.instance.addPostFrameCallback((_) { if (editingRow.index != null) { - context - .read() - .add(BoardEvent.endEditRow(editingRow.row.id)); } else { - scrollManager.scrollToBottom(editingRow.columnId, (boardContext) { - context - .read() - .add(BoardEvent.endEditRow(editingRow.row.id)); - }); + scrollManager.scrollToBottom(editingRow.group.groupId); } }); }, @@ -156,14 +149,14 @@ class _BoardContentState extends State { Widget _buildHeader( BuildContext context, - AppFlowyGroupData columnData, + AppFlowyGroupData groupData, ) { - final boardCustomData = columnData.customData as BoardCustomData; + final boardCustomData = groupData.customData as GroupData; return AppFlowyGroupHeader( title: Flexible( fit: FlexFit.tight, child: FlowyText.medium( - columnData.headerData.groupName, + groupData.headerData.groupName, fontSize: 14, overflow: TextOverflow.clip, color: context.read().textColor, @@ -180,7 +173,7 @@ class _BoardContentState extends State { ), onAddButtonClick: () { context.read().add( - BoardEvent.createHeaderRow(columnData.id), + BoardEvent.createHeaderRow(groupData.id), ); }, height: 50, @@ -218,15 +211,16 @@ class _BoardContentState extends State { Widget _buildCard( BuildContext context, - AppFlowyGroupData group, - AppFlowyGroupItem columnItem, + AppFlowyGroupData afGroupData, + AppFlowyGroupItem afGroupItem, ) { - final boardColumnItem = columnItem as BoardColumnItem; - final rowPB = boardColumnItem.row; + final groupItem = afGroupItem as GroupItem; + final groupData = afGroupData.customData as GroupData; + final rowPB = groupItem.row; final rowCache = context.read().getRowCache(rowPB.blockId); /// Return placeholder widget if the rowCache is null. - if (rowCache == null) return SizedBox(key: ObjectKey(columnItem)); + if (rowCache == null) return SizedBox(key: ObjectKey(groupItem)); final fieldController = context.read().fieldController; final gridId = context.read().gridId; @@ -241,19 +235,19 @@ class _BoardContentState extends State { context.read().state.editingRow.fold( () => null, (editingRow) { - isEditing = editingRow.row.id == columnItem.row.id; + isEditing = editingRow.row.id == groupItem.row.id; }, ); - final groupItemId = columnItem.id + group.id; + final groupItemId = groupItem.row.id + groupData.group.groupId; return AppFlowyGroupCard( key: ValueKey(groupItemId), margin: config.cardPadding, decoration: _makeBoxDecoration(context), child: BoardCard( gridId: gridId, - groupId: group.id, - fieldId: boardColumnItem.fieldContext.id, + groupId: groupData.group.groupId, + fieldId: groupItem.fieldContext.id, isEditing: isEditing, cellBuilder: cellBuilder, dataController: cardController, @@ -264,6 +258,19 @@ class _BoardContentState extends State { rowCache, context, ), + onStartEditing: () { + context.read().add( + BoardEvent.startEditingRow( + groupData.group, + groupItem.row, + ), + ); + }, + onEndEditing: () { + context + .read() + .add(BoardEvent.endEditingRow(groupItem.row.id)); + }, ), ); } @@ -345,7 +352,7 @@ extension HexColor on Color { } } -Widget? _buildHeaderIcon(BoardCustomData customData) { +Widget? _buildHeaderIcon(GroupData customData) { Widget? widget; switch (customData.fieldType) { case FieldType.Checkbox: diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/board_cell.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/board_cell.dart index 3968c6463c..549f7ba64c 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/card/board_cell.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/board_cell.dart @@ -76,6 +76,10 @@ class EditableRowNotifier { } abstract class EditableCell { + // Each cell notifier will be bind to the [EditableRowNotifier], which enable + // the row notifier receive its cells event. For example: begin editing the + // cell or end editing the cell. + // EditableCellNotifier? get editableNotifier; } diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/board_text_cell.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/board_text_cell.dart index 4a48e8fe48..3237316c13 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/card/board_text_cell.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/board_text_cell.dart @@ -42,6 +42,9 @@ class _BoardTextCellState extends State { focusNode.requestFocus(); } + // If the focusNode lost its focus, the widget's editableNotifier will + // set to false, which will cause the [EditableRowNotifier] to receive + // end edit event. focusNode.addListener(() { if (!focusNode.hasFocus) { focusWhenInit = false; @@ -131,7 +134,11 @@ class _BoardTextCellState extends State { padding: EdgeInsets.symmetric( vertical: BoardSizes.cardCellVPadding, ), - child: FlowyText.medium(state.content, fontSize: 14), + child: FlowyText.medium( + state.content, + fontSize: 14, + maxLines: null, // Enable multiple lines + ), ); } diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/card.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/card.dart index c08ece7474..bf1143523a 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/card/card.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/card.dart @@ -21,6 +21,8 @@ class BoardCard extends StatefulWidget { final CardDataController dataController; final BoardCellBuilder cellBuilder; final void Function(BuildContext) openCard; + final VoidCallback onStartEditing; + final VoidCallback onEndEditing; const BoardCard({ required this.gridId, @@ -30,6 +32,8 @@ class BoardCard extends StatefulWidget { required this.dataController, required this.cellBuilder, required this.openCard, + required this.onStartEditing, + required this.onEndEditing, Key? key, }) : super(key: key); @@ -56,6 +60,12 @@ class _BoardCardState extends State { rowNotifier.isEditing.addListener(() { if (!mounted) return; _cardBloc.add(BoardCardEvent.setIsEditing(rowNotifier.isEditing.value)); + + if (rowNotifier.isEditing.value) { + widget.onStartEditing(); + } else { + widget.onEndEditing(); + } }); popoverController = PopoverController(); diff --git a/frontend/app_flowy/packages/appflowy_board/example/lib/multi_board_list_example.dart b/frontend/app_flowy/packages/appflowy_board/example/lib/multi_board_list_example.dart index 2a256a51c4..ddf764d5ea 100644 --- a/frontend/app_flowy/packages/appflowy_board/example/lib/multi_board_list_example.dart +++ b/frontend/app_flowy/packages/appflowy_board/example/lib/multi_board_list_example.dart @@ -78,7 +78,7 @@ class _MultiBoardListExampleState extends State { height: 50, margin: config.groupItemPadding, onAddButtonClick: () { - boardController.scrollToBottom(columnData.id, (p0) {}); + boardController.scrollToBottom(columnData.id); }, ); }, diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart index 8aaf94e6d6..824e0e4a79 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart @@ -13,7 +13,8 @@ import '../rendering/board_overlay.dart'; class AppFlowyBoardScrollController { AppFlowyBoardState? _groupState; - void scrollToBottom(String groupId, void Function(BuildContext)? completed) { + void scrollToBottom(String groupId, + {void Function(BuildContext)? completed}) { _groupState?.reorderFlexActionMap[groupId]?.scrollToBottom(completed); } } @@ -39,9 +40,6 @@ class AppFlowyBoardConfig { } class AppFlowyBoard extends StatelessWidget { - /// The direction to use as the main axis. - final Axis direction = Axis.vertical; - /// The widget that will be rendered as the background of the board. final Widget? background; @@ -178,7 +176,10 @@ class _AppFlowyBoardContent extends StatefulWidget { this.headerBuilder, required this.phantomController, Key? key, - }) : reorderFlexConfig = const ReorderFlexConfig(), + }) : reorderFlexConfig = const ReorderFlexConfig( + direction: Axis.horizontal, + dragDirection: Axis.horizontal, + ), super(key: key); @override @@ -206,9 +207,7 @@ class _AppFlowyBoardContentState extends State<_AppFlowyBoardContent> { scrollController: widget.scrollController, onReorder: widget.onReorder, dataSource: widget.dataController, - direction: Axis.horizontal, interceptor: interceptor, - reorderable: true, children: _buildColumns(), ); diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_data.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_data.dart index 2fd2f1f3a1..1cc7c2e433 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_data.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_data.dart @@ -202,6 +202,14 @@ class AppFlowyBoardController extends ChangeNotifier getGroupController(groupId)?.replaceOrInsertItem(item); } + void enableGroupDragging(bool isEnable) { + for (var groupController in _groupControllers.values) { + groupController.enableDragging(isEnable); + } + + notifyListeners(); + } + /// Moves the item at [fromGroupIndex] in group with id [fromGroupId] to /// group with id [toGroupId] at [toGroupIndex] @override diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_group/group_data.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_group/group_data.dart index 5223e10e90..d26949e20a 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_group/group_data.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_group/group_data.dart @@ -5,6 +5,8 @@ import 'package:appflowy_board/src/widgets/reorder_flex/reorder_flex.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; +typedef IsDraggable = bool; + /// A item represents the generic data model of each group card. /// /// Each item displayed in the group required to implement this class. @@ -155,6 +157,15 @@ class AppFlowyGroupController extends ChangeNotifier with EquatableMixin { -1; } + void enableDragging(bool isEnable) { + groupData.draggable = isEnable; + + for (var item in groupData._items) { + item.draggable = isEnable; + } + _notify(); + } + void _notify() { notifyListeners(); } diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_state.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_state.dart index 8e1a61be1a..d24c99bfbd 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_state.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_state.dart @@ -16,13 +16,13 @@ class FlexDragTargetData extends DragTargetData { @override final int draggingIndex; - final DraggingState _state; + final DraggingState _draggingState; - Widget? get draggingWidget => _state.draggingWidget; + Widget? get draggingWidget => _draggingState.draggingWidget; - Size? get feedbackSize => _state.feedbackSize; + Size? get feedbackSize => _draggingState.feedbackSize; - bool get isDragging => _state.isDragging(); + bool get isDragging => _draggingState.isDragging(); final String dragTargetId; @@ -40,8 +40,8 @@ class FlexDragTargetData extends DragTargetData { required this.reorderFlexId, required this.reorderFlexItem, required this.dragTargetIndexKey, - required DraggingState state, - }) : _state = state; + required DraggingState draggingState, + }) : _draggingState = draggingState; @override String toString() { diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target.dart index fde9c3470a..7482eb36e0 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/drag_target.dart @@ -1,3 +1,4 @@ +import 'package:appflowy_board/appflowy_board.dart'; import 'package:appflowy_board/src/utils/log.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; @@ -78,10 +79,12 @@ class ReorderDragTarget extends StatefulWidget { final bool useMoveAnimation; - final bool draggable; + final IsDraggable draggable; final double draggingOpacity; + final Axis? dragDirection; + const ReorderDragTarget({ Key? key, required this.child, @@ -99,6 +102,7 @@ class ReorderDragTarget extends StatefulWidget { this.onLeave, this.draggableTargetBuilder, this.draggingOpacity = 0.3, + this.dragDirection, }) : super(key: key); @override @@ -115,8 +119,10 @@ class _ReorderDragTargetState Widget dragTarget = DragTarget( builder: _buildDraggableWidget, onWillAccept: (dragTargetData) { - assert(dragTargetData != null); - if (dragTargetData == null) return false; + if (dragTargetData == null) { + return false; + } + return widget.onWillAccept(dragTargetData); }, onAccept: widget.onAccept, @@ -140,9 +146,6 @@ class _ReorderDragTargetState List acceptedCandidates, List rejectedCandidates, ) { - if (!widget.draggable) { - return widget.child; - } Widget feedbackBuilder = Builder(builder: (BuildContext context) { BoxConstraints contentSizeConstraints = BoxConstraints.loose(_draggingFeedbackSize!); @@ -163,7 +166,8 @@ class _ReorderDragTargetState widget.deleteAnimationController, ) ?? Draggable( - maxSimultaneousDrags: 1, + axis: widget.dragDirection, + maxSimultaneousDrags: widget.draggable ? 1 : 0, data: widget.dragTargetData, ignoringFeedbackSemantics: false, feedback: feedbackBuilder, diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/reorder_flex.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/reorder_flex.dart index 69d763804d..2fe79c839f 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/reorder_flex.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_flex/reorder_flex.dart @@ -1,6 +1,7 @@ import 'dart:collection'; import 'dart:math'; +import 'package:appflowy_board/appflowy_board.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import '../../utils/log.dart'; @@ -29,6 +30,8 @@ abstract class ReoderFlexDataSource { abstract class ReoderFlexItem { /// [id] is used to identify the item. It must be unique. String get id; + + IsDraggable draggable = true; } /// Cache each dragTarget's key. @@ -73,8 +76,15 @@ class ReorderFlexConfig { final bool useMovePlaceholder; + /// [direction] How to place the children, default is Axis.vertical + final Axis direction; + + final Axis? dragDirection; + const ReorderFlexConfig({ this.useMoveAnimation = true, + this.direction = Axis.vertical, + this.dragDirection, }) : useMovePlaceholder = !useMoveAnimation; } @@ -82,8 +92,6 @@ class ReorderFlex extends StatefulWidget { final ReorderFlexConfig config; final List children; - /// [direction] How to place the children, default is Axis.vertical - final Axis direction; final MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start; final ScrollController? scrollController; @@ -108,8 +116,6 @@ class ReorderFlex extends StatefulWidget { final ReorderFlexAction? reorderFlexAction; - final bool reorderable; - ReorderFlex({ Key? key, this.scrollController, @@ -117,14 +123,12 @@ class ReorderFlex extends StatefulWidget { required this.children, required this.config, required this.onReorder, - this.reorderable = true, this.dragStateStorage, this.dragTargetKeys, this.onDragStarted, this.onDragEnded, this.interceptor, this.reorderFlexAction, - this.direction = Axis.vertical, }) : assert(children.every((Widget w) => w.key != null), 'All child must have a key.'), super(key: key); @@ -146,8 +150,8 @@ class ReorderFlexState extends State /// Whether or not we are currently scrolling this view to show a widget. bool _scrolling = false; - /// [dragState] records the dragging state including dragStartIndex, and phantomIndex, etc. - late DraggingState dragState; + /// [draggingState] records the dragging state including dragStartIndex, and phantomIndex, etc. + late DraggingState draggingState; /// [_animation] controls the dragging animations late DragTargetAnimation _animation; @@ -158,9 +162,9 @@ class ReorderFlexState extends State void initState() { _notifier = ReorderFlexNotifier(); final flexId = widget.reorderFlexId; - dragState = widget.dragStateStorage?.readState(flexId) ?? + draggingState = widget.dragStateStorage?.readState(flexId) ?? DraggingState(widget.reorderFlexId); - Log.trace('[DragTarget] init dragState: $dragState'); + Log.trace('[DragTarget] init dragState: $draggingState'); widget.dragStateStorage?.removeState(flexId); @@ -168,7 +172,7 @@ class ReorderFlexState extends State reorderAnimationDuration: widget.config.reorderAnimationDuration, entranceAnimateStatusChanged: (status) { if (status == AnimationStatus.completed) { - if (dragState.nextIndex == -1) return; + if (draggingState.nextIndex == -1) return; setState(() => _requestAnimationToNextIndex()); } }, @@ -225,7 +229,7 @@ class ReorderFlexState extends State indexKey, ); - children.add(_wrap(child, i, indexKey)); + children.add(_wrap(child, i, indexKey, item.draggable)); // if (widget.config.useMovePlaceholder) { // children.add(DragTargeMovePlaceholder( @@ -256,64 +260,70 @@ class ReorderFlexState extends State /// when the animation finish. if (_animation.entranceController.isCompleted) { - dragState.removePhantom(); + draggingState.removePhantom(); - if (!isAcceptingNewTarget && dragState.didDragTargetMoveToNext()) { + if (!isAcceptingNewTarget && draggingState.didDragTargetMoveToNext()) { return; } - dragState.moveDragTargetToNext(); + draggingState.moveDragTargetToNext(); _animation.animateToNext(); } } /// [child]: the child will be wrapped with dartTarget /// [childIndex]: the index of the child in a list - Widget _wrap(Widget child, int childIndex, GlobalObjectKey indexKey) { + Widget _wrap( + Widget child, + int childIndex, + GlobalObjectKey indexKey, + IsDraggable draggable, + ) { return Builder(builder: (context) { final ReorderDragTarget dragTarget = _buildDragTarget( context, child, childIndex, indexKey, + draggable, ); int shiftedIndex = childIndex; - if (dragState.isOverlapWithPhantom()) { - shiftedIndex = dragState.calculateShiftedIndex(childIndex); + if (draggingState.isOverlapWithPhantom()) { + shiftedIndex = draggingState.calculateShiftedIndex(childIndex); } Log.trace( - 'Rebuild: Group:[${dragState.reorderFlexId}] ${dragState.toString()}, childIndex: $childIndex shiftedIndex: $shiftedIndex'); - final currentIndex = dragState.currentIndex; - final dragPhantomIndex = dragState.phantomIndex; + 'Rebuild: Group:[${draggingState.reorderFlexId}] ${draggingState.toString()}, childIndex: $childIndex shiftedIndex: $shiftedIndex'); + final currentIndex = draggingState.currentIndex; + final dragPhantomIndex = draggingState.phantomIndex; if (shiftedIndex == currentIndex || childIndex == dragPhantomIndex) { Widget dragSpace; - if (dragState.draggingWidget != null) { - if (dragState.draggingWidget is PhantomWidget) { - dragSpace = dragState.draggingWidget!; + if (draggingState.draggingWidget != null) { + if (draggingState.draggingWidget is PhantomWidget) { + dragSpace = draggingState.draggingWidget!; } else { dragSpace = PhantomWidget( opacity: widget.config.draggingWidgetOpacity, - child: dragState.draggingWidget, + child: draggingState.draggingWidget, ); } } else { - dragSpace = SizedBox.fromSize(size: dragState.dropAreaSize); + dragSpace = SizedBox.fromSize(size: draggingState.dropAreaSize); } /// Returns the dragTarget it is not start dragging. The size of the /// dragTarget is the same as the the passed in child. /// - if (dragState.isNotDragging()) { + if (draggingState.isNotDragging()) { return _buildDraggingContainer(children: [dragTarget]); } /// Determine the size of the drop area to show under the dragging widget. Size? feedbackSize = Size.zero; if (widget.config.useMoveAnimation) { - feedbackSize = dragState.feedbackSize; + feedbackSize = draggingState.feedbackSize; } Widget appearSpace = _makeAppearSpace(dragSpace, feedbackSize); @@ -321,7 +331,7 @@ class ReorderFlexState extends State /// When start dragging, the dragTarget, [ReorderDragTarget], will /// return a [IgnorePointerWidget] which size is zero. - if (dragState.isPhantomAboveDragTarget()) { + if (draggingState.isPhantomAboveDragTarget()) { _notifier.updateDragTargetIndex(currentIndex); if (shiftedIndex == currentIndex && childIndex == dragPhantomIndex) { return _buildDraggingContainer(children: [ @@ -343,7 +353,7 @@ class ReorderFlexState extends State } /// - if (dragState.isPhantomBelowDragTarget()) { + if (draggingState.isPhantomBelowDragTarget()) { _notifier.updateDragTargetIndex(currentIndex); if (shiftedIndex == currentIndex && childIndex == dragPhantomIndex) { return _buildDraggingContainer(children: [ @@ -364,10 +374,10 @@ class ReorderFlexState extends State } } - assert(!dragState.isOverlapWithPhantom()); + assert(!draggingState.isOverlapWithPhantom()); List children = []; - if (dragState.isDragTargetMovingDown()) { + if (draggingState.isDragTargetMovingDown()) { children.addAll([dragTarget, appearSpace]); } else { children.addAll([appearSpace, dragTarget]); @@ -395,15 +405,17 @@ class ReorderFlexState extends State Widget child, int dragTargetIndex, GlobalObjectKey indexKey, + IsDraggable draggable, ) { final reorderFlexItem = widget.dataSource.items[dragTargetIndex]; return ReorderDragTarget( indexGlobalKey: indexKey, + draggable: draggable, dragTargetData: FlexDragTargetData( draggingIndex: dragTargetIndex, reorderFlexId: widget.reorderFlexId, reorderFlexItem: reorderFlexItem, - state: dragState, + draggingState: draggingState, dragTargetId: reorderFlexItem.id, dragTargetIndexKey: indexKey, ), @@ -432,11 +444,11 @@ class ReorderFlexState extends State setState(() { if (dragTargetData.reorderFlexId == widget.reorderFlexId) { _onReordered( - dragState.dragStartIndex, - dragState.currentIndex, + draggingState.dragStartIndex, + draggingState.currentIndex, ); } - dragState.endDragging(); + draggingState.endDragging(); widget.onDragEnded?.call(); }); }, @@ -482,8 +494,8 @@ class ReorderFlexState extends State deleteAnimationController: _animation.deleteController, draggableTargetBuilder: widget.interceptor?.draggableTargetBuilder, useMoveAnimation: widget.config.useMoveAnimation, - draggable: widget.reorderable, draggingOpacity: widget.config.draggingWidgetOpacity, + dragDirection: widget.config.dragDirection, child: child, ); } @@ -506,7 +518,7 @@ class ReorderFlexState extends State child, _animation.entranceController, feedbackSize, - widget.direction, + widget.config.direction, ); } @@ -515,7 +527,7 @@ class ReorderFlexState extends State child, _animation.phantomController, feedbackSize, - widget.direction, + widget.config.direction, ); } @@ -525,7 +537,7 @@ class ReorderFlexState extends State Size? feedbackSize, ) { setState(() { - dragState.startDragging(draggingWidget, dragIndex, feedbackSize); + draggingState.startDragging(draggingWidget, dragIndex, feedbackSize); _animation.startDragging(); }); } @@ -535,34 +547,34 @@ class ReorderFlexState extends State return; } - dragState.setStartDraggingIndex(dragTargetIndex); + draggingState.setStartDraggingIndex(dragTargetIndex); widget.dragStateStorage?.insertState( widget.reorderFlexId, - dragState, + draggingState, ); } bool handleOnWillAccept(BuildContext context, int dragTargetIndex) { - final dragIndex = dragState.dragStartIndex; + final dragIndex = draggingState.dragStartIndex; /// The [willAccept] will be true if the dargTarget is the widget that gets /// dragged and it is dragged on top of the other dragTargets. /// - bool willAccept = - dragState.dragStartIndex == dragIndex && dragIndex != dragTargetIndex; + bool willAccept = draggingState.dragStartIndex == dragIndex && + dragIndex != dragTargetIndex; setState(() { if (willAccept) { - int shiftedIndex = dragState.calculateShiftedIndex(dragTargetIndex); - dragState.updateNextIndex(shiftedIndex); + int shiftedIndex = draggingState.calculateShiftedIndex(dragTargetIndex); + draggingState.updateNextIndex(shiftedIndex); } else { - dragState.updateNextIndex(dragTargetIndex); + draggingState.updateNextIndex(dragTargetIndex); } _requestAnimationToNextIndex(isAcceptingNewTarget: true); }); Log.trace( - '[$ReorderDragTarget] ${widget.reorderFlexId} dragging state: $dragState}'); + '[$ReorderDragTarget] ${widget.reorderFlexId} dragging state: $draggingState}'); _scrollTo(context); @@ -587,7 +599,7 @@ class ReorderFlexState extends State return child; } else { return SingleChildScrollView( - scrollDirection: widget.direction, + scrollDirection: widget.config.direction, controller: _scrollController, child: child, ); @@ -595,7 +607,7 @@ class ReorderFlexState extends State } Widget _wrapContainer(List children) { - switch (widget.direction) { + switch (widget.config.direction) { case Axis.horizontal: return Row( crossAxisAlignment: CrossAxisAlignment.start, @@ -613,7 +625,7 @@ class ReorderFlexState extends State } Widget _buildDraggingContainer({required List children}) { - switch (widget.direction) { + switch (widget.config.direction) { case Axis.horizontal: return Row( crossAxisAlignment: CrossAxisAlignment.start, @@ -660,6 +672,7 @@ class ReorderFlexState extends State .ensureVisible( dragTargetRenderObject, alignment: 0.5, + alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtStart, duration: const Duration(milliseconds: 120), ) .then((value) { @@ -683,9 +696,9 @@ class ReorderFlexState extends State // If and only if the current scroll offset falls in-between the offsets // necessary to reveal the selected context at the top or bottom of the // screen, then it is already on-screen. - final double margin = widget.direction == Axis.horizontal - ? dragState.dropAreaSize.width - : dragState.dropAreaSize.height / 2.0; + final double margin = widget.config.direction == Axis.horizontal + ? draggingState.dropAreaSize.width + : draggingState.dropAreaSize.height / 2.0; if (_scrollController.hasClients) { final double scrollOffset = _scrollController.offset; final double topOffset = max( diff --git a/frontend/app_flowy/pubspec.lock b/frontend/app_flowy/pubspec.lock index a042baadd7..eaa1ad4900 100644 --- a/frontend/app_flowy/pubspec.lock +++ b/frontend/app_flowy/pubspec.lock @@ -28,7 +28,7 @@ packages: path: "packages/appflowy_board" relative: true source: path - version: "0.0.7" + version: "0.0.8" appflowy_editor: dependency: "direct main" description: From e6d4f9e3f7be345423ab54dffab6a0e7ee29353b Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Fri, 7 Oct 2022 11:24:39 +0800 Subject: [PATCH 39/48] feat: implement horizontal rule --- .../appflowy_editor/example/lib/main.dart | 4 + .../plugin/horizontal_rule_node_widget.dart | 163 ++++++++++++++++++ .../src/render/selection/cursor_widget.dart | 28 ++- .../lib/src/render/selection/selectable.dart | 9 + .../backspace_handler.dart | 6 +- .../lib/src/service/selection_service.dart | 2 + 6 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/horizontal_rule_node_widget.dart diff --git a/frontend/app_flowy/packages/appflowy_editor/example/lib/main.dart b/frontend/app_flowy/packages/appflowy_editor/example/lib/main.dart index fecec3d3e0..f6c2fd21ff 100644 --- a/frontend/app_flowy/packages/appflowy_editor/example/lib/main.dart +++ b/frontend/app_flowy/packages/appflowy_editor/example/lib/main.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:example/plugin/code_block_node_widget.dart'; +import 'package:example/plugin/horizontal_rule_node_widget.dart'; import 'package:example/plugin/tex_block_node_widget.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -121,15 +122,18 @@ class _MyHomePageState extends State { customBuilders: { 'text/code_block': CodeBlockNodeWidgetBuilder(), 'tex': TeXBlockNodeWidgetBuidler(), + 'horizontal_rule': HorizontalRuleWidgetBuilder(), }, shortcutEvents: [ enterInCodeBlock, ignoreKeysInCodeBlock, underscoreToItalic, + insertHorizontalRule, ], selectionMenuItems: [ codeBlockMenuItem, teXBlockMenuItem, + horizontalRuleMenuItem, ], ), ); diff --git a/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/horizontal_rule_node_widget.dart b/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/horizontal_rule_node_widget.dart new file mode 100644 index 0000000000..a993dd0a5c --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/horizontal_rule_node_widget.dart @@ -0,0 +1,163 @@ +import 'dart:collection'; + +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter/material.dart'; + +ShortcutEvent insertHorizontalRule = ShortcutEvent( + key: 'Horizontal rule', + command: 'Minus', + handler: _insertHorzaontalRule, +); + +ShortcutEventHandler _insertHorzaontalRule = (editorState, event) { + final selection = editorState.service.selectionService.currentSelection.value; + final textNodes = editorState.service.selectionService.currentSelectedNodes + .whereType(); + if (textNodes.length != 1 || selection == null) { + return KeyEventResult.ignored; + } + final textNode = textNodes.first; + if (textNode.toRawString() == '--') { + TransactionBuilder(editorState) + ..deleteText(textNode, 0, 2) + ..insertNode( + textNode.path, + Node( + type: 'horizontal_rule', + children: LinkedList(), + attributes: {}, + ), + ) + ..afterSelection = + Selection.single(path: textNode.path.next, startOffset: 0) + ..commit(); + return KeyEventResult.handled; + } + return KeyEventResult.ignored; +}; + +SelectionMenuItem horizontalRuleMenuItem = SelectionMenuItem( + name: () => 'Horizontal rule', + icon: const Icon(Icons.horizontal_rule), + keywords: ['horizontal rule'], + handler: (editorState, _, __) { + final selection = + editorState.service.selectionService.currentSelection.value; + final textNodes = editorState.service.selectionService.currentSelectedNodes + .whereType(); + if (selection == null || textNodes.isEmpty) { + return; + } + final textNode = textNodes.first; + if (textNode.toRawString().isEmpty) { + TransactionBuilder(editorState) + ..insertNode( + textNode.path, + Node( + type: 'horizontal_rule', + children: LinkedList(), + attributes: {}, + ), + ) + ..afterSelection = + Selection.single(path: textNode.path.next, startOffset: 0) + ..commit(); + } else { + TransactionBuilder(editorState) + ..insertNode( + selection.end.path.next, + TextNode( + type: 'text', + children: LinkedList(), + attributes: { + 'subtype': 'horizontal_rule', + }, + delta: Delta()..insert('---'), + ), + ) + ..afterSelection = selection + ..commit(); + } + }, +); + +class HorizontalRuleWidgetBuilder extends NodeWidgetBuilder { + @override + Widget build(NodeWidgetContext context) { + return _HorizontalRuleWidget( + key: context.node.key, + node: context.node, + editorState: context.editorState, + ); + } + + @override + NodeValidator get nodeValidator => (node) { + return true; + }; +} + +class _HorizontalRuleWidget extends StatefulWidget { + const _HorizontalRuleWidget({ + Key? key, + required this.node, + required this.editorState, + }) : super(key: key); + + final Node node; + final EditorState editorState; + + @override + State<_HorizontalRuleWidget> createState() => __HorizontalRuleWidgetState(); +} + +class __HorizontalRuleWidgetState extends State<_HorizontalRuleWidget> + with SelectableMixin { + RenderBox get _renderBox => context.findRenderObject() as RenderBox; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Container( + height: 1, + color: Colors.grey, + ), + ); + } + + @override + Position start() => Position(path: widget.node.path, offset: 0); + + @override + Position end() => Position(path: widget.node.path, offset: 1); + + @override + Position getPositionInOffset(Offset start) => end(); + + @override + bool get shouldCursorBlink => false; + + @override + CursorStyle get cursorStyle => CursorStyle.borderLine; + + @override + Rect? getCursorRectInPosition(Position position) { + final size = _renderBox.size; + return Rect.fromLTWH(-size.width / 2.0, 0, size.width, size.height); + } + + @override + List getRectsInSelection(Selection selection) => + [Offset.zero & _renderBox.size]; + + @override + Selection getSelectionInRange(Offset start, Offset end) => Selection.single( + path: widget.node.path, + startOffset: 0, + endOffset: 1, + ); + + @override + Offset localToGlobal(Offset offset) => _renderBox.localToGlobal(offset); +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection/cursor_widget.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection/cursor_widget.dart index 19da4b55f4..a7b68d410d 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection/cursor_widget.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection/cursor_widget.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:appflowy_editor/src/render/selection/selectable.dart'; import 'package:flutter/material.dart'; class CursorWidget extends StatefulWidget { @@ -9,9 +10,13 @@ class CursorWidget extends StatefulWidget { required this.rect, required this.color, this.blinkingInterval = 0.5, + this.shouldBlink = true, + this.cursorStyle = CursorStyle.verticalLine, }) : super(key: key); final double blinkingInterval; // milliseconds + final bool shouldBlink; + final CursorStyle cursorStyle; final Color color; final Rect rect; final LayerLink layerLink; @@ -67,11 +72,28 @@ class CursorWidgetState extends State { // Ignore the gestures in cursor // to solve the problem that cursor area cannot be selected. child: IgnorePointer( - child: Container( - color: showCursor ? widget.color : Colors.transparent, - ), + child: _buildCursor(context), ), ), ); } + + Widget _buildCursor(BuildContext context) { + var color = widget.color; + if (widget.shouldBlink && !showCursor) { + color = Colors.transparent; + } + switch (widget.cursorStyle) { + case CursorStyle.verticalLine: + return Container( + color: color, + ); + case CursorStyle.borderLine: + return Container( + decoration: BoxDecoration( + border: Border.all(color: color, width: 2), + ), + ); + } + } } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection/selectable.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection/selectable.dart index 372dbd7067..6f4f92c2e9 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection/selectable.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection/selectable.dart @@ -2,6 +2,11 @@ import 'package:appflowy_editor/src/document/position.dart'; import 'package:appflowy_editor/src/document/selection.dart'; import 'package:flutter/material.dart'; +enum CursorStyle { + verticalLine, + borderLine, +} + /// [SelectableMixin] is used for the editor to calculate the position /// and size of the selection. /// @@ -53,4 +58,8 @@ mixin SelectableMixin on State { Selection? getWorldBoundaryInOffset(Offset start) { return null; } + + bool get shouldCursorBlink => true; + + CursorStyle get cursorStyle => CursorStyle.verticalLine; } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/backspace_handler.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/backspace_handler.dart index 3f7a54810f..229d011743 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/backspace_handler.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/backspace_handler.dart @@ -3,7 +3,6 @@ import 'package:appflowy_editor/src/service/internal_key_event_handlers/number_l import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/extensions/path_extensions.dart'; // Handle delete text. ShortcutEventHandler deleteTextHandler = (editorState, event) { @@ -84,6 +83,11 @@ KeyEventResult _handleBackspace(EditorState editorState, RawKeyEvent event) { } } else { if (textNodes.isEmpty) { + if (nonTextNodes.isNotEmpty) { + transactionBuilder.afterSelection = + Selection.collapsed(selection.start); + } + transactionBuilder.commit(); return KeyEventResult.handled; } final startPosition = selection.start; diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/selection_service.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/selection_service.dart index d9b5422aa1..4eacc5674c 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/selection_service.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/selection_service.dart @@ -457,6 +457,8 @@ class _AppFlowySelectionState extends State rect: cursorRect, color: widget.cursorColor, layerLink: node.layerLink, + shouldBlink: selectable.shouldCursorBlink, + cursorStyle: selectable.cursorStyle, ), ); From e27e8e43e10cf140f0583b9550d8c9d2a541d433 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Fri, 7 Oct 2022 11:39:49 +0800 Subject: [PATCH 40/48] chore: update the menu item size for code_block, horizaontal_rule and tex_block --- .../example/lib/plugin/code_block_node_widget.dart | 6 +++++- .../example/lib/plugin/horizontal_rule_node_widget.dart | 6 +++++- .../example/lib/plugin/tex_block_node_widget.dart | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/code_block_node_widget.dart b/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/code_block_node_widget.dart index 990edf9298..c40a3f0ece 100644 --- a/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/code_block_node_widget.dart +++ b/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/code_block_node_widget.dart @@ -46,7 +46,11 @@ ShortcutEventHandler _ignorekHandler = (editorState, event) { SelectionMenuItem codeBlockMenuItem = SelectionMenuItem( name: () => 'Code Block', - icon: const Icon(Icons.abc), + icon: const Icon( + Icons.abc, + color: Colors.black, + size: 18.0, + ), keywords: ['code block'], handler: (editorState, _, __) { final selection = diff --git a/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/horizontal_rule_node_widget.dart b/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/horizontal_rule_node_widget.dart index a993dd0a5c..fca3df7b64 100644 --- a/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/horizontal_rule_node_widget.dart +++ b/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/horizontal_rule_node_widget.dart @@ -38,7 +38,11 @@ ShortcutEventHandler _insertHorzaontalRule = (editorState, event) { SelectionMenuItem horizontalRuleMenuItem = SelectionMenuItem( name: () => 'Horizontal rule', - icon: const Icon(Icons.horizontal_rule), + icon: const Icon( + Icons.horizontal_rule, + color: Colors.black, + size: 18.0, + ), keywords: ['horizontal rule'], handler: (editorState, _, __) { final selection = diff --git a/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/tex_block_node_widget.dart b/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/tex_block_node_widget.dart index e76c3e160b..ac40a31508 100644 --- a/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/tex_block_node_widget.dart +++ b/frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/tex_block_node_widget.dart @@ -6,7 +6,11 @@ import 'package:flutter_math_fork/flutter_math.dart'; SelectionMenuItem teXBlockMenuItem = SelectionMenuItem( name: () => 'Tex', - icon: const Icon(Icons.text_fields_rounded), + icon: const Icon( + Icons.text_fields_rounded, + color: Colors.black, + size: 18.0, + ), keywords: ['tex, latex, katex'], handler: (editorState, _, __) { final selection = From 323b56982bae9354a60ebe4b4517eff65abb4726 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Fri, 7 Oct 2022 12:24:10 +0800 Subject: [PATCH 41/48] chore: bump version to 0.0.6 (appflowy_editor) --- .../app_flowy/packages/appflowy_editor/CHANGELOG.md | 10 ++++++++++ .../app_flowy/packages/appflowy_editor/pubspec.yaml | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/frontend/app_flowy/packages/appflowy_editor/CHANGELOG.md b/frontend/app_flowy/packages/appflowy_editor/CHANGELOG.md index 4bc418fc81..db9b213e67 100644 --- a/frontend/app_flowy/packages/appflowy_editor/CHANGELOG.md +++ b/frontend/app_flowy/packages/appflowy_editor/CHANGELOG.md @@ -1,3 +1,13 @@ +## 0.0.6 +* Add three plugins: Code Block, LateX, and Horizontal rule. +* Support web platform. +* Support more markdown syntax conversions. + * `~ ~` to format text as strikethrough + * `_ _` to format text as italic + * \` \` to format text as code + * `[]()` to format text as link +* Fix some bugs. + ## 0.0.5 * Support customize the hotkeys for a shortcut on different platforms. * Support customize a theme. diff --git a/frontend/app_flowy/packages/appflowy_editor/pubspec.yaml b/frontend/app_flowy/packages/appflowy_editor/pubspec.yaml index 76ec0b758d..e6a4b6c71d 100644 --- a/frontend/app_flowy/packages/appflowy_editor/pubspec.yaml +++ b/frontend/app_flowy/packages/appflowy_editor/pubspec.yaml @@ -1,12 +1,13 @@ name: appflowy_editor description: A highly customizable rich-text editor for Flutter -version: 0.0.5 +version: 0.0.6 homepage: https://github.com/AppFlowy-IO/AppFlowy platforms: linux: macos: windows: + web: environment: sdk: ">=2.17.0 <3.0.0" From fa3f9c21fc97f2e2ed31f07f5252b051da2e60e7 Mon Sep 17 00:00:00 2001 From: appflowy Date: Fri, 7 Oct 2022 14:42:40 +0800 Subject: [PATCH 42/48] fix: state of AppFlowyBoard is wrong after reordering the group --- .../plugins/board/application/board_bloc.dart | 6 +-- .../application/board_data_controller.dart | 8 ++-- .../appflowy_board/lib/src/utils/log.dart | 4 ++ .../appflowy_board/lib/src/widgets/board.dart | 43 +++++++++--------- .../lib/src/widgets/board_data.dart | 10 ++++- .../lib/src/widgets/board_group/group.dart | 4 +- .../src/widgets/board_group/group_data.dart | 22 ++++++++-- .../reorder_phantom/phantom_controller.dart | 19 ++++---- .../flowy-grid/src/services/grid_editor.rs | 2 +- .../src/services/grid_view_editor.rs | 26 +++++------ .../src/services/grid_view_manager.rs | 24 +++++----- .../src/services/group/configuration.rs | 10 ++++- .../select_option_controller/util.rs | 7 +-- .../flowy-grid/tests/grid/group_test/test.rs | 44 +++++++++++++++++++ 14 files changed, 154 insertions(+), 75 deletions(-) diff --git a/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart b/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart index a02eb0dd4d..ce72413425 100644 --- a/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart +++ b/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart @@ -166,11 +166,11 @@ class BoardBloc extends Bloc { } } - void _moveGroup(String fromColumnId, String toColumnId) { + void _moveGroup(String fromGroupId, String toGroupId) { _rowService .moveGroup( - fromGroupId: fromColumnId, - toGroupId: toColumnId, + fromGroupId: fromGroupId, + toGroupId: toGroupId, ) .then((result) { result.fold((l) => null, (r) => add(BoardEvent.didReceiveError(r))); diff --git a/frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart b/frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart index 923bb4ef8c..641239e767 100644 --- a/frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart +++ b/frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart @@ -87,13 +87,13 @@ class BoardDataController { onUpdatedGroup.call(changeset.updateGroups); } - if (changeset.insertedGroups.isNotEmpty) { - onInsertedGroup.call(changeset.insertedGroups); - } - if (changeset.deletedGroups.isNotEmpty) { onDeletedGroup.call(changeset.deletedGroups); } + + if (changeset.insertedGroups.isNotEmpty) { + onInsertedGroup.call(changeset.insertedGroups); + } }, (e) => _onError?.call(e), ); diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/utils/log.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/utils/log.dart index 0b8d436d81..b73d28eed2 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/utils/log.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/utils/log.dart @@ -32,4 +32,8 @@ class Log { 'AppFlowyBoard: ❗️[Trace] - ${DateTime.now().second}=> $message'); } } + + static void error(String? message) { + debugPrint('AppFlowyBoard: ❌[Error] - ${DateTime.now().second}=> $message'); + } } diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart index 824e0e4a79..a295b575cd 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart @@ -11,11 +11,11 @@ import 'reorder_phantom/phantom_controller.dart'; import '../rendering/board_overlay.dart'; class AppFlowyBoardScrollController { - AppFlowyBoardState? _groupState; + AppFlowyBoardState? _boardState; void scrollToBottom(String groupId, {void Function(BuildContext)? completed}) { - _groupState?.reorderFlexActionMap[groupId]?.scrollToBottom(completed); + _boardState?.reorderFlexActionMap[groupId]?.scrollToBottom(completed); } } @@ -92,11 +92,7 @@ class AppFlowyBoard extends StatelessWidget { /// final AppFlowyBoardScrollController? boardScrollController; - final AppFlowyBoardState _groupState = AppFlowyBoardState(); - - late final BoardPhantomController _phantomController; - - AppFlowyBoard({ + const AppFlowyBoard({ required this.controller, required this.cardBuilder, this.background, @@ -107,12 +103,7 @@ class AppFlowyBoard extends StatelessWidget { this.groupConstraints = const BoxConstraints(maxWidth: 200), this.config = const AppFlowyBoardConfig(), Key? key, - }) : super(key: key) { - _phantomController = BoardPhantomController( - delegate: controller, - groupsState: _groupState, - ); - } + }) : super(key: key); @override Widget build(BuildContext context) { @@ -120,8 +111,14 @@ class AppFlowyBoard extends StatelessWidget { value: controller, child: Consumer( builder: (context, notifier, child) { + final boardState = AppFlowyBoardState(); + BoardPhantomController phantomController = BoardPhantomController( + delegate: controller, + groupsState: boardState, + ); + if (boardScrollController != null) { - boardScrollController!._groupState = _groupState; + boardScrollController!._boardState = boardState; } return _AppFlowyBoardContent( @@ -129,14 +126,14 @@ class AppFlowyBoard extends StatelessWidget { dataController: controller, scrollController: scrollController, scrollManager: boardScrollController, - groupState: _groupState, + boardState: boardState, background: background, - delegate: _phantomController, + delegate: phantomController, groupConstraints: groupConstraints, cardBuilder: cardBuilder, footerBuilder: footerBuilder, headerBuilder: headerBuilder, - phantomController: _phantomController, + phantomController: phantomController, onReorder: controller.moveGroup, ); }, @@ -154,7 +151,7 @@ class _AppFlowyBoardContent extends StatefulWidget { final ReorderFlexConfig reorderFlexConfig; final BoxConstraints groupConstraints; final AppFlowyBoardScrollController? scrollManager; - final AppFlowyBoardState groupState; + final AppFlowyBoardState boardState; final AppFlowyBoardCardBuilder cardBuilder; final AppFlowyBoardHeaderBuilder? headerBuilder; final AppFlowyBoardFooterBuilder? footerBuilder; @@ -167,7 +164,7 @@ class _AppFlowyBoardContent extends StatefulWidget { required this.delegate, required this.dataController, required this.scrollManager, - required this.groupState, + required this.boardState, this.scrollController, this.background, required this.groupConstraints, @@ -199,7 +196,7 @@ class _AppFlowyBoardContentState extends State<_AppFlowyBoardContent> { reorderFlexId: widget.dataController.identifier, acceptedReorderFlexId: widget.dataController.groupIds, delegate: widget.delegate, - columnsState: widget.groupState, + columnsState: widget.boardState, ); final reorderFlex = ReorderFlex( @@ -253,7 +250,7 @@ class _AppFlowyBoardContentState extends State<_AppFlowyBoardContent> { ); final reorderFlexAction = ReorderFlexActionImpl(); - widget.groupState.reorderFlexActionMap[columnData.id] = + widget.boardState.reorderFlexActionMap[columnData.id] = reorderFlexAction; return ChangeNotifierProvider.value( @@ -274,8 +271,8 @@ class _AppFlowyBoardContentState extends State<_AppFlowyBoardContent> { onReorder: widget.dataController.moveGroupItem, cornerRadius: widget.config.cornerRadius, backgroundColor: widget.config.groupBackgroundColor, - dragStateStorage: widget.groupState, - dragTargetKeys: widget.groupState, + dragStateStorage: widget.boardState, + dragTargetKeys: widget.boardState, reorderFlexAction: reorderFlexAction, ); diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_data.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_data.dart index 1cc7c2e433..43dd9728fc 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_data.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_data.dart @@ -138,7 +138,11 @@ class AppFlowyBoardController extends ChangeNotifier /// groups or get ready to reinitialize the [AppFlowyBoard]. void clear() { _groupDatas.clear(); + for (final group in _groupControllers.values) { + group.dispose(); + } _groupControllers.clear(); + notifyListeners(); } @@ -223,6 +227,8 @@ class AppFlowyBoardController extends ChangeNotifier final fromGroupController = getGroupController(fromGroupId)!; final toGroupController = getGroupController(toGroupId)!; final fromGroupItem = fromGroupController.removeAt(fromGroupIndex); + if (fromGroupItem == null) return; + if (toGroupController.items.length > toGroupIndex) { assert(toGroupController.items[toGroupIndex] is PhantomGroupItem); @@ -283,7 +289,9 @@ class AppFlowyBoardController extends ChangeNotifier Log.trace( '[$BoardPhantomController] update $groupId:$index to $groupId:$newIndex'); final item = groupController.removeAt(index, notify: false); - groupController.insert(newIndex, item, notify: false); + if (item != null) { + groupController.insert(newIndex, item, notify: false); + } } } } diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_group/group.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_group/group.dart index 880a81f666..b0c69b1070 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_group/group.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_group/group.dart @@ -156,9 +156,9 @@ class _AppFlowyBoardGroupState extends State { widget.onDragStarted?.call(index); }, onReorder: ((fromIndex, toIndex) { - if (widget.phantomController.isFromGroup(widget.groupId)) { + if (widget.phantomController.shouldReorder(widget.groupId)) { widget.onReorder(widget.groupId, fromIndex, toIndex); - widget.phantomController.transformIndex(fromIndex, toIndex); + widget.phantomController.updateIndex(fromIndex, toIndex); } }), onDragEnded: () { diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_group/group_data.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_group/group_data.dart index d26949e20a..659e87fadd 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_group/group_data.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_group/group_data.dart @@ -52,8 +52,17 @@ class AppFlowyGroupController extends ChangeNotifier with EquatableMixin { /// * [notify] the default value of [notify] is true, it will notify the /// listener. Set to false if you do not want to notify the listeners. /// - AppFlowyGroupItem removeAt(int index, {bool notify = true}) { - assert(index >= 0); + AppFlowyGroupItem? removeAt(int index, {bool notify = true}) { + if (groupData._items.length <= index) { + Log.error( + 'Fatal error, index is out of bounds. Index: $index, len: ${groupData._items.length}'); + return null; + } + + if (index < 0) { + Log.error('Invalid index:$index'); + return null; + } Log.debug('[$AppFlowyGroupController] $groupData remove item at $index'); final item = groupData._items.removeAt(index); @@ -73,12 +82,17 @@ class AppFlowyGroupController extends ChangeNotifier with EquatableMixin { /// Move the item from [fromIndex] to [toIndex]. It will do nothing if the /// [fromIndex] equal to the [toIndex]. bool move(int fromIndex, int toIndex) { - assert(fromIndex >= 0); assert(toIndex >= 0); + if (groupData._items.length < fromIndex) { + Log.error( + 'Out of bounds error. index: $fromIndex should not greater than ${groupData._items.length}'); + return false; + } if (fromIndex == toIndex) { return false; } + Log.debug( '[$AppFlowyGroupController] $groupData move item from $fromIndex to $toIndex'); final item = groupData._items.removeAt(fromIndex); @@ -126,7 +140,7 @@ class AppFlowyGroupController extends ChangeNotifier with EquatableMixin { Log.debug('[$AppFlowyGroupController] $groupData add $newItem'); } else { if (index >= groupData._items.length) { - Log.warn( + Log.error( '[$AppFlowyGroupController] unexpected items length, index should less than the count of the items. Index: $index, items count: ${items.length}'); return; } diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_phantom/phantom_controller.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_phantom/phantom_controller.dart index ad2778f6d8..7e991b34cd 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_phantom/phantom_controller.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/reorder_phantom/phantom_controller.dart @@ -46,15 +46,23 @@ class BoardPhantomController extends OverlapDragTargetDelegate required this.groupsState, }); - bool isFromGroup(String groupId) { + /// Determines whether the group should perform reorder + /// + /// Returns `true` if the fromGroupId and toGroupId of the phantomRecord + /// equal to the passed in groupId. + /// + /// Returns `true` if the phantomRecord is null + /// + bool shouldReorder(String groupId) { if (phantomRecord != null) { - return phantomRecord!.fromGroupId == groupId; + return phantomRecord!.toGroupId == groupId && + phantomRecord!.fromGroupId == groupId; } else { return true; } } - void transformIndex(int fromIndex, int toIndex) { + void updateIndex(int fromIndex, int toIndex) { if (phantomRecord == null) { return; } @@ -69,7 +77,6 @@ class BoardPhantomController extends OverlapDragTargetDelegate /// Remove the phantom in the group when the group is end dragging. void groupEndDragging(String groupId) { phantomState.setGroupIsDragging(groupId, false); - if (phantomRecord == null) return; final fromGroupId = phantomRecord!.fromGroupId; @@ -246,10 +253,6 @@ class PhantomRecord { }); void updateFromGroupIndex(int index) { - if (fromGroupIndex == index) { - return; - } - fromGroupIndex = index; } diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs index 29f7b759b8..3607a1ce74 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_editor.rs @@ -623,7 +623,7 @@ impl GridRevisionEditor { self.view_manager .move_group_row(row_rev, to_group_id, to_row_id.clone(), |row_changeset| { wrap_future(async move { - tracing::trace!("Move group row cause row data changed: {:?}", row_changeset); + tracing::trace!("Row data changed: {:?}", row_changeset); let cell_changesets = row_changeset .cell_by_field_id .into_iter() diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs index 6316135f5a..ce0edbef6f 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs @@ -79,7 +79,7 @@ impl GridViewRevisionEditor { Ok(json_str) } - pub(crate) async fn will_create_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) { + pub(crate) async fn will_create_view_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) { if params.group_id.is_none() { return; } @@ -92,7 +92,7 @@ impl GridViewRevisionEditor { .await; } - pub(crate) async fn did_create_row(&self, row_pb: &RowPB, params: &CreateRowParams) { + pub(crate) async fn did_create_view_row(&self, row_pb: &RowPB, params: &CreateRowParams) { // Send the group notification if the current view has groups match params.group_id.as_ref() { None => {} @@ -115,7 +115,7 @@ impl GridViewRevisionEditor { } #[tracing::instrument(level = "trace", skip_all)] - pub(crate) async fn did_delete_row(&self, row_rev: &RowRevision) { + pub(crate) async fn did_delete_view_row(&self, row_rev: &RowRevision) { // Send the group notification if the current view has groups; let changesets = self .mut_group_controller(|group_controller, field_rev| group_controller.did_delete_row(row_rev, &field_rev)) @@ -129,7 +129,7 @@ impl GridViewRevisionEditor { } } - pub(crate) async fn did_update_row(&self, row_rev: &RowRevision) { + pub(crate) async fn did_update_view_row(&self, row_rev: &RowRevision) { let changesets = self .mut_group_controller(|group_controller, field_rev| group_controller.did_update_row(row_rev, &field_rev)) .await; @@ -141,7 +141,7 @@ impl GridViewRevisionEditor { } } - pub(crate) async fn move_group_row( + pub(crate) async fn move_view_group_row( &self, row_rev: &RowRevision, row_changeset: &mut RowChangeset, @@ -167,14 +167,14 @@ impl GridViewRevisionEditor { } /// Only call once after grid view editor initialized #[tracing::instrument(level = "trace", skip(self))] - pub(crate) async fn load_groups(&self) -> FlowyResult> { + pub(crate) async fn load_view_groups(&self) -> FlowyResult> { let groups = self.group_controller.read().await.groups(); tracing::trace!("Number of groups: {}", groups.len()); Ok(groups.into_iter().map(GroupPB::from).collect()) } #[tracing::instrument(level = "trace", skip(self), err)] - pub(crate) async fn move_group(&self, params: MoveGroupParams) -> FlowyResult<()> { + pub(crate) async fn move_view_group(&self, params: MoveGroupParams) -> FlowyResult<()> { let _ = self .group_controller .write() @@ -206,13 +206,13 @@ impl GridViewRevisionEditor { self.group_controller.read().await.field_id().to_owned() } - pub(crate) async fn get_setting(&self) -> GridSettingPB { + pub(crate) async fn get_view_setting(&self) -> GridSettingPB { let field_revs = self.field_delegate.get_field_revs().await; let grid_setting = make_grid_setting(&*self.pad.read().await, &field_revs); grid_setting } - pub(crate) async fn get_filters(&self) -> Vec { + pub(crate) async fn get_view_filters(&self) -> Vec { let field_revs = self.field_delegate.get_field_revs().await; match self.pad.read().await.get_all_filters(&field_revs) { None => vec![], @@ -245,7 +245,7 @@ impl GridViewRevisionEditor { Ok(()) } - pub(crate) async fn delete_group(&self, params: DeleteGroupParams) -> FlowyResult<()> { + pub(crate) async fn delete_view_group(&self, params: DeleteGroupParams) -> FlowyResult<()> { self.modify(|pad| { let changeset = pad.delete_filter(¶ms.field_id, ¶ms.field_type_rev, ¶ms.group_id)?; Ok(changeset) @@ -253,7 +253,7 @@ impl GridViewRevisionEditor { .await } - pub(crate) async fn insert_filter(&self, params: InsertFilterParams) -> FlowyResult<()> { + pub(crate) async fn insert_view_filter(&self, params: InsertFilterParams) -> FlowyResult<()> { self.modify(|pad| { let filter_rev = FilterConfigurationRevision { id: gen_grid_filter_id(), @@ -267,7 +267,7 @@ impl GridViewRevisionEditor { .await } - pub(crate) async fn delete_filter(&self, delete_filter: DeleteFilterParams) -> FlowyResult<()> { + pub(crate) async fn delete_view_filter(&self, delete_filter: DeleteFilterParams) -> FlowyResult<()> { self.modify(|pad| { let changeset = pad.delete_filter( &delete_filter.field_id, @@ -324,7 +324,7 @@ impl GridViewRevisionEditor { } async fn notify_did_update_setting(&self) { - let setting = self.get_setting().await; + let setting = self.get_view_setting().await; send_dart_notification(&self.view_id, GridNotification::DidUpdateGridSetting) .payload(setting) .send(); diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs b/frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs index 98b8ee51b9..65ed1af6c5 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_view_manager.rs @@ -65,14 +65,14 @@ impl GridViewManager { /// When the row was created, we may need to modify the [RowRevision] according to the [CreateRowParams]. pub(crate) async fn will_create_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) { for view_editor in self.view_editors.iter() { - view_editor.will_create_row(row_rev, params).await; + view_editor.will_create_view_row(row_rev, params).await; } } /// Notify the view that the row was created. For the moment, the view is just sending notifications. pub(crate) async fn did_create_row(&self, row_pb: &RowPB, params: &CreateRowParams) { for view_editor in self.view_editors.iter() { - view_editor.did_create_row(row_pb, params).await; + view_editor.did_create_view_row(row_pb, params).await; } } @@ -84,7 +84,7 @@ impl GridViewManager { } Some(row_rev) => { for view_editor in self.view_editors.iter() { - view_editor.did_update_row(&row_rev).await; + view_editor.did_update_view_row(&row_rev).await; } } } @@ -102,33 +102,33 @@ impl GridViewManager { pub(crate) async fn did_delete_row(&self, row_rev: Arc) { for view_editor in self.view_editors.iter() { - view_editor.did_delete_row(&row_rev).await; + view_editor.did_delete_view_row(&row_rev).await; } } pub(crate) async fn get_setting(&self) -> FlowyResult { let view_editor = self.get_default_view_editor().await?; - Ok(view_editor.get_setting().await) + Ok(view_editor.get_view_setting().await) } pub(crate) async fn get_filters(&self) -> FlowyResult> { let view_editor = self.get_default_view_editor().await?; - Ok(view_editor.get_filters().await) + Ok(view_editor.get_view_filters().await) } pub(crate) async fn insert_or_update_filter(&self, params: InsertFilterParams) -> FlowyResult<()> { let view_editor = self.get_default_view_editor().await?; - view_editor.insert_filter(params).await + view_editor.insert_view_filter(params).await } pub(crate) async fn delete_filter(&self, params: DeleteFilterParams) -> FlowyResult<()> { let view_editor = self.get_default_view_editor().await?; - view_editor.delete_filter(params).await + view_editor.delete_view_filter(params).await } pub(crate) async fn load_groups(&self) -> FlowyResult { let view_editor = self.get_default_view_editor().await?; - let groups = view_editor.load_groups().await?; + let groups = view_editor.load_view_groups().await?; Ok(RepeatedGridGroupPB { items: groups }) } @@ -139,12 +139,12 @@ impl GridViewManager { pub(crate) async fn delete_group(&self, params: DeleteGroupParams) -> FlowyResult<()> { let view_editor = self.get_default_view_editor().await?; - view_editor.delete_group(params).await + view_editor.delete_view_group(params).await } pub(crate) async fn move_group(&self, params: MoveGroupParams) -> FlowyResult<()> { let view_editor = self.get_default_view_editor().await?; - let _ = view_editor.move_group(params).await?; + let _ = view_editor.move_view_group(params).await?; Ok(()) } @@ -161,7 +161,7 @@ impl GridViewManager { let mut row_changeset = RowChangeset::new(row_rev.id.clone()); let view_editor = self.get_default_view_editor().await?; let group_changesets = view_editor - .move_group_row(&row_rev, &mut row_changeset, &to_group_id, to_row_id.clone()) + .move_view_group_row(&row_rev, &mut row_changeset, &to_group_id, to_row_id.clone()) .await; if !row_changeset.is_empty() { diff --git a/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs b/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs index 8f3df66727..595af8174a 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs @@ -119,12 +119,20 @@ where self.mut_configuration(|configuration| { let from_index = configuration.groups.iter().position(|group| group.id == from_id); let to_index = configuration.groups.iter().position(|group| group.id == to_id); - tracing::info!("Configuration groups: {:?} ", configuration.groups); if let (Some(from), Some(to)) = &(from_index, to_index) { tracing::trace!("Move group from index:{:?} to index:{:?}", from_index, to_index); let group = configuration.groups.remove(*from); configuration.groups.insert(*to, group); } + tracing::debug!( + "Group order: {:?} ", + configuration + .groups + .iter() + .map(|group| group.name.clone()) + .collect::>() + .join(",") + ); from_index.is_some() && to_index.is_some() })?; diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs index 9c3e71bc3e..e5cbc8a8ec 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/select_option_controller/util.rs @@ -80,9 +80,9 @@ pub fn move_group_row(group: &mut Group, context: &mut MoveGroupRowContext) -> O }; // Remove the row in which group contains it - if from_index.is_some() { + if let Some(from_index) = &from_index { changeset.deleted_rows.push(row_rev.id.clone()); - tracing::debug!("Group:{} remove row:{}", group.id, row_rev.id); + tracing::debug!("Group:{} remove {} at {}", group.id, row_rev.id, from_index); group.remove_row(&row_rev.id); } @@ -97,10 +97,11 @@ pub fn move_group_row(group: &mut Group, context: &mut MoveGroupRowContext) -> O } Some(to_index) => { if to_index < group.number_of_row() { - tracing::debug!("Group:{} insert row:{} at {} ", group.id, row_rev.id, to_index); + tracing::debug!("Group:{} insert {} at {} ", group.id, row_rev.id, to_index); inserted_row.index = Some(to_index as i32); group.insert_row(to_index, row_pb); } else { + tracing::warn!("Mote to index: {} is out of bounds", to_index); tracing::debug!("Group:{} append row:{}", group.id, row_rev.id); group.add_row(row_pb); } diff --git a/frontend/rust-lib/flowy-grid/tests/grid/group_test/test.rs b/frontend/rust-lib/flowy-grid/tests/grid/group_test/test.rs index 42fdc6b61c..2040379c4c 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/group_test/test.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/group_test/test.rs @@ -350,6 +350,36 @@ async fn group_move_from_default_group_test() { #[tokio::test] async fn group_move_group_test() { + let mut test = GridGroupTest::new().await; + let group_0 = test.group_at_index(0).await; + let group_1 = test.group_at_index(1).await; + let scripts = vec![ + MoveGroup { + from_group_index: 0, + to_group_index: 1, + }, + AssertGroupRowCount { + group_index: 0, + row_count: 2, + }, + AssertGroup { + group_index: 0, + expected_group: group_1, + }, + AssertGroupRowCount { + group_index: 1, + row_count: 2, + }, + AssertGroup { + group_index: 1, + expected_group: group_0, + }, + ]; + test.run_scripts(scripts).await; +} + +#[tokio::test] +async fn group_move_group_row_after_move_group_test() { let mut test = GridGroupTest::new().await; let group_0 = test.group_at_index(0).await; let group_1 = test.group_at_index(1).await; @@ -366,6 +396,20 @@ async fn group_move_group_test() { group_index: 1, expected_group: group_0, }, + MoveRow { + from_group_index: 0, + from_row_index: 0, + to_group_index: 1, + to_row_index: 0, + }, + AssertGroupRowCount { + group_index: 0, + row_count: 1, + }, + AssertGroupRowCount { + group_index: 1, + row_count: 3, + }, ]; test.run_scripts(scripts).await; } From 2d61581eb3865750ec62a9372148780fce147099 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Fri, 7 Oct 2022 15:25:33 +0800 Subject: [PATCH 43/48] chore: flutter analyze and flutter format --- ...{flowy_editor_test.yml => appflowy_editor_test.yml} | 4 +++- .../lib/src/render/rich_text/built_in_text_widget.dart | 1 - .../render/selection_menu/selection_menu_widget.dart | 10 +++++----- .../copy_paste_handler.dart | 1 - .../enter_without_shift_in_text_node_handler.dart | 1 - .../test/extensions/path_extensions_test.dart | 1 - .../test/render/rich_text/checkbox_text_test.dart | 2 -- .../test/render/rich_text/toolbar_rich_text_test.dart | 1 - .../backspace_handler_test.dart | 1 - .../format_style_handler_test.dart | 1 - .../slash_handler_test.dart | 1 - .../white_space_handler_test.dart | 1 - .../test/service/toolbar_service_test.dart | 1 - 13 files changed, 8 insertions(+), 18 deletions(-) rename .github/workflows/{flowy_editor_test.yml => appflowy_editor_test.yml} (90%) diff --git a/.github/workflows/flowy_editor_test.yml b/.github/workflows/appflowy_editor_test.yml similarity index 90% rename from .github/workflows/flowy_editor_test.yml rename to .github/workflows/appflowy_editor_test.yml index 450f72e61d..80b9337f7b 100644 --- a/.github/workflows/flowy_editor_test.yml +++ b/.github/workflows/appflowy_editor_test.yml @@ -1,4 +1,4 @@ -name: FlowyEditor test +name: AppFlowyEditor test on: push: @@ -37,6 +37,8 @@ jobs: working-directory: frontend/app_flowy/packages/appflowy_editor run: | flutter pub get + flutter format --set-exit-if-changed . + flutter analyze . flutter test --coverage - uses: codecov/codecov-action@v3 diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/built_in_text_widget.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/built_in_text_widget.dart index f2c8693f87..a0a65d9583 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/built_in_text_widget.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/built_in_text_widget.dart @@ -1,5 +1,4 @@ import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart'; import 'package:flutter/material.dart'; abstract class BuiltInTextWidget extends StatefulWidget { diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_widget.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_widget.dart index c5a35ef73c..6bce03d15a 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_widget.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_widget.dart @@ -7,10 +7,10 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; typedef SelectionMenuItemHandler = void Function( - EditorState editorState, - SelectionMenuService menuService, - BuildContext context, - ); + EditorState editorState, + SelectionMenuService menuService, + BuildContext context, +); /// Selection Menu Item class SelectionMenuItem { @@ -23,7 +23,7 @@ class SelectionMenuItem { this.handler = (editorState, menuService, context) { _deleteToSlash(editorState); WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - handler(editorState, menuService, context); + handler(editorState, menuService, context); }); }; } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart index e48ec3426d..916ffe317e 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart @@ -1,7 +1,6 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/src/infra/html_converter.dart'; import 'package:appflowy_editor/src/document/node_iterator.dart'; -import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; import 'package:appflowy_editor/src/service/internal_key_event_handlers/number_list_helper.dart'; import 'package:flutter/material.dart'; import 'package:rich_clipboard/rich_clipboard.dart'; diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart index c31b8c3699..2a2d1deedb 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart @@ -3,7 +3,6 @@ import 'dart:collection'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/material.dart'; -import 'package:appflowy_editor/src/extensions/path_extensions.dart'; import './number_list_helper.dart'; /// Handle some cases where enter is pressed and shift is not pressed. diff --git a/frontend/app_flowy/packages/appflowy_editor/test/extensions/path_extensions_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/extensions/path_extensions_test.dart index c39c0d0e56..5d84690641 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/extensions/path_extensions_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/extensions/path_extensions_test.dart @@ -1,6 +1,5 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:appflowy_editor/src/extensions/path_extensions.dart'; void main() async { setUpAll(() { diff --git a/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/checkbox_text_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/checkbox_text_test.dart index 96cdbbce65..29dfbba136 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/checkbox_text_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/checkbox_text_test.dart @@ -1,6 +1,4 @@ import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; -import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart'; import 'package:appflowy_editor/src/extensions/text_node_extensions.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../infra/test_editor.dart'; diff --git a/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart index 9e8a5c15c9..c9d9ef9e70 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart @@ -1,5 +1,4 @@ import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; import 'package:appflowy_editor/src/extensions/text_node_extensions.dart'; import 'package:appflowy_editor/src/render/toolbar/toolbar_item_widget.dart'; import 'package:appflowy_editor/src/render/toolbar/toolbar_widget.dart'; diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/backspace_handler_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/backspace_handler_test.dart index 37ad7bade3..1e423c39e0 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/backspace_handler_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/backspace_handler_test.dart @@ -2,7 +2,6 @@ import 'dart:collection'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/src/render/image/image_node_widget.dart'; -import 'package:appflowy_editor/src/render/rich_text/flowy_rich_text.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:network_image_mock/network_image_mock.dart'; diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart index 7240c186f3..0cb4c71600 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart @@ -8,7 +8,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../infra/test_editor.dart'; -import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; void main() async { setUpAll(() { diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/slash_handler_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/slash_handler_test.dart index 4d5492e620..b3166a46fb 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/slash_handler_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/slash_handler_test.dart @@ -1,7 +1,6 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/src/render/selection_menu/selection_menu_item_widget.dart'; import 'package:appflowy_editor/src/render/selection_menu/selection_menu_service.dart'; -import 'package:appflowy_editor/src/render/selection_menu/selection_menu_widget.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../infra/test_editor.dart'; 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 fd7707a5f5..c0948a93c5 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 @@ -3,7 +3,6 @@ import 'package:appflowy_editor/src/service/internal_key_event_handlers/whitespa import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../infra/test_editor.dart'; -import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; void main() async { setUpAll(() { diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/toolbar_service_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/toolbar_service_test.dart index 5b82a5dea9..1e8f2f1f07 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/service/toolbar_service_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/service/toolbar_service_test.dart @@ -4,7 +4,6 @@ import 'package:appflowy_editor/src/render/toolbar/toolbar_item_widget.dart'; import 'package:appflowy_editor/src/render/toolbar/toolbar_widget.dart'; import 'package:flutter_test/flutter_test.dart'; import '../infra/test_editor.dart'; -import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; void main() async { setUpAll(() { From dff5e84ca56552d9a89d2fb6df751406e89bceff Mon Sep 17 00:00:00 2001 From: appflowy Date: Fri, 7 Oct 2022 15:33:54 +0800 Subject: [PATCH 44/48] chore: update appflowy_board version --- frontend/app_flowy/packages/appflowy_board/CHANGELOG.md | 6 ++++++ frontend/app_flowy/packages/appflowy_board/pubspec.yaml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/app_flowy/packages/appflowy_board/CHANGELOG.md b/frontend/app_flowy/packages/appflowy_board/CHANGELOG.md index 2a29982421..a4d3ca722b 100644 --- a/frontend/app_flowy/packages/appflowy_board/CHANGELOG.md +++ b/frontend/app_flowy/packages/appflowy_board/CHANGELOG.md @@ -1,8 +1,14 @@ +# 0.0.9 +* Enable slide to select text in card +* Fix some bugs + # 0.0.8 * Enable drag and drop group + # 0.0.7 * Rename some classes * Add documentation + # 0.0.6 * Support scroll to bottom * Fix some bugs diff --git a/frontend/app_flowy/packages/appflowy_board/pubspec.yaml b/frontend/app_flowy/packages/appflowy_board/pubspec.yaml index 35c7aa2b67..23c3b8be82 100644 --- a/frontend/app_flowy/packages/appflowy_board/pubspec.yaml +++ b/frontend/app_flowy/packages/appflowy_board/pubspec.yaml @@ -1,6 +1,6 @@ name: appflowy_board description: AppFlowyBoard is a board-style widget that consists of multi-groups. It supports drag and drop between different groups. -version: 0.0.8 +version: 0.0.9 homepage: https://github.com/AppFlowy-IO/AppFlowy repository: https://github.com/AppFlowy-IO/AppFlowy/tree/main/frontend/app_flowy/packages/appflowy_board From 2537a04fefa8f5f7ae32fe1db041ffb36b54f007 Mon Sep 17 00:00:00 2001 From: Sarang S <85063520+eigengravy@users.noreply.github.com> Date: Fri, 7 Oct 2022 19:03:09 +0530 Subject: [PATCH 45/48] feat: Translation for Malayalam (ml_IN) (#1245) --- .../appflowy_editor/lib/l10n/intl_ml_IN.arb | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_ml_IN.arb diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_ml_IN.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_ml_IN.arb new file mode 100644 index 0000000000..d48179e149 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_ml_IN.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "ml_IN", + "bold": "ബോൾഡ്", + "@bold": {}, + "bulletedList": "ബുള്ളറ്റഡ് പട്ടിക", + "@bulletedList": {}, + "checkbox": "ചെക്ക്ബോക്സ്", + "@checkbox": {}, + "embedCode": "എംബെഡഡ് കോഡ്", + "@embedCode": {}, + "heading1": "തലക്കെട്ട് 1", + "@heading1": {}, + "heading2": "തലക്കെട്ട് 2", + "@heading2": {}, + "heading3": "തലക്കെട്ട് 3", + "@heading3": {}, + "highlight": "പ്രമുഖമാക്കിക്കാട്ടുക", + "@highlight": {}, + "image": "ചിത്രം", + "@image": {}, + "italic": "ഇറ്റാലിക്", + "@italic": {}, + "link": "ലിങ്ക്", + "@link": {}, + "numberedList": "അക്കമിട്ട പട്ടിക", + "@numberedList": {}, + "quote": "ഉദ്ധരണി", + "@quote": {}, + "strikethrough": "സ്ട്രൈക്ക്ത്രൂ", + "@strikethrough": {}, + "text": "വചനം", + "@text": {}, + "underline": "അടിവരയിടുക", + "@underline": {} +} \ No newline at end of file From fa9e32d86a021609061de3a4a3e57019a9d73710 Mon Sep 17 00:00:00 2001 From: vortixhead <10776018+vortixhead@users.noreply.github.com> Date: Fri, 7 Oct 2022 20:24:05 -0500 Subject: [PATCH 46/48] feat: limit max character length on select/multiselect options (#1246) * Add maxLength of 30 characters and truncation to text fields * Add dynamic height * Add maxLength param * Add maxLength in Rounded Field --- .../cell/select_option_cell/select_option_editor.dart | 3 ++- .../widgets/cell/select_option_cell/text_field.dart | 6 ++++++ .../grid/presentation/widgets/common/text_field.dart | 7 ++++++- .../widgets/header/type_option/select_option.dart | 1 + .../widgets/header/type_option/select_option_editor.dart | 1 + .../flowy_infra_ui/lib/widget/rounded_input_field.dart | 6 ++++++ 6 files changed, 22 insertions(+), 2 deletions(-) diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart index 1b683713a3..21c2355621 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart @@ -142,11 +142,12 @@ class _TextField extends StatelessWidget { value: (option) => option); return SizedBox( - height: 42, + height: 62, child: SelectOptionTextField( options: state.options, selectedOptionMap: optionMap, distanceToText: _editorPanelWidth * 0.7, + maxLength: 30, tagController: _tagController, onClick: () => popoverMutex.close(), newText: (text) { diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/text_field.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/text_field.dart index 2bb249dbd5..93bbc1de68 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/text_field.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/text_field.dart @@ -6,6 +6,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart'; import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:app_flowy/generated/locale_keys.g.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:textfield_tags/textfield_tags.dart'; @@ -20,6 +21,7 @@ class SelectOptionTextField extends StatefulWidget { final Function(String) onSubmitted; final Function(String) newText; final VoidCallback? onClick; + final int? maxLength; const SelectOptionTextField({ required this.options, @@ -29,6 +31,7 @@ class SelectOptionTextField extends StatefulWidget { required this.onSubmitted, required this.newText, this.onClick, + this.maxLength, TextEditingController? textController, FocusNode? focusNode, Key? key, @@ -93,6 +96,9 @@ class _SelectOptionTextFieldState extends State { } }, maxLines: 1, + maxLength: widget.maxLength, + maxLengthEnforcement: + MaxLengthEnforcement.truncateAfterCompositionEnds, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500), decoration: InputDecoration( enabledBorder: OutlineInputBorder( diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/common/text_field.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/common/text_field.dart index 43d394bfe1..bd59b8b82c 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/common/text_field.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/common/text_field.dart @@ -9,6 +9,7 @@ class InputTextField extends StatefulWidget { final void Function() onCanceled; final bool autoClearWhenDone; final String text; + final int? maxLength; const InputTextField({ required this.text, @@ -16,6 +17,7 @@ class InputTextField extends StatefulWidget { required this.onCanceled, this.onChanged, this.autoClearWhenDone = false, + this.maxLength, Key? key, }) : super(key: key); @@ -41,11 +43,14 @@ class _InputTextFieldState extends State { Widget build(BuildContext context) { final theme = context.watch(); + final height = widget.maxLength == null ? 36.0 : 56.0; + return RoundedInputField( controller: _controller, focusNode: _focusNode, autoFocus: true, - height: 36, + height: height, + maxLength: widget.maxLength, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500), normalBorderColor: theme.shader4, focusBorderColor: theme.main1, diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option.dart index 06a9cd84b6..002dda79f1 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option.dart @@ -256,6 +256,7 @@ class _CreateOptionTextField extends StatelessWidget { final text = state.newOptionName.foldRight("", (a, previous) => a); return InputTextField( autoClearWhenDone: true, + maxLength: 30, text: text, onCanceled: () { context diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option_editor.dart index 53ede57f4c..ab9303b189 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option_editor.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option_editor.dart @@ -106,6 +106,7 @@ class _OptionNameTextField extends StatelessWidget { Widget build(BuildContext context) { return InputTextField( text: name, + maxLength: 30, onCanceled: () {}, onDone: (optionName) { if (name != optionName) { diff --git a/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/rounded_input_field.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/rounded_input_field.dart index 37eb782a2c..e5f6b8899a 100644 --- a/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/rounded_input_field.dart +++ b/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/rounded_input_field.dart @@ -2,6 +2,7 @@ import 'package:flowy_infra/size.dart'; import 'package:flowy_infra_ui/widget/rounded_button.dart'; import 'package:flutter/material.dart'; import 'package:flowy_infra/time/duration.dart'; +import 'package:flutter/services.dart'; class RoundedInputField extends StatefulWidget { final String? hintText; @@ -24,6 +25,7 @@ class RoundedInputField extends StatefulWidget { final FocusNode? focusNode; final TextEditingController? controller; final bool autoFocus; + final int? maxLength; const RoundedInputField({ Key? key, @@ -47,6 +49,7 @@ class RoundedInputField extends StatefulWidget { this.focusNode, this.controller, this.autoFocus = false, + this.maxLength, }) : super(key: key); @override @@ -89,6 +92,9 @@ class _RoundedInputFieldState extends State { initialValue: widget.initialValue, focusNode: widget.focusNode, autofocus: widget.autoFocus, + maxLength: widget.maxLength, + maxLengthEnforcement: + MaxLengthEnforcement.truncateAfterCompositionEnds, onChanged: (value) { inputText = value; if (widget.onChanged != null) { From 238af3ed0e9184d40dac9397e29e882ba4525db9 Mon Sep 17 00:00:00 2001 From: Ayush Date: Sat, 8 Oct 2022 06:59:29 +0530 Subject: [PATCH 47/48] Added flatpak files (#208) --- .../scripts/flatpack-buildfiles/.gitignore | 2 + .../io.appflowy.AppFlowy.desktop | 7 ++++ .../io.appflowy.AppFlowy.metainfo.xml | 38 +++++++++++++++++++ .../io.appflowy.AppFlowy.yml | 35 +++++++++++++++++ frontend/scripts/flatpack-buildfiles/logo.svg | 11 ++++++ 5 files changed, 93 insertions(+) create mode 100644 frontend/scripts/flatpack-buildfiles/.gitignore create mode 100644 frontend/scripts/flatpack-buildfiles/io.appflowy.AppFlowy.desktop create mode 100644 frontend/scripts/flatpack-buildfiles/io.appflowy.AppFlowy.metainfo.xml create mode 100644 frontend/scripts/flatpack-buildfiles/io.appflowy.AppFlowy.yml create mode 100644 frontend/scripts/flatpack-buildfiles/logo.svg diff --git a/frontend/scripts/flatpack-buildfiles/.gitignore b/frontend/scripts/flatpack-buildfiles/.gitignore new file mode 100644 index 0000000000..c267f688da --- /dev/null +++ b/frontend/scripts/flatpack-buildfiles/.gitignore @@ -0,0 +1,2 @@ +build-dir/ +.flatpak-builder/ diff --git a/frontend/scripts/flatpack-buildfiles/io.appflowy.AppFlowy.desktop b/frontend/scripts/flatpack-buildfiles/io.appflowy.AppFlowy.desktop new file mode 100644 index 0000000000..572fb2a9e7 --- /dev/null +++ b/frontend/scripts/flatpack-buildfiles/io.appflowy.AppFlowy.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Application +Name=AppFlowy +Icon=io.appflowy.AppFlowy +Exec=env GDK_GL=gles app_flowy %U +Categories=Network;Productivity; +Keywords=Notes diff --git a/frontend/scripts/flatpack-buildfiles/io.appflowy.AppFlowy.metainfo.xml b/frontend/scripts/flatpack-buildfiles/io.appflowy.AppFlowy.metainfo.xml new file mode 100644 index 0000000000..4178c3d2a5 --- /dev/null +++ b/frontend/scripts/flatpack-buildfiles/io.appflowy.AppFlowy.metainfo.xml @@ -0,0 +1,38 @@ + + + io.appflowy.AppFlowy + + AppFlowy + Open Source Notion Alternative + + CC-BY-4.0 + AGPL-3.0-only + + +

+ # Built for teams that need more control and flexibility ## 100% data control You can host AppFlowy wherever you want; no vendor lock-in. +

+

+ ## Unlimited customizations Design and modify AppFlowy your way with an open core codebase. +

+

+ ## One codebase supporting multiple platforms AppFlowy is built with Flutter and Rust. What does this mean? Faster development, better native experience, and more reliable performance. +

+

+ # Built for individuals who care about data security and mobile experience ## 100% control of your data Download and install AppFlowy on your local machine. You own and control your personal data. +

+

+ ## Extensively extensible For those with no coding experience, AppFlowy enables you to create apps that suit your needs. It's built on a community-driven toolbox, including templates, plugins, themes, and more. +

+

+ ## Truely native experience Faster, more stable with support for offline mode. It's also better integrated with different devices. Moreover, AppFlowy enables users to access features and possibilities not available on the web. +

+
+ + io.appflowy.AppFlowy.desktop + + + https://github.com/AppFlowy-IO/appflowy/raw/main/doc/imgs/welcome.png + + +
diff --git a/frontend/scripts/flatpack-buildfiles/io.appflowy.AppFlowy.yml b/frontend/scripts/flatpack-buildfiles/io.appflowy.AppFlowy.yml new file mode 100644 index 0000000000..f36b4df432 --- /dev/null +++ b/frontend/scripts/flatpack-buildfiles/io.appflowy.AppFlowy.yml @@ -0,0 +1,35 @@ +app-id: io.appflowy.AppFlowy +runtime: org.freedesktop.Platform +runtime-version: '21.08' +sdk: org.freedesktop.Sdk +command: app_flowy +separate-locales: false +finish-args: + - --share=ipc + - --socket=x11 + - --socket=fallback-x11 + - --socket=wayland + - --socket=pulseaudio + - --share=network + - --device=all +modules: + - name: appflowy + buildsystem: simple + build-commands: + # - ls . + - cp -r appflowy /app/appflowy + - chmod +x /app/appflowy/app_flowy + - install -Dm644 logo.svg /app/share/icons/hicolor/scalable/apps/io.appflowy.AppFlowy.svg + - mkdir /app/bin + - ln -s /app/appflowy/app_flowy /app/bin/app_flowy + - install -Dm644 io.appflowy.AppFlowy.desktop /app/share/applications/io.appflowy.AppFlowy.desktop + sources: + - type: archive + url: https://github.com/AppFlowy-IO/appflowy/releases/download/0.0.2/AppFlowy-linux-x86.tar.gz + sha256: b0dbe669bb9f34a65171adecaf61b02578bab5214d18a54009f0e4ec10665711 + dest: appflowy + - type: file + path: io.appflowy.AppFlowy.desktop + - type: file + path: logo.svg + diff --git a/frontend/scripts/flatpack-buildfiles/logo.svg b/frontend/scripts/flatpack-buildfiles/logo.svg new file mode 100644 index 0000000000..6b5430bfa0 --- /dev/null +++ b/frontend/scripts/flatpack-buildfiles/logo.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + From 2b8e4d3e611edf9bdc8933225dde4f9aac606c8c Mon Sep 17 00:00:00 2001 From: Enzo Lizama Date: Fri, 7 Oct 2022 21:24:28 -0500 Subject: [PATCH 48/48] test: delete file --- .../test/extensions/text_node_extensions_test.dart | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 frontend/app_flowy/packages/appflowy_editor/test/extensions/text_node_extensions_test.dart diff --git a/frontend/app_flowy/packages/appflowy_editor/test/extensions/text_node_extensions_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/extensions/text_node_extensions_test.dart deleted file mode 100644 index bec92362f4..0000000000 --- a/frontend/app_flowy/packages/appflowy_editor/test/extensions/text_node_extensions_test.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - group('TextNodeExtension::', () { - test('description', () { - final selecion = Selection.single(path: [0, 1, 2], startOffset: 0); - }); - }); -}