fix: handled thread not found result on frontend

This commit is contained in:
Mehak Nasir
2022-11-24 15:06:38 +05:00
committed by Mehak Nasir
parent 5c3d561152
commit b23846b1e4
8 changed files with 52 additions and 20 deletions

View File

@@ -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 (
<EmptyPage title={intl.formatMessage(messages.noThreadFound)} />
);
}
return (
<Spinner animation="border" variant="primary" data-testid="loading-indicator" />
);

View File

@@ -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();
});

View File

@@ -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 })),

View File

@@ -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;

View File

@@ -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;

View File

@@ -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());

View File

@@ -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 (
<div className="m-4 card p-4 align-items-center">
<Spinner animation="border" variant="primary" />
</div>
);
if (submitting) {
return (
<div className="m-4 card p-4 align-items-center">
<Spinner animation="border" variant="primary" />
</div>
);
}
if (!submitting) {
return (
<EmptyPage
title={intl.formatMessage(messages.noThreadFound)}
/>
);
}
}
const validationSchema = Yup.object().shape({

View File

@@ -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;