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/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';
|
||||
|
@ -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,
|
||||
),
|
||||
content: _ImportPanel(
|
||||
parentViewId: parentViewId,
|
||||
importCallback: callback,
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
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,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: ImportPanel(
|
||||
parentViewId: parentViewId,
|
||||
importCallback: callback,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -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;
|
||||
|
||||
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)),
|
||||
|
@ -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,
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user