Procházet zdrojové kódy

feat: create a new grid from the document slash menu (#2051)

* feat: new grid from slash menu

* feat: add translation strings

* feat: add integration test

* fix: analyzer errors
Alex Wallen před 2 roky
rodič
revize
c6ffc0057c

+ 2 - 1
frontend/appflowy_flutter/assets/translations/en.json

@@ -338,7 +338,8 @@
         "createANewBoard": "Create a new Board"
         "createANewBoard": "Create a new Board"
       },
       },
       "grid": {
       "grid": {
-        "selectAGridToLinkTo": "Select a Grid to link to"
+        "selectAGridToLinkTo": "Select a Grid to link to",
+        "createANewGrid": "Create a new Grid"
       }
       }
     },
     },
     "plugins": {
     "plugins": {

+ 64 - 0
frontend/appflowy_flutter/integration_test/switch_folder_test.dart

@@ -228,5 +228,69 @@ void main() {
       final sidebarLabel = LocaleKeys.newPageText.tr();
       final sidebarLabel = LocaleKeys.newPageText.tr();
       expect(find.text(sidebarLabel), findsOneWidget);
       expect(find.text(sidebarLabel), findsOneWidget);
     });
     });
+
+    testWidgets('/grid shortcut creates a new grid', (tester) async {
+      const folderName = 'appflowy';
+      await TestFolder.cleanTestLocation(folderName);
+      await TestFolder.setTestLocation(folderName);
+
+      await tester.initializeAppFlowy();
+
+      // tap open button
+      await mockGetDirectoryPath(folderName);
+      await tester.tapOpenFolderButton();
+
+      await tester.wait(1000);
+      await tester.expectToSeeWelcomePage();
+
+      final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
+
+      // Necessary for being able to enterText when not in debug mode
+      binding.testTextInput.register();
+
+      // Needs tab to obtain focus for the app flowy editor.
+      // by default the tap appears at the center of the widget.
+      final Finder editor = find.byType(AppFlowyEditor);
+      await tester.tap(editor);
+      await tester.pumpAndSettle();
+
+      // tester.sendText() cannot be used since the editor
+      // does not contain any EditableText widgets.
+      // to interact with the app during an integration test,
+      // simulate physical keyboard events.
+      await simulateKeyDownEvent(LogicalKeyboardKey.enter);
+      await tester.pumpAndSettle();
+      await simulateKeyDownEvent(LogicalKeyboardKey.enter);
+      await tester.pumpAndSettle();
+      await simulateKeyDownEvent(LogicalKeyboardKey.arrowLeft);
+      await tester.pumpAndSettle();
+      await simulateKeyDownEvent(LogicalKeyboardKey.slash);
+      await tester.pumpAndSettle();
+      await simulateKeyDownEvent(LogicalKeyboardKey.keyG);
+      await tester.pumpAndSettle();
+      await simulateKeyDownEvent(LogicalKeyboardKey.keyR);
+      await tester.pumpAndSettle();
+      await simulateKeyDownEvent(LogicalKeyboardKey.keyI);
+      await tester.pumpAndSettle();
+      await simulateKeyDownEvent(LogicalKeyboardKey.keyD);
+      await tester.pumpAndSettle();
+      await simulateKeyDownEvent(LogicalKeyboardKey.arrowDown);
+      await tester.pumpAndSettle();
+
+      // Checks whether the options in the selection menu
+      // for /grid exist.
+      expect(find.byType(SelectionMenuItemWidget), findsAtLeastNWidgets(2));
+
+      // Finalizes the slash command that creates the board.
+      await simulateKeyDownEvent(LogicalKeyboardKey.enter);
+      await tester.pumpAndSettle();
+
+      // Checks whether new board is referenced and properly on the page.
+      expect(find.byType(BuiltInPageWidget), findsOneWidget);
+
+      // Checks whether the new board is in the side bar.
+      final sidebarLabel = LocaleKeys.newPageText.tr();
+      expect(find.text(sidebarLabel), findsOneWidget);
+    });
   });
   });
 }
 }

+ 3 - 0
frontend/appflowy_flutter/lib/plugins/document/document_page.dart

@@ -20,6 +20,7 @@ import '../../startup/startup.dart';
 import 'application/doc_bloc.dart';
 import 'application/doc_bloc.dart';
 import 'editor_styles.dart';
 import 'editor_styles.dart';
 import 'presentation/banner.dart';
 import 'presentation/banner.dart';
+import 'presentation/plugins/grid/grid_view_menu_item.dart';
 import 'presentation/plugins/board/board_menu_item.dart';
 import 'presentation/plugins/board/board_menu_item.dart';
 
 
 class DocumentPage extends StatefulWidget {
 class DocumentPage extends StatefulWidget {
@@ -177,6 +178,8 @@ class _AppFlowyEditorPageState extends State<_AppFlowyEditorPage> {
         boardViewMenuItem(documentBloc),
         boardViewMenuItem(documentBloc),
         // Grid
         // Grid
         gridMenuItem,
         gridMenuItem,
+        // Create Grid
+        gridViewMenuItem(documentBloc),
         // Callout
         // Callout
         calloutMenuItem,
         calloutMenuItem,
         // AI
         // AI

+ 1 - 1
frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/grid/grid_menu_item.dart

@@ -17,7 +17,7 @@ SelectionMenuItem gridMenuItem = SelectionMenuItem(
           : editorState.editorStyle.selectionMenuItemIconColor,
           : editorState.editorStyle.selectionMenuItemIconColor,
     );
     );
   },
   },
-  keywords: ['grid'],
+  keywords: ['referenced grid'],
   handler: (editorState, menuService, context) {
   handler: (editorState, menuService, context) {
     showLinkToPageMenu(
     showLinkToPageMenu(
       editorState,
       editorState,

+ 60 - 0
frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/grid/grid_view_menu_item.dart

@@ -0,0 +1,60 @@
+import 'package:appflowy/generated/locale_keys.g.dart';
+import 'package:appflowy/plugins/document/application/doc_bloc.dart';
+import 'package:appflowy/plugins/document/presentation/plugins/base/insert_page_command.dart';
+import 'package:appflowy/workspace/application/app/app_service.dart';
+import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
+import 'package:appflowy_editor/appflowy_editor.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flowy_infra/image.dart';
+import 'package:flutter/material.dart';
+
+SelectionMenuItem gridViewMenuItem(DocumentBloc documentBloc) =>
+    SelectionMenuItem(
+      name: LocaleKeys.document_slashMenu_grid_createANewGrid.tr(),
+      icon: (editorState, onSelected) {
+        return svgWidget(
+          'editor/grid',
+          size: const Size.square(18.0),
+          color: onSelected
+              ? editorState.editorStyle.selectionMenuItemSelectedIconColor
+              : editorState.editorStyle.selectionMenuItemIconColor,
+        );
+      },
+      keywords: ['grid'],
+      handler: (editorState, menuService, context) async {
+        if (!documentBloc.view.hasAppId()) {
+          return;
+        }
+
+        final appId = documentBloc.view.appId;
+        final service = AppBackendService();
+
+        final result = (await service.createView(
+          appId: appId,
+          name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
+          layoutType: ViewLayoutTypePB.Grid,
+        ))
+            .getLeftOrNull();
+
+        // If the result is null, then something went wrong here.
+        if (result == null) {
+          return;
+        }
+
+        final app =
+            (await service.readApp(appId: result.appId)).getLeftOrNull();
+        // We should show an error dialog.
+        if (app == null) {
+          return;
+        }
+
+        final view =
+            (await service.getView(result.appId, result.id)).getLeftOrNull();
+        // As this.
+        if (view == null) {
+          return;
+        }
+
+        editorState.insertPage(app, view);
+      },
+    );