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

Merge branch 'master' into feat/private-legacy-pages

Yuki Takei 4 лет назад
Родитель
Сommit
3e04e5e7ee

+ 2 - 0
packages/app/resource/locales/en_US/translation.json

@@ -441,6 +441,8 @@
     "recursively": "Delete pages under this path recursively.",
     "completely": "Delete completely instead of putting it into trash."
   },
+  "deleted_pages": "Page(s) has been deleted",
+  "deleted_pages_completely": "Page(s) has been deleted completely",
   "modal_empty":{
     "empty_the_trash": "Empty The Trash",
     "notice": "The pages deleted completely are unrecoverable."

+ 2 - 0
packages/app/resource/locales/ja_JP/translation.json

@@ -440,6 +440,8 @@
     "recursively": "配下のページも削除します",
     "completely": "ゴミ箱を経由せず、完全に削除します"
   },
+  "deleted_pages": "ページをゴミ箱に入れました",
+  "deleted_pages_completely": "ページを完全に削除しました",
   "modal_empty":{
     "empty_the_trash": "ゴミ箱を空にする",
     "notice": "完全削除したページは元に戻すことができません"

+ 1 - 1
packages/app/resource/locales/zh_CN/translation.json

@@ -418,7 +418,7 @@
 		"delete_completely_restriction": "You don't have the authority to delete pages completely.",
 		"recursively": "Delete children of <code>%s</code> recursively.",
 		"completely": "Delete completely instead of putting it into trash."
-	},
+  },
 	"modal_empty": {
 		"empty_the_trash": "Empty The Trash",
 		"notice": "完全删除的页面是不可恢复的。"

+ 24 - 4
packages/app/src/components/Page/TrashPageAlert.jsx

@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useState, useEffect, useCallback } from 'react';
 import PropTypes from 'prop-types';
 
 import { withTranslation } from 'react-i18next';
@@ -10,17 +10,27 @@ import PageContainer from '~/client/services/PageContainer';
 import PutbackPageModal from '../PutbackPageModal';
 import EmptyTrashModal from '../EmptyTrashModal';
 
-import { useCurrentUpdatedAt } from '~/stores/context';
+import { useCurrentUpdatedAt, useShareLinkId } from '~/stores/context';
 import { usePageDeleteModal } from '~/stores/modal';
+import { useSWRxPageInfo } from '~/stores/page';
 
 const TrashPageAlert = (props) => {
   const { t, pageContainer } = props;
   const {
-    pageId, revisionId, path, isDeleted, lastUpdateUsername, deletedUserName, deletedAt, isAbleToDeleteCompletely,
+    pageId, revisionId, path, isDeleted, lastUpdateUsername, deletedUserName, deletedAt,
   } = pageContainer.state;
+  const { data: shareLinkId } = useShareLinkId();
+  const { data: pageInfo } = useSWRxPageInfo(pageId ?? null, shareLinkId);
   const { data: updatedAt } = useCurrentUpdatedAt();
   const [isEmptyTrashModalShown, setIsEmptyTrashModalShown] = useState(false);
   const [isPutbackPageModalShown, setIsPutbackPageModalShown] = useState(false);
+  const [isAbleToDeleteCompletely, setIsAbleToDeleteCompletely] = useState(false);
+
+  useEffect(() => {
+    if (pageInfo != null) {
+      setIsAbleToDeleteCompletely(pageInfo.isAbleToDeleteCompletely);
+    }
+  }, [pageInfo]);
 
   const { open: openDeleteModal } = usePageDeleteModal();
 
@@ -40,13 +50,23 @@ const TrashPageAlert = (props) => {
     setIsPutbackPageModalShown(false);
   }
 
+  const onDeletedHandler = useCallback((pathOrPathsToDelete, isRecursively, isCompletely) => {
+    if (typeof pathOrPathsToDelete !== 'string') {
+      return;
+    }
+
+    const path = pathOrPathsToDelete;
+    window.location.href = path;
+  }, []);
+
   function openPageDeleteModalHandler() {
     const pageToDelete = {
       pageId,
       revisionId,
       path,
     };
-    openDeleteModal([pageToDelete]);
+    const isDeleteCompletelyModal = true;
+    openDeleteModal([pageToDelete], onDeletedHandler, isAbleToDeleteCompletely, isDeleteCompletelyModal);
   }
 
   function renderEmptyButton() {

+ 7 - 2
packages/app/src/components/PageDeleteModal.tsx

@@ -1,4 +1,4 @@
-import React, { useState, FC } from 'react';
+import React, { useState, useEffect, FC } from 'react';
 import {
   Modal, ModalHeader, ModalBody, ModalFooter,
 } from 'reactstrap';
@@ -36,7 +36,7 @@ const PageDeleteModal: FC = () => {
   const isDeleteCompletelyModal = deleteModalData?.isDeleteCompletelyModal ?? false;
 
   const [isDeleteRecursively, setIsDeleteRecursively] = useState(true);
-  const [isDeleteCompletely, setIsDeleteCompletely] = useState(isDeleteCompletelyModal && isAbleToDeleteCompletely);
+  const [isDeleteCompletely, setIsDeleteCompletely] = useState(false);
   const deleteMode = isDeleteCompletely ? 'completely' : 'temporary';
 
   // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -46,6 +46,10 @@ const PageDeleteModal: FC = () => {
     setIsDeleteRecursively(!isDeleteRecursively);
   }
 
+  useEffect(() => {
+    setIsDeleteCompletely(isDeleteCompletelyModal && isAbleToDeleteCompletely);
+  }, [isAbleToDeleteCompletely, isDeleteCompletelyModal]);
+
   function changeIsDeleteCompletelyHandler() {
     if (!isAbleToDeleteCompletely) {
       return;
@@ -128,6 +132,7 @@ const PageDeleteModal: FC = () => {
         />
         <label className="custom-control-label" htmlFor="deleteRecursively">
           { t('modal_delete.delete_recursively') }
+          <p className="form-text text-muted mt-0"> { t('modal_delete.recursively') }</p>
         </label>
       </div>
     );

+ 15 - 7
packages/app/src/components/SearchPage/SearchResultContent.tsx

@@ -1,9 +1,9 @@
 import React, {
   FC, useCallback, useEffect, useRef,
 } from 'react';
+import { useTranslation } from 'react-i18next';
 
 import { DropdownItem } from 'reactstrap';
-import { useTranslation } from 'react-i18next';
 
 import { IPageWithMeta } from '~/interfaces/page';
 import { IPageSearchMeta } from '~/interfaces/search';
@@ -17,7 +17,9 @@ import { GrowiSubNavigation } from '../Navbar/GrowiSubNavigation';
 import { SubNavButtons } from '../Navbar/SubNavButtons';
 import { AdditionalMenuItemsRendererProps, ForceHideMenuItems } from '../Common/Dropdown/PageItemControl';
 
-import { usePageDuplicateModal, usePageRenameModal, usePageDeleteModal } from '~/stores/modal';
+import {
+  usePageDuplicateModal, usePageRenameModal, usePageDeleteModal, OnDeletedFunction,
+} from '~/stores/modal';
 
 
 type AdditionalMenuItemsProps = AdditionalMenuItemsRendererProps & {
@@ -97,12 +99,11 @@ export const SearchResultContent: FC<Props> = (props: Props) => {
     forceHideMenuItems,
   } = props;
 
+  const page = pageWithMeta?.pageData;
   const { open: openDuplicateModal } = usePageDuplicateModal();
   const { open: openRenameModal } = usePageRenameModal();
   const { open: openDeleteModal } = usePageDeleteModal();
 
-  const page = pageWithMeta?.pageData;
-
   const growiRenderer = appContainer.getRenderer('searchresult');
 
 
@@ -114,9 +115,16 @@ export const SearchResultContent: FC<Props> = (props: Props) => {
     openRenameModal(pageId, revisionId, path);
   }, [openRenameModal]);
 
-  const deleteItemClickedHandler = useCallback(async(pageToDelete) => {
-    openDeleteModal([pageToDelete]);
-  }, [openDeleteModal]);
+  const onDeletedHandler: OnDeletedFunction = useCallback((pathOrPathsToDelete, isRecursively, isCompletely) => {
+    if (typeof pathOrPathsToDelete !== 'string') {
+      return;
+    }
+    window.location.reload();
+  }, []);
+
+  const deleteItemClickedHandler = useCallback(async(pageToDelete, isAbleToDeleteCompletely) => {
+    openDeleteModal([pageToDelete], onDeletedHandler, isAbleToDeleteCompletely);
+  }, [onDeletedHandler, openDeleteModal]);
 
   const ControlComponents = useCallback(() => {
     if (page == null) {

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

@@ -372,7 +372,7 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
           />
         )}
         { !isRenameInputShown && ( */}
-        <a href={page._id} className="grw-pagetree-title-anchor flex-grow-1">
+        <a href={`/${page._id}`} className="grw-pagetree-title-anchor flex-grow-1">
           <p className={`text-truncate m-auto ${page.isEmpty && 'text-muted'}`}>{nodePath.basename(pageTitle as string) || '/'}</p>
         </a>
         {/* )} */}

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

@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
 import { IPageHasId } from '../../../interfaces/page';
 import { ItemNode } from './ItemNode';
 import Item from './Item';
-import { useSWRxPageAncestorsChildren, useSWRxPageChildren, useSWRxRootPage } from '../../../stores/page-listing';
+import { useSWRxPageAncestorsChildren, useSWRxPageChildren, useSWRxRootPage } from '~/stores/page-listing';
 import { TargetAndAncestors } from '~/interfaces/page-listing-results';
 import { toastError, toastSuccess } from '~/client/util/apiNotification';
 import {
@@ -127,22 +127,11 @@ const ItemsTree: FC<ItemsTreeProps> = (props: ItemsTreeProps) => {
 
     const path = pathOrPathsToDelete;
 
-    if (isRecursively) {
-      if (isCompletely) {
-        toastSuccess(t('deleted_single_page_recursively_completely', { path }));
-      }
-      else {
-        toastSuccess(t('deleted_single_page_recursively', { path }));
-      }
+    if (isCompletely) {
+      toastSuccess(t('deleted_pages_completely', { path }));
     }
     else {
-      // eslint-disable-next-line no-lonely-if
-      if (isCompletely) {
-        toastSuccess(t('deleted_single_page_completely', { path }));
-      }
-      else {
-        toastSuccess(t('deleted_single_page', { path }));
-      }
+      toastSuccess(t('deleted_pages', { path }));
     }
   };
 

+ 1 - 1
packages/app/src/server/routes/page.js

@@ -890,7 +890,7 @@ module.exports = function(crowi, app) {
    * - If revision_id is not specified => force update by the new contents.
    */
   api.update = async function(req, res) {
-    const pageBody = body ?? null;
+    const pageBody = req.body.body ?? null;
     const pageId = req.body.page_id || null;
     const revisionId = req.body.revision_id || null;
     const grant = req.body.grant || null;