import React, { useCallback, useState, useMemo } from 'react';
import nodePath from 'path';
import {
IPageInfoAll, IPageToDeleteWithMeta, pathUtils,
} from '@growi/core';
import { useTranslation } from 'next-i18next';
import { DropdownItem, DropdownToggle } from 'reactstrap';
import { unbookmark } from '~/client/services/page-operation';
import { apiv3Put } from '~/client/util/apiv3-client';
import { addBookmarkToFolder } from '~/client/util/bookmark-utils';
import { ValidationTarget } from '~/client/util/input-validator';
import { toastError, toastSuccess } from '~/client/util/toastr';
import { IPageHasId } from '~/interfaces/page';
import { useSWRxCurrentUserBookmarks } from '~/stores/bookmark';
import loggerFactory from '~/utils/logger';
import ClosableTextInput from '../Common/ClosableTextInput';
import { MenuItemType, PageItemControl } from '../Common/Dropdown/PageItemControl';
import { PageListItemS } from './PageListItemS';
const logger = loggerFactory('growi:BookmarkList');
type Props = {
page: IPageHasId
onRenamed: () => void
onUnbookmarked: () => void
onClickDeleteMenuItem: (pageToDelete: IPageToDeleteWithMeta) => void
}
export const BookmarkList = (props:Props): JSX.Element => {
const {
page, onRenamed, onUnbookmarked, onClickDeleteMenuItem,
} = props;
const { t } = useTranslation();
const [isRenameInputShown, setIsRenameInputShown] = useState(false);
const pageId = page._id;
const { data: userBookmarks, mutate: mutateUserBookmarks } = useSWRxCurrentUserBookmarks();
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]);
const additionalMenuItemOnTopRenderer = useMemo(() => {
return (
{t('bookmark_folder.move_to_root')}
);
}, [moveToRootClickedHandler, t]);
const bookmarkMenuItemClickHandler = useCallback(async() => {
await unbookmark(page._id);
onUnbookmarked();
}, [page._id, onUnbookmarked]);
const deleteMenuItemClickHandler = useCallback(async(_pageId: string, pageInfo: IPageInfoAll | undefined): Promise => {
if (page._id == null || page.path == null) {
throw Error('_id and path must not be null.');
}
const pageToDelete: IPageToDeleteWithMeta = {
data: {
_id: page._id,
revision: page.revision as string,
path: page.path,
},
meta: pageInfo,
};
onClickDeleteMenuItem(pageToDelete);
}, [onClickDeleteMenuItem, page]);
const pressEnterForRenameHandler = useCallback(async(inputText: string) => {
const parentPath = pathUtils.addTrailingSlash(nodePath.dirname(page.path ?? ''));
const newPagePath = nodePath.resolve(parentPath, inputText);
if (newPagePath === page.path) {
setIsRenameInputShown(false);
return;
}
try {
setIsRenameInputShown(false);
await apiv3Put('/pages/rename', {
pageId: page._id,
revisionId: page.revision,
newPagePath,
});
onRenamed();
toastSuccess(t('renamed_pages', { path: page.path }));
}
catch (err) {
setIsRenameInputShown(true);
logger.error('failed to fetch data', err);
toastError(err);
}
}, [onRenamed, page, t]);
return (
{ isRenameInputShown ? (
{ setIsRenameInputShown(false) }}
onPressEnter={pressEnterForRenameHandler}
validationTarget={ValidationTarget.PAGE}
/>
) : (
)}
setIsRenameInputShown(true)}
onClickDeleteMenuItem={deleteMenuItemClickHandler}
additionalMenuItemOnTopRenderer={isMoveToRoot ? (() => additionalMenuItemOnTopRenderer) : undefined}
>
);
};