TrashPageList.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import React, { useMemo, useCallback } from 'react';
  2. import { useTranslation } from 'next-i18next';
  3. import dynamic from 'next/dynamic';
  4. import { toastSuccess } from '~/client/util/toastr';
  5. import {
  6. IPageHasId,
  7. } from '~/interfaces/page';
  8. import { IPagingResult } from '~/interfaces/paging-result';
  9. import { useIsReadOnlyUser, useShowPageLimitationXL } from '~/stores/context';
  10. import { useEmptyTrashModal } from '~/stores/modal';
  11. import { useSWRxPageInfoForList, useSWRxPageList } from '~/stores/page-listing';
  12. import { MenuItemType } from './Common/Dropdown/PageItemControl';
  13. import CustomNavAndContents from './CustomNavigation/CustomNavAndContents';
  14. import { DescendantsPageListProps } from './DescendantsPageList';
  15. import EmptyTrashButton from './EmptyTrashButton';
  16. import PageListIcon from './Icons/PageListIcon';
  17. const DescendantsPageList = dynamic<DescendantsPageListProps>(() => import('./DescendantsPageList').then(mod => mod.DescendantsPageList), { ssr: false });
  18. const convertToIDataWithMeta = (page) => {
  19. return { data: page };
  20. };
  21. const useEmptyTrashButton = () => {
  22. const { t } = useTranslation();
  23. const { data: limit } = useShowPageLimitationXL();
  24. const { data: isReadOnlyUser } = useIsReadOnlyUser();
  25. const { data: pagingResult, mutate: mutatePageLists } = useSWRxPageList('/trash', 1, limit);
  26. const { open: openEmptyTrashModal } = useEmptyTrashModal();
  27. const pageIds = pagingResult?.items?.map(page => page._id);
  28. const { injectTo } = useSWRxPageInfoForList(pageIds, null, true, true);
  29. const calculateDeletablePages = useCallback((pagingResult?: IPagingResult<IPageHasId>) => {
  30. if (pagingResult == null) { return undefined }
  31. const dataWithMetas = pagingResult.items.map(page => convertToIDataWithMeta(page));
  32. const pageWithMetas = injectTo(dataWithMetas);
  33. return pageWithMetas.filter(page => page.meta?.isAbleToDeleteCompletely);
  34. }, [injectTo]);
  35. const deletablePages = calculateDeletablePages(pagingResult);
  36. const onEmptiedTrashHandler = useCallback(() => {
  37. toastSuccess(t('empty_trash'));
  38. mutatePageLists();
  39. }, [t, mutatePageLists]);
  40. const emptyTrashClickHandler = useCallback(() => {
  41. if (deletablePages == null) { return }
  42. openEmptyTrashModal(deletablePages, { onEmptiedTrash: onEmptiedTrashHandler, canDeleteAllPages: pagingResult?.totalCount === deletablePages.length });
  43. }, [deletablePages, onEmptiedTrashHandler, openEmptyTrashModal, pagingResult?.totalCount]);
  44. const emptyTrashButton = useMemo(() => {
  45. return <EmptyTrashButton onEmptyTrashButtonClick={emptyTrashClickHandler} disableEmptyButton={deletablePages?.length === 0 || !!isReadOnlyUser} />;
  46. }, [emptyTrashClickHandler, deletablePages?.length, isReadOnlyUser]);
  47. return emptyTrashButton;
  48. };
  49. const DescendantsPageListForTrash = (): JSX.Element => {
  50. const { data: limit } = useShowPageLimitationXL();
  51. return (
  52. <DescendantsPageList
  53. path="/trash"
  54. limit={limit}
  55. forceHideMenuItems={[MenuItemType.RENAME]}
  56. />
  57. );
  58. };
  59. export const TrashPageList = (): JSX.Element => {
  60. const { t } = useTranslation();
  61. const emptyTrashButton = useEmptyTrashButton();
  62. const navTabMapping = useMemo(() => {
  63. return {
  64. pagelist: {
  65. Icon: PageListIcon,
  66. Content: DescendantsPageListForTrash,
  67. i18n: t('page_list'),
  68. },
  69. };
  70. }, [t]);
  71. return (
  72. <div data-testid="trash-page-list" className="mt-5 d-edit-none">
  73. <CustomNavAndContents navTabMapping={navTabMapping} navRightElement={emptyTrashButton} />
  74. </div>
  75. );
  76. };
  77. TrashPageList.displayName = 'TrashPageList';