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;