import React from 'react'; import PropTypes from 'prop-types'; import { useTranslation } from 'react-i18next'; import { format } from 'date-fns'; import { UncontrolledTooltip } from 'reactstrap'; import { UserPicture } from '@growi/ui'; import AppContainer from '~/client/services/AppContainer'; import PageContainer from '~/client/services/PageContainer'; import { withUnstatedContainers } from '../UnstatedUtils'; import FormattedDistanceDate from '../FormattedDistanceDate'; import RevisionBody from '../Page/RevisionBody'; import Username from '../User/Username'; import CommentEditor from './CommentEditor'; import CommentControl from './CommentControl'; import HistoryIcon from '../Icons/HistoryIcon'; /** * * @author Yuki Takei * * @export * @class Comment * @extends {React.Component} */ class Comment extends React.PureComponent { constructor(props) { super(props); this.state = { html: '', isReEdit: false, }; this.isCurrentUserIsAuthor = this.isCurrentUserEqualsToAuthor.bind(this); this.isCurrentRevision = this.isCurrentRevision.bind(this); this.getRootClassName = this.getRootClassName.bind(this); this.deleteBtnClickedHandler = this.deleteBtnClickedHandler.bind(this); this.renderText = this.renderText.bind(this); this.renderHtml = this.renderHtml.bind(this); } initCurrentRenderingContext() { this.currentRenderingContext = { markdown: this.props.comment.comment, }; } componentDidMount() { this.initCurrentRenderingContext(); this.renderHtml(); } 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); } isCurrentUserEqualsToAuthor() { const { creator } = this.props.comment; if (creator == null) { return false; } return creator.username === this.props.appContainer.currentUsername; } isCurrentRevision() { return this.props.comment.revision === this.props.pageContainer.state.revisionId; } getRootClassName(comment) { let className = 'page-comment flex-column'; const { revisionId, revisionCreatedAt } = this.props.pageContainer.state; if (comment.revision === revisionId) { className += ' page-comment-current'; } else if (Date.parse(comment.createdAt) / 1000 > revisionCreatedAt) { className += ' page-comment-newer'; } else { className += ' page-comment-older'; } if (this.isCurrentUserEqualsToAuthor()) { className += ' page-comment-me'; } return className; } deleteBtnClickedHandler() { this.props.deleteBtnClicked(this.props.comment); } renderText(comment) { return {comment}; } renderRevisionBody() { const config = this.props.appContainer.getConfig(); const isMathJaxEnabled = !!config.env.MATHJAX; return ( ); } 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, context); await interceptorManager.process('postPreProcess', context); context.parsedHTML = await growiRenderer.process(context.markdown, context); await interceptorManager.process('prePostProcess', context); context.parsedHTML = await growiRenderer.postProcess(context.parsedHTML, context); await interceptorManager.process('postPostProcess', context); await interceptorManager.process('preRenderCommentHtml', context); this.setState({ html: context.parsedHTML }); await interceptorManager.process('postRenderCommentHtml', context); } render() { const { t, comment, isReadOnly, onComment, } = this.props; const commentId = comment._id; const creator = comment.creator; const isMarkdown = comment.isMarkdown; const createdAt = new Date(comment.createdAt); const updatedAt = new Date(comment.updatedAt); const isEdited = createdAt < updatedAt; const rootClassName = this.getRootClassName(comment); const commentBody = isMarkdown ? this.renderRevisionBody() : this.renderText(comment.comment); const revHref = `?revision=${comment.revision}`; const editedDateId = `editedDate-${comment._id}`; const editedDateFormatted = isEdited ? format(updatedAt, 'yyyy/MM/dd HH:mm') : null; return ( {(this.state.isReEdit && !isReadOnly) ? ( this.setState({ isReEdit: false })} onCommentButtonClicked={() => { this.setState({ isReEdit: false }); if (onComment != null) onComment(); }} /> ) : (
{commentBody}
{ isEdited && ( <>  (edited) {editedDateFormatted} )} {t('page_comment.display_the_page_when_posting_this_comment')}
{(this.isCurrentUserEqualsToAuthor() && !isReadOnly) && ( this.setState({ isReEdit: true })} /> ) }
) }
); } } Comment.propTypes = { t: PropTypes.func.isRequired, // i18next appContainer: PropTypes.instanceOf(AppContainer).isRequired, pageContainer: PropTypes.instanceOf(PageContainer).isRequired, comment: PropTypes.object.isRequired, isReadOnly: PropTypes.bool.isRequired, growiRenderer: PropTypes.object.isRequired, deleteBtnClicked: PropTypes.func.isRequired, onComment: PropTypes.func, }; const CommentWrapperFC = (props) => { const { t } = useTranslation(); return ; }; /** * Wrapper component for using unstated */ const CommentWrapper = withUnstatedContainers(CommentWrapperFC, [AppContainer, PageContainer]); export default CommentWrapper;