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

This commit is contained in:
Lucas.Xu 2023-06-12 14:27:38 +08:00 committed by GitHub
parent 4f5672c2af
commit 335861706e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 128 additions and 134 deletions

View File

@ -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';

View File

@ -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) {

View File

@ -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;
}
}
}

View File

@ -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)),

View File

@ -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,
);