BookmarkList.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. import React, { useCallback, useState } from 'react';
  2. import nodePath from 'path';
  3. import {
  4. IPageInfoAll, IPageToDeleteWithMeta, pathUtils,
  5. } from '@growi/core';
  6. import { useTranslation } from 'next-i18next';
  7. import { DropdownToggle } from 'reactstrap';
  8. import { unbookmark } from '~/client/services/page-operation';
  9. import { apiv3Put } from '~/client/util/apiv3-client';
  10. import { ValidationTarget } from '~/client/util/input-validator';
  11. import { toastError, toastSuccess } from '~/client/util/toastr';
  12. import { IPageHasId } from '~/interfaces/page';
  13. import loggerFactory from '~/utils/logger';
  14. import ClosableTextInput from '../Common/ClosableTextInput';
  15. import { MenuItemType, PageItemControl } from '../Common/Dropdown/PageItemControl';
  16. import { PageListItemS } from './PageListItemS';
  17. const logger = loggerFactory('growi:BookmarkList');
  18. type Props = {
  19. page: IPageHasId
  20. onRenamed: () => void
  21. onUnbookmarked: () => void
  22. onClickDeleteMenuItem: (pageToDelete: IPageToDeleteWithMeta) => void
  23. }
  24. export const BookmarkList = (props:Props): JSX.Element => {
  25. const {
  26. page, onRenamed, onUnbookmarked, onClickDeleteMenuItem,
  27. } = props;
  28. const { t } = useTranslation();
  29. const [isRenameInputShown, setIsRenameInputShown] = useState(false);
  30. const bookmarkMenuItemClickHandler = useCallback(async() => {
  31. await unbookmark(page._id);
  32. onUnbookmarked();
  33. }, [page._id, onUnbookmarked]);
  34. const deleteMenuItemClickHandler = useCallback(async(_pageId: string, pageInfo: IPageInfoAll | undefined): Promise<void> => {
  35. if (page._id == null || page.path == null) {
  36. throw Error('_id and path must not be null.');
  37. }
  38. const pageToDelete: IPageToDeleteWithMeta = {
  39. data: {
  40. _id: page._id,
  41. revision: page.revision as string,
  42. path: page.path,
  43. },
  44. meta: pageInfo,
  45. };
  46. onClickDeleteMenuItem(pageToDelete);
  47. }, [onClickDeleteMenuItem, page]);
  48. const pressEnterForRenameHandler = useCallback(async(inputText: string) => {
  49. const parentPath = pathUtils.addTrailingSlash(nodePath.dirname(page.path ?? ''));
  50. const newPagePath = nodePath.resolve(parentPath, inputText);
  51. if (newPagePath === page.path) {
  52. setIsRenameInputShown(false);
  53. return;
  54. }
  55. try {
  56. setIsRenameInputShown(false);
  57. await apiv3Put('/pages/rename', {
  58. pageId: page._id,
  59. revisionId: page.revision,
  60. newPagePath,
  61. });
  62. onRenamed();
  63. toastSuccess(t('renamed_pages', { path: page.path }));
  64. }
  65. catch (err) {
  66. setIsRenameInputShown(true);
  67. logger.error('failed to fetch data', err);
  68. toastError(err);
  69. }
  70. }, [onRenamed, page, t]);
  71. return (
  72. <li key={`my-bookmarks:${page?._id}`} className="list-group-item list-group-item-action border-0 py-0 pl-3 d-flex align-items-center">
  73. { isRenameInputShown ? (
  74. <ClosableTextInput
  75. value={nodePath.basename(page.path ?? '')}
  76. placeholder={t('Input page name')}
  77. onClickOutside={() => { setIsRenameInputShown(false) }}
  78. onPressEnter={pressEnterForRenameHandler}
  79. validationTarget={ValidationTarget.PAGE}
  80. />
  81. ) : (
  82. <PageListItemS page={page} />
  83. )}
  84. <PageItemControl
  85. pageId={page._id}
  86. isEnableActions
  87. forceHideMenuItems={[MenuItemType.DUPLICATE]}
  88. onClickBookmarkMenuItem={bookmarkMenuItemClickHandler}
  89. onClickRenameMenuItem={() => setIsRenameInputShown(true)}
  90. onClickDeleteMenuItem={deleteMenuItemClickHandler}
  91. >
  92. <DropdownToggle color="transparent" className="border-0 rounded btn-page-item-control p-0 grw-visible-on-hover mr-1">
  93. <i className="icon-options fa fa-rotate-90 p-1"></i>
  94. </DropdownToggle>
  95. </PageItemControl>
  96. </li>
  97. );
  98. };