PageTimeline.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import React from 'react';
  2. import { useTranslation } from 'next-i18next';
  3. import Link from 'next/link';
  4. import useSWRInfinite, { SWRInfiniteResponse } from 'swr/infinite';
  5. import { apiv3Get } from '~/client/util/apiv3-client';
  6. import { IPageHasId } from '~/interfaces/page';
  7. import { useCurrentPagePath } from '~/stores/page';
  8. import { useTimelineOptions } from '~/stores/renderer';
  9. import InfiniteScroll from './InfiniteScroll';
  10. import { RevisionLoader } from './Page/RevisionLoader';
  11. import styles from './PageTimeline.module.scss';
  12. type TimelineCardProps = {
  13. page: IPageHasId,
  14. }
  15. const TimelineCard = ({ page }: TimelineCardProps): JSX.Element => {
  16. const { data: rendererOptions } = useTimelineOptions(page.path);
  17. return (
  18. <div className={`card card-timeline ${styles['card-timeline']}`}>
  19. <div className="card-header h4 p-3">
  20. <Link href={page.path} prefetch={false}>
  21. {page.path}
  22. </Link>
  23. </div>
  24. <div className="card-body">
  25. { rendererOptions != null && (
  26. <RevisionLoader
  27. rendererOptions={rendererOptions}
  28. pageId={page._id}
  29. revisionId={page.revision}
  30. />
  31. ) }
  32. </div>
  33. </div>
  34. );
  35. };
  36. type PageTimelineResult = {
  37. pages: IPageHasId[],
  38. totalCount: number,
  39. offset: number,
  40. }
  41. const useSWRINFxPageTimeline = (path: string | undefined, limit: number) : SWRInfiniteResponse<PageTimelineResult, Error> => {
  42. return useSWRInfinite(
  43. (pageIndex, previousPageData) => {
  44. if (previousPageData != null && previousPageData.pages.length === 0) return null;
  45. if (path === undefined) return null;
  46. return ['/pages/list', path, pageIndex + 1, limit];
  47. },
  48. ([endpoint, path, page, limit]) => apiv3Get<PageTimelineResult>(endpoint, { path, page, limit }).then(response => response.data),
  49. {
  50. revalidateFirstPage: false,
  51. revalidateAll: false,
  52. },
  53. );
  54. };
  55. export const PageTimeline = (): JSX.Element => {
  56. const PER_PAGE = 3;
  57. const { t } = useTranslation();
  58. const { data: currentPagePath } = useCurrentPagePath();
  59. const swrInfinitexPageTimeline = useSWRINFxPageTimeline(currentPagePath, PER_PAGE);
  60. const { data } = swrInfinitexPageTimeline;
  61. const isEmpty = data?.[0]?.pages.length === 0;
  62. const isReachingEnd = isEmpty || (data != null && data[data.length - 1]?.pages.length < PER_PAGE);
  63. if (data == null || isEmpty) {
  64. return (
  65. <div className="mt-2">
  66. {/* eslint-disable-next-line react/no-danger */}
  67. <p>{t('custom_navigation.no_page_list')}</p>
  68. </div>
  69. );
  70. }
  71. return (
  72. <div>
  73. <InfiniteScroll
  74. swrInifiniteResponse={swrInfinitexPageTimeline}
  75. isReachingEnd={isReachingEnd}
  76. >
  77. { data != null && data.flatMap(apiResult => apiResult.pages)
  78. .map(page => (
  79. <TimelineCard key={page._id} page={page} />
  80. ))
  81. }
  82. </InfiniteScroll>
  83. </div>
  84. );
  85. };