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

create new dropdown item for page

ryoji-s 3 лет назад
Родитель
Сommit
dac25b00fe

+ 35 - 1
apps/app/src/components/Common/Dropdown/PageItemControl.tsx

@@ -1,15 +1,20 @@
-import React, { useState, useCallback, useEffect } from 'react';
+import React, {
+  useState, useCallback, useEffect, useMemo,
+} from 'react';
 
 import { useTranslation } from 'next-i18next';
 import {
   Dropdown, DropdownMenu, DropdownToggle, DropdownItem,
 } from 'reactstrap';
 
+import { addBookmarkToFolder } from '~/client/util/bookmark-utils';
+import { toastError } from '~/client/util/toastr';
 import { NotAvailableForGuest } from '~/components/NotAvailableForGuest';
 import {
   IPageInfoAll, isIPageInfoForOperation,
 } from '~/interfaces/page';
 import { IPageOperationProcessData } from '~/interfaces/page-operation';
+import { useSWRxCurrentUserBookmarks } from '~/stores/bookmark';
 import { useSWRxPageInfo } from '~/stores/page';
 import loggerFactory from '~/utils/logger';
 import { shouldRecoverPagePaths } from '~/utils/page-operation';
@@ -19,6 +24,7 @@ const logger = loggerFactory('growi:cli:PageItemControl');
 
 export const MenuItemType = {
   BOOKMARK: 'bookmark',
+  BOOKMARKS_TREE_MOVE_TO_ROOT: 'bookmarks_tree_move_to_root',
   RENAME: 'rename',
   DUPLICATE: 'duplicate',
   DELETE: 'delete',
@@ -60,6 +66,8 @@ type DropdownMenuProps = CommonProps & {
 const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.Element => {
   const { t } = useTranslation('');
 
+  const { data: userBookmarks, mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks();
+
   const {
     pageId, isLoading, pageInfo, isEnableActions, forceHideMenuItems, operationProcessData,
     onClickBookmarkMenuItem, onClickRenameMenuItem, onClickDuplicateMenuItem, onClickDeleteMenuItem,
@@ -77,6 +85,20 @@ const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.E
     await onClickBookmarkMenuItem(pageId, !pageInfo.isBookmarked);
   }, [onClickBookmarkMenuItem, pageId, pageInfo]);
 
+  const isMoveToRoot = useMemo(() => {
+    return !userBookmarks?.map(userBookmark => userBookmark._id).includes(pageId);
+  }, [pageId, userBookmarks]);
+
+  const moveToRootClickedHandler = useCallback(async() => {
+    try {
+      await addBookmarkToFolder(pageId, null);
+      await mutateUserBookmarks();
+    }
+    catch (err) {
+      toastError(err);
+    }
+  }, [mutateUserBookmarks, pageId]);
+
   // eslint-disable-next-line react-hooks/rules-of-hooks
   const renameItemClickedHandler = useCallback(async() => {
     if (onClickRenameMenuItem == null) {
@@ -172,6 +194,18 @@ const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.E
           </DropdownItem>
         ) }
 
+        {/* Bookmarks Tree Move to Root */}
+        { !forceHideMenuItems?.includes(MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT) && isEnableActions && isMoveToRoot && (
+          <DropdownItem
+            onClick={moveToRootClickedHandler}
+            className="grw-page-control-dropdown-item"
+            data-testid="add-remove-bookmark-btn"
+          >
+            <i className="fa fa-fw fa-bookmark-o grw-page-control-dropdown-icon"></i>
+            {t('bookmark_folder.move_to_root')}
+          </DropdownItem>
+        ) }
+
         {/* Move/Rename */}
         { !forceHideMenuItems?.includes(MenuItemType.RENAME) && isEnableActions && pageInfo.isMovable && (
           <DropdownItem

+ 2 - 1
apps/app/src/components/DescendantsPageListModal.tsx

@@ -11,6 +11,7 @@ import {
 import { useIsSharedUser } from '~/stores/context';
 import { useDescendantsPageListModal } from '~/stores/modal';
 
+import { MenuItemType } from './Common/Dropdown/PageItemControl';
 import { CustomNavTab } from './CustomNavigation/CustomNav';
 import CustomTabContent from './CustomNavigation/CustomTabContent';
 import { DescendantsPageListProps } from './DescendantsPageList';
@@ -51,7 +52,7 @@ export const DescendantsPageListModal = (): JSX.Element => {
           if (status == null || status.path == null || !status.isOpened) {
             return <></>;
           }
-          return <DescendantsPageList path={status.path} />;
+          return <DescendantsPageList path={status.path} forceHideMenuItems={[MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT]}/>;
         },
         i18n: t('page_list'),
         isLinkEnabled: () => !isSharedUser,

+ 2 - 0
apps/app/src/components/IdenticalPathPage.tsx

@@ -6,6 +6,7 @@ import { useTranslation } from 'next-i18next';
 import { useCurrentPathname } from '~/stores/context';
 import { useSWRxPageInfoForList, useSWRxPagesByPath } from '~/stores/page-listing';
 
+import { MenuItemType } from './Common/Dropdown/PageItemControl';
 import { PageListItemL } from './PageList/PageListItemL';
 
 
@@ -76,6 +77,7 @@ export const IdenticalPathPage = (): JSX.Element => {
                 isSelected={false}
                 isEnableActions
                 showPageUpdatedTime
+                forceHideMenuItems={[MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT]}
               />
             );
           })}

+ 1 - 0
apps/app/src/components/Navbar/SubNavButtons.tsx

@@ -207,6 +207,7 @@ const SubNavButtonsSubstance = (props: SubNavButtonsSubstanceProps): JSX.Element
 
   const forceHideMenuItemsWithBookmark = forceHideMenuItems ?? [];
   forceHideMenuItemsWithBookmark.push(MenuItemType.BOOKMARK);
+  forceHideMenuItemsWithBookmark.push(MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT);
 
   return (
     <div className="d-flex" style={{ gap: '2px' }}>

+ 2 - 2
apps/app/src/components/NotFoundPage.tsx

@@ -2,13 +2,13 @@ import React, { useMemo } from 'react';
 
 import { useTranslation } from 'next-i18next';
 
+import { MenuItemType } from './Common/Dropdown/PageItemControl';
 import CustomNavAndContents from './CustomNavigation/CustomNavAndContents';
 import { DescendantsPageList } from './DescendantsPageList';
 import PageListIcon from './Icons/PageListIcon';
 import TimeLineIcon from './Icons/TimeLineIcon';
 import { PageTimeline } from './PageTimeline';
 
-
 type NotFoundPageProps = {
   path: string,
 }
@@ -22,7 +22,7 @@ const NotFoundPage = (props: NotFoundPageProps): JSX.Element => {
     return {
       pagelist: {
         Icon: PageListIcon,
-        Content: () => <DescendantsPageList path={path} />,
+        Content: () => <DescendantsPageList path={path} forceHideMenuItems={[MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT]}/>,
         i18n: t('page_list'),
       },
       timeLine: {

+ 8 - 1
apps/app/src/components/PrivateLegacyPages.tsx

@@ -436,7 +436,14 @@ const PrivateLegacyPages = (): JSX.Element => {
         ref={searchPageBaseRef}
         pages={data?.data}
         onSelectedPagesByCheckboxesChanged={selectedPagesByCheckboxesChangedHandler}
-        forceHideMenuItems={[MenuItemType.BOOKMARK, MenuItemType.RENAME, MenuItemType.DUPLICATE, MenuItemType.REVERT, MenuItemType.PATH_RECOVERY]}
+        forceHideMenuItems={[
+          MenuItemType.BOOKMARK,
+          MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT,
+          MenuItemType.RENAME,
+          MenuItemType.DUPLICATE,
+          MenuItemType.REVERT,
+          MenuItemType.PATH_RECOVERY,
+        ]}
         // Components
         searchControl={searchControl}
         searchResultListHead={searchResultListHead}

+ 2 - 0
apps/app/src/components/SearchPage.tsx

@@ -12,6 +12,7 @@ import { IFormattedSearchResult } from '~/interfaces/search';
 import { useIsSearchServiceReachable, useShowPageLimitationL } from '~/stores/context';
 import { ISearchConditions, ISearchConfigurations, useSWRxSearch } from '~/stores/search';
 
+import { MenuItemType } from './Common/Dropdown/PageItemControl';
 import { NotAvailableForGuest } from './NotAvailableForGuest';
 import PaginationWrapper from './PaginationWrapper';
 import { OperateAllControl } from './SearchPage/OperateAllControl';
@@ -264,6 +265,7 @@ export const SearchPage = (): JSX.Element => {
       searchControl={searchControl}
       searchResultListHead={searchResultListHead}
       searchPager={searchPager}
+      forceHideMenuItems={[MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT]}
     />
   );
 };

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

@@ -30,7 +30,7 @@ import { shouldRecoverPagePaths } from '~/utils/page-operation';
 
 import ClosableTextInput from '../../Common/ClosableTextInput';
 import CountBadge from '../../Common/CountBadge';
-import { PageItemControl } from '../../Common/Dropdown/PageItemControl';
+import { MenuItemType, PageItemControl } from '../../Common/Dropdown/PageItemControl';
 
 import { ItemNode } from './ItemNode';
 
@@ -494,6 +494,7 @@ const Item: FC<ItemProps> = (props: ItemProps) => {
               isInstantRename
               // Todo: It is wanted to find a better way to pass operationProcessData to PageItemControl
               operationProcessData={page.processData}
+              forceHideMenuItems={[MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT]}
             >
               {/* pass the color property to reactstrap dropdownToggle props. https://6-4-0--reactstrap.netlify.app/components/dropdowns/  */}
               <DropdownToggle color="transparent" className="border-0 rounded btn-page-item-control p-0 grw-visible-on-hover mr-1">

+ 1 - 1
apps/app/src/components/TrashPageList.tsx

@@ -72,7 +72,7 @@ const DescendantsPageListForTrash = (): JSX.Element => {
     <DescendantsPageList
       path="/trash"
       limit={limit}
-      forceHideMenuItems={[MenuItemType.RENAME]}
+      forceHideMenuItems={[MenuItemType.RENAME, MenuItemType.BOOKMARKS_TREE_MOVE_TO_ROOT]}
     />
   );
 };