Przeglądaj źródła

Merge pull request #10594 from growilabs/feat/175737-can-create-page-when-executing-page-edit-shortcut-key-on-empty-page

feat: Can create page when executing page edit shortcut key on empty page
Shun Miyazawa 3 miesięcy temu
rodzic
commit
e4519154c5

+ 0 - 40
apps/app/src/client/components/Hotkeys/Subscribers/EditPage.jsx

@@ -1,40 +0,0 @@
-import { useEffect } from 'react';
-
-import PropTypes from 'prop-types';
-
-import { useIsEditable } from '~/states/page';
-import { EditorMode, useEditorMode } from '~/states/ui/editor';
-
-const EditPage = (props) => {
-  const isEditable = useIsEditable();
-  const { setEditorMode } = useEditorMode();
-
-  // setup effect
-  useEffect(() => {
-    if (!isEditable) {
-      return;
-    }
-
-    // ignore when dom that has 'modal in' classes exists
-    if (document.getElementsByClassName('modal in').length > 0) {
-      return;
-    }
-
-    setEditorMode(EditorMode.Editor);
-
-    // remove this
-    props.onDeleteRender(this);
-  }, [isEditable, props, setEditorMode]);
-
-  return null;
-};
-
-EditPage.propTypes = {
-  onDeleteRender: PropTypes.func.isRequired,
-};
-
-EditPage.getHotkeyStrokes = () => {
-  return [['e']];
-};
-
-export default EditPage;

+ 74 - 0
apps/app/src/client/components/Hotkeys/Subscribers/EditPage.tsx

@@ -0,0 +1,74 @@
+import { useCallback, useEffect, useRef } from 'react';
+
+import { useTranslation } from 'next-i18next';
+
+import { useStartEditing } from '~/client/services/use-start-editing';
+import { toastError } from '~/client/util/toastr';
+import { useCurrentPathname } from '~/states/global';
+import { useIsEditable, useCurrentPagePath } from '~/states/page';
+
+type Props = {
+  onDeleteRender: () => void,
+}
+
+/**
+ * Custom hook for edit page logic
+ */
+const useEditPage = (
+    onCompleted: () => void,
+    onError?: (path: string) => void,
+): void => {
+  const isEditable = useIsEditable();
+  const startEditing = useStartEditing();
+  const currentPagePath = useCurrentPagePath();
+  const currentPathname = useCurrentPathname();
+  const path = currentPagePath ?? currentPathname;
+  const isExecutedRef = useRef(false);
+
+  useEffect(() => {
+    (async() => {
+      // Prevent multiple executions
+      if (isExecutedRef.current) return;
+      isExecutedRef.current = true;
+
+      if (!isEditable) {
+        return;
+      }
+
+      // ignore when dom that has 'modal in' classes exists
+      if (document.getElementsByClassName('modal in').length > 0) {
+        return;
+      }
+
+      try {
+        await startEditing(path);
+      }
+      catch (err) {
+        onError?.(path);
+      }
+
+      onCompleted();
+    })();
+  }, [startEditing, isEditable, path, onCompleted, onError]);
+};
+
+/**
+ * EditPage component - handles hotkey 'e' for editing
+ */
+const EditPage = (props: Props): null => {
+  const { t } = useTranslation('commons');
+
+  const handleError = useCallback((path: string) => {
+    toastError(t('toaster.create_failed', { target: path }));
+  }, [t]);
+
+  useEditPage(props.onDeleteRender, handleError);
+
+  return null;
+};
+
+EditPage.getHotkeyStrokes = () => {
+  return [['e']];
+};
+
+export default EditPage;

+ 5 - 23
apps/app/src/client/components/Navbar/PageEditorModeManager.tsx

@@ -2,20 +2,15 @@ import React, {
   type ReactNode, useCallback, useMemo, type JSX,
 } from 'react';
 
-import { Origin } from '@growi/core';
-import { getParentPath } from '@growi/core/dist/utils/path-utils';
 import { useTranslation } from 'next-i18next';
 
 import { useCreatePage } from '~/client/services/create-page';
+import { useStartEditing } from '~/client/services/use-start-editing';
 import { toastError } from '~/client/util/toastr';
 import { useCurrentPageYjsData } from '~/features/collaborative-editor/states';
-import { usePageNotFound } from '~/states/page';
 import { useDeviceLargerThanMd } from '~/states/ui/device';
 import { useEditorMode, EditorMode } from '~/states/ui/editor';
 
-import { shouldCreateWipPage } from '../../../utils/should-create-wip-page';
-
-
 import styles from './PageEditorModeManager.module.scss';
 
 
@@ -66,34 +61,21 @@ export const PageEditorModeManager = (props: Props): JSX.Element => {
 
   const { t } = useTranslation('commons');
 
-  const isNotFound = usePageNotFound();
   const { setEditorMode } = useEditorMode();
   const [isDeviceLargerThanMd] = useDeviceLargerThanMd();
   const currentPageYjsData = useCurrentPageYjsData();
+  const startEditing = useStartEditing();
 
-  const { isCreating, create } = useCreatePage();
+  const { isCreating } = useCreatePage();
 
   const editButtonClickedHandler = useCallback(async () => {
-    if (!isNotFound) {
-      setEditorMode(EditorMode.Editor);
-      return;
-    }
-
-    // Create a new page if it does not exist and transit to the editor mode
     try {
-      const parentPath = path != null ? getParentPath(path) : undefined; // does not have to exist
-      await create(
-        {
-          path, parentPath, wip: shouldCreateWipPage(path), origin: Origin.View,
-        },
-      );
-
-      setEditorMode(EditorMode.Editor);
+      await startEditing(path);
     }
     catch (err) {
       toastError(t('toaster.create_failed', { target: path }));
     }
-  }, [create, isNotFound, setEditorMode, path, t]);
+  }, [startEditing, path, t]);
 
   const _isBtnDisabled = isCreating || isBtnDisabled;
 

+ 38 - 0
apps/app/src/client/services/use-start-editing.tsx

@@ -0,0 +1,38 @@
+import { useCallback } from 'react';
+
+import { Origin } from '@growi/core';
+import { getParentPath } from '@growi/core/dist/utils/path-utils';
+
+import { useCreatePage } from '~/client/services/create-page';
+import { usePageNotFound } from '~/states/page';
+import { useEditorMode, EditorMode } from '~/states/ui/editor';
+
+import { shouldCreateWipPage } from '../../utils/should-create-wip-page';
+
+export const useStartEditing = (): ((path?: string) => Promise<void>) => {
+  const isNotFound = usePageNotFound();
+  const { setEditorMode } = useEditorMode();
+  const { create } = useCreatePage();
+
+  return useCallback(async (path?: string) => {
+    if (!isNotFound) {
+      setEditorMode(EditorMode.Editor);
+      return;
+    }
+    // Create a new page if it does not exist and transit to the editor mode
+    try {
+      const parentPath = path != null ? getParentPath(path) : undefined; // does not have to exist
+      await create(
+        {
+          path, parentPath, wip: shouldCreateWipPage(path), origin: Origin.View,
+        },
+      );
+
+      setEditorMode(EditorMode.Editor);
+    }
+    catch (err) {
+      throw new Error(err);
+    }
+  }, [create, isNotFound, setEditorMode]);
+
+};