Comments.tsx 3.1 KB

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