diff --git a/src/discussions/common/ActionsDropdown.jsx b/src/discussions/common/ActionsDropdown.jsx index 3d65d45b..12c318cc 100644 --- a/src/discussions/common/ActionsDropdown.jsx +++ b/src/discussions/common/ActionsDropdown.jsx @@ -1,4 +1,4 @@ -import React, { useContext, useState } from 'react'; +import React, { useCallback, useRef, useState } from 'react'; import PropTypes from 'prop-types'; import { useSelector } from 'react-redux'; @@ -16,7 +16,6 @@ import messages from '../messages'; import { commentShape } from '../post-comments/comments/comment/proptypes'; import { postShape } from '../posts/post/proptypes'; import { inBlackoutDateRange, useActions } from '../utils'; -import { DiscussionContext } from './context'; function ActionsDropdown({ intl, @@ -26,41 +25,54 @@ function ActionsDropdown({ iconSize, dropDownIconSize, }) { + const buttonRef = useRef(); const [isOpen, open, close] = useToggle(false); const [target, setTarget] = useState(null); const actions = useActions(commentOrPost); - const { enableInContextSidebar } = useContext(DiscussionContext); - const handleActions = (action) => { + + const handleActions = useCallback((action) => { const actionFunction = actionHandlers[action]; if (actionFunction) { actionFunction(); } else { logError(`Unknown or unimplemented action ${action}`); } - }; + }, [actionHandlers]); + const blackoutDateRange = useSelector(selectBlackoutDate); // Find and remove edit action if in blackout date range. if (inBlackoutDateRange(blackoutDateRange)) { actions.splice(actions.findIndex(action => action.id === 'edit'), 1); } + + const onClickButton = useCallback(() => { + setTarget(buttonRef.current); + open(); + }, [open]); + + const onCloseModal = useCallback(() => { + close(); + setTarget(null); + }, [close]); + return ( <>
{ // 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 = () => { + const handleAbusedFlag = useCallback(() => { if (comment.abuseFlagged) { dispatch(editComment(comment.id, { flagged: !comment.abuseFlagged })); } else { showReportConfirmation(); } - }; + }, [comment.abuseFlagged, comment.id, dispatch, showReportConfirmation]); const handleDeleteConfirmation = () => { dispatch(removeComment(comment.id)); @@ -76,7 +79,7 @@ function Comment({ hideReportConfirmation(); }; - const actionHandlers = { + const actionHandlers = useMemo(() => ({ [ContentActions.EDIT_CONTENT]: () => setEditing(true), [ContentActions.ENDORSE]: async () => { await dispatch(editComment(comment.id, { endorsed: !comment.endorsed }, ContentActions.ENDORSE)); @@ -84,7 +87,7 @@ function Comment({ }, [ContentActions.DELETE]: showDeleteConfirmation, [ContentActions.REPORT]: () => handleAbusedFlag(), - }; + }), [showDeleteConfirmation, dispatch, comment.id, comment.endorsed, comment.threadId, courseId, handleAbusedFlag]); const handleLoadMoreComments = () => ( dispatch(fetchCommentResponses(comment.id, { page: currentPage + 1 })) diff --git a/src/discussions/post-comments/comments/comment/Reply.jsx b/src/discussions/post-comments/comments/comment/Reply.jsx index 412d4f1a..b2539b2e 100644 --- a/src/discussions/post-comments/comments/comment/Reply.jsx +++ b/src/discussions/post-comments/comments/comment/Reply.jsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; @@ -31,13 +31,13 @@ function Reply({ const [isDeleting, showDeleteConfirmation, hideDeleteConfirmation] = useToggle(false); const [isReporting, showReportConfirmation, hideReportConfirmation] = useToggle(false); - const handleAbusedFlag = () => { + const handleAbusedFlag = useCallback(() => { if (reply.abuseFlagged) { dispatch(editComment(reply.id, { flagged: !reply.abuseFlagged })); } else { showReportConfirmation(); } - }; + }, [dispatch, reply.abuseFlagged, reply.id, showReportConfirmation]); const handleDeleteConfirmation = () => { dispatch(removeComment(reply.id)); @@ -49,7 +49,7 @@ function Reply({ hideReportConfirmation(); }; - const actionHandlers = { + const actionHandlers = useMemo(() => ({ [ContentActions.EDIT_CONTENT]: () => setEditing(true), [ContentActions.ENDORSE]: () => dispatch(editComment( reply.id, @@ -58,7 +58,8 @@ function Reply({ )), [ContentActions.DELETE]: showDeleteConfirmation, [ContentActions.REPORT]: () => handleAbusedFlag(), - }; + }), [dispatch, handleAbusedFlag, reply.endorsed, reply.id, showDeleteConfirmation]); + const authorAvatars = useSelector(selectAuthorAvatars(reply.author)); const colorClass = AvatarOutlineAndLabelColors[reply.authorLabel]; const hasAnyAlert = useAlertBannerVisible(reply); diff --git a/src/discussions/posts/post/Post.jsx b/src/discussions/posts/post/Post.jsx index 1ecbb94d..0bfad8c5 100644 --- a/src/discussions/posts/post/Post.jsx +++ b/src/discussions/posts/post/Post.jsx @@ -1,4 +1,4 @@ -import React, { useContext } from 'react'; +import React, { useCallback, useContext, useMemo } from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; @@ -42,13 +42,14 @@ function Post({ const [isDeleting, showDeleteConfirmation, hideDeleteConfirmation] = useToggle(false); const [isReporting, showReportConfirmation, hideReportConfirmation] = useToggle(false); const [isClosing, showClosePostModal, hideClosePostModal] = useToggle(false); - const handleAbusedFlag = () => { + + 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)); @@ -64,7 +65,7 @@ function Post({ hideReportConfirmation(); }; - const actionHandlers = { + const actionHandlers = useMemo(() => ({ [ContentActions.EDIT_CONTENT]: () => history.push({ ...location, pathname: `${location.pathname}/edit`, @@ -82,7 +83,19 @@ function Post({ [ContentActions.COPY_LINK]: () => { navigator.clipboard.writeText(`${window.location.origin}/${courseId}/posts/${post.id}`); }, [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