import { Component, PropTypes } from 'react'; import { propType } from 'graphql-anywhere'; import gql from 'graphql-tag'; import moment from 'moment'; import { I18n } from 'react-i18nify'; import classnames from 'classnames'; import AddCommentForm from './add_comment_form.component'; import UpVoteButton from './up_vote_button.component'; import DownVoteButton from './down_vote_button.component'; import commentFragment from './comment.fragment.graphql'; import commentDataFragment from './comment_data.fragment.graphql'; /** * A single comment component with the author info and the comment's body * @class * @augments Component */ class Comment extends Component { constructor(props) { super(props); this.state = { showReplyForm: false }; } render() { const { comment: { id, author, body, createdAt }, articleClassName } = this.props; const formattedCreatedAt = ` ${moment(createdAt).format("LLL")}`; return (
author-avatar {author.name}

{ this._renderAlignmentBadge() } { body }

{this._renderReplyButton()} {this._renderVoteButtons()}
{this._renderReplies()} {this._renderAdditionalReplyButton()} {this._renderReplyForm()}
); } /** * Render reply button if user can reply the comment * @private * @returns {Void|DOMElement} - Render the reply button or not if user can reply */ _renderReplyButton() { const { comment: { canHaveReplies }, session } = this.props; const { showReplyForm } = this.state; if (session && canHaveReplies) { return ( ); } return  ; } /** * Render additional reply button if user can reply the comment at the bottom of a conversation * @private * @returns {Void|DOMElement} - Render the reply button or not if user can reply */ _renderAdditionalReplyButton() { const { comment: { canHaveReplies, hasReplies }, session, isRootComment } = this.props; const { showReplyForm } = this.state; if (session && canHaveReplies) { if (hasReplies && isRootComment) { return (
); } } return null; } /** * Render upVote and downVote buttons when the comment is votable * @private * @returns {Void|DOMElement} - Render the upVote and downVote buttons or not */ _renderVoteButtons() { const { comment, votable } = this.props; if (votable) { return (
); } return  ; } /** * Render comment replies alternating the css class * @private * @returns {Void|DomElement} - A wrapper element with comment replies inside */ _renderReplies() { const { comment: { id, replies }, session, votable, articleClassName } = this.props; let replyArticleClassName = 'comment comment--nested'; if (articleClassName === 'comment comment--nested') { replyArticleClassName = `${replyArticleClassName} comment--nested--alt`; } if (replies) { return (
{ replies.map((reply) => ( )) }
); } return null; } /** * Render reply form based on the current component state * @private * @returns {Void|ReactElement} - Render the AddCommentForm component or not */ _renderReplyForm() { const { session, comment } = this.props; const { showReplyForm } = this.state; if (showReplyForm) { return ( this.setState({ showReplyForm: false })} autoFocus /> ); } return null; } /** * Render alignment badge if comment's alignment is 0 or -1 * @private * @returns {Void|DOMElement} - The alignment's badge or not */ _renderAlignmentBadge() { const { comment: { alignment } } = this.props; const spanClassName = classnames('label', { success: alignment === 1, alert: alignment === -1 }); let label = ''; if (alignment === 1) { label = I18n.t('components.comment.alignment.in_favor'); } else { label = I18n.t('components.comment.alignment.against'); } if (alignment === 1 || alignment === -1) { return ( { label }   ); } return null; } } Comment.fragments = { comment: gql` ${commentFragment} ${commentDataFragment} ${UpVoteButton.fragments.comment} ${DownVoteButton.fragments.comment} `, commentData: gql` ${commentDataFragment} ${UpVoteButton.fragments.comment} ${DownVoteButton.fragments.comment} ` }; Comment.defaultProps = { articleClassName: 'comment', isRootComment: false }; Comment.propTypes = { comment: PropTypes.oneOfType([ propType(Comment.fragments.comment).isRequired, propType(Comment.fragments.commentData).isRequired ]).isRequired, session: PropTypes.shape({ user: PropTypes.any.isRequired }), articleClassName: PropTypes.string.isRequired, isRootComment: PropTypes.bool, votable: PropTypes.bool }; export default Comment;