Просмотр исходного кода

Merge pull request #6138 from weseek/imprv/95888-97972-swrize-comment-editor

move container method to CommentEditor
Yuki Takei 3 лет назад
Родитель
Сommit
dc3d1559d1

+ 1 - 3
packages/app/src/client/app.jsx

@@ -7,7 +7,6 @@ import { I18nextProvider } from 'react-i18next';
 import { SWRConfig } from 'swr';
 import { Provider } from 'unstated';
 
-import CommentContainer from '~/client/services/CommentContainer';
 import ContextExtractor from '~/client/services/ContextExtractor';
 import EditorContainer from '~/client/services/EditorContainer';
 import PageContainer from '~/client/services/PageContainer';
@@ -64,13 +63,12 @@ const socketIoContainer = appContainer.getContainer('SocketIoContainer');
 const pageContainer = new PageContainer(appContainer);
 const pageHistoryContainer = new PageHistoryContainer(appContainer, pageContainer);
 const revisionComparerContainer = new RevisionComparerContainer(appContainer, pageContainer);
-const commentContainer = new CommentContainer(appContainer);
 const editorContainer = new EditorContainer(appContainer);
 const tagContainer = new TagContainer(appContainer);
 const personalContainer = new PersonalContainer(appContainer);
 const injectableContainers = [
   appContainer, socketIoContainer, pageContainer, pageHistoryContainer, revisionComparerContainer,
-  commentContainer, editorContainer, tagContainer, personalContainer,
+  editorContainer, tagContainer, personalContainer,
 ];
 
 logger.info('unstated containers have been initialized');

+ 0 - 153
packages/app/src/client/services/CommentContainer.js

@@ -1,153 +0,0 @@
-import { Container } from 'unstated';
-
-import loggerFactory from '~/utils/logger';
-
-import { apiGet, apiPost, apiPostForm } from '../util/apiv1-client';
-import { apiv3Put } from '../util/apiv3-client';
-
-const logger = loggerFactory('growi:services:CommentContainer');
-
-/**
- *
- * @author Yuki Takei <yuki@weseek.co.jp>
- *
- * @extends {Container} unstated Container
- */
-export default class CommentContainer extends Container {
-
-  constructor(appContainer) {
-    super();
-
-    this.appContainer = appContainer;
-    this.appContainer.registerContainer(this);
-
-    const mainContent = document.querySelector('#content-main');
-
-    if (mainContent == null) {
-      logger.debug('#content-main element is not exists');
-      return;
-    }
-
-    this.state = {
-      comments: [],
-    };
-
-    this.retrieveComments = this.retrieveComments.bind(this);
-    this.checkAndUpdateImageOfCommentAuthers = this.checkAndUpdateImageOfCommentAuthers.bind(this);
-  }
-
-  /**
-   * Workaround for the mangling in production build to break constructor.name
-   */
-  static getClassName() {
-    return 'CommentContainer';
-  }
-
-  getPageContainer() {
-    return this.appContainer.getContainer('PageContainer');
-  }
-
-  findAndSplice(comment) {
-    const comments = this.state.comments;
-
-    const index = comments.indexOf(comment);
-    if (index < 0) {
-      return;
-    }
-    comments.splice(index, 1);
-
-    this.setState({ comments });
-  }
-
-  /**
-   * Load data of comments and store them in state
-   */
-  async retrieveComments() {
-    const { pageId } = this.getPageContainer().state;
-
-    // get data (desc order array)
-    const res = await apiGet('/comments.get', { page_id: pageId });
-    if (res.ok) {
-      const comments = res.comments;
-      this.setState({ comments });
-
-      this.checkAndUpdateImageOfCommentAuthers(comments);
-    }
-  }
-
-  async checkAndUpdateImageOfCommentAuthers(comments) {
-    const noImageCacheUserIds = comments.filter((comment) => {
-      const { creator } = comment;
-      return creator != null && creator.imageUrlCached == null;
-    }).map((comment) => {
-      return comment.creator._id;
-    });
-
-    if (noImageCacheUserIds.length === 0) {
-      return;
-    }
-
-    try {
-      await apiv3Put('/users/update.imageUrlCache', { userIds: noImageCacheUserIds });
-    }
-    catch (err) {
-      // Error alert doesn't apear, because user don't need to notice this error.
-      logger.error(err);
-    }
-  }
-
-  /**
-   * Load data of comments and rerender <PageComments />
-   */
-  postComment(comment, replyTo, isSlackEnabled, slackChannels) {
-    const { pageId, revisionId } = this.getPageContainer().state;
-
-    return apiPost('/comments.add', {
-      commentForm: {
-        comment,
-        page_id: pageId,
-        revision_id: revisionId,
-        replyTo,
-      },
-      slackNotificationForm: {
-        isSlackEnabled,
-        slackChannels,
-      },
-    })
-      .then((res) => {
-        if (res.ok) {
-          return this.retrieveComments();
-        }
-      });
-  }
-
-  /**
-   * Load data of comments and rerender <PageComments />
-   */
-  putComment(comment, commentId, author) {
-    const { pageId, revisionId } = this.getPageContainer().state;
-
-    return apiPost('/comments.update', {
-      commentForm: {
-        comment,
-        revision_id: revisionId,
-        comment_id: commentId,
-      },
-    })
-      .then((res) => {
-        if (res.ok) {
-          return this.retrieveComments();
-        }
-      });
-  }
-
-  deleteComment(comment) {
-    return apiPost('/comments.remove', { comment_id: comment._id })
-      .then((res) => {
-        if (res.ok) {
-          this.findAndSplice(comment);
-        }
-      });
-  }
-
-}

+ 30 - 21
packages/app/src/components/PageComment/CommentEditor.tsx

@@ -10,14 +10,16 @@ import {
 import * as toastr from 'toastr';
 
 import AppContainer from '~/client/services/AppContainer';
-import CommentContainer from '~/client/services/CommentContainer';
 import EditorContainer from '~/client/services/EditorContainer';
 import PageContainer from '~/client/services/PageContainer';
 import GrowiRenderer from '~/client/util/GrowiRenderer';
 import { apiPostForm } from '~/client/util/apiv1-client';
 import { CustomWindow } from '~/interfaces/global';
 import { IInterceptorManager } from '~/interfaces/interceptor-manager';
-import { useCurrentPagePath, useCurrentPageId, useCurrentUser } from '~/stores/context';
+import { useSWRxPageComment } from '~/stores/comment';
+import {
+  useCurrentPagePath, useCurrentPageId, useCurrentUser, useRevisionId,
+} from '~/stores/context';
 import { useSWRxSlackChannels, useIsSlackEnabled } from '~/stores/editor';
 import { useIsMobile } from '~/stores/ui';
 
@@ -46,7 +48,6 @@ const navTabMapping = {
 
 type PropsType = {
   appContainer: AppContainer,
-  commentContainer: CommentContainer,
 
   growiRenderer: GrowiRenderer,
   isForNewComment?: boolean,
@@ -67,12 +68,14 @@ type EditorRef = {
 const CommentEditor = (props: PropsType): JSX.Element => {
 
   const {
-    appContainer, commentContainer, growiRenderer, isForNewComment, replyTo,
+    appContainer, growiRenderer, isForNewComment, replyTo,
     currentCommentId, commentBody, commentCreator, onCancelButtonClicked, onCommentButtonClicked,
   } = props;
   const { data: currentUser } = useCurrentUser();
   const { data: currentPagePath } = useCurrentPagePath();
   const { data: currentPageId } = useCurrentPageId();
+  const { update: updateComment, post: postComment } = useSWRxPageComment(currentPageId);
+  const { data: revisionId } = useRevisionId();
   const { data: isMobile } = useIsMobile();
   const { data: isSlackEnabled, mutate: mutateIsSlackEnabled } = useIsSlackEnabled();
   const { data: slackChannelsData } = useSWRxSlackChannels(currentPagePath);
@@ -153,23 +156,28 @@ const CommentEditor = (props: PropsType): JSX.Element => {
     }
   }, [isForNewComment, onCancelButtonClicked]);
 
-  const postComment = useCallback(async() => {
+  const postCommentHandler = useCallback(async() => {
     try {
       if (currentCommentId != null) {
-        await commentContainer.putComment(
-          comment,
-          currentCommentId,
-          commentCreator,
-        );
+        // update current comment
+        await updateComment(comment, revisionId, currentCommentId);
       }
       else {
-        await commentContainer.postComment(
-          comment,
-          replyTo,
-          isSlackEnabled,
-          slackChannels,
-        );
+        // post new comment
+        const postCommentArgs = {
+          commentForm: {
+            comment,
+            revisionId,
+            replyTo,
+          },
+          slackNotificationForm: {
+            isSlackEnabled,
+            slackChannels,
+          },
+        };
+        await postComment(postCommentArgs);
       }
+
       initializeEditor();
 
       if (onCommentButtonClicked != null) {
@@ -181,8 +189,9 @@ const CommentEditor = (props: PropsType): JSX.Element => {
       setError(errorMessage);
     }
   }, [
-    comment, commentContainer, currentCommentId, commentCreator, initializeEditor,
+    comment, currentCommentId, initializeEditor,
     isSlackEnabled, onCommentButtonClicked, replyTo, slackChannels,
+    postComment, revisionId, updateComment,
   ]);
 
   const ctrlEnterHandler = useCallback((event) => {
@@ -190,8 +199,8 @@ const CommentEditor = (props: PropsType): JSX.Element => {
       event.preventDefault();
     }
 
-    postComment();
-  }, [postComment]);
+    postCommentHandler();
+  }, [postCommentHandler]);
 
   const apiErrorHandler = useCallback((error: Error) => {
     toastr.error(error.message, 'Error occured', {
@@ -275,7 +284,7 @@ const CommentEditor = (props: PropsType): JSX.Element => {
         outline
         color="primary"
         className="btn btn-outline-primary rounded-pill"
-        onClick={postComment}
+        onClick={postCommentHandler}
       >
         Comment
       </Button>
@@ -370,7 +379,7 @@ const CommentEditor = (props: PropsType): JSX.Element => {
  * Wrapper component for using unstated
  */
 const CommentEditorWrapper = withUnstatedContainers<unknown, Partial<PropsType>>(
-  CommentEditor, [AppContainer, PageContainer, EditorContainer, CommentContainer],
+  CommentEditor, [AppContainer, PageContainer, EditorContainer],
 );
 
 export default CommentEditorWrapper;

+ 14 - 2
packages/app/src/interfaces/comment.ts

@@ -1,8 +1,8 @@
 import { Nullable, Ref } from './common';
+import { HasObjectId } from './has-object-id';
 import { IPage } from './page';
-import { IUser } from './user';
 import { IRevision } from './revision';
-import { HasObjectId } from './has-object-id';
+import { IUser } from './user';
 
 export type IComment = {
   comment: string;
@@ -16,5 +16,17 @@ export type IComment = {
   creator: IUser,
 };
 
+export interface ICommentPostArgs {
+  commentForm: {
+    comment: string,
+    revisionId: string,
+    replyTo: string|undefined
+  },
+  slackNotificationForm: {
+    isSlackEnabled: boolean|undefined,
+    slackChannels: string|undefined,
+  },
+}
+
 export type ICommentHasId = IComment & HasObjectId;
 export type ICommentHasIdList = ICommentHasId[];

+ 49 - 4
packages/app/src/stores/comment.tsx

@@ -1,8 +1,8 @@
 import useSWR, { SWRResponse } from 'swr';
 
-import { apiGet } from '~/client/util/apiv1-client';
+import { apiGet, apiPost } from '~/client/util/apiv1-client';
 
-import { ICommentHasIdList } from '../interfaces/comment';
+import { ICommentHasIdList, ICommentPostArgs } from '../interfaces/comment';
 import { Nullable } from '../interfaces/common';
 
 type IResponseComment = {
@@ -10,10 +10,55 @@ type IResponseComment = {
   ok: boolean,
 }
 
-export const useSWRxPageComment = (pageId: Nullable<string>): SWRResponse<ICommentHasIdList, Error> => {
+type CommentOperation = {
+  update(comment: string, revisionId: string, commentId: string): Promise<void>,
+  post(args: ICommentPostArgs): Promise<void>
+}
+
+export const useSWRxPageComment = (pageId: Nullable<string>): SWRResponse<ICommentHasIdList, Error> & CommentOperation => {
   const shouldFetch: boolean = pageId != null;
-  return useSWR(
+
+  const swrResponse = useSWR(
     shouldFetch ? ['/comments.get', pageId] : null,
     (endpoint, pageId) => apiGet(endpoint, { page_id: pageId }).then((response:IResponseComment) => response.comments),
   );
+
+  const update = async(comment: string, revisionId: string, commentId: string) => {
+    const { mutate } = swrResponse;
+    await apiPost('/comments.update', {
+      commentForm: {
+        comment,
+        revision_id: revisionId,
+        comment_id: commentId,
+      },
+    });
+    mutate();
+  };
+
+  const post = async(args: ICommentPostArgs) => {
+    const { mutate } = swrResponse;
+    const { commentForm, slackNotificationForm } = args;
+    const { comment, revisionId, replyTo } = commentForm;
+    const { isSlackEnabled, slackChannels } = slackNotificationForm;
+
+    await apiPost('/comments.add', {
+      commentForm: {
+        comment,
+        page_id: pageId,
+        revision_id: revisionId,
+        replyTo,
+      },
+      slackNotificationForm: {
+        isSlackEnabled,
+        slackChannels,
+      },
+    });
+    mutate();
+  };
+
+  return {
+    ...swrResponse,
+    update,
+    post,
+  };
 };