PageAttachment.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import React, {
  2. useCallback, useMemo, useState,
  3. } from 'react';
  4. import { IAttachmentHasId } from '@growi/core';
  5. import { useSWRxAttachments } from '~/stores/attachment';
  6. import { useIsGuestUser, useIsReadOnlyUser } from '~/stores/context';
  7. import { useDeleteAttachmentModal } from '~/stores/modal';
  8. import { useSWRxCurrentPage, useCurrentPageId } from '~/stores/page';
  9. import { PageAttachmentList } from '../PageAttachment/PageAttachmentList';
  10. import PaginationWrapper from '../PaginationWrapper';
  11. // Utility
  12. const checkIfFileInUse = (markdown: string, attachment): boolean => {
  13. return markdown.indexOf(attachment._id) >= 0;
  14. };
  15. const PageAttachment = (): JSX.Element => {
  16. // Static SWRs
  17. const { data: pageId } = useCurrentPageId();
  18. const { data: isGuestUser } = useIsGuestUser();
  19. const { data: isReadOnlyUser } = useIsReadOnlyUser();
  20. const isPageAttachmentDisabled = !!isGuestUser || !!isReadOnlyUser;
  21. // States
  22. const [pageNumber, setPageNumber] = useState(1);
  23. // SWRs
  24. const { data: dataAttachments, remove } = useSWRxAttachments(pageId, pageNumber);
  25. const { open: openDeleteAttachmentModal } = useDeleteAttachmentModal();
  26. const { data: currentPage } = useSWRxCurrentPage();
  27. const markdown = currentPage?.revision.body;
  28. // Custom hooks
  29. const inUseAttachmentsMap: { [id: string]: boolean } | undefined = useMemo(() => {
  30. if (markdown == null || dataAttachments == null) {
  31. return undefined;
  32. }
  33. const attachmentEntries = dataAttachments.attachments
  34. .map((attachment) => {
  35. return [attachment._id, checkIfFileInUse(markdown, attachment)];
  36. });
  37. return Object.fromEntries(attachmentEntries);
  38. }, [dataAttachments, markdown]);
  39. // Methods
  40. const onChangePageHandler = useCallback((newPageNumber: number) => {
  41. setPageNumber(newPageNumber);
  42. }, []);
  43. const onAttachmentDeleteClicked = useCallback((attachment: IAttachmentHasId) => {
  44. openDeleteAttachmentModal(attachment, remove);
  45. }, [openDeleteAttachmentModal, remove]);
  46. // Renderers
  47. const renderPageAttachmentList = useCallback(() => {
  48. if (dataAttachments == null || inUseAttachmentsMap == null) {
  49. return (
  50. <div className="text-muted text-center">
  51. <i className="fa fa-2x fa-spinner fa-pulse mr-1"></i>
  52. </div>
  53. );
  54. }
  55. return (
  56. <PageAttachmentList
  57. attachments={dataAttachments.attachments}
  58. inUse={inUseAttachmentsMap}
  59. onAttachmentDeleteClicked={onAttachmentDeleteClicked}
  60. isUserLoggedIn={!isPageAttachmentDisabled}
  61. />
  62. );
  63. }, [dataAttachments, inUseAttachmentsMap, isPageAttachmentDisabled, onAttachmentDeleteClicked]);
  64. const renderPaginationWrapper = useCallback(() => {
  65. if (dataAttachments == null || dataAttachments.attachments.length === 0) {
  66. return <></>;
  67. }
  68. return (
  69. <PaginationWrapper
  70. activePage={pageNumber}
  71. changePage={onChangePageHandler}
  72. totalItemsCount={dataAttachments.totalAttachments}
  73. pagingLimit={dataAttachments.limit}
  74. align="center"
  75. />
  76. );
  77. }, [dataAttachments, onChangePageHandler, pageNumber]);
  78. return (
  79. <div data-testid="page-attachment">
  80. {renderPageAttachmentList()}
  81. {renderPaginationWrapper()}
  82. </div>
  83. );
  84. };
  85. export default PageAttachment;