import React, { useCallback, useState } from 'react'; import nodePath from 'path'; import type { IPageHasId, IPageInfoAll, IPageToDeleteWithMeta } from '@growi/core'; import { DevidedPagePath } from '@growi/core/dist/models'; import { pathUtils } from '@growi/core/dist/utils'; import { useRouter } from 'next/router'; import { useTranslation } from 'react-i18next'; import { UncontrolledTooltip, DropdownToggle } from 'reactstrap'; import { bookmark, unbookmark, unlink } from '~/client/services/page-operation'; import { addBookmarkToFolder, renamePage } from '~/client/util/bookmark-utils'; import { ValidationTarget } from '~/client/util/input-validator'; import { toastError, toastSuccess } from '~/client/util/toastr'; import type { BookmarkFolderItems, DragItemDataType } from '~/interfaces/bookmark-info'; import { DRAG_ITEM_TYPE } from '~/interfaces/bookmark-info'; import { usePutBackPageModal } from '~/stores/modal'; import { mutateAllPageInfo, useSWRMUTxCurrentPage, useSWRxPageInfo } from '~/stores/page'; import ClosableTextInput from '../Common/ClosableTextInput'; import { MenuItemType, PageItemControl } from '../Common/Dropdown/PageItemControl'; import { PageListItemS } from '../PageList/PageListItemS'; import { BookmarkMoveToRootBtn } from './BookmarkMoveToRootBtn'; import { DragAndDropWrapper } from './DragAndDropWrapper'; type Props = { isReadOnlyUser: boolean isOperable: boolean, bookmarkedPage: IPageHasId, level: number, parentFolder: BookmarkFolderItems | null, canMoveToRoot: boolean, onClickDeleteMenuItemHandler: (pageToDelete: IPageToDeleteWithMeta) => void, bookmarkFolderTreeMutation: () => void, } export const BookmarkItem = (props: Props): JSX.Element => { const BASE_FOLDER_PADDING = 15; const BASE_BOOKMARK_PADDING = 20; const { t } = useTranslation(); const router = useRouter(); const { isReadOnlyUser, isOperable, bookmarkedPage, onClickDeleteMenuItemHandler, parentFolder, level, canMoveToRoot, bookmarkFolderTreeMutation, } = props; const { open: openPutBackPageModal } = usePutBackPageModal(); const [isRenameInputShown, setRenameInputShown] = useState(false); const { data: pageInfo, mutate: mutatePageInfo } = useSWRxPageInfo(bookmarkedPage._id); const { trigger: mutateCurrentPage } = useSWRMUTxCurrentPage(); const dPagePath = new DevidedPagePath(bookmarkedPage.path, false, true); const { latter: pageTitle, former: formerPagePath } = dPagePath; const bookmarkItemId = `bookmark-item-${bookmarkedPage._id}`; const paddingLeft = BASE_BOOKMARK_PADDING + (BASE_FOLDER_PADDING * (level + 1)); const dragItem: Partial = { ...bookmarkedPage, parentFolder, }; const onClickMoveToRootHandler = useCallback(async() => { try { await addBookmarkToFolder(bookmarkedPage._id, null); bookmarkFolderTreeMutation(); } catch (err) { toastError(err); } }, [bookmarkFolderTreeMutation, bookmarkedPage._id]); const bookmarkMenuItemClickHandler = useCallback(async(pageId: string, shouldBookmark: boolean) => { if (shouldBookmark) { await bookmark(pageId); } else { await unbookmark(pageId); } bookmarkFolderTreeMutation(); mutatePageInfo(); }, [bookmarkFolderTreeMutation, mutatePageInfo]); const renameMenuItemClickHandler = useCallback(() => { setRenameInputShown(true); }, []); const pressEnterForRenameHandler = useCallback(async(inputText: string) => { const parentPath = pathUtils.addTrailingSlash(nodePath.dirname(bookmarkedPage.path ?? '')); const newPagePath = nodePath.resolve(parentPath, inputText); if (newPagePath === bookmarkedPage.path) { setRenameInputShown(false); return; } try { setRenameInputShown(false); await renamePage(bookmarkedPage._id, bookmarkedPage.revision, newPagePath); bookmarkFolderTreeMutation(); mutatePageInfo(); } catch (err) { setRenameInputShown(true); toastError(err); } }, [bookmarkedPage.path, bookmarkedPage._id, bookmarkedPage.revision, bookmarkFolderTreeMutation, mutatePageInfo]); const deleteMenuItemClickHandler = useCallback(async(_pageId: string, pageInfo: IPageInfoAll | undefined): Promise => { if (bookmarkedPage._id == null || bookmarkedPage.path == null) { throw Error('_id and path must not be null.'); } const pageToDelete: IPageToDeleteWithMeta = { data: { _id: bookmarkedPage._id, revision: bookmarkedPage.revision as string, path: bookmarkedPage.path, }, meta: pageInfo, }; onClickDeleteMenuItemHandler(pageToDelete); }, [bookmarkedPage._id, bookmarkedPage.path, bookmarkedPage.revision, onClickDeleteMenuItemHandler]); const putBackClickHandler = useCallback(() => { const { _id: pageId, path } = bookmarkedPage; const putBackedHandler = async() => { try { await unlink(path); mutateAllPageInfo(); bookmarkFolderTreeMutation(); router.push(`/${pageId}`); mutateCurrentPage(); toastSuccess(t('page_has_been_reverted', { path })); } catch (err) { toastError(err); } }; openPutBackPageModal({ pageId, path }, { onPutBacked: putBackedHandler }); }, [bookmarkedPage, openPutBackPageModal, bookmarkFolderTreeMutation, router, mutateCurrentPage, t]); return (
  • { isRenameInputShown ? ( { setRenameInputShown(false) }} onPressEnter={pressEnterForRenameHandler} validationTarget={ValidationTarget.PAGE} /> ) : }
    : undefined} >
    {dPagePath.isFormerRoot ? '/' : `${formerPagePath}/`}
  • ); };