import React, { useContext, useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import { useDispatch, useSelector } from 'react-redux'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { Button, useToggle } from '@edx/paragon'; import HTMLLoader from '../../../../components/HTMLLoader'; import { ContentActions, EndorsementStatus } from '../../../../data/constants'; import { AlertBanner, Confirmation, EndorsedAlertBanner } from '../../../common'; import { DiscussionContext } from '../../../common/context'; import HoverCard from '../../../common/HoverCard'; import { useUserCanAddThreadInBlackoutDate } from '../../../data/hooks'; import { fetchThread } from '../../../posts/data/thunks'; import LikeButton from '../../../posts/post/LikeButton'; import { useActions } from '../../../utils'; import { selectCommentCurrentPage, selectCommentHasMorePages, selectCommentResponses } from '../../data/selectors'; import { editComment, fetchCommentResponses, removeComment } from '../../data/thunks'; import messages from '../../messages'; import CommentEditor from './CommentEditor'; import CommentHeader from './CommentHeader'; import { commentShape } from './proptypes'; import Reply from './Reply'; function Comment({ postType, comment, showFullThread = true, isClosedPost, intl, marginBottom, }) { const dispatch = useDispatch(); const hasChildren = comment.childCount > 0; const isNested = Boolean(comment.parentId); const inlineReplies = useSelector(selectCommentResponses(comment.id)); const [isEditing, setEditing] = useState(false); const [isDeleting, showDeleteConfirmation, hideDeleteConfirmation] = useToggle(false); const [isReporting, showReportConfirmation, hideReportConfirmation] = useToggle(false); const [isReplying, setReplying] = useState(false); const hasMorePages = useSelector(selectCommentHasMorePages(comment.id)); const currentPage = useSelector(selectCommentCurrentPage(comment.id)); const userCanAddThreadInBlackoutDate = useUserCanAddThreadInBlackoutDate(); const { courseId, } = useContext(DiscussionContext); useEffect(() => { // If the comment has a parent comment, it won't have any children, so don't fetch them. if (hasChildren && !currentPage && showFullThread) { dispatch(fetchCommentResponses(comment.id, { page: 1 })); } }, [comment.id]); const actions = useActions({ ...comment, postType, }); const endorseIcons = actions.find(({ action }) => action === EndorsementStatus.ENDORSED); const handleAbusedFlag = () => { if (comment.abuseFlagged) { dispatch(editComment(comment.id, { flagged: !comment.abuseFlagged })); } else { showReportConfirmation(); } }; const handleDeleteConfirmation = () => { dispatch(removeComment(comment.id)); hideDeleteConfirmation(); }; const handleReportConfirmation = () => { dispatch(editComment(comment.id, { flagged: !comment.abuseFlagged })); hideReportConfirmation(); }; const actionHandlers = { [ContentActions.EDIT_CONTENT]: () => setEditing(true), [ContentActions.ENDORSE]: async () => { await dispatch(editComment(comment.id, { endorsed: !comment.endorsed }, ContentActions.ENDORSE)); await dispatch(fetchThread(comment.threadId, courseId)); }, [ContentActions.DELETE]: showDeleteConfirmation, [ContentActions.REPORT]: () => handleAbusedFlag(), }; const handleLoadMoreComments = () => ( dispatch(fetchCommentResponses(comment.id, { page: currentPage + 1 })) ); return (
{/* eslint-disable jsx-a11y/no-noninteractive-tabindex */}
{!comment.abuseFlagged && ( )}
setReplying(true)} onLike={() => dispatch(editComment(comment.id, { voted: !comment.voted }))} addResponseCommentButtonMessage={intl.formatMessage(messages.addComment)} isClosedPost={isClosedPost} endorseIcons={endorseIcons} /> {isEditing ? ( setEditing(false)} formClasses="pt-3" /> ) : ( )} {comment.voted && (
dispatch(editComment(comment.id, { voted: !comment.voted }))} voted={comment.voted} />
)} {inlineReplies.length > 0 && (
{/* Pass along intl since component used here is the one before it's injected with `injectIntl` */} {inlineReplies.map(inlineReply => ( ))}
)} {hasMorePages && ( )} {!isNested && showFullThread && ( isReplying ? (
setReplying(false)} />
) : ( <> {!isClosedPost && userCanAddThreadInBlackoutDate && (inlineReplies.length >= 5) && ( )} ) )}
); } Comment.propTypes = { postType: PropTypes.oneOf(['discussion', 'question']).isRequired, comment: commentShape.isRequired, showFullThread: PropTypes.bool, isClosedPost: PropTypes.bool, intl: intlShape.isRequired, marginBottom: PropTypes.bool, }; Comment.defaultProps = { showFullThread: true, isClosedPost: false, marginBottom: true, }; export default injectIntl(Comment);