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

Merge branch 'master' into feat/page-operation-block-resume

Taichi Masuyama 4 лет назад
Родитель
Сommit
46e18155dd

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

@@ -13,7 +13,7 @@ import {
 } from '~/stores/ui';
 import {
   usePageAccessoriesModal, PageAccessoriesModalContents,
-  usePageDuplicateModal, usePageRenameModal, usePageDeleteModal, OnDeletedFunction, usePagePresentationModal,
+  usePageDuplicateModal, usePageRenameModal, usePageDeleteModal, OnDeletedFunction, usePagePresentationModal, IPageForPageDeleteModal,
 } from '~/stores/modal';
 
 
@@ -202,8 +202,8 @@ const GrowiContextualSubNavigation = (props) => {
     }
   }, []);
 
-  const deleteItemClickedHandler = useCallback(async(pageToDelete, isAbleToDeleteCompletely) => {
-    openDeleteModal([pageToDelete], isAbleToDeleteCompletely, onDeletedHandler);
+  const deleteItemClickedHandler = useCallback((pageToDelete: IPageForPageDeleteModal) => {
+    openDeleteModal([pageToDelete], { onDeleted: onDeletedHandler });
   }, [onDeletedHandler, openDeleteModal]);
 
   const templateMenuItemClickHandler = useCallback(() => {

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

@@ -26,7 +26,7 @@ type CommonProps = {
   additionalMenuItemRenderer?: React.FunctionComponent<AdditionalMenuItemsRendererProps>,
   onClickDuplicateMenuItem?: (pageId: string, path: string) => void,
   onClickRenameMenuItem?: (pageId: string, revisionId: string, path: string) => void,
-  onClickDeleteMenuItem?: (pageToDelete: IPageForPageDeleteModal | null, isAbleToDeleteCompletely: boolean) => void,
+  onClickDeleteMenuItem?: (pageToDelete: IPageForPageDeleteModal) => void,
 }
 
 type SubNavButtonsSubstanceProps = CommonProps & {
@@ -121,9 +121,10 @@ const SubNavButtonsSubstance = (props: SubNavButtonsSubstanceProps): JSX.Element
       pageId,
       revisionId,
       path,
+      isAbleToDeleteCompletely: pageInfo.isAbleToDeleteCompletely,
     };
 
-    onClickDeleteMenuItem(pageToDelete, pageInfo.isAbleToDeleteCompletely);
+    onClickDeleteMenuItem(pageToDelete);
   }, [onClickDeleteMenuItem, pageId, pageInfo.isAbleToDeleteCompletely, path, revisionId]);
 
   if (!isIPageInfoForOperation(pageInfo)) {

+ 10 - 10
packages/app/src/components/Page/TrashPageAlert.jsx

@@ -11,7 +11,7 @@ import PageContainer from '~/client/services/PageContainer';
 import EmptyTrashModal from '../EmptyTrashModal';
 
 import { useCurrentUpdatedAt, useShareLinkId } from '~/stores/context';
-import { usePageDeleteModal, usePutBackPageMOdal } from '~/stores/modal';
+import { usePageDeleteModal, usePutBackPageModal } from '~/stores/modal';
 import { useSWRxPageInfo } from '~/stores/page';
 
 const TrashPageAlert = (props) => {
@@ -32,14 +32,8 @@ const TrashPageAlert = (props) => {
   const [isEmptyTrashModalShown, setIsEmptyTrashModalShown] = useState(false);
   const [isAbleToDeleteCompletely, setIsAbleToDeleteCompletely] = useState(false);
 
-  useEffect(() => {
-    if (pageInfo != null) {
-      setIsAbleToDeleteCompletely(pageInfo.isAbleToDeleteCompletely);
-    }
-  }, [pageInfo]);
-
   const { open: openDeleteModal } = usePageDeleteModal();
-  const { open: openPutBackPageModal } = usePutBackPageMOdal();
+  const { open: openPutBackPageModal } = usePutBackPageModal();
 
   function openEmptyTrashModalHandler() {
     setIsEmptyTrashModalShown(true);
@@ -68,8 +62,14 @@ const TrashPageAlert = (props) => {
       revisionId,
       path,
     };
-    const isDeleteCompletelyModal = true;
-    openDeleteModal([pageToDelete], isAbleToDeleteCompletely, onDeletedHandler, isDeleteCompletelyModal);
+    openDeleteModal(
+      [pageToDelete],
+      {
+        isAbleToDeleteCompletely: pageInfo.isAbleToDeleteCompletely,
+        forceDeleteCompletelyMode: true,
+        onDeletedHandler,
+      },
+    );
   }
 
   function renderEmptyButton() {

+ 18 - 19
packages/app/src/components/PageDeleteModal.tsx

@@ -1,4 +1,4 @@
-import React, { useState, useEffect, FC } from 'react';
+import React, { useState, FC } from 'react';
 import {
   Modal, ModalHeader, ModalBody, ModalFooter,
 } from 'reactstrap';
@@ -32,11 +32,10 @@ const PageDeleteModal: FC = () => {
   const { data: deleteModalData, close: closeDeleteModal } = usePageDeleteModal();
 
   const isOpened = deleteModalData?.isOpened ?? false;
-  const isAbleToDeleteCompletely = deleteModalData?.isAbleToDeleteCompletely ?? false;
-  const isDeleteCompletelyModal = deleteModalData?.isDeleteCompletelyModal ?? false;
+  const forceDeleteCompletelyMode = deleteModalData?.opts?.forceDeleteCompletelyMode ?? false;
 
   const [isDeleteRecursively, setIsDeleteRecursively] = useState(true);
-  const [isDeleteCompletely, setIsDeleteCompletely] = useState(false);
+  const [isDeleteCompletely, setIsDeleteCompletely] = useState(forceDeleteCompletelyMode);
   const deleteMode = isDeleteCompletely ? 'completely' : 'temporary';
 
   // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -46,12 +45,8 @@ const PageDeleteModal: FC = () => {
     setIsDeleteRecursively(!isDeleteRecursively);
   }
 
-  useEffect(() => {
-    setIsDeleteCompletely(isDeleteCompletelyModal && isAbleToDeleteCompletely);
-  }, [isAbleToDeleteCompletely, isDeleteCompletelyModal]);
-
   function changeIsDeleteCompletelyHandler() {
-    if (!isAbleToDeleteCompletely) {
+    if (forceDeleteCompletelyMode) {
       return;
     }
     setIsDeleteCompletely(!isDeleteCompletely);
@@ -79,8 +74,9 @@ const PageDeleteModal: FC = () => {
           isCompletely,
         });
 
-        if (deleteModalData.onDeleted != null) {
-          deleteModalData.onDeleted(data.paths, data.isRecursively, data.isCompletely);
+        const onDeleted = deleteModalData.opts?.onDeleted;
+        if (onDeleted != null) {
+          onDeleted(data.paths, data.isRecursively, data.isCompletely);
         }
       }
       catch (err) {
@@ -104,8 +100,9 @@ const PageDeleteModal: FC = () => {
           completely,
         }) as IDeleteSinglePageApiv1Result;
 
-        if (deleteModalData.onDeleted != null) {
-          deleteModalData.onDeleted(path, isRecursively, isCompletely);
+        const onDeleted = deleteModalData.opts?.onDeleted;
+        if (onDeleted != null) {
+          onDeleted(path, isRecursively, isCompletely);
         }
       }
       catch (err) {
@@ -138,12 +135,14 @@ const PageDeleteModal: FC = () => {
     );
   }
 
-  // DeleteCompletely is currently disabled
-  // TODO1 : Retrive isAbleToDeleteCompleltly state everywhere in the system via swr.
-  // Story: https://redmine.weseek.co.jp/issues/82222
-  // TODO2 : use toaster
-  // TASK : https://redmine.weseek.co.jp/issues/82299
   function renderDeleteCompletelyForm() {
+    let isAbleToDeleteCompletely = false;
+
+    // modify isAbleToDeleteCompletely value if every page allows completely deletion
+    if (deleteModalData != null && deleteModalData.pages != null) {
+      isAbleToDeleteCompletely = deleteModalData.pages.every(page => page.isAbleToDeleteCompletely);
+    }
+
     return (
       <div className="custom-control custom-checkbox custom-checkbox-danger">
         <input
@@ -190,7 +189,7 @@ const PageDeleteModal: FC = () => {
           {renderPagePathsToDelete()}
         </div>
         {renderDeleteRecursivelyForm()}
-        {!isDeleteCompletelyModal && renderDeleteCompletelyForm()}
+        {!forceDeleteCompletelyMode && renderDeleteCompletelyForm()}
       </ModalBody>
       <ModalFooter>
         <ApiErrorMessageList errs={errs} />

+ 26 - 7
packages/app/src/components/PageList/PageListItemL.tsx

@@ -13,7 +13,7 @@ import { UserPicture, PageListMeta } from '@growi/ui';
 import { DevidedPagePath } from '@growi/core';
 import { useIsDeviceSmallerThanLg } from '~/stores/ui';
 import {
-  usePageRenameModal, usePageDuplicateModal, usePageDeleteModal, OnDeletedFunction, usePutBackPageMOdal,
+  usePageRenameModal, usePageDuplicateModal, usePageDeleteModal, usePutBackPageModal,
 } from '~/stores/modal';
 import {
   IPageInfoAll, IPageWithMeta, isIPageInfoForEntity, isIPageInfoForListing,
@@ -66,17 +66,18 @@ const PageListItemLSubstance: ForwardRefRenderFunction<ISelectable, Props> = (pr
   const { open: openDuplicateModal } = usePageDuplicateModal();
   const { open: openRenameModal } = usePageRenameModal();
   const { open: openDeleteModal } = usePageDeleteModal();
-  const { open: openPutBackPageModal } = usePutBackPageMOdal();
+  const { open: openPutBackPageModal } = usePutBackPageModal();
 
   const elasticSearchResult = isIPageSearchMeta(pageMeta) ? pageMeta.elasticSearchResult : null;
   const revisionShortBody = isIPageInfoForListing(pageMeta) ? pageMeta.revisionShortBody : null;
 
-  const dPagePath: DevidedPagePath = new DevidedPagePath(pageData.path, true);
+  const dPagePath: DevidedPagePath = new DevidedPagePath(elasticSearchResult?.highlightedPath || pageData.path, true);
   const linkedPagePathFormer = new LinkedPagePath(dPagePath.former);
   const linkedPagePathLatter = new LinkedPagePath(dPagePath.latter);
 
   const lastUpdateDate = format(new Date(pageData.updatedAt), 'yyyy/MM/dd HH:mm:ss');
 
+
   // click event handler
   const clickHandler = useCallback(() => {
     // do nothing if mobile
@@ -102,8 +103,11 @@ const PageListItemLSubstance: ForwardRefRenderFunction<ISelectable, Props> = (pr
 
   const deleteMenuItemClickHandler = useCallback((_id, pageInfo) => {
     const { _id: pageId, revision: revisionId, path } = pageData;
-    const pageToDelete = { pageId, revisionId: revisionId as string, path };
-    openDeleteModal([pageToDelete], pageInfo.isAbleToDeleteCompletely);
+    const isAbleToDeleteCompletely = pageInfo.isAbleToDeleteCompletely;
+    const pageToDelete = {
+      pageId, revisionId: revisionId as string, path, isAbleToDeleteCompletely,
+    };
+    openDeleteModal([pageToDelete]);
   }, [pageData, openDeleteModal]);
 
   const revertMenuItemClickHandler = useCallback(() => {
@@ -115,6 +119,8 @@ const PageListItemLSubstance: ForwardRefRenderFunction<ISelectable, Props> = (pr
   // background color of list item changes when class "active" exists under 'list-group-item'
   const styleActive = !isDeviceSmallerThanLg && isSelected ? 'active' : '';
 
+  const shouldDangerouslySetInnerHTMLForPaths = elasticSearchResult != null && elasticSearchResult.highlightedPath.length > 0;
+
   return (
     <li
       key={pageData._id}
@@ -141,7 +147,10 @@ const PageListItemLSubstance: ForwardRefRenderFunction<ISelectable, Props> = (pr
           <div className="flex-grow-1 p-md-3 pl-2 py-3 pr-3">
             <div className="d-flex justify-content-between">
               {/* page path */}
-              <PagePathHierarchicalLink linkedPagePath={linkedPagePathFormer} />
+              <PagePathHierarchicalLink
+                linkedPagePath={linkedPagePathFormer}
+                shouldDangerouslySetInnerHTML={shouldDangerouslySetInnerHTMLForPaths}
+              />
               { showPageUpdatedTime && (
                 <span className="page-list-updated-at text-muted">Last update: {lastUpdateDate}</span>
               ) }
@@ -156,7 +165,17 @@ const PageListItemLSubstance: ForwardRefRenderFunction<ISelectable, Props> = (pr
                 <span className="h5 mb-0">
                   {/* Use permanent links to care for pages with the same name (Cannot use page path url) */}
                   <span className="grw-page-path-hierarchical-link text-break">
-                    <a className="page-segment" href={encodeURI(urljoin('/', pageData._id))}>{linkedPagePathLatter.pathName}</a>
+                    {shouldDangerouslySetInnerHTMLForPaths
+                      ? (
+                        <a
+                          className="page-segment"
+                          href={encodeURI(urljoin('/', pageData._id))}
+                          dangerouslySetInnerHTML={{ __html: linkedPagePathLatter.pathName }}
+                        >
+                        </a>
+                      )
+                      : <a className="page-segment" href={encodeURI(urljoin('/', pageData._id))}>{linkedPagePathLatter.pathName}</a>
+                    }
                   </span>
                 </span>
               </Clamp>

+ 17 - 4
packages/app/src/components/PagePathHierarchicalLink.jsx

@@ -7,8 +7,9 @@ import LinkedPagePath from '../models/linked-page-path';
 
 
 const PagePathHierarchicalLink = (props) => {
-  const { linkedPagePath, basePath, isInTrash } = props;
-
+  const {
+    linkedPagePath, basePath, isInTrash, shouldDangerouslySetInnerHTML,
+  } = props;
   // render root element
   if (linkedPagePath.isRoot) {
     if (basePath != null) {
@@ -52,13 +53,24 @@ const PagePathHierarchicalLink = (props) => {
   return (
     <RootElm>
       { isParentExists && (
-        <PagePathHierarchicalLink linkedPagePath={linkedPagePath.parent} basePath={basePath} isInTrash={isInTrash || linkedPagePath.isInTrash} isInnerElem />
+        <PagePathHierarchicalLink
+          linkedPagePath={linkedPagePath.parent}
+          basePath={basePath}
+          isInTrash={isInTrash || linkedPagePath.isInTrash}
+          isInnerElem
+          shouldDangerouslySetInnerHTML={shouldDangerouslySetInnerHTML}
+        />
       ) }
       { isSeparatorRequired && (
         <span className="separator">/</span>
       ) }
 
-      <a className="page-segment" href={href}>{linkedPagePath.pathName}</a>
+      {
+        shouldDangerouslySetInnerHTML
+          ? <a className="page-segment" href={href} dangerouslySetInnerHTML={{ __html: linkedPagePath.pathName }}></a>
+          : <a className="page-segment" href={href}>{linkedPagePath.pathName}</a>
+      }
+
     </RootElm>
   );
 };
@@ -67,6 +79,7 @@ PagePathHierarchicalLink.propTypes = {
   linkedPagePath: PropTypes.instanceOf(LinkedPagePath).isRequired,
   basePath: PropTypes.string,
   isInTrash: PropTypes.bool,
+  shouldDangerouslySetInnerHTML: PropTypes.bool,
 
   // !!INTERNAL USE ONLY!!
   isInnerElem: PropTypes.bool,

+ 6 - 3
packages/app/src/components/PrivateLegacyPages.tsx

@@ -1,5 +1,5 @@
 import React, {
-  useCallback, useMemo, useRef, useState,
+  useCallback, useEffect, useMemo, useRef, useState,
 } from 'react';
 import { useTranslation } from 'react-i18next';
 
@@ -214,16 +214,19 @@ export const PrivateLegacyPages = (props: Props): JSX.Element => {
     });
   }, [configurationsByPagination]);
 
+  const hitsCount = data?.meta.hitsCount;
   const { offset, limit } = conditions;
 
   const searchControl = useMemo(() => {
+    const isCheckboxDisabled = hitsCount === 0;
+
     return (
       <div className="shadow-sm">
         <div className="search-control d-flex align-items-center py-md-2 py-3 px-md-4 px-3 border-bottom border-gray">
           <div className="d-flex pl-md-2">
             <OperateAllControl
               ref={selectAllControlRef}
-              isCheckboxDisabled={!isControlEnabled}
+              isCheckboxDisabled={isCheckboxDisabled}
               onCheckboxChanged={selectAllCheckboxChangedHandler}
             >
               <UncontrolledButtonDropdown>
@@ -248,7 +251,7 @@ export const PrivateLegacyPages = (props: Props): JSX.Element => {
         </div>
       </div>
     );
-  }, [convertMenuItemClickedHandler, isControlEnabled, selectAllCheckboxChangedHandler, t]);
+  }, [convertMenuItemClickedHandler, hitsCount, isControlEnabled, selectAllCheckboxChangedHandler, t]);
 
   const searchResultListHead = useMemo(() => {
     if (data == null) {

+ 2 - 2
packages/app/src/components/PutbackPageModal.jsx

@@ -7,7 +7,7 @@ import {
 
 import { withTranslation } from 'react-i18next';
 
-import { usePutBackPageMOdal } from '~/stores/modal';
+import { usePutBackPageModal } from '~/stores/modal';
 import { apiPost } from '~/client/util/apiv1-client';
 
 import ApiErrorMessageList from './PageManagement/ApiErrorMessageList';
@@ -17,7 +17,7 @@ const PutBackPageModal = (props) => {
     t,
   } = props;
 
-  const { data: pageDataToRevert, close: closePutBackPageModal } = usePutBackPageMOdal();
+  const { data: pageDataToRevert, close: closePutBackPageModal } = usePutBackPageModal();
   const { isOpened, pageId, path } = pageDataToRevert;
 
   const [errs, setErrs] = useState(null);

+ 5 - 12
packages/app/src/components/SearchPage/SearchResultContent.tsx

@@ -18,7 +18,7 @@ import { SubNavButtons } from '../Navbar/SubNavButtons';
 import { AdditionalMenuItemsRendererProps, ForceHideMenuItems } from '../Common/Dropdown/PageItemControl';
 
 import {
-  usePageDuplicateModal, usePageRenameModal, usePageDeleteModal, OnDeletedFunction,
+  usePageDuplicateModal, usePageRenameModal, usePageDeleteModal,
 } from '~/stores/modal';
 
 
@@ -115,16 +115,9 @@ export const SearchResultContent: FC<Props> = (props: Props) => {
     openRenameModal(pageId, revisionId, path);
   }, [openRenameModal]);
 
-  const onDeletedHandler: OnDeletedFunction = useCallback((pathOrPathsToDelete, isRecursively, isCompletely) => {
-    if (typeof pathOrPathsToDelete !== 'string') {
-      return;
-    }
-    window.location.reload();
-  }, []);
-
-  const deleteItemClickedHandler = useCallback(async(pageToDelete, isAbleToDeleteCompletely) => {
-    openDeleteModal([pageToDelete], isAbleToDeleteCompletely, onDeletedHandler);
-  }, [onDeletedHandler, openDeleteModal]);
+  const deleteItemClickedHandler = useCallback((pageToDelete) => {
+    openDeleteModal([pageToDelete]);
+  }, [openDeleteModal]);
 
   const ControlComponents = useCallback(() => {
     if (page == null) {
@@ -155,7 +148,7 @@ export const SearchResultContent: FC<Props> = (props: Props) => {
         </div>
       </>
     );
-  }, [page, showPageControlDropdown, duplicateItemClickedHandler, renameItemClickedHandler, deleteItemClickedHandler]);
+  }, [page, showPageControlDropdown, forceHideMenuItems, duplicateItemClickedHandler, renameItemClickedHandler, deleteItemClickedHandler]);
 
   // return if page is null
   if (page == null) return <></>;

+ 6 - 10
packages/app/src/components/Sidebar/PageTree/Item.tsx

@@ -13,9 +13,7 @@ import { pathUtils, pagePathUtils } from '@growi/core';
 import { toastWarning, toastError, toastSuccess } from '~/client/util/apiNotification';
 
 import { useSWRxPageChildren } from '~/stores/page-listing';
-import { useSWRxPageInfo } from '~/stores/page';
 import { apiv3Put, apiv3Post } from '~/client/util/apiv3-client';
-import { useShareLinkId } from '~/stores/context';
 import { IPageForPageDeleteModal } from '~/stores/modal';
 
 import TriangleIcon from '~/components/Icons/TriangleIcon';
@@ -32,7 +30,7 @@ interface ItemProps {
   isEnabledAttachTitleHeader?: boolean
   onClickDuplicateMenuItem?(pageId: string, path: string): void
   onClickRenameMenuItem?(pageId: string, revisionId: string, path: string): void
-  onClickDeleteMenuItem?(pageToDelete: IPageForPageDeleteModal | null, isAbleToDeleteCompletely: boolean, callback?: VoidFunction): void
+  onClickDeleteMenuItem?(pageToDelete: IPageForPageDeleteModal, callback?: VoidFunction): void
   onSelfDeleted?: VoidFunction
 }
 
@@ -81,8 +79,6 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
   const { page, children } = itemNode;
 
   const [pageTitle, setPageTitle] = useState(page.path);
-  const { data: shareLinkId } = useShareLinkId();
-  const { data: pageInfo } = useSWRxPageInfo(page._id ?? null, shareLinkId);
   const [currentChildren, setCurrentChildren] = useState(children);
   const [isOpen, setIsOpen] = useState(_isOpen);
   const [isNewPageInputShown, setNewPageInputShown] = useState(false);
@@ -245,7 +241,7 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
     onClickRenameMenuItem(pageId, revisionId as string, path);
   }, [onClickRenameMenuItem, page]);
 
-  const deleteMenuItemClickHandler = useCallback(async(_pageId: string): Promise<void> => {
+  const deleteMenuItemClickHandler = useCallback(async(_pageId: string, pageInfo): Promise<void> => {
     if (onClickDeleteMenuItem == null) {
       return;
     }
@@ -260,13 +256,13 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
       pageId,
       revisionId: revisionId as string,
       path,
+      isAbleToDeleteCompletely: pageInfo?.isAbleToDeleteCompletely,
     };
-    const isAbleToDeleteCompletely = pageInfo?.isAbleToDeleteCompletely ?? false;
 
-    onClickDeleteMenuItem(pageToDelete, isAbleToDeleteCompletely, async() => {
+    onClickDeleteMenuItem(pageToDelete, async() => {
       if (onSelfDeleted != null) await onSelfDeleted();
     });
-  }, [onClickDeleteMenuItem, page, pageInfo?.isAbleToDeleteCompletely, onSelfDeleted]);
+  }, [onClickDeleteMenuItem, page, onSelfDeleted]);
 
   const onPressEnterForCreateHandler = async(inputText: string) => {
     setNewPageInputShown(false);
@@ -390,8 +386,8 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
             isEnableActions={isEnableActions}
             onClickBookmarkMenuItem={bookmarkMenuItemClickHandler}
             onClickDuplicateMenuItem={duplicateMenuItemClickHandler}
-            onClickDeleteMenuItem={deleteMenuItemClickHandler}
             onClickRenameMenuItem={renameMenuItemClickHandler}
+            onClickDeleteMenuItem={deleteMenuItemClickHandler}
           >
             <DropdownToggle color="transparent" className="border-0 rounded btn-page-item-control p-0">
               <i className="icon-options fa fa-rotate-90 text-muted p-1"></i>

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

@@ -67,7 +67,7 @@ const renderByInitialNode = (
     isEnabledAttachTitleHeader?: boolean,
     onClickDuplicateMenuItem?: (pageId: string, path: string) => void,
     onClickRenameMenuItem?: (pageId: string, revisionId: string, path: string) => void,
-    onClickDeleteMenuItem?: (pageToDelete: IPageForPageDeleteModal | null, isAbleToDeleteCompletely: boolean, onItemDeleted: VoidFunction) => void,
+    onClickDeleteMenuItem?: (pageToDelete: IPageForPageDeleteModal, onItemDeleted: VoidFunction) => void,
 ): JSX.Element => {
 
   return (
@@ -122,7 +122,7 @@ const ItemsTree: FC<ItemsTreeProps> = (props: ItemsTreeProps) => {
     openRenameModal(pageId, revisionId, path);
   };
 
-  const onClickDeleteMenuItem = (pageToDelete: IPageForPageDeleteModal, isAbleToDeleteCompletely, onItemDeleted: VoidFunction) => {
+  const onClickDeleteMenuItem = (pageToDelete: IPageForPageDeleteModal, onItemDeleted: VoidFunction) => {
     const onDeletedHandler: OnDeletedFunction = (pathOrPathsToDelete, isRecursively, isCompletely) => {
       if (typeof pathOrPathsToDelete !== 'string') {
         return;
@@ -140,7 +140,7 @@ const ItemsTree: FC<ItemsTreeProps> = (props: ItemsTreeProps) => {
       }
     };
 
-    openDeleteModal([pageToDelete], isAbleToDeleteCompletely, onDeletedHandler);
+    openDeleteModal([pageToDelete], { onDeleted: onDeletedHandler });
   };
 
   if (error1 != null || error2 != null) {

+ 1 - 4
packages/app/src/server/service/page.ts

@@ -221,15 +221,12 @@ class PageService {
 
     const Page = this.crowi.model('Page');
 
-    let pagePath = path;
-
     let page: PageModel & PageDocument & HasObjectId;
     if (pageId != null) { // prioritized
       page = await Page.findByIdAndViewer(pageId, user);
-      pagePath = page.path;
     }
     else {
-      page = await Page.findByPathAndViewer(pagePath, user);
+      page = await Page.findByPathAndViewer(path, user);
     }
 
     if (page == null) {

+ 11 - 17
packages/app/src/stores/modal.tsx

@@ -1,7 +1,6 @@
 import { SWRResponse } from 'swr';
 import { useStaticSWR } from './use-static-swr';
 import { Nullable } from '~/interfaces/common';
-import { IPageInfo } from '~/interfaces/page';
 
 
 /*
@@ -36,7 +35,11 @@ export type IPageForPageDeleteModal = {
   revisionId?: string,
   path: string
   isAbleToDeleteCompletely?: boolean,
-  isDeleteCompletelyModal?: boolean,
+}
+
+export type IDeleteModalOption = {
+  onDeleted?: OnDeletedFunction,
+  forceDeleteCompletelyMode?: boolean,
 }
 
 export type OnDeletedFunction = (pathOrPaths: string | string[], isRecursively: Nullable<true>, isCompletely: Nullable<true>) => void;
@@ -44,17 +47,13 @@ export type OnDeletedFunction = (pathOrPaths: string | string[], isRecursively:
 type DeleteModalStatus = {
   isOpened: boolean,
   pages?: IPageForPageDeleteModal[],
-  isAbleToDeleteCompletely?: boolean,
-  onDeleted?: OnDeletedFunction,
-  isDeleteCompletelyModal?: boolean,
+  opts?: IDeleteModalOption,
 }
 
 type DeleteModalStatusUtils = {
   open(
     pages?: IPageForPageDeleteModal[],
-    isAbleToDeleteCompletely?: boolean,
-    onDeleted?: OnDeletedFunction,
-    isDeleteCompletelyModal?: boolean,
+    opts?: IDeleteModalOption,
   ): Promise<DeleteModalStatus | undefined>,
   close(): Promise<DeleteModalStatus | undefined>,
 }
@@ -63,9 +62,6 @@ export const usePageDeleteModal = (status?: DeleteModalStatus): SWRResponse<Dele
   const initialData: DeleteModalStatus = {
     isOpened: false,
     pages: [],
-    onDeleted: () => {},
-    isAbleToDeleteCompletely: false,
-    isDeleteCompletelyModal: false,
   };
   const swrResponse = useStaticSWR<DeleteModalStatus, Error>('deleteModalStatus', status, { fallbackData: initialData });
 
@@ -73,13 +69,11 @@ export const usePageDeleteModal = (status?: DeleteModalStatus): SWRResponse<Dele
     ...swrResponse,
     open: (
         pages?: IPageForPageDeleteModal[],
-        isAbleToDeleteCompletely?: boolean,
-        onDeleted?: OnDeletedFunction,
-        isDeleteCompletelyModal?: boolean,
+        opts?: IDeleteModalOption,
     ) => swrResponse.mutate({
-      isOpened: true, pages, isAbleToDeleteCompletely, onDeleted, isDeleteCompletelyModal,
+      isOpened: true, pages, opts,
     }),
-    close: () => swrResponse.mutate({ isOpened: false }),
+    close: () => swrResponse.mutate({ isOpened: false, pages: [], opts: undefined }),
   };
 };
 
@@ -161,7 +155,7 @@ type PutBackPageModalUtils = {
   close():Promise<PutBackPageModalStatus | undefined>
 }
 
-export const usePutBackPageMOdal = (status?: PutBackPageModalStatus): SWRResponse<PutBackPageModalStatus, Error> & PutBackPageModalUtils => {
+export const usePutBackPageModal = (status?: PutBackPageModalStatus): SWRResponse<PutBackPageModalStatus, Error> & PutBackPageModalUtils => {
   const initialData = { isOpened: false, pageId: '', path: '' };
   const swrResponse = useStaticSWR<PutBackPageModalStatus, Error>('putBackPageModalStatus', status, { fallbackData: initialData });