Comments.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import React, { type JSX, useEffect, useMemo, useRef } from 'react';
  2. import dynamic from 'next/dynamic';
  3. import type { IRevisionHasId } from '@growi/core';
  4. import { pagePathUtils } from '@growi/core/dist/utils';
  5. import { useTranslation } from 'next-i18next';
  6. import { debounce } from 'throttle-debounce';
  7. import { useCurrentUser } from '~/states/global';
  8. import { useIsTrashPage } from '~/states/page';
  9. import { useSWRxPageComment } from '~/stores/comment';
  10. import { useSWRMUTxPageInfo } from '~/stores/page';
  11. const { isTopPage } = pagePathUtils;
  12. const PageComment = dynamic(
  13. () =>
  14. import('~/client/components/PageComment').then((mod) => mod.PageComment),
  15. { ssr: false },
  16. );
  17. const CommentEditorPre = dynamic(
  18. () =>
  19. import('./PageComment/CommentEditor').then((mod) => mod.CommentEditorPre),
  20. { ssr: false },
  21. );
  22. type CommentsProps = {
  23. pageId: string;
  24. pagePath: string;
  25. revision: IRevisionHasId;
  26. onLoaded?: () => void;
  27. };
  28. export const Comments = (props: CommentsProps): JSX.Element => {
  29. const { pageId, pagePath, revision, onLoaded } = props;
  30. const { t } = useTranslation('');
  31. const { mutate } = useSWRxPageComment(pageId);
  32. const { trigger: mutatePageInfo } = useSWRMUTxPageInfo(pageId);
  33. const isDeleted = useIsTrashPage();
  34. const currentUser = useCurrentUser();
  35. const pageCommentParentRef = useRef<HTMLDivElement>(null);
  36. const onLoadedDebounced = useMemo(
  37. () => debounce(500, () => onLoaded?.()),
  38. [onLoaded],
  39. );
  40. useEffect(() => {
  41. const parent = pageCommentParentRef.current;
  42. if (parent == null) return;
  43. const observer = new MutationObserver(() => {
  44. onLoadedDebounced();
  45. });
  46. observer.observe(parent, { childList: true, subtree: true });
  47. // no cleanup function -- 2023.07.31 Yuki Takei
  48. // see: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver/observe
  49. // > You can call observe() multiple times on the same MutationObserver
  50. // > to watch for changes to different parts of the DOM tree and/or different types of changes.
  51. }, [onLoadedDebounced]);
  52. const isTopPagePath = isTopPage(pagePath);
  53. if (pageId == null || isTopPagePath) {
  54. return <></>;
  55. }
  56. const onCommentButtonClickHandler = () => {
  57. mutate();
  58. mutatePageInfo();
  59. };
  60. return (
  61. <div className="page-comments-row mt-5 py-4 border-top d-edit-none d-print-none">
  62. <h4 className="mb-3">{t('page_comment.comments')}</h4>
  63. <div
  64. id="page-comments-list"
  65. className="page-comments-list"
  66. ref={pageCommentParentRef}
  67. >
  68. <PageComment
  69. pageId={pageId}
  70. pagePath={pagePath}
  71. revision={revision}
  72. currentUser={currentUser}
  73. isReadOnly={false}
  74. />
  75. </div>
  76. {!isDeleted && (
  77. <div id="page-comment-write">
  78. <CommentEditorPre
  79. pageId={pageId}
  80. onCommented={onCommentButtonClickHandler}
  81. revisionId={revision._id}
  82. />
  83. </div>
  84. )}
  85. </div>
  86. );
  87. };