Przeglądaj źródła

add markdown feature to search page comment but must clean codes

yuto-oweseek 4 lat temu
rodzic
commit
fe6e7e6192
1 zmienionych plików z 110 dodań i 34 usunięć
  1. 110 34
      packages/app/src/components/PageCommentList.tsx

+ 110 - 34
packages/app/src/components/PageCommentList.tsx

@@ -1,8 +1,11 @@
-import React, { FC, memo, useMemo } from 'react';
+import React, {
+  FC, useEffect, useState, memo, useMemo, useCallback,
+} from 'react';
 
 
 import { UserPicture } from '@growi/ui';
 import { UserPicture } from '@growi/ui';
 import AppContainer from '~/client/services/AppContainer';
 import AppContainer from '~/client/services/AppContainer';
 
 
+import RevisionBody from './Page/RevisionBody';
 import Username from './User/Username';
 import Username from './User/Username';
 import FormattedDistanceDate from './FormattedDistanceDate';
 import FormattedDistanceDate from './FormattedDistanceDate';
 import HistoryIcon from './Icons/HistoryIcon';
 import HistoryIcon from './Icons/HistoryIcon';
@@ -11,6 +14,8 @@ import { ICommentHasId, ICommentHasIdList } from '../interfaces/comment';
 
 
 import { useSWRxPageComment } from '../stores/comment';
 import { useSWRxPageComment } from '../stores/comment';
 
 
+import MathJaxConfigurer from '~/client/util/markdown-it/mathjax';
+
 
 
 type Props = {
 type Props = {
   appContainer: AppContainer,
   appContainer: AppContainer,
@@ -23,17 +28,60 @@ const PageCommentList:FC<Props> = memo((props:Props):JSX.Element => {
   const { appContainer, pageId, highlightKeywords } = props;
   const { appContainer, pageId, highlightKeywords } = props;
 
 
   const { data: comments } = useSWRxPageComment(pageId);
   const { data: comments } = useSWRxPageComment(pageId);
+  const [data, setData] = useState<ICommentHasIdList>([]);
 
 
-  const commentsFromOldest = useMemo(() => (comments != null ? [...comments].reverse() : null), [comments]);
+  const commentsFromOldest = useMemo(() => (data != null ? [...data].reverse() : null), [data]);
   const commentsExceptReply: ICommentHasIdList | undefined = useMemo(
   const commentsExceptReply: ICommentHasIdList | undefined = useMemo(
     () => commentsFromOldest?.filter(comment => comment.replyTo == null), [commentsFromOldest],
     () => commentsFromOldest?.filter(comment => comment.replyTo == null), [commentsFromOldest],
   );
   );
   const allReplies = {};
   const allReplies = {};
 
 
+  const renderHtml = useCallback(async(comment:string) => {
+
+    const { interceptorManager } = appContainer;
+    const growiRenderer = appContainer.getRenderer('comment');
+
+    const context: {markdown: string, parsedHTML: string} = { markdown: comment, parsedHTML: '' };
+
+    if (interceptorManager != null) {
+      await interceptorManager.process('preRenderComment', context);
+      await interceptorManager.process('prePreProcess', context);
+      context.markdown = await growiRenderer.preProcess(context.markdown);
+      await interceptorManager.process('postPreProcess', context);
+      context.parsedHTML = await growiRenderer.process(context.markdown);
+      await interceptorManager.process('prePostProcess', context);
+      context.parsedHTML = await growiRenderer.postProcess(context.parsedHTML);
+      await interceptorManager.process('postPostProcess', context);
+      await interceptorManager.process('preRenderCommentHtml', context);
+      await interceptorManager.process('postRenderCommentHtml', context);
+
+    }
+
+    return context.parsedHTML;
+
+  }, [appContainer]);
+
+  useEffect(() => {
+    const f = async() => {
+      if (comments != null) {
+        const i = await Promise.all(comments.map(comment => renderHtml(comment.comment)));
+        const t = comments.map((comment, index) => {
+          comment.comment = i[index];
+          return comment;
+        });
+        setData(t);
+      }
+
+    };
+
+    f();
+
+  }, [comments, renderHtml]);
+
   if (commentsFromOldest != null) {
   if (commentsFromOldest != null) {
     commentsFromOldest.forEach((comment) => {
     commentsFromOldest.forEach((comment) => {
       if (comment.replyTo != null) {
       if (comment.replyTo != null) {
-        allReplies[comment.replyTo] = comment;
+        allReplies[comment.replyTo] = allReplies[comment.replyTo] == null ? [comment] : [...allReplies[comment.replyTo], comment];
       }
       }
     });
     });
   }
   }
@@ -46,35 +94,67 @@ const PageCommentList:FC<Props> = memo((props:Props):JSX.Element => {
     return highlightedComment;
     return highlightedComment;
   };
   };
 
 
-  const generateCommentInnerElement = (comment: ICommentHasId) => (
-    <>
-      <div className="flex-shrink-0">
-        <UserPicture user={comment.creator} size="md" noLink noTooltip />
-      </div>
-      <div className="flex-grow-1 ml-3">
-        <div className="d-flex">
-          <div className="flex-shrink-0">
-            <Username user={comment.creator} />
-          </div>
-          <div className="flex-grow-1 ml-3 text-right">
-            <div className="page-comment-meta">
-              <HistoryIcon />
-              <a href={`#${comment._id}`}>
-                <FormattedDistanceDate id={comment._id} date={comment.createdAt} />
-              </a>
+
+  const renderRevisionBody = (comment: string) => {
+    const isMathJaxEnabled = (new MathJaxConfigurer(appContainer)).isEnabled;
+    return (
+      <RevisionBody
+        html={comment}
+        isMathJaxEnabled={isMathJaxEnabled}
+        renderMathJaxOnInit
+        additionalClassName="comment"
+      />
+    );
+  };
+
+  const renderText = (comment: string) => {
+    return <span style={{ whiteSpace: 'pre-wrap' }}>{comment}</span>;
+  };
+
+  const generateCommentInnerElement = (comment: ICommentHasId) => {
+    const highlightedCommentBody = highlightComment(comment.comment);
+    const formatedCommentBody = comment.isMarkdown ? renderRevisionBody(highlightedCommentBody) : renderText(highlightedCommentBody);
+
+    return (
+      <>
+        <div className="flex-shrink-0">
+          <UserPicture user={comment.creator} size="md" noLink noTooltip />
+        </div>
+        <div className="flex-grow-1 ml-3">
+          <div className="d-flex">
+            <div className="flex-shrink-0">
+              <Username user={comment.creator} />
+            </div>
+            <div className="flex-grow-1 ml-3 text-right">
+              <div className="page-comment-meta">
+                <HistoryIcon />
+                <a href={`#${comment._id}`}>
+                  <FormattedDistanceDate id={comment._id} date={comment.createdAt} />
+                </a>
+              </div>
             </div>
             </div>
           </div>
           </div>
+          <div className="page-comment-body">{formatedCommentBody}</div>
         </div>
         </div>
-        <div
-          className="page-comment-body text-break mt-1"
-          // eslint-disable-next-line react/no-danger
-          dangerouslySetInnerHTML={{
-            __html: highlightComment(comment.comment),
-          }}
-        />
-      </div>
-    </>
-  );
+      </>
+    );
+  };
+
+  const generateAllRepliesElement = (replyComments: ICommentHasIdList) => {
+    return (
+      replyComments.map((comment: ICommentHasId, index: number) => {
+        const lastIndex: number = replyComments.length - 1;
+        const isLastIndex: boolean = index === lastIndex;
+
+        return (
+          <div className={`d-flex ml-4 ${isLastIndex ? 'mb-5' : 'mb-3'}`}>
+            {generateCommentInnerElement(comment)}
+          </div>
+        );
+
+      })
+    );
+  };
 
 
 
 
   if (comments == null || commentsExceptReply == null) return <></>;
   if (comments == null || commentsExceptReply == null) return <></>;
@@ -94,11 +174,7 @@ const PageCommentList:FC<Props> = memo((props:Props):JSX.Element => {
               {generateCommentInnerElement(comment)}
               {generateCommentInnerElement(comment)}
             </div>
             </div>
             {/* display reply comment */}
             {/* display reply comment */}
-            {hasReply && (
-              <div className="d-flex ml-4 mb-5">
-                {generateCommentInnerElement(allReplies[comment._id])}
-              </div>
-            )}
+            {hasReply && generateAllRepliesElement(allReplies[comment._id])}
           </div>
           </div>
         );
         );
       })}
       })}