Преглед изворни кода

Implement rename page

https://youtrack.weseek.co.jp/issue/GW-7840
- Implement click rename handler
- Implement ClosableTextInput for new name input
- Add method to rename page and update bookmark list
Mudana-Grune пре 3 година
родитељ
комит
de7a8c908d
1 измењених фајлова са 64 додато и 5 уклоњено
  1. 64 5
      packages/app/src/components/Sidebar/Bookmarks.tsx

+ 64 - 5
packages/app/src/components/Sidebar/Bookmarks.tsx

@@ -1,44 +1,103 @@
 
 
-import React, { useCallback, useEffect } from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
 
 
-import { DevidedPagePath } from '@growi/core';
+import nodePath from 'path';
+
+import { DevidedPagePath, pathUtils } from '@growi/core';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import { UncontrolledTooltip, DropdownToggle } from 'reactstrap';
 import { UncontrolledTooltip, DropdownToggle } from 'reactstrap';
 
 
 import { unbookmark } from '~/client/services/page-operation';
 import { unbookmark } from '~/client/services/page-operation';
+import { toastError, toastSuccess } from '~/client/util/apiNotification';
+import { apiv3Put } from '~/client/util/apiv3-client';
 import LinkedPagePath from '~/models/linked-page-path';
 import LinkedPagePath from '~/models/linked-page-path';
 import { useSWRInifiniteBookmarkedPage } from '~/stores/bookmark';
 import { useSWRInifiniteBookmarkedPage } from '~/stores/bookmark';
 import { useCurrentUser } from '~/stores/context';
 import { useCurrentUser } from '~/stores/context';
 
 
+import ClosableTextInput, { AlertInfo, AlertType } from '../Common/ClosableTextInput';
 import { MenuItemType, PageItemControl } from '../Common/Dropdown/PageItemControl';
 import { MenuItemType, PageItemControl } from '../Common/Dropdown/PageItemControl';
 import PagePathHierarchicalLink from '../PagePathHierarchicalLink';
 import PagePathHierarchicalLink from '../PagePathHierarchicalLink';
 
 
 import InfiniteScroll from './InfiniteScroll';
 import InfiniteScroll from './InfiniteScroll';
 
 
 const BookmarksItem = ({ data, swr }) : JSX.Element => {
 const BookmarksItem = ({ data, swr }) : JSX.Element => {
+  const { t } = useTranslation('');
   const { page } = data;
   const { page } = data;
   const dPagePath = new DevidedPagePath(page.path, false, true);
   const dPagePath = new DevidedPagePath(page.path, false, true);
   const linkedPagePathLatter = new LinkedPagePath(dPagePath.latter);
   const linkedPagePathLatter = new LinkedPagePath(dPagePath.latter);
   const bookmarkItemId = `bookmark-item-${data._id}`;
   const bookmarkItemId = `bookmark-item-${data._id}`;
+  const [isRenameInputShown, setRenameInputShown] = useState(false);
 
 
   const bookmarkMenuItemClickHandler = useCallback(async() => {
   const bookmarkMenuItemClickHandler = useCallback(async() => {
     await unbookmark(page.id);
     await unbookmark(page.id);
     swr.mutate();
     swr.mutate();
   }, [swr, page]);
   }, [swr, page]);
 
 
+  const renameMenuItemClickHandler = useCallback(() => {
+    setRenameInputShown(true);
+  }, []);
+
+  const inputValidator = (title: string | null): AlertInfo | null => {
+    if (title == null || title === '' || title.trim() === '') {
+      return {
+        type: AlertType.WARNING,
+        message: t('form_validation.title_required'),
+      };
+    }
+
+    return null;
+  };
+
+  const onPressEnterForRenameHandler = useCallback(async(inputText: string) => {
+    const parentPath = pathUtils.addTrailingSlash(nodePath.dirname(page.path ?? ''));
+    const newPagePath = nodePath.resolve(parentPath, inputText);
+
+    if (newPagePath === page.path) {
+      setRenameInputShown(false);
+      return;
+    }
+
+    try {
+      setRenameInputShown(false);
+      await apiv3Put('/pages/rename', {
+        pageId: page._id,
+        revisionId: page.revision,
+        newPagePath,
+      });
+      swr.mutate();
+      toastSuccess(t('renamed_pages', { path: page.path }));
+    }
+    catch (err) {
+      setRenameInputShown(true);
+      toastError(err);
+    }
+  }, [swr, page, t]);
+
   return (
   return (
     <>
     <>
       <li className="list-group-item py-3 px-2" id={bookmarkItemId}>
       <li className="list-group-item py-3 px-2" id={bookmarkItemId}>
         <div className="d-flex w-100 justify-content-between">
         <div className="d-flex w-100 justify-content-between">
-          <h5 className="my-0">
-            <PagePathHierarchicalLink linkedPagePath={linkedPagePathLatter} basePath={dPagePath.isRoot ? undefined : dPagePath.former} />
-          </h5>
+          { isRenameInputShown ? (
+            <ClosableTextInput
+              value={nodePath.basename(page.path ?? '')}
+              placeholder={t('Input page name')}
+              onClickOutside={() => { setRenameInputShown(false) }}
+              onPressEnter={onPressEnterForRenameHandler}
+              inputValidator={inputValidator}
+            />
+          ) : (
+            <h5 className="my-0">
+              <PagePathHierarchicalLink linkedPagePath={linkedPagePathLatter} basePath={dPagePath.isRoot ? undefined : dPagePath.former} />
+            </h5>
+          )}
+
           <PageItemControl
           <PageItemControl
             pageId={page._id}
             pageId={page._id}
             isEnableActions
             isEnableActions
             forceHideMenuItems={[MenuItemType.DUPLICATE]}
             forceHideMenuItems={[MenuItemType.DUPLICATE]}
             onClickBookmarkMenuItem={bookmarkMenuItemClickHandler}
             onClickBookmarkMenuItem={bookmarkMenuItemClickHandler}
+            onClickRenameMenuItem={renameMenuItemClickHandler}
           >
           >
             <DropdownToggle color="transparent" className="border-0 rounded btn-page-item-control p-0 grw-visible-on-hover mr-1">
             <DropdownToggle color="transparent" className="border-0 rounded btn-page-item-control p-0 grw-visible-on-hover mr-1">
               <i className="icon-options fa fa-rotate-90 p-1"></i>
               <i className="icon-options fa fa-rotate-90 p-1"></i>