Przeglądaj źródła

Merge pull request #8541 from weseek/fix/141664-unable-to-create-pages-from-page-create-modal

fix: Unable to create pages from PageCreateModal
Shun Miyazawa 2 lat temu
rodzic
commit
bb9c1aa411

+ 18 - 0
apps/app/src/client/services/use-toastr-on-error.tsx

@@ -0,0 +1,18 @@
+import { useCallback } from 'react';
+
+import { useTranslation } from 'react-i18next';
+
+import { toastError } from '~/client/util/toastr';
+
+export const useToastrOnError = <P, R>(method?: (param?: P) => Promise<R|undefined>): (param?: P) => Promise<R|undefined> => {
+  const { t } = useTranslation('commons');
+
+  return useCallback(async(param) => {
+    try {
+      return await method?.(param);
+    }
+    catch (err) {
+      toastError(t('toaster.create_failed', { target: 'a page' }));
+    }
+  }, [method, t]);
+};

+ 54 - 90
apps/app/src/components/PageCreateModal.jsx → apps/app/src/components/PageCreateModal.tsx

@@ -5,33 +5,38 @@ import React, {
 import { pagePathUtils, pathUtils } from '@growi/core/dist/utils';
 import { pagePathUtils, pathUtils } from '@growi/core/dist/utils';
 import { format } from 'date-fns';
 import { format } from 'date-fns';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
-import { useRouter } from 'next/router';
 import {
 import {
   Modal, ModalHeader, ModalBody, UncontrolledButtonDropdown, DropdownToggle, DropdownMenu, DropdownItem,
   Modal, ModalHeader, ModalBody, UncontrolledButtonDropdown, DropdownToggle, DropdownMenu, DropdownItem,
 } from 'reactstrap';
 } from 'reactstrap';
 import { debounce } from 'throttle-debounce';
 import { debounce } from 'throttle-debounce';
 
 
-import { toastError } from '~/client/util/toastr';
+import { useCreateTemplatePage } from '~/client/services/create-page';
+import { useCreatePageAndTransit } from '~/client/services/create-page/use-create-page-and-transit';
+import { useToastrOnError } from '~/client/services/use-toastr-on-error';
 import { useCurrentUser, useIsSearchServiceReachable } from '~/stores/context';
 import { useCurrentUser, useIsSearchServiceReachable } from '~/stores/context';
 import { usePageCreateModal } from '~/stores/modal';
 import { usePageCreateModal } from '~/stores/modal';
-import { EditorMode, useEditorMode } from '~/stores/ui';
+
 
 
 import PagePathAutoComplete from './PagePathAutoComplete';
 import PagePathAutoComplete from './PagePathAutoComplete';
 
 
 import styles from './PageCreateModal.module.scss';
 import styles from './PageCreateModal.module.scss';
 
 
 const {
 const {
-  isCreatablePage, generateEditorPath, isUsersHomepage,
+  isCreatablePage, isUsersHomepage,
 } = pagePathUtils;
 } = pagePathUtils;
 
 
-const PageCreateModal = () => {
+const PageCreateModal: React.FC = () => {
   const { t } = useTranslation();
   const { t } = useTranslation();
-  const router = useRouter();
 
 
   const { data: currentUser } = useCurrentUser();
   const { data: currentUser } = useCurrentUser();
 
 
   const { data: pageCreateModalData, close: closeCreateModal } = usePageCreateModal();
   const { data: pageCreateModalData, close: closeCreateModal } = usePageCreateModal();
-  const { isOpened, path } = pageCreateModalData;
+
+  const path = pageCreateModalData?.path;
+  const isOpened = pageCreateModalData?.isOpened ?? false;
+
+  const { createAndTransit } = useCreatePageAndTransit();
+  const { createTemplate } = useCreateTemplatePage();
 
 
   const { data: isReachable } = useIsSearchServiceReachable();
   const { data: isReachable } = useIsSearchServiceReachable();
   const pathname = path || '';
   const pathname = path || '';
@@ -39,26 +44,13 @@ const PageCreateModal = () => {
   const isCreatable = isCreatablePage(pathname) || isUsersHomepage(pathname);
   const isCreatable = isCreatablePage(pathname) || isUsersHomepage(pathname);
   const pageNameInputInitialValue = isCreatable ? pathUtils.addTrailingSlash(pathname) : '/';
   const pageNameInputInitialValue = isCreatable ? pathUtils.addTrailingSlash(pathname) : '/';
   const now = format(new Date(), 'yyyy/MM/dd');
   const now = format(new Date(), 'yyyy/MM/dd');
+  const todaysParentPath = [userHomepagePath, t('create_page_dropdown.todays.memo', { ns: 'commons' }), now].join('/');
 
 
-  const { mutate: mutateEditorMode } = useEditorMode();
-
-  const [todayInput1, setTodayInput1] = useState(t('Memo'));
-  const [todayInput2, setTodayInput2] = useState('');
+  const [todayInput, setTodayInput] = useState('');
   const [pageNameInput, setPageNameInput] = useState(pageNameInputInitialValue);
   const [pageNameInput, setPageNameInput] = useState(pageNameInputInitialValue);
   const [template, setTemplate] = useState(null);
   const [template, setTemplate] = useState(null);
   const [isMatchedWithUserHomepagePath, setIsMatchedWithUserHomepagePath] = useState(false);
   const [isMatchedWithUserHomepagePath, setIsMatchedWithUserHomepagePath] = useState(false);
 
 
-  // ensure pageNameInput is synced with selectedPagePath || currentPagePath
-  useEffect(() => {
-    if (isOpened) {
-      setPageNameInput(isCreatable ? pathUtils.addTrailingSlash(pathname) : '/');
-    }
-  }, [isOpened, pathname, isCreatable]);
-
-  useEffect(() => {
-    setTodayInput1(t('Memo'));
-  }, [t]);
-
   const checkIsUsersHomepageDebounce = useMemo(() => {
   const checkIsUsersHomepageDebounce = useMemo(() => {
     const checkIsUsersHomepage = () => {
     const checkIsUsersHomepage = () => {
       setIsMatchedWithUserHomepagePath(isUsersHomepage(pageNameInput));
       setIsMatchedWithUserHomepagePath(isUsersHomepage(pageNameInput));
@@ -69,10 +61,11 @@ const PageCreateModal = () => {
 
 
   useEffect(() => {
   useEffect(() => {
     if (isOpened) {
     if (isOpened) {
-      checkIsUsersHomepageDebounce(pageNameInput);
+      checkIsUsersHomepageDebounce();
     }
     }
   }, [isOpened, checkIsUsersHomepageDebounce, pageNameInput]);
   }, [isOpened, checkIsUsersHomepageDebounce, pageNameInput]);
 
 
+
   function transitBySubmitEvent(e, transitHandler) {
   function transitBySubmitEvent(e, transitHandler) {
     // prevent page transition by submit
     // prevent page transition by submit
     e.preventDefault();
     e.preventDefault();
@@ -80,19 +73,11 @@ const PageCreateModal = () => {
   }
   }
 
 
   /**
   /**
-   * change todayInput1
-   * @param {string} value
-   */
-  function onChangeTodayInput1Handler(value) {
-    setTodayInput1(value);
-  }
-
-  /**
-   * change todayInput2
+   * change todayInput
    * @param {string} value
    * @param {string} value
    */
    */
-  function onChangeTodayInput2Handler(value) {
-    setTodayInput2(value);
+  function onChangeTodayInputHandler(value) {
+    setTodayInput(value);
   }
   }
 
 
   /**
   /**
@@ -103,53 +88,41 @@ const PageCreateModal = () => {
     setTemplate(value);
     setTemplate(value);
   }
   }
 
 
-  /**
-   * join path, check if creatable, then redirect
-   * @param {string} paths
-   */
-  const redirectToEditor = useCallback(async(...paths) => {
-    try {
-      const editorPath = generateEditorPath(...paths);
-      await router.push(editorPath);
-      mutateEditorMode(EditorMode.Editor);
-
-      // close modal
-      closeCreateModal();
-    }
-    catch (err) {
-      toastError(err);
-    }
-  }, [closeCreateModal, mutateEditorMode, router]);
-
   /**
   /**
    * access today page
    * access today page
    */
    */
-  function createTodayPage() {
-    let tmpTodayInput1 = todayInput1;
-    if (tmpTodayInput1 === '') {
-      tmpTodayInput1 = t('Memo');
-    }
-    redirectToEditor(userHomepagePath, tmpTodayInput1, now, todayInput2);
-  }
+  const createTodayPage = useCallback(async() => {
+    const joinedPath = [todaysParentPath, todayInput].join('/');
+    return createAndTransit(
+      { path: joinedPath, wip: true },
+      { shouldCheckPageExists: true, onTerminated: closeCreateModal },
+    );
+  }, [closeCreateModal, createAndTransit, todayInput, todaysParentPath]);
 
 
   /**
   /**
    * access input page
    * access input page
    */
    */
-  function createInputPage() {
-    redirectToEditor(pageNameInput);
-  }
-
-  const ppacSubmitHandler = useCallback((input) => {
-    redirectToEditor(input);
-  }, [redirectToEditor]);
+  const createInputPage = useCallback(async() => {
+    return createAndTransit(
+      { path: pageNameInput, optionalParentPath: '/', wip: true },
+      { shouldCheckPageExists: true, onTerminated: closeCreateModal },
+    );
+  }, [closeCreateModal, createAndTransit, pageNameInput]);
 
 
   /**
   /**
    * access template page
    * access template page
    */
    */
-  function createTemplatePage(e) {
-    const pageName = (template === 'children') ? '_template' : '__template';
-    redirectToEditor(pathname, pageName);
-  }
+  const createTemplatePage = useCallback(async() => {
+
+    const label = (template === 'children') ? '_template' : '__template';
+
+    await createTemplate?.(label);
+    closeCreateModal();
+  }, [closeCreateModal, createTemplate, template]);
+
+  const createTodaysMemoWithToastr = useToastrOnError(createTodayPage);
+  const createInputPageWithToastr = useToastrOnError(createInputPage);
+  const createTemplateWithToastr = useToastrOnError(createTemplatePage);
 
 
   function renderCreateTodayForm() {
   function renderCreateTodayForm() {
     if (!isOpened) {
     if (!isOpened) {
@@ -158,31 +131,22 @@ const PageCreateModal = () => {
     return (
     return (
       <div className="row">
       <div className="row">
         <fieldset className="col-12 mb-4">
         <fieldset className="col-12 mb-4">
-          <h3 className="grw-modal-head pb-2">{t("Create today's")}</h3>
+          <h3 className="grw-modal-head pb-2">{t('create_page_dropdown.todays.desc', { ns: 'commons' })}</h3>
 
 
           <div className="d-sm-flex align-items-center justify-items-between">
           <div className="d-sm-flex align-items-center justify-items-between">
 
 
             <div className="d-flex align-items-center flex-fill flex-wrap flex-lg-nowrap">
             <div className="d-flex align-items-center flex-fill flex-wrap flex-lg-nowrap">
-              <div className="d-flex align-items-center">
-                <span>{userHomepagePath}/</span>
-                <form onSubmit={e => transitBySubmitEvent(e, createTodayPage)}>
-                  <input
-                    type="text"
-                    className="page-today-input1 form-control text-center mx-2"
-                    value={todayInput1}
-                    onChange={e => onChangeTodayInput1Handler(e.target.value)}
-                  />
-                </form>
-                <span className="page-today-suffix">/{now}/</span>
+              <div className="d-flex align-items-center text-nowrap">
+                <span>{todaysParentPath}/</span>
               </div>
               </div>
-              <form className="mt-1 mt-lg-0 ms-lg-2 w-100" onSubmit={e => transitBySubmitEvent(e, createTodayPage)}>
+              <form className="mt-1 mt-lg-0 ms-lg-2 w-100" onSubmit={(e) => { transitBySubmitEvent(e, createTodaysMemoWithToastr) }}>
                 <input
                 <input
                   type="text"
                   type="text"
                   className="page-today-input2 form-control w-100"
                   className="page-today-input2 form-control w-100"
                   id="page-today-input2"
                   id="page-today-input2"
                   placeholder={t('Input page name (optional)')}
                   placeholder={t('Input page name (optional)')}
-                  value={todayInput2}
-                  onChange={e => onChangeTodayInput2Handler(e.target.value)}
+                  value={todayInput}
+                  onChange={e => onChangeTodayInputHandler(e.target.value)}
                 />
                 />
               </form>
               </form>
             </div>
             </div>
@@ -192,7 +156,7 @@ const PageCreateModal = () => {
                 type="button"
                 type="button"
                 data-testid="btn-create-memo"
                 data-testid="btn-create-memo"
                 className="grw-btn-create-page btn btn-outline-primary rounded-pill text-nowrap ms-3"
                 className="grw-btn-create-page btn btn-outline-primary rounded-pill text-nowrap ms-3"
-                onClick={createTodayPage}
+                onClick={createTodaysMemoWithToastr}
               >
               >
                 <span className="material-symbols-outlined">description</span>{t('Create')}
                 <span className="material-symbols-outlined">description</span>{t('Create')}
               </button>
               </button>
@@ -221,13 +185,13 @@ const PageCreateModal = () => {
                   <PagePathAutoComplete
                   <PagePathAutoComplete
                     initializedPath={pageNameInputInitialValue}
                     initializedPath={pageNameInputInitialValue}
                     addTrailingSlash
                     addTrailingSlash
-                    onSubmit={ppacSubmitHandler}
+                    onSubmit={createInputPageWithToastr}
                     onInputChange={value => setPageNameInput(value)}
                     onInputChange={value => setPageNameInput(value)}
                     autoFocus
                     autoFocus
                   />
                   />
                 )
                 )
                 : (
                 : (
-                  <form onSubmit={e => transitBySubmitEvent(e, createInputPage)}>
+                  <form onSubmit={(e) => { transitBySubmitEvent(e, createInputPageWithToastr) }}>
                     <input
                     <input
                       type="text"
                       type="text"
                       value={pageNameInput}
                       value={pageNameInput}
@@ -245,7 +209,7 @@ const PageCreateModal = () => {
                 type="button"
                 type="button"
                 data-testid="btn-create-page-under-below"
                 data-testid="btn-create-page-under-below"
                 className="grw-btn-create-page btn btn-outline-primary rounded-pill text-nowrap ms-3"
                 className="grw-btn-create-page btn btn-outline-primary rounded-pill text-nowrap ms-3"
-                onClick={createInputPage}
+                onClick={createInputPageWithToastr}
                 disabled={isMatchedWithUserHomepagePath}
                 disabled={isMatchedWithUserHomepagePath}
               >
               >
                 <span className="material-symbols-outlined">description</span>{t('Create')}
                 <span className="material-symbols-outlined">description</span>{t('Create')}
@@ -300,7 +264,7 @@ const PageCreateModal = () => {
                 data-testid="grw-btn-edit-page"
                 data-testid="grw-btn-edit-page"
                 type="button"
                 type="button"
                 className="grw-btn-create-page btn btn-outline-primary rounded-pill text-nowrap ms-3"
                 className="grw-btn-create-page btn btn-outline-primary rounded-pill text-nowrap ms-3"
-                onClick={createTemplatePage}
+                onClick={createTemplateWithToastr}
                 disabled={template == null}
                 disabled={template == null}
               >
               >
                 <span className="material-symbols-outlined">description</span>{t('Edit')}
                 <span className="material-symbols-outlined">description</span>{t('Edit')}

+ 2 - 17
apps/app/src/components/Sidebar/PageCreateButton/PageCreateButton.tsx

@@ -1,10 +1,9 @@
-import React, { useState, useCallback } from 'react';
+import React, { useState } from 'react';
 
 
-import { useTranslation } from 'react-i18next';
 import { Dropdown } from 'reactstrap';
 import { Dropdown } from 'reactstrap';
 
 
 import { useCreateTemplatePage } from '~/client/services/create-page';
 import { useCreateTemplatePage } from '~/client/services/create-page';
-import { toastError } from '~/client/util/toastr';
+import { useToastrOnError } from '~/client/services/use-toastr-on-error';
 
 
 import { CreateButton } from './CreateButton';
 import { CreateButton } from './CreateButton';
 import { DropendMenu } from './DropendMenu';
 import { DropendMenu } from './DropendMenu';
@@ -12,20 +11,6 @@ import { DropendToggle } from './DropendToggle';
 import { useCreateNewPage, useCreateTodaysMemo } from './hooks';
 import { useCreateNewPage, useCreateTodaysMemo } from './hooks';
 
 
 
 
-const useToastrOnError = <P, R>(method?: (param?: P) => Promise<R|undefined>): (param?: P) => Promise<R|undefined> => {
-  const { t } = useTranslation('commons');
-
-  return useCallback(async(param) => {
-    try {
-      return await method?.(param);
-    }
-    catch (err) {
-      toastError(t('toaster.create_failed', { target: 'a page' }));
-    }
-  }, [method, t]);
-};
-
-
 export const PageCreateButton = React.memo((): JSX.Element => {
 export const PageCreateButton = React.memo((): JSX.Element => {
   const [isHovered, setIsHovered] = useState(false);
   const [isHovered, setIsHovered] = useState(false);