Prechádzať zdrojové kódy

Merge pull request #5391 from weseek/imprv/88786-refactoring-page-duplicate-modal

imprv: refactoring page duplicate modal
cao 4 rokov pred
rodič
commit
65340cf908

+ 3 - 3
packages/app/src/components/Navbar/GrowiContextualSubNavigation.tsx

@@ -15,7 +15,7 @@ import {
   useIsAbleToShowPageEditorModeManager, useIsAbleToShowPageAuthors,
   useIsAbleToShowPageEditorModeManager, useIsAbleToShowPageAuthors,
 } from '~/stores/ui';
 } from '~/stores/ui';
 import {
 import {
-  usePageAccessoriesModal, PageAccessoriesModalContents,
+  usePageAccessoriesModal, PageAccessoriesModalContents, IPageForPageDuplicateModal,
   usePageDuplicateModal, usePageRenameModal, IPageForPageRenameModal, usePageDeleteModal, usePagePresentationModal, IPageForPageDeleteModal,
   usePageDuplicateModal, usePageRenameModal, IPageForPageRenameModal, usePageDeleteModal, usePagePresentationModal, IPageForPageDeleteModal,
 } from '~/stores/modal';
 } from '~/stores/modal';
 
 
@@ -180,8 +180,8 @@ const GrowiContextualSubNavigation = (props) => {
   // eslint-disable-next-line react-hooks/exhaustive-deps
   // eslint-disable-next-line react-hooks/exhaustive-deps
   }, [pageId]);
   }, [pageId]);
 
 
-  const duplicateItemClickedHandler = useCallback(async(pageId, path) => {
-    openDuplicateModal(pageId, path);
+  const duplicateItemClickedHandler = useCallback(async(page: IPageForPageDuplicateModal) => {
+    openDuplicateModal(page);
   }, [openDuplicateModal]);
   }, [openDuplicateModal]);
 
 
   const renameItemClickedHandler = useCallback(async(page: IPageForPageRenameModal) => {
   const renameItemClickedHandler = useCallback(async(page: IPageForPageRenameModal) => {

+ 4 - 3
packages/app/src/components/Navbar/SubNavButtons.tsx

@@ -6,7 +6,7 @@ import { useSWRxPageInfo } from '../../stores/page';
 import { useSWRBookmarkInfo } from '../../stores/bookmark';
 import { useSWRBookmarkInfo } from '../../stores/bookmark';
 import { useSWRxUsersList } from '../../stores/user';
 import { useSWRxUsersList } from '../../stores/user';
 import { useIsGuestUser } from '~/stores/context';
 import { useIsGuestUser } from '~/stores/context';
-import { IPageForPageDeleteModal, IPageForPageRenameModal } from '~/stores/modal';
+import { IPageForPageDeleteModal, IPageForPageRenameModal, IPageForPageDuplicateModal } from '~/stores/modal';
 
 
 import SubscribeButton from '../SubscribeButton';
 import SubscribeButton from '../SubscribeButton';
 import LikeButtons from '../LikeButtons';
 import LikeButtons from '../LikeButtons';
@@ -24,7 +24,7 @@ type CommonProps = {
   showPageControlDropdown?: boolean,
   showPageControlDropdown?: boolean,
   forceHideMenuItems?: ForceHideMenuItems,
   forceHideMenuItems?: ForceHideMenuItems,
   additionalMenuItemRenderer?: React.FunctionComponent<AdditionalMenuItemsRendererProps>,
   additionalMenuItemRenderer?: React.FunctionComponent<AdditionalMenuItemsRendererProps>,
-  onClickDuplicateMenuItem?: (pageId: string, path: string) => void,
+  onClickDuplicateMenuItem?: (pageToDuplicate: IPageForPageDuplicateModal) => void,
   onClickRenameMenuItem?: (pageToRename: IPageForPageRenameModal) => void,
   onClickRenameMenuItem?: (pageToRename: IPageForPageRenameModal) => void,
   onClickDeleteMenuItem?: (pageToDelete: IPageForPageDeleteModal) => void,
   onClickDeleteMenuItem?: (pageToDelete: IPageForPageDeleteModal) => void,
 }
 }
@@ -100,8 +100,9 @@ const SubNavButtonsSubstance = (props: SubNavButtonsSubstanceProps): JSX.Element
     if (onClickDuplicateMenuItem == null || path == null) {
     if (onClickDuplicateMenuItem == null || path == null) {
       return;
       return;
     }
     }
+    const page: IPageForPageDuplicateModal = { pageId, path };
 
 
-    onClickDuplicateMenuItem(pageId, path);
+    onClickDuplicateMenuItem(page);
   }, [onClickDuplicateMenuItem, pageId, path]);
   }, [onClickDuplicateMenuItem, pageId, path]);
 
 
   const renameMenuItemClickHandler = useCallback(async(_pageId: string): Promise<void> => {
   const renameMenuItemClickHandler = useCallback(async(_pageId: string): Promise<void> => {

+ 10 - 9
packages/app/src/components/PageDuplicateModal.jsx

@@ -27,9 +27,10 @@ const PageDuplicateModal = (props) => {
   const config = appContainer.getConfig();
   const config = appContainer.getConfig();
   const isReachable = config.isSearchServiceReachable;
   const isReachable = config.isSearchServiceReachable;
   const { crowi } = appContainer.config;
   const { crowi } = appContainer.config;
-  const { data: pagesDataToDuplicate, close: closeDuplicateModal } = usePageDuplicateModal();
+  const { data: duplicateModalData, close: closeDuplicateModal } = usePageDuplicateModal();
 
 
-  const { isOpened, path, pageId } = pagesDataToDuplicate;
+  const { isOpened, page } = duplicateModalData;
+  const { pageId, path } = page;
 
 
   const [pageNameInput, setPageNameInput] = useState(path);
   const [pageNameInput, setPageNameInput] = useState(path);
 
 
@@ -40,7 +41,7 @@ const PageDuplicateModal = (props) => {
   const [isDuplicateRecursivelyWithoutExistPath, setIsDuplicateRecursivelyWithoutExistPath] = useState(true);
   const [isDuplicateRecursivelyWithoutExistPath, setIsDuplicateRecursivelyWithoutExistPath] = useState(true);
   const [existingPaths, setExistingPaths] = useState([]);
   const [existingPaths, setExistingPaths] = useState([]);
 
 
-  const checkExistPaths = async(newParentPath) => {
+  const checkExistPaths = useCallback(async(newParentPath) => {
     try {
     try {
       const res = await appContainer.apiv3Get('/page/exist-paths', { fromPath: path, toPath: newParentPath });
       const res = await appContainer.apiv3Get('/page/exist-paths', { fromPath: path, toPath: newParentPath });
       const { existPaths } = res.data;
       const { existPaths } = res.data;
@@ -50,15 +51,15 @@ const PageDuplicateModal = (props) => {
       setErrs(err);
       setErrs(err);
       toastError(t('modal_rename.label.Fail to get exist path'));
       toastError(t('modal_rename.label.Fail to get exist path'));
     }
     }
-  };
+  }, [appContainer, path, t]);
 
 
-  // eslint-disable-next-line react-hooks/exhaustive-deps
-  const checkExistPathsDebounce = useCallback(
-    debounce(1000, checkExistPaths), [pageId, path],
-  );
+
+  const checkExistPathsDebounce = useCallback(() => {
+    debounce(1000, checkExistPaths);
+  }, [checkExistPaths]);
 
 
   useEffect(() => {
   useEffect(() => {
-    if (pageId != null && pageNameInput !== path) {
+    if (pageId != null && path != null && pageNameInput !== path) {
       checkExistPathsDebounce(pageNameInput, subordinatedPages);
       checkExistPathsDebounce(pageNameInput, subordinatedPages);
     }
     }
   }, [pageNameInput, subordinatedPages, path, pageId, checkExistPathsDebounce]);
   }, [pageNameInput, subordinatedPages, path, pageId, checkExistPathsDebounce]);

+ 5 - 2
packages/app/src/components/PageList/PageListItemL.tsx

@@ -93,8 +93,11 @@ const PageListItemLSubstance: ForwardRefRenderFunction<ISelectable, Props> = (pr
   }, [isDeviceSmallerThanLg, onClickItem, pageData._id]);
   }, [isDeviceSmallerThanLg, onClickItem, pageData._id]);
 
 
   const duplicateMenuItemClickHandler = useCallback(() => {
   const duplicateMenuItemClickHandler = useCallback(() => {
-    const { _id: pageId, path } = pageData;
-    openDuplicateModal(pageId, path);
+    const page = {
+      pageId: pageData._id,
+      path: pageData.path,
+    };
+    openDuplicateModal(page);
   }, [openDuplicateModal, pageData]);
   }, [openDuplicateModal, pageData]);
 
 
   const renameMenuItemClickHandler = useCallback(() => {
   const renameMenuItemClickHandler = useCallback(() => {

+ 3 - 0
packages/app/src/components/PagePathAutoComplete.jsx

@@ -30,6 +30,9 @@ const PagePathAutoComplete = (props) => {
   }
   }
 
 
   function getKeywordOnInit(path) {
   function getKeywordOnInit(path) {
+    if (path == null) {
+      return;
+    }
     return addTrailingSlash
     return addTrailingSlash
       ? pathUtils.addTrailingSlash(path)
       ? pathUtils.addTrailingSlash(path)
       : pathUtils.removeTrailingSlash(path);
       : pathUtils.removeTrailingSlash(path);

+ 2 - 2
packages/app/src/components/SearchPage/SearchResultContent.tsx

@@ -107,8 +107,8 @@ export const SearchResultContent: FC<Props> = (props: Props) => {
   const growiRenderer = appContainer.getRenderer('searchresult');
   const growiRenderer = appContainer.getRenderer('searchresult');
 
 
 
 
-  const duplicateItemClickedHandler = useCallback(async(pageId, path) => {
-    openDuplicateModal(pageId, path);
+  const duplicateItemClickedHandler = useCallback(async(pageToDuplicate) => {
+    openDuplicateModal(pageToDuplicate);
   }, [openDuplicateModal]);
   }, [openDuplicateModal]);
 
 
   const renameItemClickedHandler = useCallback(async(pageToRename) => {
   const renameItemClickedHandler = useCallback(async(pageToRename) => {

+ 5 - 3
packages/app/src/components/Sidebar/PageTree/Item.tsx

@@ -14,7 +14,7 @@ import { toastWarning, toastError, toastSuccess } from '~/client/util/apiNotific
 
 
 import { useSWRxPageChildren } from '~/stores/page-listing';
 import { useSWRxPageChildren } from '~/stores/page-listing';
 import { apiv3Put, apiv3Post } from '~/client/util/apiv3-client';
 import { apiv3Put, apiv3Post } from '~/client/util/apiv3-client';
-import { IPageForPageRenameModal, IPageForPageDeleteModal } from '~/stores/modal';
+import { IPageForPageRenameModal, IPageForPageDuplicateModal, IPageForPageDeleteModal } from '~/stores/modal';
 
 
 import TriangleIcon from '~/components/Icons/TriangleIcon';
 import TriangleIcon from '~/components/Icons/TriangleIcon';
 import { bookmark, unbookmark } from '~/client/services/page-operation';
 import { bookmark, unbookmark } from '~/client/services/page-operation';
@@ -28,7 +28,7 @@ interface ItemProps {
   targetPathOrId?: string
   targetPathOrId?: string
   isOpen?: boolean
   isOpen?: boolean
   isEnabledAttachTitleHeader?: boolean
   isEnabledAttachTitleHeader?: boolean
-  onClickDuplicateMenuItem?(pageId: string, path: string): void
+  onClickDuplicateMenuItem?(pageToDuplicate: IPageForPageDuplicateModal): void
   onClickRenameMenuItem?(pageToRename: IPageForPageRenameModal): void
   onClickRenameMenuItem?(pageToRename: IPageForPageRenameModal): void
   onClickDeleteMenuItem?(pageToDelete: IPageForPageDeleteModal): void
   onClickDeleteMenuItem?(pageToDelete: IPageForPageDeleteModal): void
 }
 }
@@ -197,7 +197,9 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
       throw Error('Any of _id and path must not be null.');
       throw Error('Any of _id and path must not be null.');
     }
     }
 
 
-    onClickDuplicateMenuItem(pageId, path);
+    const pageToDuplicate = { pageId, path };
+
+    onClickDuplicateMenuItem(pageToDuplicate);
   }, [onClickDuplicateMenuItem, page]);
   }, [onClickDuplicateMenuItem, page]);
 
 
 
 

+ 4 - 4
packages/app/src/components/Sidebar/PageTree/ItemsTree.tsx

@@ -9,7 +9,7 @@ import { TargetAndAncestors } from '~/interfaces/page-listing-results';
 import { OnDeletedFunction } from '~/interfaces/ui';
 import { OnDeletedFunction } from '~/interfaces/ui';
 import { toastError, toastSuccess } from '~/client/util/apiNotification';
 import { toastError, toastSuccess } from '~/client/util/apiNotification';
 import {
 import {
-  IPageForPageDeleteModal, usePageDuplicateModal, IPageForPageRenameModal, usePageRenameModal, usePageDeleteModal,
+  IPageForPageDeleteModal, IPageForPageDuplicateModal, usePageDuplicateModal, IPageForPageRenameModal, usePageRenameModal, usePageDeleteModal,
 } from '~/stores/modal';
 } from '~/stores/modal';
 import { smoothScrollIntoView } from '~/client/util/smooth-scroll';
 import { smoothScrollIntoView } from '~/client/util/smooth-scroll';
 
 
@@ -68,7 +68,7 @@ const renderByInitialNode = (
     isEnableActions: boolean,
     isEnableActions: boolean,
     targetPathOrId?: string,
     targetPathOrId?: string,
     isEnabledAttachTitleHeader?: boolean,
     isEnabledAttachTitleHeader?: boolean,
-    onClickDuplicateMenuItem?: (pageId: string, path: string) => void,
+    onClickDuplicateMenuItem?: (pageToDuplicate: IPageForPageDuplicateModal) => void,
     onClickRenameMenuItem?: (pageToRename: IPageForPageRenameModal) => void,
     onClickRenameMenuItem?: (pageToRename: IPageForPageRenameModal) => void,
     onClickDeleteMenuItem?: (pageToDelete: IPageForPageDeleteModal) => void,
     onClickDeleteMenuItem?: (pageToDelete: IPageForPageDeleteModal) => void,
 ): JSX.Element => {
 ): JSX.Element => {
@@ -122,8 +122,8 @@ const ItemsTree: FC<ItemsTreeProps> = (props: ItemsTreeProps) => {
     }
     }
   }, [ancestorsChildrenData]);
   }, [ancestorsChildrenData]);
 
 
-  const onClickDuplicateMenuItem = (pageId: string, path: string) => {
-    openDuplicateModal(pageId, path);
+  const onClickDuplicateMenuItem = (pageToDuplicate: IPageForPageDuplicateModal) => {
+    openDuplicateModal(pageToDuplicate);
   };
   };
 
 
   const onClickRenameMenuItem = (pageToRename: IPageForPageRenameModal) => {
   const onClickRenameMenuItem = (pageToRename: IPageForPageRenameModal) => {

+ 16 - 6
packages/app/src/stores/modal.tsx

@@ -82,25 +82,35 @@ export type IPageForPageDuplicateModal = {
   path: string
   path: string
 }
 }
 
 
+export type IDuplicateModalOption = {
+  onDeleted?: OnDeletedFunction,
+}
+
 type DuplicateModalStatus = {
 type DuplicateModalStatus = {
   isOpened: boolean,
   isOpened: boolean,
-  pageId?: string,
-  path?: string,
+  page?: IPageForPageDuplicateModal,
+  opts?: IDuplicateModalOption,
 }
 }
 
 
 type DuplicateModalStatusUtils = {
 type DuplicateModalStatusUtils = {
-  open(pageId: string, path: string): Promise<DuplicateModalStatus | undefined>
+  open(
+    page?: IPageForPageDuplicateModal,
+    opts?: IRenameModalOption
+  ): Promise<DuplicateModalStatus | undefined>
   close(): Promise<DuplicateModalStatus | undefined>
   close(): Promise<DuplicateModalStatus | undefined>
 }
 }
 
 
 export const usePageDuplicateModal = (status?: DuplicateModalStatus): SWRResponse<DuplicateModalStatus, Error> & DuplicateModalStatusUtils => {
 export const usePageDuplicateModal = (status?: DuplicateModalStatus): SWRResponse<DuplicateModalStatus, Error> & DuplicateModalStatusUtils => {
-  const initialData: DuplicateModalStatus = { isOpened: false, pageId: '', path: '' };
+  const initialData: DuplicateModalStatus = { isOpened: false, page: { pageId: '', path: '/' } };
   const swrResponse = useStaticSWR<DuplicateModalStatus, Error>('duplicateModalStatus', status, { fallbackData: initialData });
   const swrResponse = useStaticSWR<DuplicateModalStatus, Error>('duplicateModalStatus', status, { fallbackData: initialData });
 
 
   return {
   return {
     ...swrResponse,
     ...swrResponse,
-    open: (pageId: string, path: string) => swrResponse.mutate({ isOpened: true, pageId, path }),
-    close: () => swrResponse.mutate({ isOpened: false }),
+    open: (
+        page?: IPageForPageDuplicateModal,
+        opts?: IDuplicateModalOption,
+    ) => swrResponse.mutate({ isOpened: true, page, opts }),
+    close: () => swrResponse.mutate({ isOpened: false, page: { pageId: '', path: '/' } }),
   };
   };
 };
 };