diff --git a/frontend/appflowy_flutter/assets/translations/en.json b/frontend/appflowy_flutter/assets/translations/en.json index 8518f7a4e2..f4da7002e9 100644 --- a/frontend/appflowy_flutter/assets/translations/en.json +++ b/frontend/appflowy_flutter/assets/translations/en.json @@ -231,7 +231,7 @@ "addFilter": "Add Filter", "deleteFilter": "Delete filter", "filterBy": "Filter by...", - "typeAValue": "Type a value...", + "typeAValue": "Type a value...", "layout": "Layout", "databaseLayout": "Layout" }, @@ -414,7 +414,13 @@ }, "optionAction": { "click": "Click", - "toOpenMenu": " to open menu" + "toOpenMenu": " to open menu", + "delete": "Delete", + "duplicate": "Duplicate", + "turnInto": "Turn into", + "moveUp": "Move up", + "moveDown": "Move down", + "color": "Color" } } }, diff --git a/frontend/appflowy_flutter/integration_test/document_test.dart b/frontend/appflowy_flutter/integration_test/document_test.dart new file mode 100644 index 0000000000..93e753a9a1 --- /dev/null +++ b/frontend/appflowy_flutter/integration_test/document_test.dart @@ -0,0 +1,90 @@ +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +import 'util/util.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('document', () { + const location = 'appflowy'; + + setUp(() async { + await TestFolder.cleanTestLocation(location); + await TestFolder.setTestLocation(location); + }); + + tearDown(() async { + await TestFolder.cleanTestLocation(location); + }); + + tearDownAll(() async { + await TestFolder.cleanTestLocation(null); + }); + + testWidgets('create a new document when launching app in first time', + (tester) async { + await tester.initializeAppFlowy(); + + await tester.tapGoButton(); + + // create a new document + await tester.tapAddButton(); + await tester.tapCreateDocumentButton(); + await tester.pumpAndSettle(); + + // expect to see a new document + tester.expectToSeePageName( + LocaleKeys.menuAppHeader_defaultNewPageName.tr(), + ); + // and with one paragraph block + expect(find.byType(TextBlockComponentWidget), findsOneWidget); + }); + + testWidgets('delete the readme page and restore it', (tester) async { + await tester.initializeAppFlowy(); + + await tester.tapGoButton(); + + // delete the readme page + await tester.hoverOnPageName(readme); + await tester.tapDeletePageButton(); + + // the banner should show up and the readme page should be gone + tester.expectToSeeDocumentBanner(); + tester.expectNotToSeePageName(readme); + + // restore the readme page + await tester.tapRestoreButton(); + + // the banner should be gone and the readme page should be back + tester.expectNotToSeeDocumentBanner(); + tester.expectToSeePageName(readme); + }); + + testWidgets('delete the readme page and delete it permanently', + (tester) async { + await tester.initializeAppFlowy(); + + await tester.tapGoButton(); + + // delete the readme page + await tester.hoverOnPageName(readme); + await tester.tapDeletePageButton(); + + // the banner should show up and the readme page should be gone + tester.expectToSeeDocumentBanner(); + tester.expectNotToSeePageName(readme); + + // delete the page permanently + await tester.tapDeletePermanentlyButton(); + + // the banner should be gone and the readme page should be gone + tester.expectNotToSeeDocumentBanner(); + tester.expectNotToSeePageName(readme); + }); + }); +} diff --git a/frontend/appflowy_flutter/integration_test/runner.dart b/frontend/appflowy_flutter/integration_test/runner.dart index b848ce1fc3..41a78d4eef 100644 --- a/frontend/appflowy_flutter/integration_test/runner.dart +++ b/frontend/appflowy_flutter/integration_test/runner.dart @@ -4,6 +4,7 @@ import 'board_test.dart' as board_test; import 'switch_folder_test.dart' as switch_folder_test; import 'empty_document_test.dart' as empty_document_test; import 'open_ai_smart_menu_test.dart' as smart_menu_test; +import 'document_test.dart' as document_test; /// The main task runner for all integration tests in AppFlowy. /// @@ -18,4 +19,5 @@ void main() { board_test.main(); empty_document_test.main(); smart_menu_test.main(); + document_test.main(); } diff --git a/frontend/appflowy_flutter/integration_test/switch_folder_test.dart b/frontend/appflowy_flutter/integration_test/switch_folder_test.dart index 6dcd41b081..40862b5c0d 100644 --- a/frontend/appflowy_flutter/integration_test/switch_folder_test.dart +++ b/frontend/appflowy_flutter/integration_test/switch_folder_test.dart @@ -51,7 +51,7 @@ void main() { // Click create button again await tester.tapCreateButton(); - await tester.expectToSeeWelcomePage(); + tester.expectToSeeWelcomePage(); await TestFolder.cleanTestLocation(folderName); }); @@ -69,7 +69,7 @@ void main() { await tester.tapOpenFolderButton(); await tester.wait(1000); - await tester.expectToSeeWelcomePage(); + tester.expectToSeeWelcomePage(); await TestFolder.cleanTestLocation(folderName); }); @@ -84,7 +84,7 @@ void main() { await tester.initializeAppFlowy(); await tester.tapGoButton(); - await tester.expectToSeeWelcomePage(); + tester.expectToSeeWelcomePage(); // switch to user B { @@ -99,7 +99,7 @@ void main() { await mockGetDirectoryPath(userB); await tester.tapCustomLocationButton(); await tester.pumpAndSettle(); - await tester.expectToSeeWelcomePage(); + tester.expectToSeeWelcomePage(); } // switch to the userA @@ -116,7 +116,7 @@ void main() { await tester.tapCustomLocationButton(); await tester.pumpAndSettle(); - await tester.expectToSeeWelcomePage(); + tester.expectToSeeWelcomePage(); expect(find.textContaining(userA), findsOneWidget); } @@ -131,7 +131,7 @@ void main() { await tester.tapCustomLocationButton(); await tester.pumpAndSettle(); - await tester.expectToSeeWelcomePage(); + tester.expectToSeeWelcomePage(); expect(find.textContaining(userB), findsOneWidget); } @@ -145,7 +145,7 @@ void main() { await tester.tapGoButton(); // home and readme document - await tester.expectToSeeWelcomePage(); + tester.expectToSeeWelcomePage(); // open settings and restore the location await tester.openSettings(); diff --git a/frontend/appflowy_flutter/integration_test/util/base.dart b/frontend/appflowy_flutter/integration_test/util/base.dart index ec7dd07fa0..be426a61c3 100644 --- a/frontend/appflowy_flutter/integration_test/util/base.dart +++ b/frontend/appflowy_flutter/integration_test/util/base.dart @@ -91,7 +91,7 @@ extension AppFlowyTestBase on WidgetTester { String tr, { int milliseconds = 500, }) async { - final button = find.text(tr); + final button = find.text(tr, findRichText: true); await tapButton( button, milliseconds: milliseconds, diff --git a/frontend/appflowy_flutter/integration_test/util/launch.dart b/frontend/appflowy_flutter/integration_test/util/launch.dart index 5f2cc6ba09..7d2f9cdb33 100644 --- a/frontend/appflowy_flutter/integration_test/util/launch.dart +++ b/frontend/appflowy_flutter/integration_test/util/launch.dart @@ -1,23 +1,100 @@ +import 'dart:ui'; + import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/document/presentation/banner.dart'; +import 'package:appflowy/user/presentation/skip_log_in_screen.dart'; import 'package:appflowy/workspace/presentation/home/home_stack.dart'; +import 'package:appflowy/workspace/presentation/home/menu/app/header/add_button.dart'; +import 'package:appflowy/workspace/presentation/home/menu/app/section/item.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter_test/flutter_test.dart'; import 'base.dart'; +const String readme = 'Read me'; + extension AppFlowyLaunch on WidgetTester { Future<void> tapGoButton() async { - await tapButtonWithName(LocaleKeys.letsGoButtonText.tr()); - return; + final goButton = find.byType(GoButton); + await tapButton(goButton); } Future<void> tapCreateButton() async { await tapButtonWithName(LocaleKeys.settings_files_create.tr()); - return; } - Future<void> expectToSeeWelcomePage() async { + void expectToSeeWelcomePage() { expect(find.byType(HomeStack), findsOneWidget); expect(find.textContaining('Read me'), findsNWidgets(2)); } + + Future<void> tapAddButton() async { + final addButton = find.byType(AddButton); + await tapButton(addButton); + } + + Future<void> tapCreateDocumentButton() async { + await tapButtonWithName(LocaleKeys.document_menuName.tr()); + } + + Finder findPageName(String name) { + return find.byWidgetPredicate( + (widget) => widget is ViewSectionItem && widget.view.name == name, + ); + } + + void expectToSeePageName(String name) { + final pageName = findPageName(name); + expect(pageName, findsOneWidget); + } + + void expectNotToSeePageName(String name) { + final pageName = findPageName(name); + expect(pageName, findsNothing); + } + + Future<void> hoverOnPageName(String name) async { + final pageName = find.byWidgetPredicate( + (widget) => widget is ViewSectionItem && widget.view.name == name, + ); + + final gesture = await createGesture(kind: PointerDeviceKind.mouse); + await gesture.addPointer(location: Offset.zero); + addTearDown(gesture.removePointer); + await pump(); + await gesture.moveTo(getCenter(pageName)); + await pumpAndSettle(); + } + + Future<void> tapPageOptionButton() async { + final optionButton = find.byType(ViewDisclosureButton); + await tapButton(optionButton); + } + + Future<void> tapDeletePageButton() async { + await tapPageOptionButton(); + await tapButtonWithName(ViewDisclosureAction.delete.name); + } + + void expectToSeeDocumentBanner() { + expect(find.byType(DocumentBanner), findsOneWidget); + } + + void expectNotToSeeDocumentBanner() { + expect(find.byType(DocumentBanner), findsNothing); + } + + Future<void> tapRestoreButton() async { + final restoreButton = find.textContaining( + LocaleKeys.deletePagePrompt_restore.tr(), + ); + await tapButton(restoreButton); + } + + Future<void> tapDeletePermanentlyButton() async { + final restoreButton = find.textContaining( + LocaleKeys.deletePagePrompt_deletePermanent.tr(), + ); + await tapButton(restoreButton); + } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/application/doc_bloc.dart b/frontend/appflowy_flutter/lib/plugins/document/application/doc_bloc.dart index 7518548309..8f2b77b591 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/application/doc_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/application/doc_bloc.dart @@ -66,7 +66,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> { await _subscribe(state); emit(state); }, - deleted: (Deleted value) async { + moveToTrash: (MoveToTrash value) async { emit(state.copyWith(isDeleted: true)); }, restore: (Restore value) async { @@ -74,11 +74,13 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> { }, deletePermanently: (DeletePermanently value) async { final result = await _trashService.deleteViews([view.id]); - emit(state.copyWith(forceClose: result.swap().isLeft())); + final forceClose = result.fold((l) => true, (r) => false); + emit(state.copyWith(forceClose: forceClose)); }, restorePage: (RestorePage value) async { final result = await _trashService.putback(view.id); - emit(state.copyWith(isDeleted: result.swap().isRight())); + final isDeleted = result.fold((l) => false, (r) => true); + emit(state.copyWith(isDeleted: isDeleted)); }, ); } @@ -98,8 +100,12 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> { /// subscribe to the view(document page) change void _onViewChanged() { _viewListener.start( - onViewDeleted: (r) => - r.swap().map((r) => add(const DocumentEvent.deleted())), + onViewMoveToTrash: (r) { + r.swap().map((r) => add(const DocumentEvent.moveToTrash())); + }, + onViewDeleted: (r) { + r.swap().map((r) => add(const DocumentEvent.moveToTrash())); + }, onViewRestored: (r) => r.swap().map((r) => add(const DocumentEvent.restore())), ); @@ -161,7 +167,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> { @freezed class DocumentEvent with _$DocumentEvent { const factory DocumentEvent.initial() = Initial; - const factory DocumentEvent.deleted() = Deleted; + const factory DocumentEvent.moveToTrash() = MoveToTrash; const factory DocumentEvent.restore() = Restore; const factory DocumentEvent.restorePage() = RestorePage; const factory DocumentEvent.deletePermanently() = DeletePermanently; diff --git a/frontend/appflowy_flutter/lib/plugins/document/application/document_data_pb_extension.dart b/frontend/appflowy_flutter/lib/plugins/document/application/document_data_pb_extension.dart index 67767939da..36b852b11b 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/application/document_data_pb_extension.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/application/document_data_pb_extension.dart @@ -88,35 +88,22 @@ extension DocumentDataPBFromTo on DocumentDataPB { } } -class _BackendKeys { - const _BackendKeys._(); - - static const String text = 'text'; -} - extension BlockToNode on BlockPB { Node toNode({ Iterable<Node>? children, }) { return Node( id: id, - type: _typeAdapter(ty), + type: ty, attributes: _dataAdapter(ty, data), children: children ?? [], ); } - String _typeAdapter(String ty) { - final adapter = { - _BackendKeys.text: ParagraphBlockKeys.type, - }; - return adapter[ty] ?? ty; - } - Attributes _dataAdapter(String ty, String data) { final map = Attributes.from(jsonDecode(data)); final adapter = { - 'text': (Attributes map) => map + ParagraphBlockKeys.type: (Attributes map) => map ..putIfAbsent( 'delta', () => Delta().toJson(), @@ -134,7 +121,7 @@ extension NodeToBlock on Node { assert(id.isNotEmpty); final block = BlockPB.create() ..id = id - ..ty = _typeAdapter(type) + ..ty = type ..data = _dataAdapter(type, attributes); if (childrenId != null && childrenId.isNotEmpty) { block.childrenId = childrenId; @@ -145,13 +132,6 @@ extension NodeToBlock on Node { return block; } - String _typeAdapter(String type) { - final adapter = { - 'paragraph': 'text', - }; - return adapter[type] ?? type; - } - String _dataAdapter(String type, Attributes attributes) { return jsonEncode(attributes); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart index f2e8f10afa..85f432cfcb 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart @@ -112,6 +112,7 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> { child: ConstrainedBox( constraints: const BoxConstraints( maxWidth: double.infinity, + maxHeight: double.infinity, ), child: FloatingToolbar( style: styleCustomizer.floatingToolbarStyleBuilder(), @@ -225,7 +226,7 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> { ]; final colorAction = [ - // OptionAction.divider, + OptionAction.divider, OptionAction.color, ]; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option_action.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option_action.dart index 403b7cd709..2e15776b59 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option_action.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option_action.dart @@ -1,9 +1,11 @@ +import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/type_option/select_option_editor.dart'; import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart'; import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:appflowy_editor/appflowy_editor.dart' hide FlowySvg; import 'package:appflowy_popover/appflowy_popover.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/image.dart'; import 'package:flutter/material.dart'; import 'package:styled_widget/styled_widget.dart'; @@ -45,7 +47,7 @@ class ColorOptionAction extends PopoverActionCell { @override String get name { - return 'Color'; // todo: l10n + return LocaleKeys.toolbar_color.tr(); } @override @@ -55,7 +57,6 @@ class ColorOptionAction extends PopoverActionCell { if (selection == null) { return const SizedBox.shrink(); } - // TODO: should we support multiple selection? final node = editorState.getNodeAtPath(selection.start.path); if (node == null) { return const SizedBox.shrink(); @@ -70,6 +71,8 @@ class ColorOptionAction extends PopoverActionCell { return SelectOptionColorList( selectedColor: selectedColor, onSelectedColor: (color) { + controller.close(); + final nodes = editorState.getNodesInSelection(selection); final transaction = editorState.transaction; for (final node in nodes) { @@ -79,8 +82,6 @@ class ColorOptionAction extends PopoverActionCell { }); } editorState.apply(transaction); - - controller.close(); }, ); }; @@ -141,22 +142,22 @@ class OptionActionWrapper extends ActionCell { switch (inner) { // TODO: l10n case OptionAction.delete: - description = 'Delete'; + description = LocaleKeys.document_plugins_optionAction_delete.tr(); break; case OptionAction.duplicate: - description = 'Duplicate'; + description = LocaleKeys.document_plugins_optionAction_duplicate.tr(); break; case OptionAction.turnInto: - description = 'Turn into'; + description = LocaleKeys.document_plugins_optionAction_turnInto.tr(); break; case OptionAction.moveUp: - description = 'Move up'; + description = LocaleKeys.document_plugins_optionAction_moveUp.tr(); break; case OptionAction.moveDown: - description = 'Move down'; + description = LocaleKeys.document_plugins_optionAction_moveDown.tr(); break; case OptionAction.color: - description = 'Color'; + description = LocaleKeys.document_plugins_optionAction_color.tr(); break; case OptionAction.divider: throw UnimplementedError(); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/built_in_page_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/built_in_page_widget.dart index 0d23510182..8fef6b674a 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/built_in_page_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/built_in_page_widget.dart @@ -69,6 +69,10 @@ class _BuiltInPageWidgetState extends State<BuiltInPageWidget> { return _build(context, page); } if (snapshot.connectionState == ConnectionState.done) { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + // just delete the page if it is not found + _deletePage(); + }); return const Center( child: FlowyText('Cannot load the page'), ); @@ -174,6 +178,12 @@ class _BuiltInPageWidgetState extends State<BuiltInPageWidget> { ), ); } + + Future<void> _deletePage() async { + final transaction = widget.editorState.transaction; + transaction.deleteNode(widget.node); + widget.editorState.apply(transaction); + } } enum _ActionType { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/link_to_page_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/link_to_page_widget.dart index 29168fb3d8..55d633260d 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/link_to_page_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/link_to_page_widget.dart @@ -23,7 +23,8 @@ void showLinkToPageMenu( final top = alignment == Alignment.bottomLeft ? offset.dy : null; final bottom = alignment == Alignment.topLeft ? offset.dy : null; - final linkToPageMenuEntry = FullScreenOverlayEntry( + late OverlayEntry linkToPageMenuEntry; + linkToPageMenuEntry = FullScreenOverlayEntry( top: top, bottom: bottom, left: offset.dx, @@ -35,6 +36,7 @@ void showLinkToPageMenu( hintText: pageType.toHintText(), onSelected: (appPB, viewPB) { editorState.insertPage(appPB, viewPB); + linkToPageMenuEntry.remove(); }, ), ), diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/cover/change_cover_popover.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/cover/change_cover_popover.dart index 947a8de00e..1b8ba14620 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/cover/change_cover_popover.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/cover/change_cover_popover.dart @@ -465,17 +465,11 @@ class _CoverColorPickerState extends State<CoverColorPicker> { }, platform: TargetPlatform.windows, ), - child: ListView.builder( - controller: scrollController, - shrinkWrap: true, - itemCount: widget.backgroundColorOptions.length, - scrollDirection: Axis.horizontal, - itemBuilder: (context, index) { - return _buildColorItems( - widget.backgroundColorOptions, - widget.selectedBackgroundColorHex, - ); - }, + child: SingleChildScrollView( + child: _buildColorItems( + widget.backgroundColorOptions, + widget.selectedBackgroundColorHex, + ), ), ), ); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/cover/cover_node_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/cover/cover_node_widget.dart index 992d0b28ee..8962a0987f 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/cover/cover_node_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/cover/cover_node_widget.dart @@ -303,10 +303,11 @@ class _CoverImageState extends State<_CoverImage> { CoverSelectionType get selectionType => CoverSelectionType.fromString( widget.node.attributes[CoverBlockKeys.selectionType], ); - Color get color => Color( - int.tryParse(widget.node.attributes[CoverBlockKeys.selection]) ?? - 0xFFFFFFFF, - ); + Color get color { + final hex = widget.node.attributes[CoverBlockKeys.selection] as String?; + return hex?.toColor() ?? Colors.white; + } + bool get hasIcon => widget.node.attributes[CoverBlockKeys.iconSelection] == null ? false diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/divider/divider_character_shortcut_event.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/divider/divider_character_shortcut_event.dart index 3fcb6c47a9..d0987eccf1 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/divider/divider_character_shortcut_event.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/divider/divider_character_shortcut_event.dart @@ -35,7 +35,7 @@ CharacterShortcutEventHandler _convertMinusesToDividerHandler = ..insertNode(path, dividerNode()) ..insertNode(path, paragraphNode()) ..deleteNode(node) - ..afterSelection = Selection.collapse(path.next, 0); + ..afterSelection = Selection.collapse(path, 0); editorState.apply(transaction); return true; }; @@ -61,7 +61,9 @@ SelectionMenuItem dividerMenuItem = SelectionMenuItem( } final insertedPath = delta.isEmpty ? path : path.next; final transaction = editorState.transaction - ..insertNode(insertedPath, dividerNode()); + ..insertNode(insertedPath, dividerNode()) + ..insertNode(insertedPath, paragraphNode()) + ..afterSelection = Selection.collapse(insertedPath.next, 0); editorState.apply(transaction); }, ); diff --git a/frontend/appflowy_flutter/lib/user/application/user_service.dart b/frontend/appflowy_flutter/lib/user/application/user_service.dart index 60ec1e26f8..c14ccdd5c3 100644 --- a/frontend/appflowy_flutter/lib/user/application/user_service.dart +++ b/frontend/appflowy_flutter/lib/user/application/user_service.dart @@ -70,7 +70,7 @@ class UserBackendService { Future<Either<List<WorkspacePB>, FlowyError>> getWorkspaces() { final request = WorkspaceIdPB.create(); - return FolderEventReadWorkspaces(request).send().then((result) { + return FolderEventReadAllWorkspaces(request).send().then((result) { return result.fold( (workspaces) => left(workspaces.items), (error) => right(error), diff --git a/frontend/appflowy_flutter/lib/user/presentation/router.dart b/frontend/appflowy_flutter/lib/user/presentation/router.dart index d0845d41d7..8546172b36 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/router.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/router.dart @@ -50,7 +50,7 @@ class AuthRouter { BuildContext context, UserProfilePB userProfile, ) async { - final result = await FolderEventReadCurrentWorkspace().send(); + final result = await FolderEventGetCurrentWorkspace().send(); result.fold( (workspaceSettingPB) => pushHomeScreenWithWorkSpace( context, @@ -75,7 +75,7 @@ class SplashRoute { ), ); - FolderEventReadCurrentWorkspace().send().then((result) { + FolderEventGetCurrentWorkspace().send().then((result) { result.fold( (workspaceSettingPB) => pushHomeScreen(context, userProfile, workspaceSettingPB), diff --git a/frontend/appflowy_flutter/lib/user/presentation/skip_log_in_screen.dart b/frontend/appflowy_flutter/lib/user/presentation/skip_log_in_screen.dart index 7b37e7d226..bb04f39ec0 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/skip_log_in_screen.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/skip_log_in_screen.dart @@ -126,7 +126,7 @@ class _SkipLogInScreenState extends State<SkipLogInScreen> { Log.error(error); }, (user) { - FolderEventReadCurrentWorkspace().send().then((result) { + FolderEventGetCurrentWorkspace().send().then((result) { _openCurrentWorkspace(context, user, result); }); }, diff --git a/frontend/appflowy_flutter/lib/user/presentation/splash_screen.dart b/frontend/appflowy_flutter/lib/user/presentation/splash_screen.dart index aefcada89d..3c59bab1ec 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/splash_screen.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/splash_screen.dart @@ -70,7 +70,7 @@ class SplashScreen extends StatelessWidget { Authenticated authenticated, ) async { final userProfile = authenticated.userProfile; - final result = await FolderEventReadCurrentWorkspace().send(); + final result = await FolderEventGetCurrentWorkspace().send(); result.fold( (workspaceSetting) { getIt<SplashRoute>().pushHomeScreen( diff --git a/frontend/appflowy_flutter/lib/workspace/application/view/view_service.dart b/frontend/appflowy_flutter/lib/workspace/application/view/view_service.dart index f8e0ae8962..1e8849673e 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/view/view_service.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/view/view_service.dart @@ -122,7 +122,7 @@ class ViewBackendService { ViewLayoutPB layoutType, ) async { final result = <(ViewPB, List<ViewPB>)>[]; - return FolderEventReadCurrentWorkspace().send().then((value) async { + return FolderEventGetCurrentWorkspace().send().then((value) async { final workspaces = value.getLeftOrNull<WorkspaceSettingPB>(); if (workspaces != null) { final views = workspaces.workspace.views; diff --git a/frontend/appflowy_flutter/lib/workspace/application/workspace/workspace_service.dart b/frontend/appflowy_flutter/lib/workspace/application/workspace/workspace_service.dart index 525302af34..47b45141f6 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/workspace/workspace_service.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/workspace/workspace_service.dart @@ -35,7 +35,7 @@ class WorkspaceService { Future<Either<WorkspacePB, FlowyError>> getWorkspace() { final payload = WorkspaceIdPB.create()..value = workspaceId; - return FolderEventReadWorkspaces(payload).send().then((result) { + return FolderEventReadAllWorkspaces(payload).send().then((result) { return result.fold( (workspaces) { assert(workspaces.items.length == 1); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_exporter_widget.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_exporter_widget.dart index da15ee74c8..46cda916e3 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_exporter_widget.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_exporter_widget.dart @@ -35,7 +35,7 @@ class _FileExporterWidgetState extends State<FileExporterWidget> { @override Widget build(BuildContext context) { return FutureBuilder<dartz.Either<WorkspaceSettingPB, FlowyError>>( - future: FolderEventReadCurrentWorkspace().send(), + future: FolderEventGetCurrentWorkspace().send(), builder: (context, snapshot) { if (snapshot.hasData && snapshot.connectionState == ConnectionState.done) { diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/pop_up_action.dart b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/pop_up_action.dart index 761877f239..2d1d699a2d 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/pop_up_action.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/pop_up_action.dart @@ -179,7 +179,6 @@ class PopoverActionCellWidget<T extends PopoverAction> extends StatelessWidget { final rightIcon = actionCell.rightIcon(Theme.of(context).colorScheme.onSurface); return AppFlowyPopover( - // mutex: mutex, controller: popoverController, asBarrier: true, popupBuilder: (context) => actionCell.builder( diff --git a/frontend/appflowy_flutter/test/bloc_test/home_test/app_bloc_test.dart b/frontend/appflowy_flutter/test/bloc_test/home_test/app_bloc_test.dart index 878bf71d23..b99f532e25 100644 --- a/frontend/appflowy_flutter/test/bloc_test/home_test/app_bloc_test.dart +++ b/frontend/appflowy_flutter/test/bloc_test/home_test/app_bloc_test.dart @@ -127,7 +127,7 @@ void main() { ..add(const DocumentEvent.initial()); await blocResponseFuture(); - final workspaceSetting = await FolderEventReadCurrentWorkspace() + final workspaceSetting = await FolderEventGetCurrentWorkspace() .send() .then((result) => result.fold((l) => l, (r) => throw Exception())); workspaceSetting.latestView.id == document1.id; @@ -148,7 +148,7 @@ void main() { final grid = bloc.state.latestCreatedView; assert(grid!.name == "grid 2"); - var workspaceSetting = await FolderEventReadCurrentWorkspace() + var workspaceSetting = await FolderEventGetCurrentWorkspace() .send() .then((result) => result.fold((l) => l, (r) => throw Exception())); workspaceSetting.latestView.id == grid!.id; @@ -159,7 +159,7 @@ void main() { ..add(const DocumentEvent.initial()); await blocResponseFuture(); - workspaceSetting = await FolderEventReadCurrentWorkspace() + workspaceSetting = await FolderEventGetCurrentWorkspace() .send() .then((result) => result.fold((l) => l, (r) => throw Exception())); workspaceSetting.latestView.id == document.id; diff --git a/frontend/appflowy_flutter/test/bloc_test/home_test/home_bloc_test.dart b/frontend/appflowy_flutter/test/bloc_test/home_test/home_bloc_test.dart index e072466de7..6360e6f9c3 100644 --- a/frontend/appflowy_flutter/test/bloc_test/home_test/home_bloc_test.dart +++ b/frontend/appflowy_flutter/test/bloc_test/home_test/home_bloc_test.dart @@ -15,7 +15,7 @@ void main() { }); test('initi home screen', () async { - final workspaceSetting = await FolderEventReadCurrentWorkspace() + final workspaceSetting = await FolderEventGetCurrentWorkspace() .send() .then((result) => result.fold((l) => l, (r) => throw Exception())); await blocResponseFuture(); @@ -28,7 +28,7 @@ void main() { }); test('open the document', () async { - final workspaceSetting = await FolderEventReadCurrentWorkspace() + final workspaceSetting = await FolderEventGetCurrentWorkspace() .send() .then((result) => result.fold((l) => l, (r) => throw Exception())); await blocResponseFuture(); diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.lock b/frontend/appflowy_tauri/src-tauri/Cargo.lock index 1becda17cb..3f9d86f3ec 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.lock +++ b/frontend/appflowy_tauri/src-tauri/Cargo.lock @@ -99,7 +99,7 @@ checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "appflowy-integrate" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" dependencies = [ "anyhow", "collab", @@ -1024,7 +1024,7 @@ dependencies = [ [[package]] name = "collab" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" dependencies = [ "anyhow", "bytes", @@ -1042,7 +1042,7 @@ dependencies = [ [[package]] name = "collab-client-ws" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" dependencies = [ "bytes", "collab-sync", @@ -1060,7 +1060,7 @@ dependencies = [ [[package]] name = "collab-database" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" dependencies = [ "anyhow", "async-trait", @@ -1086,7 +1086,7 @@ dependencies = [ [[package]] name = "collab-derive" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" dependencies = [ "proc-macro2", "quote", @@ -1098,7 +1098,7 @@ dependencies = [ [[package]] name = "collab-document" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" dependencies = [ "anyhow", "collab", @@ -1115,7 +1115,7 @@ dependencies = [ [[package]] name = "collab-folder" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" dependencies = [ "anyhow", "collab", @@ -1134,7 +1134,7 @@ dependencies = [ [[package]] name = "collab-persistence" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" dependencies = [ "bincode", "chrono", @@ -1154,7 +1154,7 @@ dependencies = [ [[package]] name = "collab-plugins" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" dependencies = [ "anyhow", "async-trait", @@ -1184,7 +1184,7 @@ dependencies = [ [[package]] name = "collab-sync" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2" dependencies = [ "bytes", "collab", diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts index 6cb486e35b..1a8104084d 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts @@ -2,7 +2,7 @@ import { currentUserActions } from '../../stores/reducers/current-user/slice'; import { useAppDispatch, useAppSelector } from '../../stores/store'; import { UserProfilePB } from '../../../services/backend/events/flowy-user'; import { AuthBackendService, UserBackendService } from '../../stores/effects/user/user_bd_svc'; -import { FolderEventReadCurrentWorkspace } from '../../../services/backend/events/flowy-folder2'; +import { FolderEventGetCurrentWorkspace } from '../../../services/backend/events/flowy-folder2'; import { WorkspaceSettingPB } from '../../../services/backend/models/flowy-folder2/workspace'; import { Log } from '../../utils/log'; @@ -90,7 +90,7 @@ export const useAuth = () => { } async function _openWorkspace() { - return FolderEventReadCurrentWorkspace(); + return FolderEventGetCurrentWorkspace(); } return { currentUser, checkUser, register, login, logout }; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/DatabaseTestHelper.ts b/frontend/appflowy_tauri/src/appflowy_app/components/tests/DatabaseTestHelper.ts index 51080322e6..8712fc8035 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/DatabaseTestHelper.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/tests/DatabaseTestHelper.ts @@ -5,7 +5,7 @@ import { ViewPB, WorkspaceSettingPB, } from '../../../services/backend'; -import { FolderEventReadCurrentWorkspace } from '../../../services/backend/events/flowy-folder2'; +import { FolderEventGetCurrentWorkspace } from '../../../services/backend/events/flowy-folder2'; import { AppBackendService } from '../../stores/effects/folder/app/app_bd_svc'; import { DatabaseController } from '../../stores/effects/database/database_controller'; import { RowInfo } from '../../stores/effects/database/row/row_cache'; @@ -31,7 +31,7 @@ import { Log } from '$app/utils/log'; // Create a database view for specific layout type // Do not use it production code. Just for testing export async function createTestDatabaseView(layout: ViewLayoutPB): Promise<ViewPB> { - const workspaceSetting: WorkspaceSettingPB = await FolderEventReadCurrentWorkspace().then((result) => result.unwrap()); + const workspaceSetting: WorkspaceSettingPB = await FolderEventGetCurrentWorkspace().then((result) => result.unwrap()); const appService = new AppBackendService(workspaceSetting.workspace.id); return await appService.createView({ name: 'New Grid', layoutType: layout }); } diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/DocumentTestHelper.ts b/frontend/appflowy_tauri/src/appflowy_app/components/tests/DocumentTestHelper.ts index 4656d06577..f43f22e5a2 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/DocumentTestHelper.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/tests/DocumentTestHelper.ts @@ -1,9 +1,9 @@ import { ViewLayoutPB, WorkspaceSettingPB } from '@/services/backend'; -import { FolderEventReadCurrentWorkspace } from '@/services/backend/events/flowy-folder2'; +import { FolderEventGetCurrentWorkspace } from '@/services/backend/events/flowy-folder2'; import { AppBackendService } from '$app/stores/effects/folder/app/app_bd_svc'; export async function createTestDocument() { - const workspaceSetting: WorkspaceSettingPB = await FolderEventReadCurrentWorkspace().then((result) => result.unwrap()); + const workspaceSetting: WorkspaceSettingPB = await FolderEventGetCurrentWorkspace().then((result) => result.unwrap()); const app = workspaceSetting.workspace.views[0]; const appService = new AppBackendService(app.id); return await appService.createView({ name: 'New Document', layoutType: ViewLayoutPB.Document }); diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/folder/workspace/workspace_bd_svc.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/folder/workspace/workspace_bd_svc.ts index 13ee9b3091..be8b1cdde5 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/folder/workspace/workspace_bd_svc.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/folder/workspace/workspace_bd_svc.ts @@ -3,7 +3,7 @@ import { FolderEventCreateView, FolderEventMoveItem, FolderEventReadWorkspaceViews, - FolderEventReadWorkspaces, + FolderEventReadAllWorkspaces, } from '@/services/backend/events/flowy-folder2'; import { CreateViewPayloadPB, @@ -35,7 +35,7 @@ export class WorkspaceBackendService { getWorkspace = () => { const payload = WorkspaceIdPB.fromObject({ value: this.workspaceId }); - return FolderEventReadWorkspaces(payload).then((result) => { + return FolderEventReadAllWorkspaces(payload).then((result) => { if (result.ok) { const workspaces = result.val.items; if (workspaces.length === 0) { diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_bd_svc.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_bd_svc.ts index 2159dc761c..e2bcc32e6f 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_bd_svc.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_bd_svc.ts @@ -21,8 +21,8 @@ import { import { FolderEventCreateWorkspace, FolderEventOpenWorkspace, - FolderEventReadCurrentWorkspace, - FolderEventReadWorkspaces, + FolderEventGetCurrentWorkspace, + FolderEventReadAllWorkspaces, } from '@/services/backend/events/flowy-folder2'; export class UserBackendService { @@ -54,7 +54,7 @@ export class UserBackendService { }; getCurrentWorkspace = async (): Promise<WorkspaceSettingPB> => { - const result = await FolderEventReadCurrentWorkspace(); + const result = await FolderEventGetCurrentWorkspace(); if (result.ok) { return result.val; } else { @@ -64,7 +64,7 @@ export class UserBackendService { getWorkspaces = () => { const payload = WorkspaceIdPB.fromObject({}); - return FolderEventReadWorkspaces(payload); + return FolderEventReadAllWorkspaces(payload); }; openWorkspace = (workspaceId: string) => { diff --git a/frontend/rust-lib/flowy-folder2/src/event_map.rs b/frontend/rust-lib/flowy-folder2/src/event_map.rs index 8ef03cac35..95c0a02eeb 100644 --- a/frontend/rust-lib/flowy-folder2/src/event_map.rs +++ b/frontend/rust-lib/flowy-folder2/src/event_map.rs @@ -12,10 +12,10 @@ pub fn init(folder: Arc<Folder2Manager>) -> AFPlugin { // Workspace .event(FolderEvent::CreateWorkspace, create_workspace_handler) .event( - FolderEvent::ReadCurrentWorkspace, - read_cur_workspace_setting_handler, + FolderEvent::GetCurrentWorkspace, + read_cur_workspace_setting_handler, ) - .event(FolderEvent::ReadWorkspaces, read_workspaces_handler) + .event(FolderEvent::ReadAllWorkspaces, read_workspaces_handler) .event(FolderEvent::OpenWorkspace, open_workspace_handler) .event(FolderEvent::ReadWorkspaceViews, read_workspace_views_handler) // View @@ -45,11 +45,11 @@ pub enum FolderEvent { /// Read the current opening workspace. Currently, we only support one workspace #[event(output = "WorkspaceSettingPB")] - ReadCurrentWorkspace = 1, + GetCurrentWorkspace = 1, - /// Return a list of workspaces that the current user can access + /// Return a list of workspaces that the current user can access. #[event(input = "WorkspaceIdPB", output = "RepeatedWorkspacePB")] - ReadWorkspaces = 2, + ReadAllWorkspaces = 2, /// Delete the workspace #[event(input = "WorkspaceIdPB")] @@ -59,7 +59,8 @@ pub enum FolderEvent { #[event(input = "WorkspaceIdPB", output = "WorkspacePB")] OpenWorkspace = 4, - /// Return a list of views that belong to this workspace. + /// Return a list of views of the current workspace. + /// Only the first level of child views are included. #[event(input = "WorkspaceIdPB", output = "RepeatedViewPB")] ReadWorkspaceViews = 5, diff --git a/frontend/rust-lib/flowy-folder2/src/manager.rs b/frontend/rust-lib/flowy-folder2/src/manager.rs index 4c204e4c88..2108f052d5 100644 --- a/frontend/rust-lib/flowy-folder2/src/manager.rs +++ b/frontend/rust-lib/flowy-folder2/src/manager.rs @@ -18,8 +18,8 @@ use lib_infra::util::timestamp; use crate::deps::{FolderCloudService, FolderUser}; use crate::entities::{ - view_pb_with_child_views, CreateViewParams, CreateWorkspaceParams, RepeatedTrashPB, - RepeatedViewPB, RepeatedWorkspacePB, UpdateViewParams, ViewPB, + view_pb_with_child_views, CreateViewParams, CreateWorkspaceParams, DeletedViewPB, + RepeatedTrashPB, RepeatedViewPB, RepeatedWorkspacePB, UpdateViewParams, ViewPB, }; use crate::notification::{ send_notification, send_workspace_notification, send_workspace_setting_notification, @@ -68,6 +68,8 @@ impl Folder2Manager { } } + /// Return a list of views of the current workspace. + /// Only the first level of child views are included. pub async fn get_current_workspace_views(&self) -> FlowyResult<Vec<ViewPB>> { let workspace_id = self .mutex_folder @@ -315,6 +317,14 @@ impl Folder2Manager { folder.set_current_view(""); } } + + // notify the parent view that the view is moved to trash + send_notification(view_id, FolderNotification::DidMoveViewToTrash) + .payload(DeletedViewPB { + view_id: view_id.to_string(), + index: None, + }) + .send(); }); Ok(()) diff --git a/frontend/rust-lib/flowy-folder2/tests/workspace/folder_test.rs b/frontend/rust-lib/flowy-folder2/tests/workspace/folder_test.rs index c68da3041c..735f09c52f 100644 --- a/frontend/rust-lib/flowy-folder2/tests/workspace/folder_test.rs +++ b/frontend/rust-lib/flowy-folder2/tests/workspace/folder_test.rs @@ -4,14 +4,14 @@ use flowy_folder2::entities::CreateWorkspacePayloadPB; use flowy_test::{event_builder::*, FlowyCoreTest}; #[tokio::test] -async fn workspace_read_all() { +async fn read_all_workspace_test() { let mut test = FolderTest::new().await; test.run_scripts(vec![ReadAllWorkspaces]).await; assert!(!test.all_workspace.is_empty()); } #[tokio::test] -async fn workspace_create() { +async fn create_workspace_test() { let mut test = FolderTest::new().await; let name = "My new workspace".to_owned(); let desc = "Daily routines".to_owned(); @@ -34,7 +34,7 @@ async fn workspace_create() { } #[tokio::test] -async fn workspace_read() { +async fn get_workspace_test() { let mut test = FolderTest::new().await; let workspace = test.workspace.clone(); @@ -47,21 +47,21 @@ async fn workspace_read() { } #[tokio::test] -async fn workspace_create_with_apps() { +async fn create_parent_view_test() { let mut test = FolderTest::new().await; test - .run_scripts(vec![CreateApp { + .run_scripts(vec![CreateParentView { name: "App".to_string(), desc: "App description".to_string(), }]) .await; let app = test.parent_view.clone(); - test.run_scripts(vec![RefreshRootView(app.id)]).await; + test.run_scripts(vec![ReloadParentView(app.id)]).await; } #[tokio::test] -async fn workspace_create_with_invalid_name() { +async fn create_parent_view_with_invalid_name() { for (name, code) in invalid_workspace_name_test_case() { let sdk = FlowyCoreTest::new(); let request = CreateWorkspacePayloadPB { @@ -84,46 +84,46 @@ async fn workspace_create_with_invalid_name() { #[tokio::test] #[should_panic] -async fn app_delete() { +async fn delete_parent_view_test() { let mut test = FolderTest::new().await; - let app = test.parent_view.clone(); + let parent_view = test.parent_view.clone(); test - .run_scripts(vec![DeleteRootView, RefreshRootView(app.id)]) + .run_scripts(vec![DeleteParentView, ReloadParentView(parent_view.id)]) .await; } #[tokio::test] -async fn app_delete_then_restore() { +async fn delete_parent_view_then_restore() { let mut test = FolderTest::new().await; test - .run_scripts(vec![RefreshRootView(test.parent_view.id.clone())]) + .run_scripts(vec![ReloadParentView(test.parent_view.id.clone())]) .await; let parent_view = test.parent_view.clone(); test .run_scripts(vec![ - DeleteRootView, + DeleteParentView, RestoreAppFromTrash, - RefreshRootView(parent_view.id.clone()), - AssertRootView(parent_view), + ReloadParentView(parent_view.id.clone()), + AssertParentView(parent_view), ]) .await; } #[tokio::test] -async fn app_update() { +async fn update_parent_view_test() { let mut test = FolderTest::new().await; - let app = test.parent_view.clone(); + let parent_view = test.parent_view.clone(); let new_name = "😁 hell world".to_owned(); - assert_ne!(app.name, new_name); + assert_ne!(parent_view.name, new_name); test .run_scripts(vec![ - UpdateRootView { + UpdateParentView { name: Some(new_name.clone()), desc: None, }, - RefreshRootView(app.id), + ReloadParentView(parent_view.id), ]) .await; assert_eq!(test.parent_view.name, new_name); @@ -145,7 +145,7 @@ async fn app_create_with_view() { desc: "Grid description".to_owned(), layout: ViewLayout::Grid, }, - RefreshRootView(app.id), + ReloadParentView(app.id), ]) .await; @@ -212,7 +212,7 @@ async fn view_delete_all() { desc: "Grid description".to_owned(), layout: ViewLayout::Grid, }, - RefreshRootView(parent_view.id.clone()), + ReloadParentView(parent_view.id.clone()), ]) .await; @@ -230,7 +230,7 @@ async fn view_delete_all() { test .run_scripts(vec![ DeleteViews(view_ids), - RefreshRootView(parent_view.id), + ReloadParentView(parent_view.id), ReadTrash, ]) .await; @@ -242,7 +242,7 @@ async fn view_delete_all() { #[tokio::test] async fn view_delete_all_permanent() { let mut test = FolderTest::new().await; - let app = test.parent_view.clone(); + let parent_view = test.parent_view.clone(); test .run_scripts(vec![ CreateView { @@ -250,7 +250,7 @@ async fn view_delete_all_permanent() { desc: "View A description".to_owned(), layout: ViewLayout::Document, }, - RefreshRootView(app.id.clone()), + ReloadParentView(parent_view.id.clone()), ]) .await; @@ -263,7 +263,7 @@ async fn view_delete_all_permanent() { test .run_scripts(vec![ DeleteViews(view_ids), - RefreshRootView(app.id), + ReloadParentView(parent_view.id), DeleteAllTrash, ReadTrash, ]) diff --git a/frontend/rust-lib/flowy-folder2/tests/workspace/script.rs b/frontend/rust-lib/flowy-folder2/tests/workspace/script.rs index dc3e3c0267..25ac6daf01 100644 --- a/frontend/rust-lib/flowy-folder2/tests/workspace/script.rs +++ b/frontend/rust-lib/flowy-folder2/tests/workspace/script.rs @@ -16,17 +16,17 @@ pub enum FolderScript { ReadWorkspace(Option<String>), // App - CreateApp { + CreateParentView { name: String, desc: String, }, - AssertRootView(ViewPB), - RefreshRootView(String), - UpdateRootView { + AssertParentView(ViewPB), + ReloadParentView(String), + UpdateParentView { name: Option<String>, desc: Option<String>, }, - DeleteRootView, + DeleteParentView, // View CreateView { @@ -107,24 +107,23 @@ impl FolderTest { let workspace = read_workspace(sdk, workspace_id).await.pop().unwrap(); self.workspace = workspace; }, - FolderScript::CreateApp { name, desc } => { + FolderScript::CreateParentView { name, desc } => { let app = create_app(sdk, &self.workspace.id, &name, &desc).await; self.parent_view = app; }, - FolderScript::AssertRootView(app) => { + FolderScript::AssertParentView(app) => { assert_eq!(self.parent_view, app, "App not equal"); }, - FolderScript::RefreshRootView(app_id) => { - let app = read_view(sdk, &app_id).await; - self.parent_view = app; + FolderScript::ReloadParentView(parent_view_id) => { + let parent_view = read_view(sdk, &parent_view_id).await; + self.parent_view = parent_view; }, - FolderScript::UpdateRootView { name, desc } => { + FolderScript::UpdateParentView { name, desc } => { update_view(sdk, &self.parent_view.id, name, desc).await; }, - FolderScript::DeleteRootView => { + FolderScript::DeleteParentView => { delete_view(sdk, vec![self.parent_view.id.clone()]).await; }, - FolderScript::CreateView { name, desc, layout } => { let view = create_view(sdk, &self.parent_view.id, &name, &desc, layout).await; self.child_view = view; @@ -189,7 +188,7 @@ pub async fn read_workspace(sdk: &FlowyCoreTest, workspace_id: Option<String>) - value: workspace_id, }; let repeated_workspace = EventBuilder::new(sdk.clone()) - .event(ReadWorkspaces) + .event(ReadAllWorkspaces) .payload(request.clone()) .async_send() .await @@ -256,7 +255,7 @@ pub async fn create_view( } pub async fn read_view(sdk: &FlowyCoreTest, view_id: &str) -> ViewPB { - let view_id: ViewIdPB = view_id.into(); + let view_id = ViewIdPB::from(view_id); EventBuilder::new(sdk.clone()) .event(ReadView) .payload(view_id) diff --git a/frontend/rust-lib/flowy-test/tests/user/supabase_test/workspace_test.rs b/frontend/rust-lib/flowy-test/tests/user/supabase_test/workspace_test.rs index 446e197ac6..5117a6923b 100644 --- a/frontend/rust-lib/flowy-test/tests/user/supabase_test/workspace_test.rs +++ b/frontend/rust-lib/flowy-test/tests/user/supabase_test/workspace_test.rs @@ -1,6 +1,6 @@ use crate::user::supabase_test::helper::get_supabase_config; use flowy_folder2::entities::WorkspaceSettingPB; -use flowy_folder2::event_map::FolderEvent::ReadCurrentWorkspace; +use flowy_folder2::event_map::FolderEvent::GetCurrentWorkspace; use flowy_test::{event_builder::EventBuilder, FlowyCoreTest}; use flowy_user::entities::{AuthTypePB, ThirdPartyAuthPB, UserProfilePB}; @@ -27,7 +27,7 @@ async fn initial_workspace_test() { .parse::<UserProfilePB>(); let workspace_settings = EventBuilder::new(test.clone()) - .event(ReadCurrentWorkspace) + .event(GetCurrentWorkspace) .async_send() .await .parse::<WorkspaceSettingPB>(); diff --git a/frontend/scripts/makefile/env.toml b/frontend/scripts/makefile/env.toml index e797e005ee..5ea2965387 100644 --- a/frontend/scripts/makefile/env.toml +++ b/frontend/scripts/makefile/env.toml @@ -1,8 +1,14 @@ [tasks.appflowy-flutter-deps-tools] -run_task = { name = ["install_flutter_prerequests","install_diesel"] } +run_task = { name = ["install_flutter_prerequests"] } [tasks.appflowy-tauri-deps-tools] -run_task = { name = ["install_tauri_prerequests","install_diesel"] } +run_task = { name = ["install_tauri_prerequests"] } + +[tasks.appflowy-flutter-dev-tools] +run_task = { name = ["appflowy-flutter-deps-tools","install_diesel"] } + +[tasks.appflowy-tauri-dev-tools] +run_task = { name = ["appflowy-tauri-deps-tools","install_diesel"] } [tasks.install_windows_deps.windows] dependencies=["check_duckscript_installation", "check_visual_studio_installation", "check_vcpkg", "install_vcpkg_sqlite", "install_rust_vcpkg_cli"] @@ -78,9 +84,9 @@ dependencies = ["check_vcpkg"] [tasks.install_targets.mac] script = """ -rustup target add x86_64-apple-ios -rustup target add x86_64-apple-darwin -rustup target add aarch64-apple-ios +#rustup target add x86_64-apple-ios +#rustup target add x86_64-apple-darwin +#rustup target add aarch64-apple-ios rustup target add aarch64-apple-darwin """