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

manage comments data in CommentContainer

Yuki Takei 6 лет назад
Родитель
Сommit
7b6b2eb0ea

+ 20 - 15
src/client/js/app.js

@@ -315,23 +315,28 @@ const componentMappings = {
 // additional definitions if data exists
 let pageComments = null;
 if (pageId) {
+  // create unstated container instance
+  const commentContainer = new CommentContainer(crowi, pageId, pageRevisionId);
+
   componentMappings['page-comments-list'] = (
     <I18nextProvider i18n={i18n}>
-      <PageComments
-        ref={(elem) => {
-          if (pageComments == null) {
-            pageComments = elem;
-          }
-        }}
-        revisionCreatedAt={pageRevisionCreatedAt}
-        pageId={pageId}
-        pagePath={pagePath}
-        editorOptions={pageEditorOptions}
-        slackChannels={slackChannels}
-        crowi={crowi}
-        crowiOriginRenderer={crowiRenderer}
-        revisionId={pageRevisionId}
-      />
+      <Provider inject={[commentContainer]}>
+        <PageComments
+          ref={(elem) => {
+            if (pageComments == null) {
+              pageComments = elem;
+            }
+          }}
+          revisionCreatedAt={pageRevisionCreatedAt}
+          pageId={pageId}
+          pagePath={pagePath}
+          editorOptions={pageEditorOptions}
+          slackChannels={slackChannels}
+          crowi={crowi}
+          crowiOriginRenderer={crowiRenderer}
+          revisionId={pageRevisionId}
+        />
+      </Provider>
     </I18nextProvider>
   );
   componentMappings['page-attachment'] = <PageAttachment pageId={pageId} markdown={markdown} crowi={crowi} />;

+ 38 - 0
src/client/js/components/PageComment/CommentContainer.jsx

@@ -14,6 +14,10 @@ export default class CommentContainer extends Container {
     this.crowi = crowi;
     this.pageId = pageId;
     this.revisionId = revisionId;
+
+    this.state = {
+      comments: [],
+    };
   }
 
   init() {
@@ -22,6 +26,31 @@ export default class CommentContainer extends Container {
     }
   }
 
+  /**
+   * Load data of comments and store them in state
+   */
+  retrieveComments() {
+    // get data (desc order array)
+    this.crowi.apiGet('/comments.get', { page_id: this.pageId })
+      .then((res) => {
+        if (res.ok) {
+          this.setState({ comments: res.comments });
+        }
+      });
+  }
+
+  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 rerender <PageComments />
    */
@@ -42,6 +71,15 @@ export default class CommentContainer extends Container {
     });
   }
 
+  deleteComment(comment) {
+    return this.crowi.apiPost('/comments.remove', { comment_id: comment._id })
+      .then((res) => {
+        if (res.ok) {
+          this.findAndSplice(comment);
+        }
+      });
+  }
+
   onUpload(file) {
     const endpoint = '/attachments.add';
 

+ 57 - 63
src/client/js/components/PageComments.js

@@ -1,8 +1,9 @@
+/* eslint-disable react/no-multi-comp */
 /* eslint-disable react/no-access-state-in-setstate */
 import React from 'react';
 import PropTypes from 'prop-types';
 
-import { Provider } from 'unstated';
+import { Subscribe } from 'unstated';
 
 import { withTranslation } from 'react-i18next';
 import GrowiRenderer from '../util/GrowiRenderer';
@@ -28,17 +29,14 @@ class PageComments extends React.Component {
     super(props);
 
     this.state = {
-      // desc order array
-      comments: [],
-
-      children: {},
-
       isLayoutTypeGrowi: false,
 
       // for deleting comment
       commentToDelete: undefined,
       isDeleteConfirmModalShown: false,
       errorMessageForDeleting: undefined,
+
+      showEditorIds: new Set(),
     };
 
     this.growiRenderer = new GrowiRenderer(this.props.crowi, this.props.crowiOriginRenderer, { mode: 'comment' });
@@ -48,12 +46,11 @@ class PageComments extends React.Component {
     this.deleteComment = this.deleteComment.bind(this);
     this.showDeleteConfirmModal = this.showDeleteConfirmModal.bind(this);
     this.closeDeleteConfirmModal = this.closeDeleteConfirmModal.bind(this);
-    this.replyToComment = this.replyToComment.bind(this);
+    this.replyButtonClickedHandler = this.replyButtonClickedHandler.bind(this);
   }
 
   componentWillMount() {
     this.init();
-    this.retrieveData = this.retrieveData.bind(this);
   }
 
   init() {
@@ -64,25 +61,7 @@ class PageComments extends React.Component {
     const layoutType = this.props.crowi.getConfig().layoutType;
     this.setState({ isLayoutTypeGrowi: layoutType === 'crowi-plus' || layoutType === 'growi' });
 
-    this.retrieveData();
-  }
-
-  /**
-   * Load data of comments and store them in state
-   */
-  retrieveData() {
-    // get data (desc order array)
-    this.props.crowi.apiGet('/comments.get', { page_id: this.props.pageId })
-      .then((res) => {
-        if (res.ok) {
-          this.setState({ comments: res.comments });
-          const tempChildren = {};
-          res.comments.forEach((comment) => {
-            tempChildren[comment._id] = React.createRef();
-          });
-          this.setState({ children: tempChildren });
-        }
-      });
+    this.props.commentContainer.retrieveComments();
   }
 
   confirmToDeleteComment(comment) {
@@ -90,18 +69,11 @@ class PageComments extends React.Component {
     this.showDeleteConfirmModal();
   }
 
-  replyToComment(comment) {
-    this.state.children[comment._id].toggleEditor();
-  }
-
   deleteComment() {
     const comment = this.state.commentToDelete;
 
-    this.props.crowi.apiPost('/comments.remove', { comment_id: comment._id })
-      .then((res) => {
-        if (res.ok) {
-          this.findAndSplice(comment);
-        }
+    this.props.commentContainer.deleteComment(comment)
+      .then(() => {
         this.closeDeleteConfirmModal();
       })
       .catch((err) => {
@@ -109,18 +81,6 @@ class PageComments extends React.Component {
       });
   }
 
-  findAndSplice(comment) {
-    const comments = this.state.comments;
-
-    const index = comments.indexOf(comment);
-    if (index < 0) {
-      return;
-    }
-    comments.splice(index, 1);
-
-    this.setState({ comments });
-  }
-
   showDeleteConfirmModal() {
     this.setState({ isDeleteConfirmModalShown: true });
   }
@@ -133,6 +93,11 @@ class PageComments extends React.Component {
     });
   }
 
+  replyButtonClickedHandler(commentId) {
+    const ids = this.state.showEditorIds.add(commentId);
+    this.setState({ showEditorIds: ids });
+  }
+
   // inserts reply after each corresponding comment
   reorderBasedOnReplies(comments, replies) {
     // const connections = this.findConnections(comments, replies);
@@ -156,28 +121,27 @@ class PageComments extends React.Component {
    * @memberOf PageComments
    */
   generateCommentElements(comments, replies) {
-    // create unstated container instance
-    const commentContainer = new CommentContainer(this.props.crowi, this.props.pageId, this.props.revisionId);
-
     const commentsWithReplies = this.reorderBasedOnReplies(comments, replies);
     return commentsWithReplies.map((comment) => {
+
+      const commentId = comment._id;
+      const showEditor = this.state.showEditorIds.has(commentId);
+
       return (
-        <div key={comment._id}>
+        <div key={commentId}>
           <Comment
             comment={comment}
             deleteBtnClicked={this.confirmToDeleteComment}
             crowiRenderer={this.growiRenderer}
-            onReplyButtonClicked={this.replyToComment}
+            onReplyButtonClicked={() => { this.replyButtonClickedHandler(commentId) }}
             crowi={this.props.crowi}
           />
-          { true && (
-            <Provider key={comment._id} inject={[commentContainer]}>
-              <CommentEditor
-                crowi={this.props.crowi}
-                crowiOriginRenderer={this.props.crowiOriginRenderer}
-                editorOptions={this.props.editorOptions}
-              />
-            </Provider>
+          { showEditor && (
+            <CommentEditor
+              crowi={this.props.crowi}
+              crowiOriginRenderer={this.props.crowiOriginRenderer}
+              editorOptions={this.props.editorOptions}
+            />
           )}
         </div>
       );
@@ -192,7 +156,7 @@ class PageComments extends React.Component {
     const newerReplies = [];
     const olderReplies = [];
 
-    let comments = this.state.comments;
+    let comments = this.props.commentContainer.state.comments;
     if (this.state.isLayoutTypeGrowi) {
       // replace with asc order array
       comments = comments.slice().reverse(); // non-destructive reverse
@@ -310,7 +274,37 @@ class PageComments extends React.Component {
 
 }
 
+/**
+ * Wrapper component for using unstated
+ */
+class PageCommentsWrapper extends React.Component {
+
+  render() {
+    return (
+      <Subscribe to={[CommentContainer]}>
+        { commentContainer => (
+          // eslint-disable-next-line arrow-body-style
+          <PageComments commentContainer={commentContainer} {...this.props} />
+        )}
+      </Subscribe>
+    );
+  }
+
+}
+
+PageCommentsWrapper.propTypes = {
+  crowi: PropTypes.object.isRequired,
+  crowiOriginRenderer: PropTypes.object.isRequired,
+  pageId: PropTypes.string.isRequired,
+  revisionId: PropTypes.string.isRequired,
+  revisionCreatedAt: PropTypes.number,
+  pagePath: PropTypes.string,
+  editorOptions: PropTypes.object,
+  slackChannels: PropTypes.string,
+};
 PageComments.propTypes = {
+  commentContainer: PropTypes.object.isRequired,
+
   crowi: PropTypes.object.isRequired,
   crowiOriginRenderer: PropTypes.object.isRequired,
   pageId: PropTypes.string.isRequired,
@@ -321,4 +315,4 @@ PageComments.propTypes = {
   slackChannels: PropTypes.string,
 };
 
-export default withTranslation(null, { withRef: true })(PageComments);
+export default withTranslation(null, { withRef: true })(PageCommentsWrapper);