Просмотр исходного кода

Merge pull request #8174 from weseek/feat/132680-create-edit-button-handler

feat: Create page when click edit button if page is not found
Ryoji Shimizu 2 лет назад
Родитель
Сommit
ebc53ebcc6

+ 8 - 4
apps/app/src/components/Navbar/GrowiContextualSubNavigation.tsx

@@ -28,6 +28,7 @@ import { mutatePageTree } from '~/stores/page-listing';
 import {
   useEditorMode, useIsAbleToShowPageManagement,
   useIsAbleToChangeEditorMode,
+  useSelectedGrant,
 } from '~/stores/ui';
 
 import CreateTemplateModal from '../CreateTemplateModal';
@@ -41,7 +42,6 @@ import { Skeleton } from '../Skeleton';
 import styles from './GrowiContextualSubNavigation.module.scss';
 import PageEditorModeManagerStyles from './PageEditorModeManager.module.scss';
 
-
 const PageEditorModeManager = dynamic(
   () => import('./PageEditorModeManager').then(mod => mod.PageEditorModeManager),
   { ssr: false, loading: () => <Skeleton additionalClass={`${PageEditorModeManagerStyles['grw-page-editor-mode-manager-skeleton']}`} /> },
@@ -190,13 +190,14 @@ const GrowiContextualSubNavigation = (props: GrowiContextualSubNavigationProps):
   const revision = currentPage?.revision;
   const revisionId = (revision != null && isPopulated(revision)) ? revision._id : undefined;
 
-  const { data: editorMode, mutate: mutateEditorMode } = useEditorMode();
+  const { data: editorMode } = useEditorMode();
   const { data: pageId } = useCurrentPageId();
   const { data: currentUser } = useCurrentUser();
   const { data: isGuestUser } = useIsGuestUser();
   const { data: isReadOnlyUser } = useIsReadOnlyUser();
   const { data: isSharedUser } = useIsSharedUser();
   const { data: isContainerFluid } = useIsContainerFluid();
+  const { data: grantData } = useSelectedGrant();
 
   const { data: isAbleToShowPageManagement } = useIsAbleToShowPageManagement();
   const { data: isAbleToChangeEditorMode } = useIsAbleToChangeEditorMode();
@@ -213,6 +214,8 @@ const GrowiContextualSubNavigation = (props: GrowiContextualSubNavigationProps):
   const { mutate: mutatePageInfo } = useSWRxPageInfo(pageId);
 
   const path = currentPage?.path ?? currentPathname;
+  const grant = currentPage?.grant ?? grantData?.grant;
+  const grantUserGroupId = currentPage?.grantedGroup?._id ?? grantData?.grantedGroup?.id;
 
   // TODO: implement tags for editor
   // refs: https://redmine.weseek.co.jp/issues/132125
@@ -236,7 +239,6 @@ const GrowiContextualSubNavigation = (props: GrowiContextualSubNavigationProps):
 
   const { isLinkSharingDisabled } = props;
 
-
   // TODO: implement tags for editor
   // refs: https://redmine.weseek.co.jp/issues/132125
   // const tagsUpdatedHandlerForEditMode = useCallback((newTags: string[]): void => {
@@ -355,7 +357,9 @@ const GrowiContextualSubNavigation = (props: GrowiContextualSubNavigationProps):
           <PageEditorModeManager
             editorMode={editorMode}
             isBtnDisabled={!!isGuestUser || !!isReadOnlyUser}
-            onPageEditorModeButtonClicked={viewType => mutateEditorMode(viewType)}
+            path={path}
+            grant={grant}
+            grantUserGroupId={grantUserGroupId}
           />
         )}
       </div>

+ 22 - 12
apps/app/src/components/Navbar/PageEditorModeManager.tsx

@@ -1,9 +1,11 @@
-import React, { type ReactNode, useCallback } from 'react';
+import React, { type ReactNode, useCallback, useState } from 'react';
 
 import { useTranslation } from 'next-i18next';
 
 import { EditorMode, useIsDeviceSmallerThanMd } from '~/stores/ui';
 
+import { useOnPageEditorModeButtonClicked } from './hooks';
+
 import styles from './PageEditorModeManager.module.scss';
 
 
@@ -41,28 +43,36 @@ const PageEditorModeButton = React.memo((props: PageEditorModeButtonProps) => {
 
 type Props = {
   editorMode: EditorMode | undefined,
-  onPageEditorModeButtonClicked?: (editorMode: EditorMode) => void,
-  isBtnDisabled?: boolean,
+  isBtnDisabled: boolean,
+  path?: string,
+  grant?: number,
+  grantUserGroupId?: string
 }
 
 export const PageEditorModeManager = (props: Props): JSX.Element => {
   const {
     editorMode = EditorMode.View,
     isBtnDisabled,
-    onPageEditorModeButtonClicked,
+    path,
+    grant,
+    grantUserGroupId,
   } = props;
 
   const { t } = useTranslation();
+  const [isCreating, setIsCreating] = useState(false);
+
   const { data: isDeviceSmallerThanMd } = useIsDeviceSmallerThanMd();
 
-  const pageEditorModeButtonClickedHandler = useCallback((viewType) => {
-    if (isBtnDisabled ?? false) {
+  const onPageEditorModeButtonClicked = useOnPageEditorModeButtonClicked(setIsCreating, path, grant, grantUserGroupId);
+  const _isBtnDisabled = isCreating || isBtnDisabled;
+
+  const pageEditorModeButtonClickedHandler = useCallback((viewType: EditorMode) => {
+    if (_isBtnDisabled) {
       return;
     }
-    if (onPageEditorModeButtonClicked != null) {
-      onPageEditorModeButtonClicked(viewType);
-    }
-  }, [isBtnDisabled, onPageEditorModeButtonClicked]);
+
+    onPageEditorModeButtonClicked?.(viewType);
+  }, [_isBtnDisabled, onPageEditorModeButtonClicked]);
 
   return (
     <>
@@ -76,7 +86,7 @@ export const PageEditorModeManager = (props: Props): JSX.Element => {
           <PageEditorModeButton
             currentEditorMode={editorMode}
             editorMode={EditorMode.View}
-            isBtnDisabled={isBtnDisabled}
+            isBtnDisabled={_isBtnDisabled}
             onClick={pageEditorModeButtonClickedHandler}
           >
             <span className="material-symbols-outlined fs-4">play_arrow</span>{t('View')}
@@ -86,7 +96,7 @@ export const PageEditorModeManager = (props: Props): JSX.Element => {
           <PageEditorModeButton
             currentEditorMode={editorMode}
             editorMode={EditorMode.Editor}
-            isBtnDisabled={isBtnDisabled}
+            isBtnDisabled={_isBtnDisabled}
             onClick={pageEditorModeButtonClickedHandler}
           >
             <span className="material-symbols-outlined me-1 fs-5">edit_square</span>{t('Edit')}

+ 58 - 0
apps/app/src/components/Navbar/hooks.tsx

@@ -0,0 +1,58 @@
+import { useCallback } from 'react';
+
+import { useTranslation } from 'next-i18next';
+import { useRouter } from 'next/router';
+
+import { createPage } from '~/client/services/page-operation';
+import { toastError } from '~/client/util/toastr';
+import { useIsNotFound } from '~/stores/page';
+import { EditorMode, useEditorMode } from '~/stores/ui';
+import loggerFactory from '~/utils/logger';
+
+const logger = loggerFactory('growi:Navbar:GrowiContextualSubNavigation');
+
+export const useOnPageEditorModeButtonClicked = (
+    setIsCreating:React.Dispatch<React.SetStateAction<boolean>>,
+    path?: string,
+    grant?: number,
+    grantUserGroupId?: string,
+): (editorMode: EditorMode) => Promise<void> => {
+  const router = useRouter();
+  const { t } = useTranslation('commons');
+  const { data: isNotFound } = useIsNotFound();
+  const { mutate: mutateEditorMode } = useEditorMode();
+
+  return useCallback(async(editorMode: EditorMode) => {
+    if (isNotFound == null || path == null || grant == null) {
+      return;
+    }
+
+    if (editorMode === EditorMode.Editor && isNotFound) {
+      try {
+        setIsCreating(true);
+
+        const params = {
+          isSlackEnabled: false,
+          slackChannels: '',
+          grant,
+          pageTags: [],
+          grantUserGroupId,
+        };
+
+        const response = await createPage(path, '', params);
+
+        // Should not mutateEditorMode as it might prevent transitioning during mutation
+        router.push(`${response.page.id}#edit`);
+      }
+      catch (err) {
+        logger.warn(err);
+        toastError(t('toaster.create_failed', { target: path }));
+      }
+      finally {
+        setIsCreating(false);
+      }
+    }
+
+    mutateEditorMode(editorMode);
+  }, [grant, grantUserGroupId, isNotFound, mutateEditorMode, path, router, setIsCreating, t]);
+};