Explorar o código

feat: support importing multiple md files all at once (#2778)

Lucas.Xu hai 1 ano
pai
achega
335861706e

+ 1 - 0
frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/add_button.dart

@@ -2,6 +2,7 @@ import 'package:appflowy/plugins/document/document.dart';
 import 'package:appflowy/startup/plugin/plugin.dart';
 import 'package:appflowy/startup/startup.dart';
 import 'package:appflowy/workspace/presentation/home/menu/app/header/import/import_panel.dart';
+import 'package:appflowy/workspace/presentation/home/menu/app/header/import/import_type.dart';
 import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
 import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/image.dart';

+ 39 - 117
frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/import/import_panel.dart

@@ -7,10 +7,10 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/migration/
 import 'package:appflowy/startup/startup.dart';
 import 'package:appflowy/util/file_picker/file_picker_service.dart';
 import 'package:appflowy/workspace/application/settings/share/import_service.dart';
+import 'package:appflowy/workspace/presentation/home/menu/app/header/import/import_type.dart';
 import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
 import 'package:appflowy_editor/appflowy_editor.dart';
 import 'package:file_picker/file_picker.dart';
-import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flowy_infra_ui/style_widget/container.dart';
 import 'package:flutter/material.dart';
@@ -29,105 +29,32 @@ Future<void> showImportPanel(
   BuildContext context,
   ImportCallback callback,
 ) async {
-  await showDialog(
+  await FlowyOverlay.show(
     context: context,
-    builder: (context) {
-      return AlertDialog(
-        title: FlowyText.semibold(
-          LocaleKeys.moreAction_import.tr(),
-          fontSize: 20,
-          color: Theme.of(context).colorScheme.tertiary,
+    builder: (context) => FlowyDialog(
+      backgroundColor: Theme.of(context).colorScheme.surface,
+      title: FlowyText.semibold(
+        LocaleKeys.moreAction_import.tr(),
+        fontSize: 20,
+        color: Theme.of(context).colorScheme.tertiary,
+      ),
+      child: Padding(
+        padding: const EdgeInsets.symmetric(
+          vertical: 10.0,
+          horizontal: 20.0,
         ),
-        content: _ImportPanel(
+        child: ImportPanel(
           parentViewId: parentViewId,
           importCallback: callback,
         ),
-        contentPadding: const EdgeInsets.symmetric(
-          vertical: 10.0,
-          horizontal: 20.0,
-        ),
-      );
-    },
+      ),
+    ),
   );
 }
 
-enum ImportType {
-  historyDocument,
-  historyDatabase,
-  markdownOrText,
-  databaseCSV;
-
-  @override
-  String toString() {
-    switch (this) {
-      case ImportType.historyDocument:
-        return 'Document from v0.1';
-      case ImportType.historyDatabase:
-        return 'Database from v0.1';
-      case ImportType.markdownOrText:
-        return 'Text & Markdown';
-      case ImportType.databaseCSV:
-        return 'CSV';
-      default:
-        assert(false, 'Unsupported Type $this');
-        return '';
-    }
-  }
-
-  Widget? Function(BuildContext context) get icon => (context) {
-        var name = '';
-        switch (this) {
-          case ImportType.historyDocument:
-            name = 'editor/board';
-          case ImportType.historyDatabase:
-            name = 'editor/documents';
-          case ImportType.databaseCSV:
-            name = 'editor/board';
-          case ImportType.markdownOrText:
-            name = 'editor/text';
-          default:
-            assert(false, 'Unsupported Type $this');
-            return null;
-        }
-        return svgWidget(
-          name,
-          color: Theme.of(context).iconTheme.color,
-        );
-      };
-
-  List<String> get allowedExtensions {
-    switch (this) {
-      case ImportType.historyDocument:
-        return ['afdoc'];
-      case ImportType.historyDatabase:
-        return ['afdb'];
-      case ImportType.markdownOrText:
-        return ['md', 'txt'];
-      case ImportType.databaseCSV:
-        return ['csv'];
-      default:
-        assert(false, 'Unsupported Type $this');
-        return [];
-    }
-  }
-
-  bool get allowMultiSelect {
-    switch (this) {
-      case ImportType.historyDocument:
-      case ImportType.databaseCSV:
-        return true;
-      case ImportType.historyDatabase:
-      case ImportType.markdownOrText:
-        return false;
-      default:
-        assert(false, 'Unsupported Type $this');
-        return false;
-    }
-  }
-}
-
-class _ImportPanel extends StatefulWidget {
-  const _ImportPanel({
+class ImportPanel extends StatelessWidget {
+  const ImportPanel({
+    super.key,
     required this.parentViewId,
     required this.importCallback,
   });
@@ -135,11 +62,6 @@ class _ImportPanel extends StatefulWidget {
   final String parentViewId;
   final ImportCallback importCallback;
 
-  @override
-  State<_ImportPanel> createState() => _ImportPanelState();
-}
-
-class _ImportPanelState extends State<_ImportPanel> {
   @override
   Widget build(BuildContext context) {
     final width = MediaQuery.of(context).size.width * 0.7;
@@ -151,35 +73,35 @@ class _ImportPanelState extends State<_ImportPanel> {
       child: GridView.count(
         childAspectRatio: 1 / .2,
         crossAxisCount: 2,
-        children: ImportType.values.map(
-          (e) {
-            return Card(
-              child: FlowyButton(
-                leftIcon: e.icon(context),
-                leftIconSize: const Size.square(20),
-                text: FlowyText.medium(
-                  e.toString(),
-                  fontSize: 15,
-                  overflow: TextOverflow.ellipsis,
+        children: ImportType.values
+            .map(
+              (e) => Card(
+                child: FlowyButton(
+                  leftIcon: e.icon(context),
+                  leftIconSize: const Size.square(20),
+                  text: FlowyText.medium(
+                    e.toString(),
+                    fontSize: 15,
+                    overflow: TextOverflow.ellipsis,
+                  ),
+                  onTap: () async {
+                    await _importFile(parentViewId, e);
+                    if (context.mounted) {
+                      FlowyOverlay.pop(context);
+                    }
+                  },
                 ),
-                onTap: () async {
-                  await _importFile(widget.parentViewId, e);
-                  if (mounted) {
-                    Navigator.of(context).pop();
-                  }
-                },
               ),
-            );
-          },
-        ).toList(),
+            )
+            .toList(),
       ),
     );
   }
 
   Future<void> _importFile(String parentViewId, ImportType importType) async {
     final result = await getIt<FilePickerService>().pickFiles(
-      allowMultiple: importType.allowMultiSelect,
       type: FileType.custom,
+      allowMultiple: importType.allowMultiSelect,
       allowedExtensions: importType.allowedExtensions,
     );
     if (result == null || result.files.isEmpty) {

+ 63 - 0
frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/import/import_type.dart

@@ -0,0 +1,63 @@
+import 'package:flowy_infra/image.dart';
+import 'package:flutter/material.dart';
+
+enum ImportType {
+  historyDocument,
+  historyDatabase,
+  markdownOrText,
+  databaseCSV;
+
+  @override
+  String toString() {
+    switch (this) {
+      case ImportType.historyDocument:
+        return 'Document from v0.1';
+      case ImportType.historyDatabase:
+        return 'Database from v0.1';
+      case ImportType.markdownOrText:
+        return 'Text & Markdown';
+      case ImportType.databaseCSV:
+        return 'CSV';
+    }
+  }
+
+  WidgetBuilder get icon => (context) {
+        final String name;
+        switch (this) {
+          case ImportType.historyDocument:
+            name = 'editor/board';
+          case ImportType.historyDatabase:
+            name = 'editor/documents';
+          case ImportType.databaseCSV:
+            name = 'editor/board';
+          case ImportType.markdownOrText:
+            name = 'editor/text';
+        }
+        return FlowySvg(
+          name: name,
+        );
+      };
+
+  List<String> get allowedExtensions {
+    switch (this) {
+      case ImportType.historyDocument:
+        return ['afdoc'];
+      case ImportType.historyDatabase:
+        return ['afdb'];
+      case ImportType.markdownOrText:
+        return ['md', 'txt'];
+      case ImportType.databaseCSV:
+        return ['csv'];
+    }
+  }
+
+  bool get allowMultiSelect {
+    switch (this) {
+      case ImportType.historyDocument:
+      case ImportType.databaseCSV:
+      case ImportType.historyDatabase:
+      case ImportType.markdownOrText:
+        return true;
+    }
+  }
+}

+ 11 - 8
frontend/appflowy_flutter/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_dialog.dart

@@ -6,19 +6,22 @@ const overlayContainerMaxWidth = 760.0;
 const overlayContainerMinWidth = 320.0;
 
 class FlowyDialog extends StatelessWidget {
-  final Widget? title;
-  final ShapeBorder? shape;
-  final Widget child;
-  final BoxConstraints? constraints;
-  final EdgeInsets padding;
   const FlowyDialog({
+    super.key,
     required this.child,
     this.title,
     this.shape,
     this.constraints,
     this.padding = _overlayContainerPadding,
-    Key? key,
-  }) : super(key: key);
+    this.backgroundColor,
+  });
+
+  final Widget? title;
+  final ShapeBorder? shape;
+  final Widget child;
+  final BoxConstraints? constraints;
+  final EdgeInsets padding;
+  final Color? backgroundColor;
 
   @override
   Widget build(BuildContext context) {
@@ -26,7 +29,7 @@ class FlowyDialog extends StatelessWidget {
     final size = windowSize * 0.7;
     return SimpleDialog(
         contentPadding: EdgeInsets.zero,
-        backgroundColor: Theme.of(context).cardColor,
+        backgroundColor: backgroundColor ?? Theme.of(context).cardColor,
         title: title,
         shape: shape ??
             RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),

+ 13 - 8
frontend/appflowy_flutter/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_overlay.dart

@@ -83,8 +83,10 @@ class FlowyOverlay extends StatefulWidget {
 
   final Widget child;
 
-  static FlowyOverlayState of(BuildContext context,
-      {bool rootOverlay = false}) {
+  static FlowyOverlayState of(
+    BuildContext context, {
+    bool rootOverlay = false,
+  }) {
     FlowyOverlayState? state = maybeOf(context, rootOverlay: rootOverlay);
     assert(() {
       if (state == null) {
@@ -97,8 +99,10 @@ class FlowyOverlay extends StatefulWidget {
     return state!;
   }
 
-  static FlowyOverlayState? maybeOf(BuildContext context,
-      {bool rootOverlay = false}) {
+  static FlowyOverlayState? maybeOf(
+    BuildContext context, {
+    bool rootOverlay = false,
+  }) {
     FlowyOverlayState? state;
     if (rootOverlay) {
       state = context.findRootAncestorStateOfType<FlowyOverlayState>();
@@ -108,10 +112,11 @@ class FlowyOverlay extends StatefulWidget {
     return state;
   }
 
-  static void show(
-      {required BuildContext context,
-      required Widget Function(BuildContext context) builder}) {
-    showDialog(
+  static Future<void> show({
+    required BuildContext context,
+    required WidgetBuilder builder,
+  }) async {
+    await showDialog(
       context: context,
       builder: builder,
     );