Quellcode durchsuchen

feat: support markdown export and customize save path (#1339)

Lucas.Xu vor 2 Jahren
Ursprung
Commit
aa58c79dbb

+ 15 - 31
frontend/app_flowy/lib/plugins/doc/application/share_bloc.dart

@@ -1,7 +1,5 @@
-import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
-import 'package:app_flowy/startup/tasks/rust_sdk.dart';
 import 'package:app_flowy/plugins/doc/application/share_service.dart';
 import 'package:app_flowy/workspace/application/markdown/document_markdown.dart';
 import 'package:flowy_sdk/protobuf/flowy-document/entities.pb.dart';
@@ -20,11 +18,14 @@ class DocShareBloc extends Bloc<DocShareEvent, DocShareState> {
       : super(const DocShareState.initial()) {
     on<DocShareEvent>((event, emit) async {
       await event.map(
-        shareMarkdown: (ShareMarkdown value) async {
+        shareMarkdown: (ShareMarkdown shareMarkdown) async {
           await service.exportMarkdown(view).then((result) {
             result.fold(
-              (value) => emit(DocShareState.finish(
-                  left(_convertDocumentToMarkdown(value)))),
+              (value) => emit(
+                DocShareState.finish(
+                  left(_saveMarkdown(value, shareMarkdown.path)),
+                ),
+              ),
               (error) => emit(DocShareState.finish(right(error))),
             );
           });
@@ -37,40 +38,23 @@ class DocShareBloc extends Bloc<DocShareEvent, DocShareState> {
     });
   }
 
-  ExportDataPB _convertDocumentToMarkdown(ExportDataPB value) {
-    final json = jsonDecode(value.data);
-    final document = Document.fromJson(json);
-    final result = documentToMarkdown(document);
-    value.data = result;
-    writeFile(result);
+  ExportDataPB _saveMarkdown(ExportDataPB value, String path) {
+    final markdown = _convertDocumentToMarkdown(value);
+    value.data = markdown;
+    File(path).writeAsStringSync(markdown);
     return value;
   }
 
-  Future<Directory> get _exportDir async {
-    Directory documentsDir = await appFlowyDocumentDirectory();
-
-    return documentsDir;
-  }
-
-  Future<String> get _localPath async {
-    final dir = await _exportDir;
-    return dir.path;
-  }
-
-  Future<File> get _localFile async {
-    final path = await _localPath;
-    return File('$path/${view.name}.md');
-  }
-
-  Future<File> writeFile(String md) async {
-    final file = await _localFile;
-    return file.writeAsString(md);
+  String _convertDocumentToMarkdown(ExportDataPB value) {
+    final json = jsonDecode(value.data);
+    final document = Document.fromJson(json);
+    return documentToMarkdown(document);
   }
 }
 
 @freezed
 class DocShareEvent with _$DocShareEvent {
-  const factory DocShareEvent.shareMarkdown() = ShareMarkdown;
+  const factory DocShareEvent.shareMarkdown(String path) = ShareMarkdown;
   const factory DocShareEvent.shareText() = ShareText;
   const factory DocShareEvent.shareLink() = ShareLink;
 }

+ 20 - 8
frontend/app_flowy/lib/plugins/doc/document.dart

@@ -14,6 +14,7 @@ import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart';
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:clipboard/clipboard.dart';
 import 'package:easy_localization/easy_localization.dart';
+import 'package:file_picker/file_picker.dart';
 import 'package:flowy_infra/size.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/widget/rounded_button.dart';
@@ -138,7 +139,9 @@ class DocumentShareButton extends StatelessWidget {
                     height: 30,
                     width: 100,
                   ),
-                  child: const ShareActionList(),
+                  child: ShareActionList(
+                    view: view,
+                  ),
                 ),
               ),
             );
@@ -165,11 +168,17 @@ class DocumentShareButton extends StatelessWidget {
 }
 
 class ShareActionList extends StatelessWidget {
-  const ShareActionList({Key? key}) : super(key: key);
+  const ShareActionList({
+    Key? key,
+    required this.view,
+  }) : super(key: key);
+
+  final ViewPB view;
 
   @override
   Widget build(BuildContext context) {
     final theme = context.watch<AppTheme>();
+    final docShareBloc = context.read<DocShareBloc>();
     return PopoverActionList<ShareActionWrapper>(
       direction: PopoverDirection.bottomWithCenterAligned,
       actions: ShareAction.values
@@ -184,14 +193,17 @@ class ShareActionList extends StatelessWidget {
           onPressed: () => controller.show(),
         );
       },
-      onSelected: (action, controller) {
+      onSelected: (action, controller) async {
         switch (action.inner) {
           case ShareAction.markdown:
-            context
-                .read<DocShareBloc>()
-                .add(const DocShareEvent.shareMarkdown());
-            showMessageToast(
-                'Exported to: ${LocaleKeys.notifications_export_path.tr()}');
+            final exportPath = await FilePicker.platform.saveFile(
+              dialogTitle: '',
+              fileName: '${view.name}.md',
+            );
+            if (exportPath != null) {
+              docShareBloc.add(DocShareEvent.shareMarkdown(exportPath));
+              showMessageToast('Exported to: $exportPath');
+            }
             break;
           case ShareAction.copyLink:
             NavigatorAlertDialog(

+ 8 - 5
frontend/app_flowy/lib/workspace/application/markdown/src/delta_markdown_encoder.dart

@@ -65,7 +65,8 @@ class DeltaMarkdownEncoder extends Converter<String, String> {
     // First close any current styles if needed
     final markedForRemoval = <Attribute>[];
     // Close the styles in reverse order, e.g. **_ for _**Test**_.
-    for (final value in currentInlineStyle.attributes.values.toList().reversed) {
+    for (final value
+        in currentInlineStyle.attributes.values.toList().reversed) {
       // TODO(tillf): Is block correct?
       if (value.scope == AttributeScope.BLOCK) {
         continue;
@@ -122,8 +123,10 @@ class DeltaMarkdownEncoder extends Converter<String, String> {
         // Close any open inline styles.
         _handleInline(lineBuffer, '', null);
 
-        final lineBlock =
-            Style.fromJson(attributes).attributes.values.singleWhereOrNull((a) => a.scope == AttributeScope.BLOCK);
+        final lineBlock = Style.fromJson(attributes)
+            .attributes
+            .values
+            .singleWhereOrNull((a) => a.scope == AttributeScope.BLOCK);
 
         if (lineBlock == currentBlockStyle) {
           currentBlockLines.add(lineBuffer.toString());
@@ -228,7 +231,7 @@ class DeltaMarkdownEncoder extends Converter<String, String> {
     } else if (attribute.key == Attribute.strikeThrough.key) {
       buffer.write(!close ? '~~' : '~~');
     } else {
-      throw ArgumentError('Cannot handle $attribute');
+      // do nothing, just skip the unknown attribute.
     }
   }
 
@@ -256,7 +259,7 @@ class DeltaMarkdownEncoder extends Converter<String, String> {
     } else if (block.key == Attribute.list.key) {
       buffer.write('* ');
     } else {
-      throw ArgumentError('Cannot handle block $block');
+      // do nothing, just skip the unknown attribute.
     }
   }
 

+ 8 - 1
frontend/app_flowy/packages/flowy_infra_ui/example/pubspec.lock

@@ -8,6 +8,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.0.1"
+  appflowy_popover:
+    dependency: transitive
+    description:
+      path: "../../appflowy_popover"
+      relative: true
+    source: path
+    version: "0.0.1"
   async:
     dependency: transitive
     description:
@@ -234,7 +241,7 @@ packages:
     source: hosted
     version: "2.0.1"
   provider:
-    dependency: transitive
+    dependency: "direct main"
     description:
       name: provider
       url: "https://pub.dartlang.org"

+ 3 - 3
frontend/app_flowy/packages/flowy_infra_ui/flowy_infra_ui_platform_interface/pubspec.lock

@@ -61,7 +61,7 @@ packages:
       name: flutter_lints
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.0.4"
+    version: "2.0.1"
   flutter_test:
     dependency: "direct dev"
     description: flutter
@@ -73,7 +73,7 @@ packages:
       name: lints
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.0.1"
+    version: "2.0.1"
   matcher:
     dependency: transitive
     description:
@@ -164,5 +164,5 @@ packages:
     source: hosted
     version: "2.1.2"
 sdks:
-  dart: ">=2.17.0-0 <3.0.0"
+  dart: ">=2.17.0 <3.0.0"
   flutter: ">=1.17.0"

+ 3 - 3
frontend/app_flowy/packages/flowy_infra_ui/flowy_infra_ui_web/pubspec.lock

@@ -68,7 +68,7 @@ packages:
       name: flutter_lints
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.0.4"
+    version: "2.0.1"
   flutter_test:
     dependency: "direct dev"
     description: flutter
@@ -92,7 +92,7 @@ packages:
       name: lints
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.0.1"
+    version: "2.0.1"
   matcher:
     dependency: transitive
     description:
@@ -183,5 +183,5 @@ packages:
     source: hosted
     version: "2.1.2"
 sdks:
-  dart: ">=2.17.0-0 <3.0.0"
+  dart: ">=2.17.0 <3.0.0"
   flutter: ">=1.17.0"

+ 8 - 1
frontend/app_flowy/packages/flowy_infra_ui/pubspec.lock

@@ -8,8 +8,15 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.0.1"
+  appflowy_popover:
+    dependency: "direct main"
+    description:
+      path: "../appflowy_popover"
+      relative: true
+    source: path
+    version: "0.0.1"
   async:
-    dependency: transitive
+    dependency: "direct main"
     description:
       name: async
       url: "https://pub.dartlang.org"

+ 4 - 4
frontend/app_flowy/packages/flowy_sdk/example/pubspec.lock

@@ -70,7 +70,7 @@ packages:
       name: dartz
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.10.0-nullsafety.2"
+    version: "0.10.1"
   fake_async:
     dependency: transitive
     description:
@@ -122,7 +122,7 @@ packages:
       name: flutter_lints
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.0.3"
+    version: "2.0.1"
   flutter_test:
     dependency: "direct dev"
     description: flutter
@@ -165,7 +165,7 @@ packages:
       name: lints
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.0.1"
+    version: "2.0.1"
   logger:
     dependency: transitive
     description:
@@ -305,5 +305,5 @@ packages:
     source: hosted
     version: "3.0.0"
 sdks:
-  dart: ">=2.17.0-0 <3.0.0"
+  dart: ">=2.17.0 <3.0.0"
   flutter: ">=1.17.0"

+ 4 - 4
frontend/app_flowy/packages/flowy_sdk/pubspec.lock

@@ -168,7 +168,7 @@ packages:
       name: dartz
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.10.0-nullsafety.2"
+    version: "0.10.1"
   fake_async:
     dependency: transitive
     description:
@@ -208,7 +208,7 @@ packages:
       name: flutter_lints
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.0.3"
+    version: "2.0.1"
   flutter_test:
     dependency: "direct dev"
     description: flutter
@@ -290,7 +290,7 @@ packages:
       name: lints
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.0.1"
+    version: "2.0.1"
   logger:
     dependency: "direct main"
     description:
@@ -500,5 +500,5 @@ packages:
     source: hosted
     version: "3.1.0"
 sdks:
-  dart: ">=2.17.0-0 <3.0.0"
+  dart: ">=2.17.0 <3.0.0"
   flutter: ">=1.17.0"

+ 7 - 0
frontend/app_flowy/pubspec.lock

@@ -393,6 +393,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "6.1.2"
+  file_picker:
+    dependency: "direct main"
+    description:
+      name: file_picker
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "4.6.1"
   fixnum:
     dependency: "direct main"
     description:

+ 1 - 0
frontend/app_flowy/pubspec.yaml

@@ -92,6 +92,7 @@ dependencies:
   textstyle_extensions: "2.0.0-nullsafety"
   shared_preferences: ^2.0.15
   google_fonts: ^3.0.1
+  file_picker: <=5.0.0
 
 dev_dependencies:
   flutter_lints: ^2.0.1