From b72dbae4f05b300573a8269580f3906b34f9a4d9 Mon Sep 17 00:00:00 2001 From: ayeshoali Date: Tue, 22 Nov 2022 19:15:12 +0500 Subject: [PATCH 01/10] style: confirmation modal added when reporting content --- src/discussions/comments/comment/Comment.jsx | 25 +++++++++++++++--- src/discussions/comments/comment/Reply.jsx | 26 ++++++++++++++++--- src/discussions/comments/messages.js | 20 ++++++++++++++ src/discussions/common/ActionsDropdown.jsx | 2 +- ...eleteConfirmation.jsx => Confirmation.jsx} | 22 +++++++++++----- src/discussions/common/index.js | 2 +- src/discussions/messages.js | 5 ++++ src/discussions/posts/post/Post.jsx | 25 +++++++++++++++--- src/discussions/posts/post/messages.js | 10 +++++++ src/index.scss | 4 +-- 10 files changed, 120 insertions(+), 21 deletions(-) rename src/discussions/common/{DeleteConfirmation.jsx => Confirmation.jsx} (64%) diff --git a/src/discussions/comments/comment/Comment.jsx b/src/discussions/comments/comment/Comment.jsx index 2cdb6bc2..1b67a8b0 100644 --- a/src/discussions/comments/comment/Comment.jsx +++ b/src/discussions/comments/comment/Comment.jsx @@ -9,7 +9,7 @@ import { Button, useToggle } from '@edx/paragon'; import HTMLLoader from '../../../components/HTMLLoader'; import { ContentActions } from '../../../data/constants'; -import { AlertBanner, DeleteConfirmation, EndorsedAlertBanner } from '../../common'; +import { AlertBanner, Confirmation, EndorsedAlertBanner } from '../../common'; import { selectBlackoutDate } from '../../data/selectors'; import { fetchThread } from '../../posts/data/thunks'; import { inBlackoutDateRange } from '../../utils'; @@ -35,6 +35,7 @@ function Comment({ 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)); @@ -54,7 +55,13 @@ function Comment({ await dispatch(fetchThread(comment.threadId)); }, [ContentActions.DELETE]: showDeleteConfirmation, - [ContentActions.REPORT]: () => dispatch(editComment(comment.id, { flagged: !comment.abuseFlagged })), + [ContentActions.REPORT]: () => { + if (comment.abuseFlagged) { + dispatch(editComment(comment.id, { flagged: !comment.abuseFlagged })); + } else { + showReportConfirmation(); + } + }, }; const handleLoadMoreComments = () => ( @@ -64,7 +71,7 @@ function Comment({ return (
- + {!comment.abuseFlagged && ( + { + dispatch(editComment(comment.id, { flagged: !comment.abuseFlagged })); + hideReportConfirmation(); + }} + /> + )}
diff --git a/src/discussions/comments/comment/Reply.jsx b/src/discussions/comments/comment/Reply.jsx index f44e887d..bec40d24 100644 --- a/src/discussions/comments/comment/Reply.jsx +++ b/src/discussions/comments/comment/Reply.jsx @@ -10,7 +10,7 @@ import { Avatar, useToggle } from '@edx/paragon'; import HTMLLoader from '../../../components/HTMLLoader'; import { AvatarOutlineAndLabelColors, ContentActions } from '../../../data/constants'; import { - ActionsDropdown, AlertBanner, AuthorLabel, DeleteConfirmation, + ActionsDropdown, AlertBanner, AuthorLabel, Confirmation, } from '../../common'; import timeLocale from '../../common/time-locale'; import { useAlertBannerVisible } from '../../data/hooks'; @@ -29,6 +29,7 @@ function Reply({ const dispatch = useDispatch(); const [isEditing, setEditing] = useState(false); const [isDeleting, showDeleteConfirmation, hideDeleteConfirmation] = useToggle(false); + const [isReporting, showReportConfirmation, hideReportConfirmation] = useToggle(false); const actionHandlers = { [ContentActions.EDIT_CONTENT]: () => setEditing(true), [ContentActions.ENDORSE]: () => dispatch(editComment( @@ -37,7 +38,13 @@ function Reply({ ContentActions.ENDORSE, )), [ContentActions.DELETE]: showDeleteConfirmation, - [ContentActions.REPORT]: () => dispatch(editComment(reply.id, { flagged: !reply.abuseFlagged })), + [ContentActions.REPORT]: () => { + if (reply.abuseFlagged) { + dispatch(editComment(reply.id, { flagged: !reply.abuseFlagged })); + } else { + showReportConfirmation(); + } + }, }; const authorAvatars = useSelector(selectAuthorAvatars(reply.author)); const colorClass = AvatarOutlineAndLabelColors[reply.authorLabel]; @@ -45,7 +52,7 @@ function Reply({ return (
- - + {!reply.abuseFlagged && ( + { + dispatch(editComment(reply.id, { flagged: !reply.abuseFlagged })); + hideReportConfirmation(); + }} + /> + )} {hasAnyAlert && (
diff --git a/src/discussions/comments/messages.js b/src/discussions/comments/messages.js index 3fe4a355..dd9427f6 100644 --- a/src/discussions/comments/messages.js +++ b/src/discussions/comments/messages.js @@ -148,6 +148,26 @@ const messages = defineMessages({ defaultMessage: 'Are you sure you want to permanently delete this comment?', description: 'Text displayed in confirmation dialog when deleting a comment', }, + reportResponseTitle: { + id: 'discussions.editor.response.response.title', + defaultMessage: 'Report inappropriate content?', + description: 'Title of confirmation dialog shown when reporting a response', + }, + reportResponseDescription: { + id: 'discussions.editor.delete.response.description', + defaultMessage: 'The discussion moderation team will review this content and take appropriate action.', + description: 'Text displayed in confirmation dialog when deleting a response', + }, + reportCommentTitle: { + id: 'discussions.editor.report.comment.title', + defaultMessage: 'Report inappropriate content?', + description: 'Title of confirmation dialog shown when reporting a comment', + }, + reportCommentDescription: { + id: 'discussions.editor.report.comment.description', + defaultMessage: 'The discussion moderation team will review this content and take appropriate action.', + description: 'Text displayed in confirmation dialog when deleting a response', + }, editReasonCode: { id: 'discussions.editor.comments.editReasonCode', defaultMessage: 'Reason for editing', diff --git a/src/discussions/common/ActionsDropdown.jsx b/src/discussions/common/ActionsDropdown.jsx index 55faeeaf..df5ae7d9 100644 --- a/src/discussions/common/ActionsDropdown.jsx +++ b/src/discussions/common/ActionsDropdown.jsx @@ -64,7 +64,7 @@ function ActionsDropdown({ > {actions.map(action => ( - {action.action === ContentActions.DELETE + {(action.action === ContentActions.DELETE || action.action === ContentActions.REPORT) && } @@ -26,11 +27,13 @@ function DeleteConfirmation({ - + {intl.formatMessage(messages.deleteConfirmationCancel)} - @@ -38,13 +41,18 @@ function DeleteConfirmation({ ); } -DeleteConfirmation.propTypes = { +Confirmation.propTypes = { intl: intlShape.isRequired, isOpen: PropTypes.bool.isRequired, onClose: PropTypes.func.isRequired, - onDelete: PropTypes.func.isRequired, + onDelete: PropTypes.func, + onReport: PropTypes.func, title: PropTypes.string.isRequired, description: PropTypes.string.isRequired, }; +Confirmation.defaultProps = { + onDelete: undefined, + onReport: undefined, +}; -export default injectIntl(DeleteConfirmation); +export default injectIntl(Confirmation); diff --git a/src/discussions/common/index.js b/src/discussions/common/index.js index 4bee420d..f4ca7213 100644 --- a/src/discussions/common/index.js +++ b/src/discussions/common/index.js @@ -1,5 +1,5 @@ export { default as ActionsDropdown } from './ActionsDropdown'; export { default as AlertBanner } from './AlertBanner'; export { default as AuthorLabel } from './AuthorLabel'; -export { default as DeleteConfirmation } from './DeleteConfirmation'; +export { default as Confirmation } from './Confirmation'; export { default as EndorsedAlertBanner } from './EndorsedAlertBanner'; diff --git a/src/discussions/messages.js b/src/discussions/messages.js index 0d84cd9c..deeb748f 100644 --- a/src/discussions/messages.js +++ b/src/discussions/messages.js @@ -81,6 +81,11 @@ const messages = defineMessages({ defaultMessage: 'Delete', description: 'Delete button shown on delete confirmation dialog', }, + reportConfirmationConfirm: { + id: 'discussions.report.confirmation.button.confirm', + defaultMessage: 'Confirm', + description: 'Confirm button shown on report confirmation dialog', + }, emptyAllTopics: { id: 'discussions.empty.allTopics', defaultMessage: diff --git a/src/discussions/posts/post/Post.jsx b/src/discussions/posts/post/Post.jsx index 870cfedf..3ecd59a1 100644 --- a/src/discussions/posts/post/Post.jsx +++ b/src/discussions/posts/post/Post.jsx @@ -10,7 +10,7 @@ import { Hyperlink, useToggle } from '@edx/paragon'; import HTMLLoader from '../../../components/HTMLLoader'; import { ContentActions } from '../../../data/constants'; import { selectorForUnitSubsection, selectTopicContext } from '../../../data/selectors'; -import { AlertBanner, DeleteConfirmation } from '../../common'; +import { AlertBanner, Confirmation } from '../../common'; import { selectModerationSettings } from '../../data/selectors'; import { selectTopic } from '../../topics/data/selectors'; import { removeThread, updateExistingThread } from '../data/thunks'; @@ -34,6 +34,7 @@ function Post({ 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 actionHandlers = { [ContentActions.EDIT_CONTENT]: () => history.push({ @@ -52,7 +53,13 @@ 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]: () => dispatch(updateExistingThread(post.id, { flagged: !post.abuseFlagged })), + [ContentActions.REPORT]: () => { + if (post.abuseFlagged) { + dispatch(updateExistingThread(post.id, { flagged: !post.abuseFlagged })); + } else { + showReportConfirmation(); + } + }, }; const getTopicCategoryName = topicData => ( @@ -61,7 +68,7 @@ function Post({ return (
- + {!post.abuseFlagged && ( + { + dispatch(updateExistingThread(post.id, { flagged: !post.abuseFlagged })); + hideReportConfirmation(); + }} + /> + )}
diff --git a/src/discussions/posts/post/messages.js b/src/discussions/posts/post/messages.js index e74fda5c..ec41fd3b 100644 --- a/src/discussions/posts/post/messages.js +++ b/src/discussions/posts/post/messages.js @@ -71,6 +71,16 @@ const messages = defineMessages({ id: 'discussions.editor.delete.post.description', defaultMessage: 'Are you sure you want to permanently delete this post?', }, + reportPostTitle: { + id: 'discussions.editor.delete.post.title', + defaultMessage: 'Report inappropriate content?', + description: 'Title of confirmation dialog shown when reporting a post', + }, + reportPostDescription: { + id: 'discussions.editor.delete.post.description', + defaultMessage: 'The discussion moderation team will review this content and take appropriate action.', + description: 'Text displayed in confirmation dialog when deleting a post', + }, closePostModalTitle: { id: 'discussions.post.closePostModal.title', defaultMessage: 'Close post', diff --git a/src/index.scss b/src/index.scss index 738c50d4..34c5ee28 100755 --- a/src/index.scss +++ b/src/index.scss @@ -242,11 +242,11 @@ header { } .zindex-5000 { - z-index: 5000; + z-index: 5000 ; } #paragon-portal-root .pgn__modal-layer { - z-index: 1500 !important; + z-index: 5000 !important; } @media only screen and (max-width: 767px) { From cf8f08172f66d0e8c717b87ef91a4ff4c4cf985d Mon Sep 17 00:00:00 2001 From: ayeshoali Date: Wed, 23 Nov 2022 14:49:47 +0500 Subject: [PATCH 02/10] fix: fixed test cases --- .../comments/CommentsView.test.jsx | 77 +++++++++++++++---- src/discussions/comments/messages.js | 2 +- 2 files changed, 64 insertions(+), 15 deletions(-) diff --git a/src/discussions/comments/CommentsView.test.jsx b/src/discussions/comments/CommentsView.test.jsx index 52c01a81..eae9ee70 100644 --- a/src/discussions/comments/CommentsView.test.jsx +++ b/src/discussions/comments/CommentsView.test.jsx @@ -381,6 +381,7 @@ describe('CommentsView', () => { }); expect(testLocation.pathname).toBe(`/${courseId}/posts/${discussionPostId}/edit`); }); + it('should allow pinning the post', async () => { renderComponent(discussionPostId); await act(async () => { @@ -394,6 +395,7 @@ describe('CommentsView', () => { }); assertLastUpdateData({ pinned: false }); }); + it('should allow reporting the post', async () => { renderComponent(discussionPostId); await act(async () => { @@ -405,6 +407,11 @@ describe('CommentsView', () => { await act(async () => { fireEvent.click(screen.getByRole('button', { name: /report/i })); }); + expect(screen.queryByRole('dialog', { name: /Report \w+/i, exact: false })).toBeInTheDocument(); + await act(async () => { + fireEvent.click(screen.queryByRole('button', { name: /Confirm/i })); + }); + expect(screen.queryByRole('dialog', { name: /Report \w+/i, exact: false })).not.toBeInTheDocument(); assertLastUpdateData({ abuse_flagged: true }); }); @@ -423,12 +430,8 @@ describe('CommentsView', () => { expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject({ voted: true }); }); - it.each([ - ['endorsing comments', 'Endorse', { endorsed: true }], - ['reporting comments', 'Report', { abuse_flagged: true }], - ])('handles %s', async (label, buttonLabel, patchData) => { + it('handles endorsing comments', async () => { renderComponent(discussionPostId); - // Wait for the content to load await screen.findByText('comment number 7', { exact: false }); @@ -438,11 +441,36 @@ describe('CommentsView', () => { await act(async () => { fireEvent.click(actionButtons[1]); }); + await act(async () => { - fireEvent.click(screen.getByRole('button', { name: buttonLabel })); + fireEvent.click(screen.getByRole('button', { name: /Endorse/i })); }); expect(axiosMock.history.patch).toHaveLength(2); - expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject(patchData); + expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject({ endorsed: true }); + }); + + it('handles reporting comments', async () => { + renderComponent(discussionPostId); + // Wait for the content to load + await screen.findByText('comment number 7', { exact: false }); + + // There should be three buttons, one for the post, the second for the + // comment and the third for a response to that comment + const actionButtons = screen.queryAllByRole('button', { name: /actions menu/i }); + await act(async () => { + fireEvent.click(actionButtons[1]); + }); + + await act(async () => { + fireEvent.click(screen.getByRole('button', { name: /Report/i })); + }); + expect(screen.queryByRole('dialog', { name: /Report \w+/i, exact: false })).toBeInTheDocument(); + await act(async () => { + fireEvent.click(screen.queryByRole('button', { name: /Confirm/i })); + }); + expect(screen.queryByRole('dialog', { name: /Report \w+/i, exact: false })).not.toBeInTheDocument(); + expect(axiosMock.history.patch).toHaveLength(2); + expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject({ abuse_flagged: true }); }); }); @@ -624,12 +652,8 @@ describe('CommentsView', () => { expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject({ voted: true }); }); - it.each([ - ['endorsing comments', 'Endorse', { endorsed: true }], - ['reporting comments', 'Report', { abuse_flagged: true }], - ])('handles %s', async (label, buttonLabel, patchData) => { + it('handles endorsing comments', async () => { renderComponent(discussionPostId); - // Wait for the content to load await screen.findByText('comment number 7', { exact: false }); @@ -639,11 +663,36 @@ describe('CommentsView', () => { await act(async () => { fireEvent.click(actionButtons[1]); }); + await act(async () => { - fireEvent.click(screen.getByRole('button', { name: buttonLabel })); + fireEvent.click(screen.getByRole('button', { name: /Endorse/i })); }); expect(axiosMock.history.patch).toHaveLength(2); - expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject(patchData); + expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject({ endorsed: true }); + }); + + it('handles reporting comments', async () => { + renderComponent(discussionPostId); + // Wait for the content to load + await screen.findByText('comment number 7', { exact: false }); + + // There should be three buttons, one for the post, the second for the + // comment and the third for a response to that comment + const actionButtons = screen.queryAllByRole('button', { name: /actions menu/i }); + await act(async () => { + fireEvent.click(actionButtons[1]); + }); + + await act(async () => { + fireEvent.click(screen.getByRole('button', { name: /Report/i })); + }); + expect(screen.queryByRole('dialog', { name: /Report \w+/i, exact: false })).toBeInTheDocument(); + await act(async () => { + fireEvent.click(screen.queryByRole('button', { name: /Confirm/i })); + }); + expect(screen.queryByRole('dialog', { name: /Report \w+/i, exact: false })).not.toBeInTheDocument(); + expect(axiosMock.history.patch).toHaveLength(2); + expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject({ abuse_flagged: true }); }); }); diff --git a/src/discussions/comments/messages.js b/src/discussions/comments/messages.js index dd9427f6..8e66efa8 100644 --- a/src/discussions/comments/messages.js +++ b/src/discussions/comments/messages.js @@ -154,7 +154,7 @@ const messages = defineMessages({ description: 'Title of confirmation dialog shown when reporting a response', }, reportResponseDescription: { - id: 'discussions.editor.delete.response.description', + id: 'discussions.editor.response.description', defaultMessage: 'The discussion moderation team will review this content and take appropriate action.', description: 'Text displayed in confirmation dialog when deleting a response', }, From 5f477cb93f3c1678325790ea21afc8e6bf91a756 Mon Sep 17 00:00:00 2001 From: ayeshoali Date: Wed, 23 Nov 2022 15:22:09 +0500 Subject: [PATCH 03/10] fix: report icon changed to correct report icon --- src/discussions/common/AlertBanner.jsx | 4 ++-- src/discussions/posts/post/messages.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/discussions/common/AlertBanner.jsx b/src/discussions/common/AlertBanner.jsx index 75ade68e..ca30a023 100644 --- a/src/discussions/common/AlertBanner.jsx +++ b/src/discussions/common/AlertBanner.jsx @@ -6,7 +6,7 @@ import { useSelector } from 'react-redux'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { Alert } from '@edx/paragon'; -import { Error } from '@edx/paragon/icons'; +import { Report } from '@edx/paragon/icons'; import { commentShape } from '../comments/comment/proptypes'; import messages from '../comments/messages'; @@ -28,7 +28,7 @@ function AlertBanner({ return ( <> {canSeeReportedBanner && ( - + {intl.formatMessage(messages.abuseFlaggedMessage)} )} diff --git a/src/discussions/posts/post/messages.js b/src/discussions/posts/post/messages.js index ec41fd3b..f1bc34c6 100644 --- a/src/discussions/posts/post/messages.js +++ b/src/discussions/posts/post/messages.js @@ -72,12 +72,12 @@ const messages = defineMessages({ defaultMessage: 'Are you sure you want to permanently delete this post?', }, reportPostTitle: { - id: 'discussions.editor.delete.post.title', + id: 'discussions.editor.report.post.title', defaultMessage: 'Report inappropriate content?', description: 'Title of confirmation dialog shown when reporting a post', }, reportPostDescription: { - id: 'discussions.editor.delete.post.description', + id: 'discussions.editor.report.post.description', defaultMessage: 'The discussion moderation team will review this content and take appropriate action.', description: 'Text displayed in confirmation dialog when deleting a post', }, From 8e449acde774de11e6da97078d7f21bd5673eb14 Mon Sep 17 00:00:00 2001 From: ayeshoali Date: Mon, 28 Nov 2022 14:09:14 +0500 Subject: [PATCH 04/10] refactor: changed deleteconfirm and reportconfirm rename to comfirmAction and made it required --- src/data/constants.js | 9 +++++++++ src/discussions/comments/comment/Comment.jsx | 4 ++-- src/discussions/comments/comment/Reply.jsx | 4 ++-- src/discussions/common/Confirmation.jsx | 19 ++++++++----------- src/discussions/posts/post/Post.jsx | 4 ++-- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/data/constants.js b/src/data/constants.js index 357d3b14..5dccef0b 100644 --- a/src/data/constants.js +++ b/src/data/constants.js @@ -13,6 +13,15 @@ export const ThreadType = { DISCUSSION: 'discussion', }; +/** + * Enum for report Confirmation. + * @readonly + * @enum {string} + */ +export const ReportConfirmation = { + Report: 'Report inappropriate content?', +}; + /** * Enum to map between endorsement status and friendly name. * @readonly diff --git a/src/discussions/comments/comment/Comment.jsx b/src/discussions/comments/comment/Comment.jsx index 1b67a8b0..c3adf7b9 100644 --- a/src/discussions/comments/comment/Comment.jsx +++ b/src/discussions/comments/comment/Comment.jsx @@ -76,7 +76,7 @@ function Comment({ title={intl.formatMessage(messages.deleteResponseTitle)} description={intl.formatMessage(messages.deleteResponseDescription)} onClose={hideDeleteConfirmation} - onDelete={() => { + comfirmAction={() => { dispatch(removeComment(comment.id)); hideDeleteConfirmation(); }} @@ -87,7 +87,7 @@ function Comment({ title={intl.formatMessage(messages.reportResponseTitle)} description={intl.formatMessage(messages.reportResponseDescription)} onClose={hideReportConfirmation} - onReport={() => { + comfirmAction={() => { dispatch(editComment(comment.id, { flagged: !comment.abuseFlagged })); hideReportConfirmation(); }} diff --git a/src/discussions/comments/comment/Reply.jsx b/src/discussions/comments/comment/Reply.jsx index bec40d24..ce8a74a4 100644 --- a/src/discussions/comments/comment/Reply.jsx +++ b/src/discussions/comments/comment/Reply.jsx @@ -57,7 +57,7 @@ function Reply({ title={intl.formatMessage(messages.deleteCommentTitle)} description={intl.formatMessage(messages.deleteCommentDescription)} onClose={hideDeleteConfirmation} - onDelete={() => { + comfirmAction={() => { dispatch(removeComment(reply.id)); hideDeleteConfirmation(); }} @@ -68,7 +68,7 @@ function Reply({ title={intl.formatMessage(messages.reportCommentTitle)} description={intl.formatMessage(messages.reportCommentDescription)} onClose={hideReportConfirmation} - onReport={() => { + comfirmAction={() => { dispatch(editComment(reply.id, { flagged: !reply.abuseFlagged })); hideReportConfirmation(); }} diff --git a/src/discussions/common/Confirmation.jsx b/src/discussions/common/Confirmation.jsx index cfdd9221..6db98e89 100644 --- a/src/discussions/common/Confirmation.jsx +++ b/src/discussions/common/Confirmation.jsx @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { ActionRow, Button, ModalDialog } from '@edx/paragon'; +import { ReportConfirmation } from '../../data/constants'; import messages from '../messages'; function Confirmation({ @@ -12,9 +13,10 @@ function Confirmation({ title, description, onClose, - onDelete, - onReport, + comfirmAction, }) { + const ifDeleteConfirmation = title === ReportConfirmation.Report || false; + return ( @@ -27,11 +29,11 @@ function Confirmation({ - + {intl.formatMessage(messages.deleteConfirmationCancel)} - @@ -45,14 +47,9 @@ Confirmation.propTypes = { intl: intlShape.isRequired, isOpen: PropTypes.bool.isRequired, onClose: PropTypes.func.isRequired, - onDelete: PropTypes.func, - onReport: PropTypes.func, + comfirmAction: PropTypes.func.isRequired, title: PropTypes.string.isRequired, description: PropTypes.string.isRequired, }; -Confirmation.defaultProps = { - onDelete: undefined, - onReport: undefined, -}; export default injectIntl(Confirmation); diff --git a/src/discussions/posts/post/Post.jsx b/src/discussions/posts/post/Post.jsx index 3ecd59a1..607b2a1f 100644 --- a/src/discussions/posts/post/Post.jsx +++ b/src/discussions/posts/post/Post.jsx @@ -73,7 +73,7 @@ function Post({ title={intl.formatMessage(messages.deletePostTitle)} description={intl.formatMessage(messages.deletePostDescription)} onClose={hideDeleteConfirmation} - onDelete={() => { + comfirmAction={() => { dispatch(removeThread(post.id)); history.push('.'); hideDeleteConfirmation(); @@ -85,7 +85,7 @@ function Post({ title={intl.formatMessage(messages.reportPostTitle)} description={intl.formatMessage(messages.reportPostDescription)} onClose={hideReportConfirmation} - onReport={() => { + comfirmAction={() => { dispatch(updateExistingThread(post.id, { flagged: !post.abuseFlagged })); hideReportConfirmation(); }} From 42f1efd0a07e5ea339057eedfa99d1e639eeef86 Mon Sep 17 00:00:00 2001 From: ayeshoali Date: Mon, 28 Nov 2022 17:27:13 +0500 Subject: [PATCH 05/10] refactor: confirmation modal modified to handle variants --- src/data/constants.js | 9 ------- src/discussions/comments/comment/Comment.jsx | 19 +++++++++------ src/discussions/comments/comment/Reply.jsx | 20 ++++++++++------ src/discussions/comments/messages.js | 5 ++++ src/discussions/common/Confirmation.jsx | 25 +++++++++++++------- src/discussions/messages.js | 19 ++++++--------- src/discussions/posts/post/Post.jsx | 20 ++++++++++------ src/discussions/posts/post/messages.js | 5 ++++ 8 files changed, 71 insertions(+), 51 deletions(-) diff --git a/src/data/constants.js b/src/data/constants.js index 5dccef0b..357d3b14 100644 --- a/src/data/constants.js +++ b/src/data/constants.js @@ -13,15 +13,6 @@ export const ThreadType = { DISCUSSION: 'discussion', }; -/** - * Enum for report Confirmation. - * @readonly - * @enum {string} - */ -export const ReportConfirmation = { - Report: 'Report inappropriate content?', -}; - /** * Enum to map between endorsement status and friendly name. * @readonly diff --git a/src/discussions/comments/comment/Comment.jsx b/src/discussions/comments/comment/Comment.jsx index c3adf7b9..9be8e7f8 100644 --- a/src/discussions/comments/comment/Comment.jsx +++ b/src/discussions/comments/comment/Comment.jsx @@ -48,6 +48,14 @@ function Comment({ } }, [comment.id]); + const handleAbusedFlag = () => { + if (comment.abuseFlagged) { + dispatch(editComment(comment.id, { flagged: !comment.abuseFlagged })); + } else { + showReportConfirmation(); + } + }; + const actionHandlers = { [ContentActions.EDIT_CONTENT]: () => setEditing(true), [ContentActions.ENDORSE]: async () => { @@ -55,13 +63,7 @@ function Comment({ await dispatch(fetchThread(comment.threadId)); }, [ContentActions.DELETE]: showDeleteConfirmation, - [ContentActions.REPORT]: () => { - if (comment.abuseFlagged) { - dispatch(editComment(comment.id, { flagged: !comment.abuseFlagged })); - } else { - showReportConfirmation(); - } - }, + [ContentActions.REPORT]: () => handleAbusedFlag(), }; const handleLoadMoreComments = () => ( @@ -80,6 +82,8 @@ function Comment({ dispatch(removeComment(comment.id)); hideDeleteConfirmation(); }} + closeButtonVaraint="tertiary" + confirmButtonText={intl.formatMessage(messages.deleteConfirmationDelete)} /> {!comment.abuseFlagged && ( )} diff --git a/src/discussions/comments/comment/Reply.jsx b/src/discussions/comments/comment/Reply.jsx index ce8a74a4..a59144d5 100644 --- a/src/discussions/comments/comment/Reply.jsx +++ b/src/discussions/comments/comment/Reply.jsx @@ -30,6 +30,15 @@ function Reply({ const [isEditing, setEditing] = useState(false); const [isDeleting, showDeleteConfirmation, hideDeleteConfirmation] = useToggle(false); const [isReporting, showReportConfirmation, hideReportConfirmation] = useToggle(false); + + const handleAbusedFlag = () => { + if (reply.abuseFlagged) { + dispatch(editComment(reply.id, { flagged: !reply.abuseFlagged })); + } else { + showReportConfirmation(); + } + }; + const actionHandlers = { [ContentActions.EDIT_CONTENT]: () => setEditing(true), [ContentActions.ENDORSE]: () => dispatch(editComment( @@ -38,13 +47,7 @@ function Reply({ ContentActions.ENDORSE, )), [ContentActions.DELETE]: showDeleteConfirmation, - [ContentActions.REPORT]: () => { - if (reply.abuseFlagged) { - dispatch(editComment(reply.id, { flagged: !reply.abuseFlagged })); - } else { - showReportConfirmation(); - } - }, + [ContentActions.REPORT]: () => handleAbusedFlag(), }; const authorAvatars = useSelector(selectAuthorAvatars(reply.author)); const colorClass = AvatarOutlineAndLabelColors[reply.authorLabel]; @@ -61,6 +64,8 @@ function Reply({ dispatch(removeComment(reply.id)); hideDeleteConfirmation(); }} + closeButtonVaraint="tertiary" + confirmButtonText={intl.formatMessage(messages.deleteConfirmationDelete)} /> {!reply.abuseFlagged && ( )} {hasAnyAlert && ( diff --git a/src/discussions/comments/messages.js b/src/discussions/comments/messages.js index 8e66efa8..fc662073 100644 --- a/src/discussions/comments/messages.js +++ b/src/discussions/comments/messages.js @@ -148,6 +148,11 @@ const messages = defineMessages({ defaultMessage: 'Are you sure you want to permanently delete this comment?', description: 'Text displayed in confirmation dialog when deleting a comment', }, + deleteConfirmationDelete: { + id: 'discussions.delete.confirmation.button.delete', + defaultMessage: 'Delete', + description: 'Delete button shown on delete confirmation dialog', + }, reportResponseTitle: { id: 'discussions.editor.response.response.title', defaultMessage: 'Report inappropriate content?', diff --git a/src/discussions/common/Confirmation.jsx b/src/discussions/common/Confirmation.jsx index 6db98e89..06f84b31 100644 --- a/src/discussions/common/Confirmation.jsx +++ b/src/discussions/common/Confirmation.jsx @@ -4,7 +4,6 @@ import PropTypes from 'prop-types'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { ActionRow, Button, ModalDialog } from '@edx/paragon'; -import { ReportConfirmation } from '../../data/constants'; import messages from '../messages'; function Confirmation({ @@ -14,9 +13,10 @@ function Confirmation({ description, onClose, comfirmAction, + closeButtonVaraint, + confirmButtonVariant, + confirmButtonText, }) { - const ifDeleteConfirmation = title === ReportConfirmation.Report || false; - return ( @@ -29,13 +29,11 @@ function Confirmation({ - - {intl.formatMessage(messages.deleteConfirmationCancel)} + + {intl.formatMessage(messages.confirmationCancel)} - @@ -50,6 +48,15 @@ Confirmation.propTypes = { comfirmAction: PropTypes.func.isRequired, title: PropTypes.string.isRequired, description: PropTypes.string.isRequired, + closeButtonVaraint: PropTypes.string, + confirmButtonVariant: PropTypes.string, + confirmButtonText: PropTypes.string, +}; + +Confirmation.defaultProps = { + closeButtonVaraint: 'default', + confirmButtonVariant: 'primary', + confirmButtonText: '', }; export default injectIntl(Confirmation); diff --git a/src/discussions/messages.js b/src/discussions/messages.js index deeb748f..51e0b8c8 100644 --- a/src/discussions/messages.js +++ b/src/discussions/messages.js @@ -31,6 +31,11 @@ const messages = defineMessages({ defaultMessage: 'Delete', description: 'Action to delete a post or comment', }, + confirmationConfirm: { + id: 'discussions.confirmation.button.confirm', + defaultMessage: 'Confirm', + description: 'Confirm button shown on confirmation dialog', + }, closeAction: { id: 'discussions.actions.close', defaultMessage: 'Close', @@ -71,21 +76,11 @@ const messages = defineMessages({ defaultMessage: 'Unmark as answered', description: 'Action to unmark a comment as answering a post', }, - deleteConfirmationCancel: { - id: 'discussions.delete.confirmation.button.cancel', + confirmationCancel: { + id: 'discussions.modal.confirmation.button.cancel', defaultMessage: 'Cancel', description: 'Cancel button shown on delete confirmation dialog', }, - deleteConfirmationDelete: { - id: 'discussions.delete.confirmation.button.delete', - defaultMessage: 'Delete', - description: 'Delete button shown on delete confirmation dialog', - }, - reportConfirmationConfirm: { - id: 'discussions.report.confirmation.button.confirm', - defaultMessage: 'Confirm', - description: 'Confirm button shown on report confirmation dialog', - }, emptyAllTopics: { id: 'discussions.empty.allTopics', defaultMessage: diff --git a/src/discussions/posts/post/Post.jsx b/src/discussions/posts/post/Post.jsx index 607b2a1f..d4f8377a 100644 --- a/src/discussions/posts/post/Post.jsx +++ b/src/discussions/posts/post/Post.jsx @@ -36,6 +36,15 @@ function Post({ const [isDeleting, showDeleteConfirmation, hideDeleteConfirmation] = useToggle(false); const [isReporting, showReportConfirmation, hideReportConfirmation] = useToggle(false); const [isClosing, showClosePostModal, hideClosePostModal] = useToggle(false); + + const handleAbusedFlag = () => { + if (post.abuseFlagged) { + dispatch(updateExistingThread(post.id, { flagged: !post.abuseFlagged })); + } else { + showReportConfirmation(); + } + }; + const actionHandlers = { [ContentActions.EDIT_CONTENT]: () => history.push({ ...location, @@ -53,13 +62,7 @@ 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]: () => { - if (post.abuseFlagged) { - dispatch(updateExistingThread(post.id, { flagged: !post.abuseFlagged })); - } else { - showReportConfirmation(); - } - }, + [ContentActions.REPORT]: () => handleAbusedFlag(), }; const getTopicCategoryName = topicData => ( @@ -78,6 +81,8 @@ function Post({ history.push('.'); hideDeleteConfirmation(); }} + closeButtonVaraint="tertiary" + confirmButtonText={intl.formatMessage(messages.deleteConfirmationDelete)} /> {!post.abuseFlagged && ( )} diff --git a/src/discussions/posts/post/messages.js b/src/discussions/posts/post/messages.js index f1bc34c6..f9c76ba4 100644 --- a/src/discussions/posts/post/messages.js +++ b/src/discussions/posts/post/messages.js @@ -71,6 +71,11 @@ const messages = defineMessages({ id: 'discussions.editor.delete.post.description', defaultMessage: 'Are you sure you want to permanently delete this post?', }, + deleteConfirmationDelete: { + id: 'discussions.post.delete.confirmation.button.delete', + defaultMessage: 'Delete', + description: 'Delete button shown on delete confirmation dialog', + }, reportPostTitle: { id: 'discussions.editor.report.post.title', defaultMessage: 'Report inappropriate content?', From 7912d70388ee65cc4f1fbc73446f189849aaff80 Mon Sep 17 00:00:00 2001 From: ayeshoali Date: Wed, 30 Nov 2022 19:03:30 +0500 Subject: [PATCH 06/10] refactor: handled confirm action to functions --- src/discussions/comments/comment/Comment.jsx | 20 +++++++++++------- src/discussions/comments/comment/Reply.jsx | 20 +++++++++++------- src/discussions/posts/post/Post.jsx | 22 ++++++++++++-------- src/index.scss | 2 +- 4 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/discussions/comments/comment/Comment.jsx b/src/discussions/comments/comment/Comment.jsx index 9be8e7f8..d0d3f840 100644 --- a/src/discussions/comments/comment/Comment.jsx +++ b/src/discussions/comments/comment/Comment.jsx @@ -56,6 +56,16 @@ function Comment({ } }; + 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 () => { @@ -78,10 +88,7 @@ function Comment({ title={intl.formatMessage(messages.deleteResponseTitle)} description={intl.formatMessage(messages.deleteResponseDescription)} onClose={hideDeleteConfirmation} - comfirmAction={() => { - dispatch(removeComment(comment.id)); - hideDeleteConfirmation(); - }} + comfirmAction={handleDeleteConfirmation} closeButtonVaraint="tertiary" confirmButtonText={intl.formatMessage(messages.deleteConfirmationDelete)} /> @@ -91,10 +98,7 @@ function Comment({ title={intl.formatMessage(messages.reportResponseTitle)} description={intl.formatMessage(messages.reportResponseDescription)} onClose={hideReportConfirmation} - comfirmAction={() => { - dispatch(editComment(comment.id, { flagged: !comment.abuseFlagged })); - hideReportConfirmation(); - }} + comfirmAction={handleReportConfirmation} confirmButtonVariant="danger" /> )} diff --git a/src/discussions/comments/comment/Reply.jsx b/src/discussions/comments/comment/Reply.jsx index a59144d5..bece7975 100644 --- a/src/discussions/comments/comment/Reply.jsx +++ b/src/discussions/comments/comment/Reply.jsx @@ -39,6 +39,16 @@ function Reply({ } }; + const handleDeleteConfirmation = () => { + dispatch(removeComment(reply.id)); + hideDeleteConfirmation(); + }; + + const handleReportConfirmation = () => { + dispatch(editComment(reply.id, { flagged: !reply.abuseFlagged })); + hideReportConfirmation(); + }; + const actionHandlers = { [ContentActions.EDIT_CONTENT]: () => setEditing(true), [ContentActions.ENDORSE]: () => dispatch(editComment( @@ -60,10 +70,7 @@ function Reply({ title={intl.formatMessage(messages.deleteCommentTitle)} description={intl.formatMessage(messages.deleteCommentDescription)} onClose={hideDeleteConfirmation} - comfirmAction={() => { - dispatch(removeComment(reply.id)); - hideDeleteConfirmation(); - }} + comfirmAction={handleDeleteConfirmation} closeButtonVaraint="tertiary" confirmButtonText={intl.formatMessage(messages.deleteConfirmationDelete)} /> @@ -73,10 +80,7 @@ function Reply({ title={intl.formatMessage(messages.reportCommentTitle)} description={intl.formatMessage(messages.reportCommentDescription)} onClose={hideReportConfirmation} - comfirmAction={() => { - dispatch(editComment(reply.id, { flagged: !reply.abuseFlagged })); - hideReportConfirmation(); - }} + comfirmAction={handleReportConfirmation} confirmButtonVariant="danger" /> )} diff --git a/src/discussions/posts/post/Post.jsx b/src/discussions/posts/post/Post.jsx index d4f8377a..95e0aa7f 100644 --- a/src/discussions/posts/post/Post.jsx +++ b/src/discussions/posts/post/Post.jsx @@ -45,6 +45,17 @@ function Post({ } }; + const handleDeleteConfirmation = () => { + dispatch(removeThread(post.id)); + history.push('.'); + hideDeleteConfirmation(); + }; + + const handleReportConfirmation = () => { + dispatch(updateExistingThread(post.id, { flagged: !post.abuseFlagged })); + hideReportConfirmation(); + }; + const actionHandlers = { [ContentActions.EDIT_CONTENT]: () => history.push({ ...location, @@ -76,11 +87,7 @@ function Post({ title={intl.formatMessage(messages.deletePostTitle)} description={intl.formatMessage(messages.deletePostDescription)} onClose={hideDeleteConfirmation} - comfirmAction={() => { - dispatch(removeThread(post.id)); - history.push('.'); - hideDeleteConfirmation(); - }} + comfirmAction={handleDeleteConfirmation} closeButtonVaraint="tertiary" confirmButtonText={intl.formatMessage(messages.deleteConfirmationDelete)} /> @@ -90,10 +97,7 @@ function Post({ title={intl.formatMessage(messages.reportPostTitle)} description={intl.formatMessage(messages.reportPostDescription)} onClose={hideReportConfirmation} - comfirmAction={() => { - dispatch(updateExistingThread(post.id, { flagged: !post.abuseFlagged })); - hideReportConfirmation(); - }} + comfirmAction={handleReportConfirmation} confirmButtonVariant="danger" /> )} diff --git a/src/index.scss b/src/index.scss index 34c5ee28..b35a84d3 100755 --- a/src/index.scss +++ b/src/index.scss @@ -242,7 +242,7 @@ header { } .zindex-5000 { - z-index: 5000 ; + z-index: 5000; } #paragon-portal-root .pgn__modal-layer { From b23846b1e4bd53434b6455cd7d3add7ff420fd61 Mon Sep 17 00:00:00 2001 From: Mehak Nasir Date: Thu, 24 Nov 2022 15:06:38 +0500 Subject: [PATCH 07/10] fix: handled thread not found result on frontend --- src/discussions/comments/CommentsView.jsx | 13 +++++++++-- .../comments/CommentsView.test.jsx | 8 +++---- src/discussions/comments/comment/Comment.jsx | 9 +++++--- src/discussions/comments/messages.js | 5 +++++ src/discussions/posts/data/api.js | 4 ++-- src/discussions/posts/data/thunks.js | 6 ++--- .../posts/post-editor/PostEditor.jsx | 22 ++++++++++++++----- src/discussions/posts/post-editor/messages.js | 5 +++++ 8 files changed, 52 insertions(+), 20 deletions(-) diff --git a/src/discussions/comments/CommentsView.jsx b/src/discussions/comments/CommentsView.jsx index 77565562..9555a42b 100644 --- a/src/discussions/comments/CommentsView.jsx +++ b/src/discussions/comments/CommentsView.jsx @@ -18,6 +18,7 @@ import { import { useDispatchWithState } from '../../data/hooks'; import { DiscussionContext } from '../common/context'; import { useIsOnDesktop } from '../data/hooks'; +import { EmptyPage } from '../empty-posts'; import { Post } from '../posts'; import { selectThread } from '../posts/data/selectors'; import { fetchThread, markThreadAsRead } from '../posts/data/thunks'; @@ -133,9 +134,9 @@ DiscussionCommentsView.propTypes = { }; function CommentsView({ intl }) { + const [isLoading, submitDispatch] = useDispatchWithState(); const { postId } = useParams(); const thread = usePost(postId); - const dispatch = useDispatch(); const history = useHistory(); const location = useLocation(); const isOnDesktop = useIsOnDesktop(); @@ -143,8 +144,16 @@ function CommentsView({ intl }) { courseId, learnerUsername, category, topicId, page, inContext, } = useContext(DiscussionContext); + useEffect(() => { + if (!thread) { submitDispatch(fetchThread(postId, courseId, true)); } + }, [postId]); + if (!thread) { - dispatch(fetchThread(postId, true)); + if (!isLoading) { + return ( + + ); + } return ( ); diff --git a/src/discussions/comments/CommentsView.test.jsx b/src/discussions/comments/CommentsView.test.jsx index 52c01a81..d0389e0e 100644 --- a/src/discussions/comments/CommentsView.test.jsx +++ b/src/discussions/comments/CommentsView.test.jsx @@ -102,7 +102,7 @@ function renderComponent(postId) { } describe('CommentsView', () => { - beforeEach(async () => { + beforeEach(() => { initializeMockApp({ authenticatedUser: { userId: 3, @@ -147,7 +147,7 @@ describe('CommentsView', () => { )]; }); - await executeThunk(fetchThreads(courseId), store.dispatch, store.getState); + executeThunk(fetchThreads(courseId), store.dispatch, store.getState); mockAxiosReturnPagedComments(); mockAxiosReturnPagedCommentsResponses(); }); @@ -449,9 +449,9 @@ describe('CommentsView', () => { describe('for discussion thread', () => { const findLoadMoreCommentsButton = () => screen.findByTestId('load-more-comments'); - it('shown spinner when post isn\'t loaded', async () => { + it('shown post not found when post id does not belong to course', async () => { renderComponent('unloaded-id'); - expect(await screen.findByTestId('loading-indicator')) + expect(await screen.findByText('Thread not found', { exact: true })) .toBeInTheDocument(); }); diff --git a/src/discussions/comments/comment/Comment.jsx b/src/discussions/comments/comment/Comment.jsx index 2cdb6bc2..d6876339 100644 --- a/src/discussions/comments/comment/Comment.jsx +++ b/src/discussions/comments/comment/Comment.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; @@ -10,6 +10,7 @@ import { Button, useToggle } from '@edx/paragon'; import HTMLLoader from '../../../components/HTMLLoader'; import { ContentActions } from '../../../data/constants'; import { AlertBanner, DeleteConfirmation, EndorsedAlertBanner } from '../../common'; +import { DiscussionContext } from '../../common/context'; import { selectBlackoutDate } from '../../data/selectors'; import { fetchThread } from '../../posts/data/thunks'; import { inBlackoutDateRange } from '../../utils'; @@ -39,7 +40,9 @@ function Comment({ const hasMorePages = useSelector(selectCommentHasMorePages(comment.id)); const currentPage = useSelector(selectCommentCurrentPage(comment.id)); const blackoutDateRange = useSelector(selectBlackoutDate); - + 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) { @@ -51,7 +54,7 @@ function Comment({ [ContentActions.EDIT_CONTENT]: () => setEditing(true), [ContentActions.ENDORSE]: async () => { await dispatch(editComment(comment.id, { endorsed: !comment.endorsed }, ContentActions.ENDORSE)); - await dispatch(fetchThread(comment.threadId)); + await dispatch(fetchThread(comment.threadId, courseId)); }, [ContentActions.DELETE]: showDeleteConfirmation, [ContentActions.REPORT]: () => dispatch(editComment(comment.id, { flagged: !comment.abuseFlagged })), diff --git a/src/discussions/comments/messages.js b/src/discussions/comments/messages.js index 3fe4a355..85838798 100644 --- a/src/discussions/comments/messages.js +++ b/src/discussions/comments/messages.js @@ -182,6 +182,11 @@ const messages = defineMessages({ defaultMessage: '{time} ago', description: 'Time text for endorse banner', }, + noThreadFound: { + id: 'discussion.thread.notFound', + defaultMessage: 'Thread not found', + description: 'message to show on screen if the request thread is not found in course', + }, }); export default messages; diff --git a/src/discussions/posts/data/api.js b/src/discussions/posts/data/api.js index ed8c6328..04f2b326 100644 --- a/src/discussions/posts/data/api.js +++ b/src/discussions/posts/data/api.js @@ -71,8 +71,8 @@ export async function getThreads( * @param {string} threadId * @returns {Promise<{}>} */ -export async function getThread(threadId) { - const params = { requested_fields: 'profile_image' }; +export async function getThread(threadId, courseId) { + const params = { requested_fields: 'profile_image', course_id: courseId }; const url = `${threadsApiUrl}${threadId}/`; const { data } = await getAuthenticatedHttpClient().get(url, { params }); return data; diff --git a/src/discussions/posts/data/thunks.js b/src/discussions/posts/data/thunks.js index 04269def..7b3f2665 100644 --- a/src/discussions/posts/data/thunks.js +++ b/src/discussions/posts/data/thunks.js @@ -154,18 +154,18 @@ export function fetchThreads(courseId, { }; } -export function fetchThread(threadId, isDirectLinkPost = false) { +export function fetchThread(threadId, courseId, isDirectLinkPost = false) { return async (dispatch) => { try { dispatch(fetchThreadRequest({ threadId })); - const data = await getThread(threadId); + const data = await getThread(threadId, courseId); if (isDirectLinkPost) { dispatch(fetchThreadByDirectLinkSuccess({ ...normaliseThreads(camelCaseObject(data)), page: 1 })); } else { dispatch(fetchThreadSuccess(normaliseThreads(camelCaseObject(data)))); } } catch (error) { - if (getHttpErrorStatus(error) === 403) { + if (getHttpErrorStatus(error) === 403 || getHttpErrorStatus(error) === 404) { dispatch(fetchThreadDenied()); } else { dispatch(fetchThreadFailed()); diff --git a/src/discussions/posts/post-editor/PostEditor.jsx b/src/discussions/posts/post-editor/PostEditor.jsx index 74c5f918..0ab48773 100644 --- a/src/discussions/posts/post-editor/PostEditor.jsx +++ b/src/discussions/posts/post-editor/PostEditor.jsx @@ -33,6 +33,7 @@ import { selectUserIsGroupTa, selectUserIsStaff, } from '../../data/selectors'; +import { EmptyPage } from '../../empty-posts'; import { selectCoursewareTopics, selectNonCoursewareIds, selectNonCoursewareTopics } from '../../topics/data/selectors'; import { discussionsPath, formikCompatibleHandler, isFormikFieldInvalid, useCommentsPagePath, @@ -193,16 +194,25 @@ function PostEditor({ dispatch(fetchCourseCohorts(courseId)); } if (editExisting) { - dispatch(fetchThread(postId)); + dispatchSubmit(fetchThread(postId, courseId)); } }, [courseId, editExisting]); if (editExisting && !post) { - return ( -
- -
- ); + if (submitting) { + return ( +
+ +
+ ); + } + if (!submitting) { + return ( + + ); + } } const validationSchema = Yup.object().shape({ diff --git a/src/discussions/posts/post-editor/messages.js b/src/discussions/posts/post-editor/messages.js index c5b56362..240228ac 100644 --- a/src/discussions/posts/post-editor/messages.js +++ b/src/discussions/posts/post-editor/messages.js @@ -131,6 +131,11 @@ const messages = defineMessages({ defaultMessage: 'Unnamed subcategory', description: 'display string for topics with missing names', }, + noThreadFound: { + id: 'discussion.thread.notFound', + defaultMessage: 'Thread not found', + description: 'message to show on screen if the request thread is not found in course', + }, }); export default messages; From 21176131a79b488ec7f4acfa912e101c888ed7ae Mon Sep 17 00:00:00 2001 From: ayeshoali Date: Thu, 1 Dec 2022 16:05:40 +0500 Subject: [PATCH 08/10] fix: removed divider from report in action drop down --- src/discussions/common/ActionsDropdown.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/discussions/common/ActionsDropdown.jsx b/src/discussions/common/ActionsDropdown.jsx index df5ae7d9..09310b0a 100644 --- a/src/discussions/common/ActionsDropdown.jsx +++ b/src/discussions/common/ActionsDropdown.jsx @@ -64,7 +64,7 @@ function ActionsDropdown({ > {actions.map(action => ( - {(action.action === ContentActions.DELETE || action.action === ContentActions.REPORT) + {(action.action === ContentActions.DELETE) && } Date: Wed, 30 Nov 2022 16:45:43 +0500 Subject: [PATCH 09/10] style: 3 level topic hierarchy accomodated when incontext discussions is enabled --- src/discussions/posts/post/Post.jsx | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/discussions/posts/post/Post.jsx b/src/discussions/posts/post/Post.jsx index 95e0aa7f..35521ea7 100644 --- a/src/discussions/posts/post/Post.jsx +++ b/src/discussions/posts/post/Post.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useContext } from 'react'; import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; @@ -11,6 +11,7 @@ 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 { selectModerationSettings } from '../../data/selectors'; import { selectTopic } from '../../topics/data/selectors'; import { removeThread, updateExistingThread } from '../data/thunks'; @@ -56,6 +57,7 @@ function Post({ hideReportConfirmation(); }; + const { inContext } = useContext(DiscussionContext); const actionHandlers = { [ContentActions.EDIT_CONTENT]: () => history.push({ ...location, @@ -107,13 +109,23 @@ function Post({
{topicContext && topic && ( -
+
{intl.formatMessage(messages.relatedTo)}{' '} - {`${getTopicCategoryName(topic)} / ${topic.name}`} + {inContext + ? ( + <> + {topicContext.chapterName} + / + {topicContext.verticalName} + / + {topicContext.unitName} + + ) + : `${getTopicCategoryName(topic)} / ${topic.name}`}
)} From 9d9377bb8c0749fe06fb21ce682436714ced206e Mon Sep 17 00:00:00 2001 From: ayeshoali Date: Thu, 1 Dec 2022 16:17:11 +0500 Subject: [PATCH 10/10] refactor: used classname in conditional class --- src/discussions/posts/post/Post.jsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/discussions/posts/post/Post.jsx b/src/discussions/posts/post/Post.jsx index 35521ea7..3a5a43fa 100644 --- a/src/discussions/posts/post/Post.jsx +++ b/src/discussions/posts/post/Post.jsx @@ -1,6 +1,7 @@ import React, { useContext } from 'react'; import PropTypes from 'prop-types'; +import classNames from 'classnames'; import { useDispatch, useSelector } from 'react-redux'; import { useHistory, useLocation } from 'react-router-dom'; @@ -109,7 +110,9 @@ function Post({
{topicContext && topic && ( -
+
{intl.formatMessage(messages.relatedTo)}{' '} {topicContext.chapterName} / - {topicContext.verticalName} + {topicContext.verticalName} / {topicContext.unitName}