Sfoglia il codice sorgente

refactor CommentEditor

Yuki Takei 2 anni fa
parent
commit
8da97c6695

+ 28 - 58
apps/app/src/components/PageComment/CommentEditor.tsx

@@ -1,6 +1,6 @@
 import type { ReactNode } from 'react';
 import React, {
-  useCallback, useState, useRef, useEffect,
+  useCallback, useState, useEffect,
   useMemo,
 } from 'react';
 
@@ -10,15 +10,13 @@ import {
 import { UserPicture } from '@growi/ui/dist/components';
 import { useTranslation } from 'next-i18next';
 import dynamic from 'next/dynamic';
-import { useRouter } from 'next/router';
 import {
   TabContent, TabPane,
 } from 'reactstrap';
 
 import { uploadAttachments } from '~/client/services/upload-attachments';
 import { toastError } from '~/client/util/toastr';
-import type { IEditorMethods } from '~/interfaces/editor-methods';
-import { useSWRxPageComment, useSWRxEditingCommentsNum } from '~/stores/comment';
+import { useSWRxPageComment } from '~/stores/comment';
 import {
   useCurrentUser, useIsSlackConfigured, useAcceptedUploadFileType,
 } from '~/stores/context';
@@ -26,6 +24,7 @@ import {
   useSWRxSlackChannels, useIsSlackEnabled, useIsEnabledUnsavedWarning, useEditorSettings,
 } from '~/stores/editor';
 import { useCurrentPagePath } from '~/stores/page';
+import { useEditingCommentsNum } from '~/stores/ui';
 import { useNextThemes } from '~/stores/use-next-themes';
 import loggerFactory from '~/utils/logger';
 
@@ -88,36 +87,20 @@ export const CommentEditor = (props: CommentEditorProps): JSX.Element => {
   const {
     increment: incrementEditingCommentsNum,
     decrement: decrementEditingCommentsNum,
-  } = useSWRxEditingCommentsNum();
+  } = useEditingCommentsNum();
   const { mutate: mutateResolvedTheme } = useResolvedThemeForEditor();
-  const { data: codeMirrorEditor } = useCodeMirrorEditorIsolated(GlobalCodeMirrorEditorKey.COMMENT);
   const { resolvedTheme } = useNextThemes();
   mutateResolvedTheme({ themeData: resolvedTheme });
 
-  const [comment, setComment] = useState(commentBody ?? '');
+  const { data: codeMirrorEditor } = useCodeMirrorEditorIsolated(currentCommentId ?? GlobalCodeMirrorEditorKey.COMMENT_NEW);
+
+  const [isInitialized, setInitialized] = useState(true);
   const [showPreview, setShowPreview] = useState(false);
   const [error, setError] = useState();
   const [slackChannels, setSlackChannels] = useState<string>('');
-  const [incremented, setIncremented] = useState(false);
 
   const { t } = useTranslation('');
 
-  const editorRef = useRef<IEditorMethods>(null);
-
-  const router = useRouter();
-
-  // UnControlled CodeMirror value is not reset on page transition, so explicitly set the value to the initial value
-  const onRouterChangeComplete = useCallback(() => {
-    editorRef.current?.setValue('');
-  }, []);
-
-  useEffect(() => {
-    router.events.on('routeChangeComplete', onRouterChangeComplete);
-    return () => {
-      router.events.off('routeChangeComplete', onRouterChangeComplete);
-    };
-  }, [onRouterChangeComplete, router.events]);
-
   const handleSelect = useCallback((showPreview: boolean) => {
     setShowPreview(showPreview);
   }, []);
@@ -142,21 +125,20 @@ export const CommentEditor = (props: CommentEditorProps): JSX.Element => {
   }, []);
 
   const initializeEditor = useCallback(async() => {
-    const editingCommentsNum = comment !== '' ? await decrementEditingCommentsNum() : undefined;
+    if (!isInitialized) {
+      const editingCommentsNum = await decrementEditingCommentsNum();
+      if (editingCommentsNum != null && editingCommentsNum === 0) {
+        mutateIsEnabledUnsavedWarning(false); // must be after clearing comment or else onChange will override bool
+      }
+    }
 
-    setComment('');
+    setInitialized(true);
     setShowPreview(false);
     setError(undefined);
-    initializeSlackEnabled();
-    // reset value
-    if (editorRef.current == null) { return }
-    editorRef.current.setValue('');
 
-    if (editingCommentsNum != null && editingCommentsNum === 0) {
-      mutateIsEnabledUnsavedWarning(false); // must be after clearing comment or else onChange will override bool
-    }
+    initializeSlackEnabled();
 
-  }, [initializeSlackEnabled, comment, decrementEditingCommentsNum, mutateIsEnabledUnsavedWarning]);
+  }, [isInitialized, initializeSlackEnabled, decrementEditingCommentsNum, mutateIsEnabledUnsavedWarning]);
 
   const cancelButtonClickedHandler = useCallback(() => {
     initializeEditor();
@@ -164,16 +146,18 @@ export const CommentEditor = (props: CommentEditorProps): JSX.Element => {
   }, [onCancelButtonClicked, initializeEditor]);
 
   const postCommentHandler = useCallback(async() => {
+    const commentBody = codeMirrorEditor?.getDoc();
+
     try {
       if (currentCommentId != null) {
         // update current comment
-        await updateComment(comment, revisionId, currentCommentId);
+        await updateComment(commentBody, revisionId, currentCommentId);
       }
       else {
         // post new comment
         const postCommentArgs = {
           commentForm: {
-            comment,
+            comment: commentBody,
             revisionId,
             replyTo,
           },
@@ -198,10 +182,8 @@ export const CommentEditor = (props: CommentEditorProps): JSX.Element => {
       const errorMessage = err.message || 'An unknown error occured when posting comment';
       setError(errorMessage);
     }
-  }, [
-    currentCommentId, initializeEditor, onCommentButtonClicked, codeMirrorEditor,
-    updateComment, comment, revisionId, replyTo, isSlackEnabled, slackChannels, postComment,
-  ]);
+  // eslint-disable-next-line max-len
+  }, [currentCommentId, initializeEditor, onCommentButtonClicked, codeMirrorEditor, updateComment, revisionId, replyTo, isSlackEnabled, slackChannels, postComment]);
 
   // the upload event handler
   const uploadHandler = useCallback((files: File[]) => {
@@ -222,23 +204,10 @@ export const CommentEditor = (props: CommentEditorProps): JSX.Element => {
     });
   }, [codeMirrorEditor, pageId]);
 
-  // const onChangeHandler = useCallback((newValue: string, isClean: boolean) => {
-  //   setComment(newValue);
-  //   if (!isClean && !incremented) {
-  //     incrementEditingCommentsNum();
-  //     setIncremented(true);
-  //   }
-  //   mutateIsEnabledUnsavedWarning(!isClean);
-  // }, [mutateIsEnabledUnsavedWarning, incrementEditingCommentsNum, incremented]);
-
-  const onChangeHandler = useCallback((newValue: string) => {
-    setComment(newValue);
-
-    if (!incremented) {
-      incrementEditingCommentsNum();
-      setIncremented(true);
-    }
-  }, [incrementEditingCommentsNum, incremented]);
+  const onChangeHandler = useCallback(() => {
+    incrementEditingCommentsNum();
+    setInitialized(false);
+  }, [incrementEditingCommentsNum]);
 
   // initialize CodeMirrorEditor
   useEffect(() => {
@@ -283,6 +252,7 @@ export const CommentEditor = (props: CommentEditorProps): JSX.Element => {
         <TabContent activeTab={showPreview ? 'comment_preview' : 'comment_editor'}>
           <TabPane tabId="comment_editor">
             <CodeMirrorEditorComment
+              commentId={currentCommentId}
               acceptedUploadFileType={acceptedUploadFileType}
               onChange={onChangeHandler}
               onSave={postCommentHandler}
@@ -292,7 +262,7 @@ export const CommentEditor = (props: CommentEditorProps): JSX.Element => {
           </TabPane>
           <TabPane tabId="comment_preview">
             <div className="comment-preview-container">
-              <CommentPreview markdown={comment} />
+              <CommentPreview markdown={codeMirrorEditor?.getDoc()} />
             </div>
           </TabPane>
         </TabContent>

+ 3 - 22
apps/app/src/stores/comment.tsx

@@ -1,11 +1,10 @@
 import type { Nullable } from '@growi/core';
-import useSWR, { SWRResponse } from 'swr';
+import type { SWRResponse } from 'swr';
+import useSWR from 'swr';
 
 import { apiGet, apiPost } from '~/client/util/apiv1-client';
 
-import { ICommentHasIdList, ICommentPostArgs } from '../interfaces/comment';
-
-import { useStaticSWR } from './use-static-swr';
+import type { ICommentHasIdList, ICommentPostArgs } from '../interfaces/comment';
 
 type IResponseComment = {
   comments: ICommentHasIdList,
@@ -64,21 +63,3 @@ export const useSWRxPageComment = (pageId: Nullable<string>): SWRResponse<IComme
     post,
   };
 };
-
-type EditingCommentsNumOperation = {
-  increment(): Promise<number | undefined>,
-  decrement(): Promise<number | undefined>,
-}
-
-export const useSWRxEditingCommentsNum = (): SWRResponse<number, Error> & EditingCommentsNumOperation => {
-  const swrResponse = useStaticSWR<number, Error>('editingCommentsNum', undefined, { fallbackData: 0 });
-
-  return {
-    ...swrResponse,
-    increment: () => swrResponse.mutate((swrResponse.data ?? 0) + 1),
-    decrement: () => {
-      const newValue = (swrResponse.data ?? 0) - 1;
-      return swrResponse.mutate(Math.max(0, newValue));
-    },
-  };
-};

+ 26 - 0
apps/app/src/stores/ui.tsx

@@ -384,6 +384,32 @@ export const usePageTreeDescCountMap = (initialData?: UpdateDescCountData): SWRR
   };
 };
 
+
+type EditingCommentsNumOperation = {
+  increment(): Promise<number | undefined>,
+  decrement(): Promise<number | undefined>,
+}
+
+export const useEditingCommentsNum = (): SWRResponse<number, Error> & EditingCommentsNumOperation => {
+  const swrResponse = useSWRStatic<number, Error>('editingCommentsNum', undefined, { fallbackData: 0 });
+
+  const { mutate } = swrResponse;
+
+  const increment = useCallback(() => {
+    return mutate(prevData => (prevData ?? 0) + 1);
+  }, [mutate]);
+  const decrement = useCallback(() => {
+    return mutate(prevData => Math.max(0, (prevData ?? 0) - 1));
+  }, [mutate]);
+
+  return {
+    ...swrResponse,
+    increment,
+    decrement,
+  };
+};
+
+
 /** **********************************************************
  *                          SWR Hooks
  *                Determined value by context

+ 9 - 5
packages/editor/src/components/CodeMirrorEditorComment.tsx

@@ -14,14 +14,18 @@ const additionalExtensions: Extension[] = [
 ];
 
 
-type Props = CodeMirrorEditorProps & object
+type Props = CodeMirrorEditorProps & {
+  commentId?: string,
+}
 
 export const CodeMirrorEditorComment = (props: Props): JSX.Element => {
   const {
-    onSave, ...otherProps
+    commentId,
+    onSave, ...rest
   } = props;
 
-  const { data: codeMirrorEditor } = useCodeMirrorEditorIsolated(GlobalCodeMirrorEditorKey.COMMENT);
+  const key = commentId ?? GlobalCodeMirrorEditorKey.COMMENT_NEW;
+  const { data: codeMirrorEditor } = useCodeMirrorEditorIsolated(key);
 
   // setup additional extensions
   useEffect(() => {
@@ -55,9 +59,9 @@ export const CodeMirrorEditorComment = (props: Props): JSX.Element => {
 
   return (
     <CodeMirrorEditor
-      editorKey={GlobalCodeMirrorEditorKey.COMMENT}
+      editorKey={key}
       onSave={onSave}
-      {...otherProps}
+      {...rest}
     />
   );
 };

+ 1 - 1
packages/editor/src/consts/global-code-mirror-editor-key.ts

@@ -1,6 +1,6 @@
 export const GlobalCodeMirrorEditorKey = {
   MAIN: 'main',
-  COMMENT: 'comment',
+  COMMENT_NEW: 'comment_new',
   DIFF: 'diff',
   READONLY: 'readonly',
 } as const;