Yuki Takei 5 лет назад
Родитель
Сommit
aaddc7129f

+ 9 - 16
src/client/js/components/PageComment/Comment.jsx

@@ -35,17 +35,13 @@ class Comment extends React.PureComponent {
       isReEdit: false,
     };
 
-    this.growiRenderer = this.props.appContainer.getRenderer('comment');
-
     this.isCurrentUserIsAuthor = this.isCurrentUserEqualsToAuthor.bind(this);
     this.isCurrentRevision = this.isCurrentRevision.bind(this);
     this.getRootClassName = this.getRootClassName.bind(this);
     this.getRevisionLabelClassName = this.getRevisionLabelClassName.bind(this);
-    this.editBtnClickedHandler = this.editBtnClickedHandler.bind(this);
     this.deleteBtnClickedHandler = this.deleteBtnClickedHandler.bind(this);
     this.renderText = this.renderText.bind(this);
     this.renderHtml = this.renderHtml.bind(this);
-    this.commentButtonClickedHandler = this.commentButtonClickedHandler.bind(this);
   }
 
 
@@ -114,14 +110,6 @@ class Comment extends React.PureComponent {
       this.isCurrentRevision() ? 'badge-primary' : 'badge-secondary'}`;
   }
 
-  editBtnClickedHandler() {
-    this.setState({ isReEdit: !this.state.isReEdit });
-  }
-
-  commentButtonClickedHandler() {
-    this.editBtnClickedHandler();
-  }
-
   deleteBtnClickedHandler() {
     this.props.deleteBtnClicked(this.props.comment);
   }
@@ -187,12 +175,13 @@ class Comment extends React.PureComponent {
 
         {this.state.isReEdit ? (
           <CommentEditor
-            growiRenderer={this.growiRenderer}
+            growiRenderer={this.props.growiRenderer}
             currentCommentId={commentId}
             commentBody={comment.comment}
             replyTo={undefined}
-            commentButtonClickedHandler={this.commentButtonClickedHandler}
             commentCreator={creator.username}
+            onCancelButtonClicked={() => this.setState({ isReEdit: false })}
+            onCommentButtonClicked={() => this.setState({ isReEdit: false })}
           />
         ) : (
           <div id={commentId} className={rootClassName}>
@@ -216,8 +205,12 @@ class Comment extends React.PureComponent {
                 ) }
                 <span className="ml-2"><a className={revisionLavelClassName} href={revHref}>{revFirst8Letters}</a></span>
               </div>
-              {this.checkPermissionToControlComment()
-                  && <CommentControl onClickDeleteBtn={this.deleteBtnClickedHandler} onClickEditBtn={this.editBtnClickedHandler} />}
+              { this.checkPermissionToControlComment() && (
+                <CommentControl
+                  onClickDeleteBtn={this.deleteBtnClickedHandler}
+                  onClickEditBtn={() => this.setState({ isReEdit: true })}
+                />
+              ) }
             </div>
           </div>
           )

+ 172 - 116
src/client/js/components/PageComment/CommentEditor.jsx

@@ -38,6 +38,7 @@ class CommentEditor extends React.Component {
     const isUploadableFile = config.upload.file;
 
     this.state = {
+      isReadyToUse: !this.props.isForNewComment,
       comment: this.props.commentBody || '',
       isMarkdown: true,
       html: '',
@@ -51,14 +52,16 @@ class CommentEditor extends React.Component {
     this.updateState = this.updateState.bind(this);
     this.updateStateCheckbox = this.updateStateCheckbox.bind(this);
 
-    this.postHandler = this.postHandler.bind(this);
+    this.cancelButtonClickedHandler = this.cancelButtonClickedHandler.bind(this);
+    this.commentButtonClickedHandler = this.commentButtonClickedHandler.bind(this);
+    this.ctrlEnterHandler = this.ctrlEnterHandler.bind(this);
+    this.postComment = this.postComment.bind(this);
     this.uploadHandler = this.uploadHandler.bind(this);
 
     this.renderHtml = this.renderHtml.bind(this);
     this.handleSelect = this.handleSelect.bind(this);
     this.onSlackEnabledFlagChange = this.onSlackEnabledFlagChange.bind(this);
     this.onSlackChannelsChange = this.onSlackChannelsChange.bind(this);
-    this.toggleEditor = this.toggleEditor.bind(this);
   }
 
   updateState(value) {
@@ -85,11 +88,6 @@ class CommentEditor extends React.Component {
     this.props.commentContainer.setState({ slackChannels });
   }
 
-  toggleEditor() {
-    const targetId = this.props.replyTo || this.props.currentCommentId;
-    this.props.commentButtonClickedHandler(targetId);
-  }
-
   initializeEditor() {
     this.setState({
       comment: '',
@@ -100,36 +98,65 @@ class CommentEditor extends React.Component {
     });
     // reset value
     this.editor.setValue('');
-    this.toggleEditor();
   }
 
-  /**
-   * Post comment with CommentContainer and update state
-   */
-  async postHandler(event) {
+  cancelButtonClickedHandler() {
+    const { isForNewComment, onCancelButtonClicked } = this.props;
+
+    // change state to not ready
+    // when this editor is for the new comment mode
+    if (isForNewComment) {
+      this.setState({ isReadyToUse: false });
+    }
+
+    if (onCancelButtonClicked != null) {
+      const { replyTo, currentCommentId } = this.props;
+      onCancelButtonClicked(replyTo || currentCommentId);
+    }
+  }
+
+  commentButtonClickedHandler() {
+    this.postComment();
+  }
+
+  ctrlEnterHandler(event) {
     if (event != null) {
       event.preventDefault();
     }
 
+    this.postComment();
+  }
+
+  /**
+   * Post comment with CommentContainer and update state
+   */
+  async postComment() {
+    const {
+      commentContainer, replyTo, currentCommentId, commentCreator, onCommentButtonClicked,
+    } = this.props;
     try {
-      if (this.props.currentCommentId != null) {
-        await this.props.commentContainer.putComment(
+      if (currentCommentId != null) {
+        await commentContainer.putComment(
           this.state.comment,
           this.state.isMarkdown,
-          this.props.currentCommentId,
-          this.props.commentCreator,
+          currentCommentId,
+          commentCreator,
         );
       }
       else {
         await this.props.commentContainer.postComment(
           this.state.comment,
           this.state.isMarkdown,
-          this.props.replyTo,
-          this.props.commentContainer.state.isSlackEnabled,
-          this.props.commentContainer.state.slackChannels,
+          replyTo,
+          commentContainer.state.isSlackEnabled,
+          commentContainer.state.slackChannels,
         );
       }
       this.initializeEditor();
+
+      if (onCommentButtonClicked != null) {
+        onCommentButtonClicked(replyTo || currentCommentId);
+      }
     }
     catch (err) {
       const errorMessage = err.message || 'An unknown error occured when posting comment';
@@ -212,7 +239,19 @@ class CommentEditor extends React.Component {
     return { __html: html };
   }
 
-  render() {
+  renderBeforeReady() {
+    return (
+      <button
+        type="button"
+        className="btn btn-lg btn-link center-block"
+        onClick={() => this.setState({ isReadyToUse: true })}
+      >
+        <i className="icon-bubble"></i> Add Comment
+      </button>
+    );
+  }
+
+  renderReady() {
     const { appContainer, commentContainer } = this.props;
     const { activeTab } = this.state;
 
@@ -221,7 +260,7 @@ class CommentEditor extends React.Component {
 
     const errorMessage = <span className="text-danger text-right mr-2">{this.state.errorMessage}</span>;
     const cancelButton = (
-      <Button outline color="danger" size="xs" className="btn btn-outline-danger rounded-pill" onClick={this.toggleEditor}>
+      <Button outline color="danger" size="xs" className="btn btn-outline-danger rounded-pill" onClick={this.cancelButtonClickedHandler}>
         Cancel
       </Button>
     );
@@ -230,120 +269,128 @@ class CommentEditor extends React.Component {
         outline
         color="primary"
         className="btn btn-outline-primary rounded-pill"
-        onClick={this.postHandler}
+        onClick={this.commentButtonClickedHandler}
       >
         Comment
       </Button>
     );
 
     return (
-      <div className="form page-comment-form">
-        <div className="comment-form">
-          <div className="comment-form-user">
-            <UserPicture user={appContainer.currentUser} />
-          </div>
-          <div className="comment-form-main">
-            <div className="comment-write">
-              <Nav tabs>
-                <NavItem>
-                  <NavLink type="button" className={activeTab === 1 ? 'active' : ''} onClick={() => this.handleSelect(1)}>
+      <>
+        <div className="comment-write">
+          <Nav tabs>
+            <NavItem>
+              <NavLink type="button" className={activeTab === 1 ? 'active' : ''} onClick={() => this.handleSelect(1)}>
                     Write
-                  </NavLink>
-                </NavItem>
-                { this.state.isMarkdown && (
-                  <NavItem>
-                    <NavLink type="button" className={activeTab === 2 ? 'active' : ''} onClick={() => this.handleSelect(2)}>
+              </NavLink>
+            </NavItem>
+            { this.state.isMarkdown && (
+            <NavItem>
+              <NavLink type="button" className={activeTab === 2 ? 'active' : ''} onClick={() => this.handleSelect(2)}>
                       Preview
-                    </NavLink>
-                  </NavItem>
+              </NavLink>
+            </NavItem>
                 ) }
-              </Nav>
-              <TabContent activeTab={activeTab}>
-                <TabPane tabId={1}>
-                  <Editor
-                    ref={(c) => { this.editor = c }}
-                    value={this.state.comment}
-                    isGfmMode={this.state.isMarkdown}
-                    lineNumbers={false}
-                    isMobile={appContainer.isMobile}
-                    isUploadable={this.state.isUploadable}
-                    isUploadableFile={this.state.isUploadableFile}
-                    emojiStrategy={emojiStrategy}
-                    onChange={this.updateState}
-                    onUpload={this.uploadHandler}
-                    onCtrlEnter={this.postHandler}
-                  />
-                </TabPane>
-                <TabPane tabId={2}>
-                  <div className="comment-form-preview">
-                    {commentPreview}
-                  </div>
-                </TabPane>
-              </TabContent>
-            </div>
-            <div className="comment-submit">
-              <div className="d-flex">
-                <label className="mr-2">
-                  {activeTab === 1 && (
-                    <span className="custom-control custom-checkbox">
-                      <input
-                        type="checkbox"
-                        className="custom-control-input"
-                        id="comment-form-is-markdown"
-                        name="isMarkdown"
-                        checked={this.state.isMarkdown}
-                        value="1"
-                        onChange={this.updateStateCheckbox}
-                      />
-                      <label
-                        className="ml-2 custom-control-label"
-                        htmlFor="comment-form-is-markdown"
-                      >
-                        Markdown
-                      </label>
-                    </span>
-                  ) }
-                </label>
-                <span className="flex-grow-1" />
-                <span className="d-none d-sm-inline">{ this.state.errorMessage && errorMessage }</span>
-                { this.state.hasSlackConfig
-                  && (
-                  <div className="form-inline align-self-center mr-md-2">
-                    <SlackNotification
-                      isSlackEnabled={commentContainer.state.isSlackEnabled}
-                      slackChannels={commentContainer.state.slackChannels}
-                      onEnabledFlagChange={this.onSlackEnabledFlagChange}
-                      onChannelChange={this.onSlackChannelsChange}
-                    />
-                  </div>
-                  )
-                }
-                <div className="d-none d-sm-block">
-                  <span className="mr-2">{cancelButton}</span><span>{submitButton}</span>
-                </div>
+          </Nav>
+          <TabContent activeTab={activeTab}>
+            <TabPane tabId={1}>
+              <Editor
+                ref={(c) => { this.editor = c }}
+                value={this.state.comment}
+                isGfmMode={this.state.isMarkdown}
+                lineNumbers={false}
+                isMobile={appContainer.isMobile}
+                isUploadable={this.state.isUploadable}
+                isUploadableFile={this.state.isUploadableFile}
+                emojiStrategy={emojiStrategy}
+                onChange={this.updateState}
+                onUpload={this.uploadHandler}
+                onCtrlEnter={this.ctrlEnterHandler}
+              />
+            </TabPane>
+            <TabPane tabId={2}>
+              <div className="comment-form-preview">
+                {commentPreview}
               </div>
-              <div className="d-block d-sm-none mt-2">
-                <div className="d-flex justify-content-end">
-                  { this.state.errorMessage && errorMessage }
-                  <span className="mr-2">{cancelButton}</span><span>{submitButton}</span>
-                </div>
+            </TabPane>
+          </TabContent>
+        </div>
+
+        <div className="comment-submit">
+          <div className="d-flex">
+            <label className="mr-2">
+              {activeTab === 1 && (
+              <span className="custom-control custom-checkbox">
+                <input
+                  type="checkbox"
+                  className="custom-control-input"
+                  id="comment-form-is-markdown"
+                  name="isMarkdown"
+                  checked={this.state.isMarkdown}
+                  value="1"
+                  onChange={this.updateStateCheckbox}
+                />
+                <label
+                  className="ml-2 custom-control-label"
+                  htmlFor="comment-form-is-markdown"
+                >
+                  Markdown
+                </label>
+              </span>
+                  ) }
+            </label>
+            <span className="flex-grow-1" />
+            <span className="d-none d-sm-inline">{ this.state.errorMessage && errorMessage }</span>
+            { this.state.hasSlackConfig
+              && (
+              <div className="form-inline align-self-center mr-md-2">
+                <SlackNotification
+                  isSlackEnabled={commentContainer.state.isSlackEnabled}
+                  slackChannels={commentContainer.state.slackChannels}
+                  onEnabledFlagChange={this.onSlackEnabledFlagChange}
+                  onChannelChange={this.onSlackChannelsChange}
+                />
               </div>
+              )
+            }
+            <div className="d-none d-sm-block">
+              <span className="mr-2">{cancelButton}</span><span>{submitButton}</span>
+            </div>
+          </div>
+          <div className="d-block d-sm-none mt-2">
+            <div className="d-flex justify-content-end">
+              { this.state.errorMessage && errorMessage }
+              <span className="mr-2">{cancelButton}</span><span>{submitButton}</span>
             </div>
           </div>
         </div>
+      </>
+    );
+  }
+
+  render() {
+    const { appContainer } = this.props;
+    const { isReadyToUse } = this.state;
+
+    return (
+      <div className="form page-comment-form">
+        <div className="comment-form">
+          <div className="comment-form-user">
+            <UserPicture user={appContainer.currentUser} noLink noTooltip />
+          </div>
+          <div className="comment-form-main">
+            { !isReadyToUse
+              ? this.renderBeforeReady()
+              : this.renderReady()
+            }
+          </div>
+        </div>
       </div>
     );
   }
 
 }
 
-/**
- * Wrapper component for using unstated
- */
-const CommentEditorWrapper = (props) => {
-  return createSubscribedElement(CommentEditor, props, [AppContainer, PageContainer, EditorContainer, CommentContainer]);
-};
-
 CommentEditor.propTypes = {
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
@@ -351,11 +398,20 @@ CommentEditor.propTypes = {
   commentContainer: PropTypes.instanceOf(CommentContainer).isRequired,
 
   growiRenderer: PropTypes.instanceOf(GrowiRenderer).isRequired,
+  isForNewComment: PropTypes.bool,
   replyTo: PropTypes.string,
   currentCommentId: PropTypes.string,
   commentBody: PropTypes.string,
   commentCreator: PropTypes.string,
-  commentButtonClickedHandler: PropTypes.func.isRequired,
+  onCancelButtonClicked: PropTypes.func,
+  onCommentButtonClicked: PropTypes.func,
+};
+
+/**
+ * Wrapper component for using unstated
+ */
+const CommentEditorWrapper = (props) => {
+  return createSubscribedElement(CommentEditor, props, [AppContainer, PageContainer, EditorContainer, CommentContainer]);
 };
 
 export default CommentEditorWrapper;

+ 10 - 64
src/client/js/components/PageComment/CommentEditorLazyRenderer.jsx

@@ -3,75 +3,21 @@ import PropTypes from 'prop-types';
 
 import { createSubscribedElement } from '../UnstatedUtils';
 import AppContainer from '../../services/AppContainer';
-import UserPicture from '../User/UserPicture';
 
 import CommentEditor from './CommentEditor';
 
-class CommentEditorLazyRenderer extends React.Component {
+const CommentEditorLazyRenderer = (props) => {
 
-  constructor(props) {
-    super(props);
+  const growiRenderer = props.appContainer.getRenderer('comment');
 
-    this.state = {
-      isEditorShown: false,
-    };
-
-    this.growiRenderer = this.props.appContainer.getRenderer('comment');
-
-    this.showCommentFormBtnClickHandler = this.showCommentFormBtnClickHandler.bind(this);
-  }
-
-  showCommentFormBtnClickHandler() {
-    this.setState({ isEditorShown: !this.state.isEditorShown });
-  }
-
-  render() {
-    const { appContainer } = this.props;
-    const user = appContainer.currentUser;
-    const isLoggedIn = user != null;
-
-    if (!isLoggedIn) {
-      return <React.Fragment></React.Fragment>;
-    }
-
-    return (
-      <React.Fragment>
-
-        { !this.state.isEditorShown && (
-          <div className="form page-comment-form">
-            <div className="comment-form">
-              <div className="comment-form-user">
-                <UserPicture user={user} />
-              </div>
-              <div className="comment-form-main">
-                { !this.state.isEditorShown && (
-                  <button
-                    type="button"
-                    className="btn btn-lg btn-link center-block"
-                    onClick={this.showCommentFormBtnClickHandler}
-                  >
-                    <i className="icon-bubble"></i> Add Comment
-                  </button>
-                ) }
-              </div>
-            </div>
-          </div>
-        ) }
-
-        { this.state.isEditorShown && (
-          <CommentEditor
-            growiRenderer={this.growiRenderer}
-            replyTo={undefined}
-            commentButtonClickedHandler={this.showCommentFormBtnClickHandler}
-          >
-          </CommentEditor>
-        ) }
-
-      </React.Fragment>
-    );
-  }
-
-}
+  return (
+    <CommentEditor
+      growiRenderer={growiRenderer}
+      replyTo={undefined}
+      isForNewComment
+    />
+  );
+};
 
 /**
  * Wrapper component for using unstated

+ 5 - 5
src/client/js/components/PageComment/ReplayComments.jsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 
-import { Button, Collapse } from 'reactstrap';
+import { Collapse } from 'reactstrap';
 
 import AppContainer from '../../services/AppContainer';
 import PageContainer from '../../services/PageContainer';
@@ -79,13 +79,13 @@ class ReplayComments extends React.PureComponent {
               <div>{hiddenElements}</div>
             </Collapse>
             <div className="text-center">
-              <Button
-                bsStyle="link"
-                className="page-comments-list-toggle-older"
+              <button
+                type="button"
+                className="page-comments-list-toggle-older btn btn-link"
                 onClick={this.toggleIsOlderRepliesShown}
               >
                 {toggleButtonIcon} {toggleButtonLabel}
-              </Button>
+              </button>
             </div>
           </div>
         )}

+ 14 - 3
src/client/js/components/PageComments.jsx

@@ -50,7 +50,9 @@ class PageComments extends React.Component {
     this.showDeleteConfirmModal = this.showDeleteConfirmModal.bind(this);
     this.closeDeleteConfirmModal = this.closeDeleteConfirmModal.bind(this);
     this.replyButtonClickedHandler = this.replyButtonClickedHandler.bind(this);
-    this.commentButtonClickedHandler = this.commentButtonClickedHandler.bind(this);
+    this.editorCancelHandler = this.editorCancelHandler.bind(this);
+    this.editorCommentHandler = this.editorCommentHandler.bind(this);
+    this.resetEditor = this.resetEditor.bind(this);
   }
 
   componentWillMount() {
@@ -99,7 +101,15 @@ class PageComments extends React.Component {
     this.setState({ showEditorIds: ids });
   }
 
-  commentButtonClickedHandler(commentId) {
+  editorCancelHandler(commentId) {
+    this.resetEditor(commentId);
+  }
+
+  editorCommentHandler(commentId) {
+    this.resetEditor(commentId);
+  }
+
+  resetEditor(commentId) {
     this.setState((prevState) => {
       prevState.showEditorIds.delete(commentId);
       return {
@@ -169,7 +179,8 @@ class PageComments extends React.Component {
             <CommentEditor
               growiRenderer={this.growiRenderer}
               replyTo={commentId}
-              commentButtonClickedHandler={this.commentButtonClickedHandler}
+              onCancelButtonClicked={this.editorCancelHandler}
+              onCommentButtonClicked={this.editorCommentHandler}
             />
           </div>
         )}