mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: support importing multiple md files all at once (#2778)
This commit is contained in:
parent
4f5672c2af
commit
335861706e
@ -2,6 +2,7 @@ import 'package:appflowy/plugins/document/document.dart';
|
|||||||
import 'package:appflowy/startup/plugin/plugin.dart';
|
import 'package:appflowy/startup/plugin/plugin.dart';
|
||||||
import 'package:appflowy/startup/startup.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_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/workspace/presentation/widgets/pop_up_action.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:flowy_infra/image.dart';
|
import 'package:flowy_infra/image.dart';
|
||||||
|
@ -7,10 +7,10 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/migration/
|
|||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:appflowy/util/file_picker/file_picker_service.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/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_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:file_picker/file_picker.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/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/container.dart';
|
import 'package:flowy_infra_ui/style_widget/container.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -29,105 +29,32 @@ Future<void> showImportPanel(
|
|||||||
BuildContext context,
|
BuildContext context,
|
||||||
ImportCallback callback,
|
ImportCallback callback,
|
||||||
) async {
|
) async {
|
||||||
await showDialog(
|
await FlowyOverlay.show(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) => FlowyDialog(
|
||||||
return AlertDialog(
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
title: FlowyText.semibold(
|
title: FlowyText.semibold(
|
||||||
LocaleKeys.moreAction_import.tr(),
|
LocaleKeys.moreAction_import.tr(),
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
color: Theme.of(context).colorScheme.tertiary,
|
color: Theme.of(context).colorScheme.tertiary,
|
||||||
),
|
),
|
||||||
content: _ImportPanel(
|
child: Padding(
|
||||||
parentViewId: parentViewId,
|
padding: const EdgeInsets.symmetric(
|
||||||
importCallback: callback,
|
|
||||||
),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 10.0,
|
vertical: 10.0,
|
||||||
horizontal: 20.0,
|
horizontal: 20.0,
|
||||||
),
|
),
|
||||||
);
|
child: ImportPanel(
|
||||||
},
|
parentViewId: parentViewId,
|
||||||
|
importCallback: callback,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ImportType {
|
class ImportPanel extends StatelessWidget {
|
||||||
historyDocument,
|
const ImportPanel({
|
||||||
historyDatabase,
|
super.key,
|
||||||
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({
|
|
||||||
required this.parentViewId,
|
required this.parentViewId,
|
||||||
required this.importCallback,
|
required this.importCallback,
|
||||||
});
|
});
|
||||||
@ -135,11 +62,6 @@ class _ImportPanel extends StatefulWidget {
|
|||||||
final String parentViewId;
|
final String parentViewId;
|
||||||
final ImportCallback importCallback;
|
final ImportCallback importCallback;
|
||||||
|
|
||||||
@override
|
|
||||||
State<_ImportPanel> createState() => _ImportPanelState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ImportPanelState extends State<_ImportPanel> {
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final width = MediaQuery.of(context).size.width * 0.7;
|
final width = MediaQuery.of(context).size.width * 0.7;
|
||||||
@ -151,35 +73,35 @@ class _ImportPanelState extends State<_ImportPanel> {
|
|||||||
child: GridView.count(
|
child: GridView.count(
|
||||||
childAspectRatio: 1 / .2,
|
childAspectRatio: 1 / .2,
|
||||||
crossAxisCount: 2,
|
crossAxisCount: 2,
|
||||||
children: ImportType.values.map(
|
children: ImportType.values
|
||||||
(e) {
|
.map(
|
||||||
return Card(
|
(e) => Card(
|
||||||
child: FlowyButton(
|
child: FlowyButton(
|
||||||
leftIcon: e.icon(context),
|
leftIcon: e.icon(context),
|
||||||
leftIconSize: const Size.square(20),
|
leftIconSize: const Size.square(20),
|
||||||
text: FlowyText.medium(
|
text: FlowyText.medium(
|
||||||
e.toString(),
|
e.toString(),
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
overflow: TextOverflow.ellipsis,
|
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 {
|
Future<void> _importFile(String parentViewId, ImportType importType) async {
|
||||||
final result = await getIt<FilePickerService>().pickFiles(
|
final result = await getIt<FilePickerService>().pickFiles(
|
||||||
allowMultiple: importType.allowMultiSelect,
|
|
||||||
type: FileType.custom,
|
type: FileType.custom,
|
||||||
|
allowMultiple: importType.allowMultiSelect,
|
||||||
allowedExtensions: importType.allowedExtensions,
|
allowedExtensions: importType.allowedExtensions,
|
||||||
);
|
);
|
||||||
if (result == null || result.files.isEmpty) {
|
if (result == null || result.files.isEmpty) {
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,19 +6,22 @@ const overlayContainerMaxWidth = 760.0;
|
|||||||
const overlayContainerMinWidth = 320.0;
|
const overlayContainerMinWidth = 320.0;
|
||||||
|
|
||||||
class FlowyDialog extends StatelessWidget {
|
class FlowyDialog extends StatelessWidget {
|
||||||
final Widget? title;
|
|
||||||
final ShapeBorder? shape;
|
|
||||||
final Widget child;
|
|
||||||
final BoxConstraints? constraints;
|
|
||||||
final EdgeInsets padding;
|
|
||||||
const FlowyDialog({
|
const FlowyDialog({
|
||||||
|
super.key,
|
||||||
required this.child,
|
required this.child,
|
||||||
this.title,
|
this.title,
|
||||||
this.shape,
|
this.shape,
|
||||||
this.constraints,
|
this.constraints,
|
||||||
this.padding = _overlayContainerPadding,
|
this.padding = _overlayContainerPadding,
|
||||||
Key? key,
|
this.backgroundColor,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
|
final Widget? title;
|
||||||
|
final ShapeBorder? shape;
|
||||||
|
final Widget child;
|
||||||
|
final BoxConstraints? constraints;
|
||||||
|
final EdgeInsets padding;
|
||||||
|
final Color? backgroundColor;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -26,7 +29,7 @@ class FlowyDialog extends StatelessWidget {
|
|||||||
final size = windowSize * 0.7;
|
final size = windowSize * 0.7;
|
||||||
return SimpleDialog(
|
return SimpleDialog(
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
backgroundColor: Theme.of(context).cardColor,
|
backgroundColor: backgroundColor ?? Theme.of(context).cardColor,
|
||||||
title: title,
|
title: title,
|
||||||
shape: shape ??
|
shape: shape ??
|
||||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
|
@ -83,8 +83,10 @@ class FlowyOverlay extends StatefulWidget {
|
|||||||
|
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
static FlowyOverlayState of(BuildContext context,
|
static FlowyOverlayState of(
|
||||||
{bool rootOverlay = false}) {
|
BuildContext context, {
|
||||||
|
bool rootOverlay = false,
|
||||||
|
}) {
|
||||||
FlowyOverlayState? state = maybeOf(context, rootOverlay: rootOverlay);
|
FlowyOverlayState? state = maybeOf(context, rootOverlay: rootOverlay);
|
||||||
assert(() {
|
assert(() {
|
||||||
if (state == null) {
|
if (state == null) {
|
||||||
@ -97,8 +99,10 @@ class FlowyOverlay extends StatefulWidget {
|
|||||||
return state!;
|
return state!;
|
||||||
}
|
}
|
||||||
|
|
||||||
static FlowyOverlayState? maybeOf(BuildContext context,
|
static FlowyOverlayState? maybeOf(
|
||||||
{bool rootOverlay = false}) {
|
BuildContext context, {
|
||||||
|
bool rootOverlay = false,
|
||||||
|
}) {
|
||||||
FlowyOverlayState? state;
|
FlowyOverlayState? state;
|
||||||
if (rootOverlay) {
|
if (rootOverlay) {
|
||||||
state = context.findRootAncestorStateOfType<FlowyOverlayState>();
|
state = context.findRootAncestorStateOfType<FlowyOverlayState>();
|
||||||
@ -108,10 +112,11 @@ class FlowyOverlay extends StatefulWidget {
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void show(
|
static Future<void> show({
|
||||||
{required BuildContext context,
|
required BuildContext context,
|
||||||
required Widget Function(BuildContext context) builder}) {
|
required WidgetBuilder builder,
|
||||||
showDialog(
|
}) async {
|
||||||
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: builder,
|
builder: builder,
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user