import React, { useCallback, useContext, useMemo } from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import { useDispatch, useSelector } from 'react-redux'; import { useHistory, useLocation } from 'react-router-dom'; import { getConfig } from '@edx/frontend-platform'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { Hyperlink, useToggle } from '@edx/paragon'; import HTMLLoader from '../../../components/HTMLLoader'; import { ContentActions } from '../../../data/constants'; import { selectorForUnitSubsection, selectTopicContext } from '../../../data/selectors'; import { AlertBanner, Confirmation } from '../../common'; import { DiscussionContext } from '../../common/context'; import HoverCard from '../../common/HoverCard'; import { selectModerationSettings, selectUserHasModerationPrivileges } from '../../data/selectors'; import { selectTopic } from '../../topics/data/selectors'; import { removeThread, updateExistingThread } from '../data/thunks'; import ClosePostReasonModal from './ClosePostReasonModal'; import messages from './messages'; import PostFooter from './PostFooter'; import PostHeader from './PostHeader'; import { postShape } from './proptypes'; const Post = ({ post, intl, handleAddResponseButton, }) => { const location = useLocation(); const history = useHistory(); const dispatch = useDispatch(); const { enableInContextSidebar } = useContext(DiscussionContext); const courseId = useSelector((state) => state.config.id); const topic = useSelector(selectTopic(post.topicId)); const getTopicSubsection = useSelector(selectorForUnitSubsection); const topicContext = useSelector(selectTopicContext(post.topicId)); const { reasonCodesEnabled } = useSelector(selectModerationSettings); const [isDeleting, showDeleteConfirmation, hideDeleteConfirmation] = useToggle(false); const [isReporting, showReportConfirmation, hideReportConfirmation] = useToggle(false); const [isClosing, showClosePostModal, hideClosePostModal] = useToggle(false); const userHasModerationPrivileges = useSelector(selectUserHasModerationPrivileges); const displayPostFooter = post.following || post.voteCount || post.closed || (post.groupId && userHasModerationPrivileges); const handleAbusedFlag = useCallback(() => { if (post.abuseFlagged) { dispatch(updateExistingThread(post.id, { flagged: !post.abuseFlagged })); } else { showReportConfirmation(); } }, [dispatch, post.abuseFlagged, post.id, showReportConfirmation]); const handleDeleteConfirmation = async () => { await dispatch(removeThread(post.id)); history.push({ pathname: '.', search: enableInContextSidebar && '?inContextSidebar', }); hideDeleteConfirmation(); }; const handleReportConfirmation = () => { dispatch(updateExistingThread(post.id, { flagged: !post.abuseFlagged })); hideReportConfirmation(); }; const handlePostCopyLink = useCallback(() => { const postURL = new URL(`${getConfig().PUBLIC_PATH}${courseId}/posts/${post.id}`, window.location.origin); navigator.clipboard.writeText(postURL.href); }, [window.location.origin, post.id, courseId]); const actionHandlers = useMemo(() => ({ [ContentActions.EDIT_CONTENT]: () => history.push({ ...location, pathname: `${location.pathname}/edit`, }), [ContentActions.DELETE]: showDeleteConfirmation, [ContentActions.CLOSE]: () => { if (post.closed) { dispatch(updateExistingThread(post.id, { closed: false })); } else if (reasonCodesEnabled) { showClosePostModal(); } else { dispatch(updateExistingThread(post.id, { closed: true })); } }, [ContentActions.COPY_LINK]: handlePostCopyLink, [ContentActions.PIN]: () => dispatch(updateExistingThread(post.id, { pinned: !post.pinned })), [ContentActions.REPORT]: () => handleAbusedFlag(), }), [ showDeleteConfirmation, history, location, post.closed, post.id, post.pinned, reasonCodesEnabled, dispatch, showClosePostModal, courseId, handleAbusedFlag, ]); const getTopicCategoryName = topicData => ( topicData.usageKey ? getTopicSubsection(topicData.usageKey)?.displayName : topicData.categoryId ); const getTopicInfo = topicData => ( getTopicCategoryName(topicData) ? `${getTopicCategoryName(topicData)} / ${topicData.name}` : `${topicData.name}` ); return (
{!post.abuseFlagged && ( )} dispatch(updateExistingThread(post.id, { voted: !post.voted }))} onFollow={() => dispatch(updateExistingThread(post.id, { following: !post.following }))} isClosedPost={post.closed} />
{(topicContext || topic) && (
{intl.formatMessage(messages.relatedTo)}{' '} {(topicContext && !topic) ? ( <> {topicContext.chapterName} / {topicContext.verticalName} / {topicContext.unitName} ) : getTopicInfo(topic)}
)} {displayPostFooter && } { dispatch(updateExistingThread(post.id, { closed: true, closeReasonCode })); hideClosePostModal(); }} />
); }; Post.propTypes = { intl: intlShape.isRequired, post: postShape.isRequired, handleAddResponseButton: PropTypes.func.isRequired, }; export default injectIntl(Post);