feat: support creating database at the first level (#5627)

* feat: support creating database at the first level

* chore: add loading indicator when importing file
This commit is contained in:
Lucas.Xu 2024-06-26 13:27:33 +08:00 committed by GitHub
parent 7d96c2dfd4
commit f812040f04
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 122 additions and 71 deletions

View File

@ -54,6 +54,7 @@ class _MobileSpaceState extends State<MobileSpace> {
context.read<SpaceBloc>().add( context.read<SpaceBloc>().add(
SpaceEvent.createPage( SpaceEvent.createPage(
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(), name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
layout: ViewLayoutPB.Document,
index: 0, index: 0,
), ),
); );
@ -99,6 +100,7 @@ class _MobileSpaceState extends State<MobileSpace> {
context.read<SpaceBloc>().add( context.read<SpaceBloc>().add(
SpaceEvent.createPage( SpaceEvent.createPage(
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(), name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
layout: ViewLayoutPB.Document,
), ),
); );
} }

View File

@ -124,6 +124,7 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
SpaceEvent.createPage( SpaceEvent.createPage(
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(), name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
index: 0, index: 0,
layout: ViewLayoutPB.Document,
), ),
); );
} }
@ -216,7 +217,7 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
await _setSpaceExpandStatus(space, isExpanded); await _setSpaceExpandStatus(space, isExpanded);
emit(state.copyWith(isExpanded: isExpanded)); emit(state.copyWith(isExpanded: isExpanded));
}, },
createPage: (name, index) async { createPage: (name, layout, index) async {
final parentViewId = state.currentSpace?.id; final parentViewId = state.currentSpace?.id;
if (parentViewId == null) { if (parentViewId == null) {
return; return;
@ -224,7 +225,7 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
final result = await ViewBackendService.createView( final result = await ViewBackendService.createView(
name: name, name: name,
layoutType: ViewLayoutPB.Document, layoutType: layout,
parentViewId: parentViewId, parentViewId: parentViewId,
index: index, index: index,
); );
@ -631,6 +632,7 @@ class SpaceEvent with _$SpaceEvent {
const factory SpaceEvent.expand(ViewPB space, bool isExpanded) = _Expand; const factory SpaceEvent.expand(ViewPB space, bool isExpanded) = _Expand;
const factory SpaceEvent.createPage({ const factory SpaceEvent.createPage({
required String name, required String name,
required ViewLayoutPB layout,
int? index, int? index,
}) = _CreatePage; }) = _CreatePage;
const factory SpaceEvent.delete(ViewPB? space) = _Delete; const factory SpaceEvent.delete(ViewPB? space) = _Delete;

View File

@ -158,7 +158,7 @@ class DesktopHomeScreen extends StatelessWidget {
delegate: DesktopHomeScreenStackAdaptor(context), delegate: DesktopHomeScreenStackAdaptor(context),
userProfile: userProfile, userProfile: userProfile,
); );
final menu = _buildHomeSidebar( final sidebar = _buildHomeSidebar(
context, context,
layout: layout, layout: layout,
userProfile: userProfile, userProfile: userProfile,
@ -170,7 +170,7 @@ class DesktopHomeScreen extends StatelessWidget {
return _layoutWidgets( return _layoutWidgets(
layout: layout, layout: layout,
homeStack: homeStack, homeStack: homeStack,
homeMenu: menu, sidebar: sidebar,
editPanel: editPanel, editPanel: editPanel,
bubble: const QuestionBubble(), bubble: const QuestionBubble(),
homeMenuResizer: homeMenuResizer, homeMenuResizer: homeMenuResizer,
@ -253,7 +253,7 @@ class DesktopHomeScreen extends StatelessWidget {
Widget _layoutWidgets({ Widget _layoutWidgets({
required HomeLayout layout, required HomeLayout layout,
required Widget homeMenu, required Widget sidebar,
required Widget homeStack, required Widget homeStack,
required Widget editPanel, required Widget editPanel,
required Widget bubble, required Widget bubble,
@ -287,7 +287,7 @@ class DesktopHomeScreen extends StatelessWidget {
bottom: 0, bottom: 0,
width: layout.editPanelWidth, width: layout.editPanelWidth,
), ),
homeMenu sidebar
.animatedPanelX( .animatedPanelX(
closeX: -layout.menuWidth, closeX: -layout.menuWidth,
isClosed: !layout.showMenu, isClosed: !layout.showMenu,

View File

@ -1,19 +1,21 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/application/document_data_pb_extension.dart'; import 'package:appflowy/plugins/document/application/document_data_pb_extension.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/migration/editor_migration.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/migration/editor_migration.dart';
import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/startup/startup.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/sidebar/import/import_type.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/import/import_type.dart';
import 'package:appflowy/workspace/presentation/home/toast.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/file_picker/file_picker_service.dart'; import 'package:flowy_infra/file_picker/file_picker_service.dart';
import 'package:appflowy_editor/appflowy_editor.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';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
@ -67,10 +69,12 @@ class ImportPanel extends StatefulWidget {
class _ImportPanelState extends State<ImportPanel> { class _ImportPanelState extends State<ImportPanel> {
final flowyContainerFocusNode = FocusNode(); final flowyContainerFocusNode = FocusNode();
final ValueNotifier<bool> showLoading = ValueNotifier(false);
@override @override
void dispose() { void dispose() {
flowyContainerFocusNode.dispose(); flowyContainerFocusNode.dispose();
showLoading.dispose();
super.dispose(); super.dispose();
} }
@ -87,37 +91,52 @@ class _ImportPanelState extends State<ImportPanel> {
FlowyOverlay.pop(context); FlowyOverlay.pop(context);
} }
}, },
child: FlowyContainer( child: Stack(
Theme.of(context).colorScheme.surface, children: [
height: height, FlowyContainer(
width: width, Theme.of(context).colorScheme.surface,
child: GridView.count( height: height,
childAspectRatio: 1 / .2, width: width,
crossAxisCount: 2, child: GridView.count(
children: ImportType.values childAspectRatio: 1 / .2,
.where((element) => element.enableOnRelease) crossAxisCount: 2,
.map( children: ImportType.values
(e) => Card( .where((element) => element.enableOnRelease)
child: FlowyButton( .map(
leftIcon: e.icon(context), (e) => Card(
leftIconSize: const Size.square(20), child: FlowyButton(
text: FlowyText.medium( leftIcon: e.icon(context),
e.toString(), leftIconSize: const Size.square(20),
fontSize: 15, text: FlowyText.medium(
overflow: TextOverflow.ellipsis, e.toString(),
color: Theme.of(context).colorScheme.tertiary, fontSize: 15,
overflow: TextOverflow.ellipsis,
color: Theme.of(context).colorScheme.tertiary,
),
onTap: () async {
await _importFile(widget.parentViewId, e);
if (context.mounted) {
FlowyOverlay.pop(context);
}
},
),
), ),
onTap: () async { )
await _importFile(widget.parentViewId, e); .toList(),
if (context.mounted) { ),
FlowyOverlay.pop(context); ),
} ValueListenableBuilder(
}, valueListenable: showLoading,
), builder: (context, showLoading, child) {
), if (!showLoading) {
) return const SizedBox.shrink();
.toList(), }
), return const Center(
child: CircularProgressIndicator(),
);
},
),
],
), ),
); );
} }
@ -132,6 +151,8 @@ class _ImportPanelState extends State<ImportPanel> {
return; return;
} }
showLoading.value = true;
for (final file in result.files) { for (final file in result.files) {
final path = file.path; final path = file.path;
if (path == null) { if (path == null) {
@ -145,43 +166,60 @@ class _ImportPanelState extends State<ImportPanel> {
case ImportType.historyDocument: case ImportType.historyDocument:
final bytes = _documentDataFrom(importType, data); final bytes = _documentDataFrom(importType, data);
if (bytes != null) { if (bytes != null) {
await ImportBackendService.importData( final result = await ImportBackendService.importData(
bytes, bytes,
name, name,
parentViewId, parentViewId,
ImportTypePB.HistoryDocument, ImportTypePB.HistoryDocument,
); );
result.onFailure((error) {
showSnackBarMessage(context, error.msg);
Log.error('Failed to import markdown $error');
});
} }
break; break;
case ImportType.historyDatabase: case ImportType.historyDatabase:
await ImportBackendService.importData( final result = await ImportBackendService.importData(
utf8.encode(data), utf8.encode(data),
name, name,
parentViewId, parentViewId,
ImportTypePB.HistoryDatabase, ImportTypePB.HistoryDatabase,
); );
result.onFailure((error) {
showSnackBarMessage(context, error.msg);
Log.error('Failed to import history database $error');
});
break; break;
case ImportType.databaseRawData: case ImportType.databaseRawData:
await ImportBackendService.importData( final result = await ImportBackendService.importData(
utf8.encode(data), utf8.encode(data),
name, name,
parentViewId, parentViewId,
ImportTypePB.RawDatabase, ImportTypePB.RawDatabase,
); );
result.onFailure((error) {
showSnackBarMessage(context, error.msg);
Log.error('Failed to import database raw data $error');
});
break; break;
case ImportType.databaseCSV: case ImportType.databaseCSV:
await ImportBackendService.importData( final result = await ImportBackendService.importData(
utf8.encode(data), utf8.encode(data),
name, name,
parentViewId, parentViewId,
ImportTypePB.CSV, ImportTypePB.CSV,
); );
result.onFailure((error) {
showSnackBarMessage(context, error.msg);
Log.error('Failed to import CSV $error');
});
break; break;
default: default:
assert(false, 'Unsupported Type $importType'); assert(false, 'Unsupported Type $importType');
} }
} }
showLoading.value = false;
widget.importCallback(importType, '', null); widget.importCallback(importType, '', null);
} }
} }

View File

@ -55,6 +55,7 @@ class SidebarNewPageButton extends StatelessWidget {
SpaceEvent.createPage( SpaceEvent.createPage(
name: viewName, name: viewName,
index: 0, index: 0,
layout: ViewLayoutPB.Document,
), ),
); );
} else { } else {

View File

@ -6,7 +6,6 @@ import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
import 'package:appflowy/workspace/presentation/home/hotkeys.dart'; import 'package:appflowy/workspace/presentation/home/hotkeys.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart'; import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/favorites/favorite_folder.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/favorites/favorite_folder.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/shared/rename_view_dialog.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/create_space_popup.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/create_space_popup.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/shared_widget.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/shared_widget.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/sidebar_space_header.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/sidebar_space_header.dart';
@ -103,7 +102,11 @@ class _SpaceState extends State<_Space> {
SidebarSpaceHeader( SidebarSpaceHeader(
isExpanded: state.isExpanded, isExpanded: state.isExpanded,
space: currentSpace, space: currentSpace,
onAdded: () => _showCreatePagePopup(context, currentSpace), onAdded: (layout) => _showCreatePagePopup(
context,
currentSpace,
layout,
),
onCreateNewSpace: () => _showCreateSpaceDialog(context), onCreateNewSpace: () => _showCreateSpaceDialog(context),
onCollapseAllPages: () => isExpandedNotifier.value = true, onCollapseAllPages: () => isExpandedNotifier.value = true,
), ),
@ -150,23 +153,20 @@ class _SpaceState extends State<_Space> {
); );
} }
void _showCreatePagePopup(BuildContext context, ViewPB space) { void _showCreatePagePopup(
createViewAndShowRenameDialogIfNeeded( BuildContext context,
context, ViewPB space,
LocaleKeys.newPageText.tr(), ViewLayoutPB layout,
(viewName, _) { ) {
if (viewName.isNotEmpty) { context.read<SpaceBloc>().add(
context.read<SpaceBloc>().add( SpaceEvent.createPage(
SpaceEvent.createPage( name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
name: viewName, layout: layout,
index: 0, index: 0,
), ),
); );
context.read<SpaceBloc>().add(SpaceEvent.expand(space, true)); context.read<SpaceBloc>().add(SpaceEvent.expand(space, true));
}
},
);
} }
void _switchToNextSpace() { void _switchToNextSpace() {

View File

@ -1,6 +1,5 @@
import 'dart:io'; import 'dart:io';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/util/theme_extension.dart'; import 'package:appflowy/util/theme_extension.dart';
import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart'; import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart';
@ -9,6 +8,7 @@ import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/manage_s
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/shared_widget.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/shared_widget.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_action_type.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_action_type.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_more_popup.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_more_popup.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/view_add_button.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
@ -29,7 +29,7 @@ class SidebarSpaceHeader extends StatefulWidget {
}); });
final ViewPB space; final ViewPB space;
final VoidCallback onAdded; final void Function(ViewLayoutPB layout) onAdded;
final VoidCallback onCreateNewSpace; final VoidCallback onCreateNewSpace;
final VoidCallback onCollapseAllPages; final VoidCallback onCollapseAllPages;
final bool isExpanded; final bool isExpanded;
@ -87,7 +87,7 @@ class _SidebarSpaceHeaderState extends State<SidebarSpaceHeader> {
left: 3, left: 3,
top: 3, top: 3,
bottom: 3, bottom: 3,
right: isHovered.value || onEditing ? 66 : 0, right: isHovered.value || onEditing ? 88 : 0,
child: SpacePopup( child: SpacePopup(
child: _buildChild(), child: _buildChild(),
), ),
@ -144,12 +144,20 @@ class _SidebarSpaceHeaderState extends State<SidebarSpaceHeader> {
onAction: _onAction, onAction: _onAction,
), ),
const HSpace(8.0), const HSpace(8.0),
FlowyIconButton( ViewAddButton(
width: 24, parentViewId: widget.space.id,
tooltipText: LocaleKeys.sideBar_addAPage.tr(), onEditing: (_) {},
iconPadding: const EdgeInsets.all(4.0), onSelected: (
icon: const FlowySvg(FlowySvgs.view_item_add_s), pluginBuilder,
onPressed: widget.onAdded, name,
initialDataBytes,
openAfterCreated,
createNewView,
) {
if (createNewView) {
widget.onAdded(pluginBuilder.layoutType!);
}
},
), ),
], ],
), ),