|
@@ -16,6 +16,7 @@ import RevisionBody from '../Page/RevisionBody';
|
|
|
import UserPicture from '../User/UserPicture';
|
|
import UserPicture from '../User/UserPicture';
|
|
|
import Username from '../User/Username';
|
|
import Username from '../User/Username';
|
|
|
import CommentEditor from './CommentEditor';
|
|
import CommentEditor from './CommentEditor';
|
|
|
|
|
+import CommentControl from './CommentControl';
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
*
|
|
*
|
|
@@ -25,7 +26,7 @@ import CommentEditor from './CommentEditor';
|
|
|
* @class Comment
|
|
* @class Comment
|
|
|
* @extends {React.Component}
|
|
* @extends {React.Component}
|
|
|
*/
|
|
*/
|
|
|
-class Comment extends React.Component {
|
|
|
|
|
|
|
+class Comment extends React.PureComponent {
|
|
|
|
|
|
|
|
constructor(props) {
|
|
constructor(props) {
|
|
|
super(props);
|
|
super(props);
|
|
@@ -33,7 +34,7 @@ class Comment extends React.Component {
|
|
|
this.state = {
|
|
this.state = {
|
|
|
html: '',
|
|
html: '',
|
|
|
isOlderRepliesShown: false,
|
|
isOlderRepliesShown: false,
|
|
|
- showReEditorIds: new Set(),
|
|
|
|
|
|
|
+ isReEdit: false,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
this.growiRenderer = this.props.appContainer.getRenderer('comment');
|
|
this.growiRenderer = this.props.appContainer.getRenderer('comment');
|
|
@@ -42,23 +43,39 @@ class Comment extends React.Component {
|
|
|
this.isCurrentRevision = this.isCurrentRevision.bind(this);
|
|
this.isCurrentRevision = this.isCurrentRevision.bind(this);
|
|
|
this.getRootClassName = this.getRootClassName.bind(this);
|
|
this.getRootClassName = this.getRootClassName.bind(this);
|
|
|
this.getRevisionLabelClassName = this.getRevisionLabelClassName.bind(this);
|
|
this.getRevisionLabelClassName = this.getRevisionLabelClassName.bind(this);
|
|
|
|
|
+ this.editBtnClickedHandler = this.editBtnClickedHandler.bind(this);
|
|
|
this.deleteBtnClickedHandler = this.deleteBtnClickedHandler.bind(this);
|
|
this.deleteBtnClickedHandler = this.deleteBtnClickedHandler.bind(this);
|
|
|
this.renderText = this.renderText.bind(this);
|
|
this.renderText = this.renderText.bind(this);
|
|
|
this.renderHtml = this.renderHtml.bind(this);
|
|
this.renderHtml = this.renderHtml.bind(this);
|
|
|
this.commentButtonClickedHandler = this.commentButtonClickedHandler.bind(this);
|
|
this.commentButtonClickedHandler = this.commentButtonClickedHandler.bind(this);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- componentWillMount() {
|
|
|
|
|
- this.renderHtml(this.props.comment.comment);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ initCurrentRenderingContext() {
|
|
|
|
|
+ this.currentRenderingContext = {
|
|
|
|
|
+ markdown: this.props.comment.comment,
|
|
|
|
|
+ };
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- componentWillReceiveProps(nextProps) {
|
|
|
|
|
- this.renderHtml(nextProps.comment.comment);
|
|
|
|
|
|
|
+ componentDidMount() {
|
|
|
|
|
+ this.initCurrentRenderingContext();
|
|
|
|
|
+ this.renderHtml();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // not used
|
|
|
|
|
- setMarkdown(markdown) {
|
|
|
|
|
- this.renderHtml(markdown);
|
|
|
|
|
|
|
+ componentDidUpdate(prevProps) {
|
|
|
|
|
+ const { comment: prevComment } = prevProps;
|
|
|
|
|
+ const { comment } = this.props;
|
|
|
|
|
+
|
|
|
|
|
+ // render only when props.markdown is updated
|
|
|
|
|
+ if (comment !== prevComment) {
|
|
|
|
|
+ this.initCurrentRenderingContext();
|
|
|
|
|
+ this.renderHtml();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const { interceptorManager } = this.props.appContainer;
|
|
|
|
|
+
|
|
|
|
|
+ interceptorManager.process('postRenderCommentHtml', this.currentRenderingContext);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
checkPermissionToControlComment() {
|
|
checkPermissionToControlComment() {
|
|
@@ -99,18 +116,12 @@ class Comment extends React.Component {
|
|
|
this.isCurrentRevision() ? 'label-primary' : 'label-default'}`;
|
|
this.isCurrentRevision() ? 'label-primary' : 'label-default'}`;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- editBtnClickedHandler(commentId) {
|
|
|
|
|
- const ids = this.state.showReEditorIds.add(commentId);
|
|
|
|
|
- this.setState({ showReEditorIds: ids });
|
|
|
|
|
|
|
+ editBtnClickedHandler() {
|
|
|
|
|
+ this.setState({ isReEdit: !this.state.isReEdit });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- commentButtonClickedHandler(commentId) {
|
|
|
|
|
- this.setState((prevState) => {
|
|
|
|
|
- prevState.showReEditorIds.delete(commentId);
|
|
|
|
|
- return {
|
|
|
|
|
- showReEditorIds: prevState.showReEditorIds,
|
|
|
|
|
- };
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ commentButtonClickedHandler() {
|
|
|
|
|
+ this.editBtnClickedHandler();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
deleteBtnClickedHandler() {
|
|
deleteBtnClickedHandler() {
|
|
@@ -142,35 +153,23 @@ class Comment extends React.Component {
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- renderHtml(markdown) {
|
|
|
|
|
- const context = {
|
|
|
|
|
- markdown,
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const growiRenderer = this.props.growiRenderer;
|
|
|
|
|
- const interceptorManager = this.props.appContainer.interceptorManager;
|
|
|
|
|
- interceptorManager.process('preRenderComment', context)
|
|
|
|
|
- .then(() => { return interceptorManager.process('prePreProcess', context) })
|
|
|
|
|
- .then(() => {
|
|
|
|
|
- context.markdown = growiRenderer.preProcess(context.markdown);
|
|
|
|
|
- })
|
|
|
|
|
- .then(() => { return interceptorManager.process('postPreProcess', context) })
|
|
|
|
|
- .then(() => {
|
|
|
|
|
- const parsedHTML = growiRenderer.process(context.markdown);
|
|
|
|
|
- context.parsedHTML = parsedHTML;
|
|
|
|
|
- })
|
|
|
|
|
- .then(() => { return interceptorManager.process('prePostProcess', context) })
|
|
|
|
|
- .then(() => {
|
|
|
|
|
- context.parsedHTML = growiRenderer.postProcess(context.parsedHTML);
|
|
|
|
|
- })
|
|
|
|
|
- .then(() => { return interceptorManager.process('postPostProcess', context) })
|
|
|
|
|
- .then(() => { return interceptorManager.process('preRenderCommentHtml', context) })
|
|
|
|
|
- .then(() => {
|
|
|
|
|
- this.setState({ html: context.parsedHTML });
|
|
|
|
|
- })
|
|
|
|
|
- // process interceptors for post rendering
|
|
|
|
|
- .then(() => { return interceptorManager.process('postRenderCommentHtml', context) });
|
|
|
|
|
-
|
|
|
|
|
|
|
+ async renderHtml() {
|
|
|
|
|
+
|
|
|
|
|
+ const { growiRenderer, appContainer } = this.props;
|
|
|
|
|
+ const { interceptorManager } = appContainer;
|
|
|
|
|
+ const context = this.currentRenderingContext;
|
|
|
|
|
+
|
|
|
|
|
+ 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);
|
|
|
|
|
+ this.setState({ html: context.parsedHTML });
|
|
|
|
|
+ await interceptorManager.process('postRenderCommentHtml', context);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
renderReply(reply) {
|
|
renderReply(reply) {
|
|
@@ -223,32 +222,20 @@ class Comment extends React.Component {
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
<React.Fragment>
|
|
<React.Fragment>
|
|
|
- { areThereHiddenReplies && (
|
|
|
|
|
|
|
+ {areThereHiddenReplies && (
|
|
|
<div className="page-comments-hidden-replies">
|
|
<div className="page-comments-hidden-replies">
|
|
|
<Collapse in={this.state.isOlderRepliesShown}>
|
|
<Collapse in={this.state.isOlderRepliesShown}>
|
|
|
<div>{hiddenElements}</div>
|
|
<div>{hiddenElements}</div>
|
|
|
</Collapse>
|
|
</Collapse>
|
|
|
<div className="text-center">{toggleButton}</div>
|
|
<div className="text-center">{toggleButton}</div>
|
|
|
</div>
|
|
</div>
|
|
|
- ) }
|
|
|
|
|
|
|
+ )}
|
|
|
|
|
|
|
|
{shownElements}
|
|
{shownElements}
|
|
|
</React.Fragment>
|
|
</React.Fragment>
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- renderCommentControl(comment) {
|
|
|
|
|
- return (
|
|
|
|
|
- <div className="page-comment-control">
|
|
|
|
|
- <button type="button" className="btn btn-link p-2" onClick={() => { this.editBtnClickedHandler(comment._id) }}>
|
|
|
|
|
- <i className="ti-pencil"></i>
|
|
|
|
|
- </button>
|
|
|
|
|
- <button type="button" className="btn btn-link p-2 mr-2" onClick={this.deleteBtnClickedHandler}>
|
|
|
|
|
- <i className="ti-close"></i>
|
|
|
|
|
- </button>
|
|
|
|
|
- </div>
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
render() {
|
|
render() {
|
|
|
const comment = this.props.comment;
|
|
const comment = this.props.comment;
|
|
@@ -259,8 +246,6 @@ class Comment extends React.Component {
|
|
|
const updatedAt = new Date(comment.updatedAt);
|
|
const updatedAt = new Date(comment.updatedAt);
|
|
|
const isEdited = createdAt < updatedAt;
|
|
const isEdited = createdAt < updatedAt;
|
|
|
|
|
|
|
|
- const showReEditor = this.state.showReEditorIds.has(commentId);
|
|
|
|
|
-
|
|
|
|
|
const rootClassName = this.getRootClassName(comment);
|
|
const rootClassName = this.getRootClassName(comment);
|
|
|
const commentDate = formatDistanceStrict(createdAt, new Date());
|
|
const commentDate = formatDistanceStrict(createdAt, new Date());
|
|
|
const commentBody = isMarkdown ? this.renderRevisionBody() : this.renderText(comment.comment);
|
|
const commentBody = isMarkdown ? this.renderRevisionBody() : this.renderText(comment.comment);
|
|
@@ -284,7 +269,7 @@ class Comment extends React.Component {
|
|
|
return (
|
|
return (
|
|
|
<React.Fragment>
|
|
<React.Fragment>
|
|
|
|
|
|
|
|
- {showReEditor ? (
|
|
|
|
|
|
|
+ {this.state.isReEdit ? (
|
|
|
<CommentEditor
|
|
<CommentEditor
|
|
|
growiRenderer={this.growiRenderer}
|
|
growiRenderer={this.growiRenderer}
|
|
|
currentCommentId={commentId}
|
|
currentCommentId={commentId}
|
|
@@ -305,18 +290,19 @@ class Comment extends React.Component {
|
|
|
<OverlayTrigger overlay={commentDateTooltip} placement="bottom">
|
|
<OverlayTrigger overlay={commentDateTooltip} placement="bottom">
|
|
|
<span><a href={`#${commentId}`}>{commentDate}</a></span>
|
|
<span><a href={`#${commentId}`}>{commentDate}</a></span>
|
|
|
</OverlayTrigger>
|
|
</OverlayTrigger>
|
|
|
- { isEdited && (
|
|
|
|
|
- <OverlayTrigger overlay={editedDateTooltip} placement="bottom">
|
|
|
|
|
- <span> (edited)</span>
|
|
|
|
|
- </OverlayTrigger>
|
|
|
|
|
- ) }
|
|
|
|
|
|
|
+ {isEdited && (
|
|
|
|
|
+ <OverlayTrigger overlay={editedDateTooltip} placement="bottom">
|
|
|
|
|
+ <span> (edited)</span>
|
|
|
|
|
+ </OverlayTrigger>
|
|
|
|
|
+ )}
|
|
|
<span className="ml-2"><a className={revisionLavelClassName} href={revHref}>{revFirst8Letters}</a></span>
|
|
<span className="ml-2"><a className={revisionLavelClassName} href={revHref}>{revFirst8Letters}</a></span>
|
|
|
</div>
|
|
</div>
|
|
|
- { this.checkPermissionToControlComment() && this.renderCommentControl(comment) }
|
|
|
|
|
|
|
+ {this.checkPermissionToControlComment()
|
|
|
|
|
+ && <CommentControl onClickDeleteBtn={this.deleteBtnClickedHandler} onClickEditBtn={this.editBtnClickedHandler} />}
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
{this.renderReplies()}
|
|
{this.renderReplies()}
|
|
|
|
|
|
|
|
</React.Fragment>
|
|
</React.Fragment>
|