From b23846b1e4bd53434b6455cd7d3add7ff420fd61 Mon Sep 17 00:00:00 2001 From: Mehak Nasir Date: Thu, 24 Nov 2022 15:06:38 +0500 Subject: [PATCH] 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;