Browse Source

feat: add toggle list and callout node parser (#3700)

Lucas.Xu 1 year ago
parent
commit
7406c5e6a5

+ 11 - 1
frontend/appflowy_flutter/integration_test/share_markdown_test.dart

@@ -4,6 +4,7 @@ import 'package:appflowy/plugins/document/presentation/share/share_button.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:integration_test/integration_test.dart';
 import 'package:path/path.dart' as p;
+
 import 'util/mock/mock_file_picker.dart';
 import 'util/util.dart';
 
@@ -78,7 +79,7 @@ void main() {
   });
 }
 
-const expectedMarkdown = r'''
+const expectedMarkdown = '''
 # Welcome to AppFlowy!
 ## Here are the basics
 - [ ] Click anywhere and just start typing.
@@ -105,6 +106,15 @@ fn main() {
 ## Have a question❓
 > Click `?` at the bottom right for help and support.
 
+> 🥰
+> 
+> Like AppFlowy? Follow us:
+> [GitHub](https://github.com/AppFlowy-IO/AppFlowy)
+> [Twitter](https://twitter.com/appflowy): @appflowy
+> [Newsletter](https://blog-appflowy.ghost.io/)
+> 
+
+
 
 
 ''';

+ 27 - 0
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/callout_node_parser.dart

@@ -0,0 +1,27 @@
+import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
+import 'package:appflowy_editor/appflowy_editor.dart';
+
+class CalloutNodeParser extends NodeParser {
+  const CalloutNodeParser();
+
+  @override
+  String get id => CalloutBlockKeys.type;
+
+  @override
+  String transform(Node node, DocumentMarkdownEncoder? encoder) {
+    assert(node.children.isEmpty);
+    final icon = node.attributes[CalloutBlockKeys.icon];
+    final delta = node.delta ?? Delta()
+      ..insert('');
+    final String markdown = DeltaMarkdownEncoder()
+        .convert(delta)
+        .split('\n')
+        .map((e) => '> $e')
+        .join('\n');
+    return '''
+> $icon
+$markdown
+
+''';
+  }
+}

+ 1 - 1
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/code_block_node_parser.dart

@@ -7,7 +7,7 @@ class CodeBlockNodeParser extends NodeParser {
   String get id => 'code_block';
 
   @override
-  String transform(Node node) {
+  String transform(Node node, DocumentMarkdownEncoder? encoder) {
     return '```\n${node.attributes['code_block']}\n```';
   }
 }

+ 1 - 1
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/divider_node_parser.dart

@@ -7,7 +7,7 @@ class DividerNodeParser extends NodeParser {
   String get id => 'divider';
 
   @override
-  String transform(Node node) {
+  String transform(Node node, DocumentMarkdownEncoder? encoder) {
     return '---\n';
   }
 }

+ 5 - 0
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/document_markdown_parsers.dart

@@ -0,0 +1,5 @@
+export 'callout_node_parser.dart';
+export 'code_block_node_parser.dart';
+export 'divider_node_parser.dart';
+export 'math_equation_node_parser.dart';
+export 'toggle_list_node_parser.dart';

+ 5 - 0
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/markdown_parsers.dart

@@ -0,0 +1,5 @@
+export 'callout_node_parser.dart';
+export 'code_block_node_parser.dart';
+export 'divider_node_parser.dart';
+export 'math_equation_node_parser.dart';
+export 'toggle_list_node_parser.dart';

+ 1 - 1
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/math_equation_node_parser.dart

@@ -8,7 +8,7 @@ class MathEquationNodeParser extends NodeParser {
   String get id => MathEquationBlockKeys.type;
 
   @override
-  String transform(Node node) {
+  String transform(Node node, DocumentMarkdownEncoder? encoder) {
     return '\$\$${node.attributes[id]}\$\$';
   }
 }

+ 44 - 0
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/toggle_list_node_parser.dart

@@ -0,0 +1,44 @@
+import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
+import 'package:appflowy_editor/appflowy_editor.dart';
+
+enum ToggleListExportStyle {
+  github,
+  markdown,
+}
+
+class ToggleListNodeParser extends NodeParser {
+  const ToggleListNodeParser({
+    this.exportStyle = ToggleListExportStyle.markdown,
+  });
+
+  final ToggleListExportStyle exportStyle;
+
+  @override
+  String get id => ToggleListBlockKeys.type;
+
+  @override
+  String transform(Node node, DocumentMarkdownEncoder? encoder) {
+    final delta = node.delta ?? Delta()
+      ..insert('');
+    String markdown = DeltaMarkdownEncoder().convert(delta);
+    final details = encoder?.convertNodes(
+      node.children,
+      withIndent: true,
+    );
+    switch (exportStyle) {
+      case ToggleListExportStyle.github:
+        return '''<details>
+<summary>$markdown</summary>
+
+$details
+</details>
+''';
+      case ToggleListExportStyle.markdown:
+        markdown = '- $markdown\n';
+        if (details != null && details.isNotEmpty) {
+          markdown += details;
+        }
+        return markdown;
+    }
+  }
+}

+ 1 - 0
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart

@@ -28,6 +28,7 @@ export 'openai/widgets/auto_completion_node_widget.dart';
 export 'openai/widgets/smart_edit_node_widget.dart';
 export 'openai/widgets/smart_edit_toolbar_item.dart';
 export 'outline/outline_block_component.dart';
+export 'parsers/markdown_parsers.dart';
 export 'table/table_menu.dart';
 export 'table/table_option_action.dart';
 export 'toggle/toggle_block_component.dart';

+ 10 - 8
frontend/appflowy_flutter/lib/workspace/application/export/document_exporter.dart

@@ -3,15 +3,21 @@ import 'dart:convert';
 import 'package:appflowy/generated/locale_keys.g.dart';
 import 'package:appflowy/plugins/document/application/document_data_pb_extension.dart';
 import 'package:appflowy/plugins/document/application/prelude.dart';
-import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/code_block_node_parser.dart';
-import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/divider_node_parser.dart';
-import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/math_equation_node_parser.dart';
+import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/markdown_parsers.dart';
 import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
 import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
 import 'package:appflowy_editor/appflowy_editor.dart';
 import 'package:dartz/dartz.dart';
 import 'package:easy_localization/easy_localization.dart';
 
+const List<NodeParser> _customParsers = [
+  DividerNodeParser(),
+  MathEquationNodeParser(),
+  CodeBlockNodeParser(),
+  CalloutNodeParser(),
+  ToggleListNodeParser(),
+];
+
 enum DocumentExportType {
   json,
   markdown,
@@ -43,11 +49,7 @@ class DocumentExporter {
         case DocumentExportType.markdown:
           final markdown = documentToMarkdown(
             document,
-            customParsers: [
-              const DividerNodeParser(),
-              const MathEquationNodeParser(),
-              const CodeBlockNodeParser(),
-            ],
+            customParsers: _customParsers,
           );
           return right(markdown);
         case DocumentExportType.text:

+ 2 - 2
frontend/appflowy_flutter/pubspec.lock

@@ -54,8 +54,8 @@ packages:
     dependency: "direct main"
     description:
       path: "."
-      ref: d1027c4
-      resolved-ref: d1027c4d83a8cf78227e7d742c050ca945cef23a
+      ref: "9ae85ea"
+      resolved-ref: "9ae85ea162606b79483c49550266c154c0cb500c"
       url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
     source: git
     version: "1.4.4"

+ 1 - 1
frontend/appflowy_flutter/pubspec.yaml

@@ -47,7 +47,7 @@ dependencies:
   appflowy_editor:
     git:
       url: https://github.com/AppFlowy-IO/appflowy-editor.git
-      ref: "d1027c4"
+      ref: "9ae85ea"
   appflowy_popover:
     path: packages/appflowy_popover
 

+ 71 - 4
frontend/appflowy_flutter/test/unit_test/editor/share_markdown_test.dart

@@ -1,8 +1,6 @@
 import 'dart:convert';
 
-import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/code_block_node_parser.dart';
-import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/divider_node_parser.dart';
-import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/math_equation_node_parser.dart';
+import 'package:appflowy/plugins/document/presentation/editor_plugins/parsers/document_markdown_parsers.dart';
 import 'package:appflowy_editor/appflowy_editor.dart';
 import 'package:flutter_test/flutter_test.dart';
 
@@ -35,7 +33,7 @@ void main() {
       );
       expect(result, r'$$E = MC^2$$');
     });
-    // Changes
+
     test('code block', () {
       const text = '''
 {
@@ -63,6 +61,7 @@ void main() {
       );
       expect(result, '```\nSome Code\n```');
     });
+
     test('divider', () {
       const text = '''
 {
@@ -87,5 +86,73 @@ void main() {
       );
       expect(result, '---\n');
     });
+
+    test('callout', () {
+      const text = '''
+{
+    "document":{
+        "type":"page",
+        "children":[
+            {
+                "type":"callout",
+                "data":{
+                    "icon": "😁",
+                    "delta": [
+                        {
+                            "insert": "Callout"
+                        }
+                    ]
+                }
+            }
+        ]
+    }
+}
+''';
+      final document = Document.fromJson(
+        Map<String, Object>.from(json.decode(text)),
+      );
+      final result = documentToMarkdown(
+        document,
+        customParsers: [
+          const CalloutNodeParser(),
+        ],
+      );
+      expect(result, '''> 😁
+> Callout
+
+''');
+    });
+
+    test('toggle list', () {
+      const text = '''
+{
+    "document":{
+        "type":"page",
+        "children":[
+            {
+                "type":"toggle_list",
+                "data":{
+                    "delta": [
+                        {
+                            "insert": "Toggle list"
+                        }
+                    ]
+                }
+            }
+        ]
+    }
+}
+''';
+      final document = Document.fromJson(
+        Map<String, Object>.from(json.decode(text)),
+      );
+      final result = documentToMarkdown(
+        document,
+        customParsers: [
+          const ToggleListNodeParser(),
+        ],
+      );
+      expect(result, '- Toggle list\n');
+    });
   });
 }