Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 10 additions & 14 deletions src/certificates/data/thunks.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
/* istanbul ignore file */
import { RequestStatus } from '../../data/constants';
import {
hideProcessingNotification,
showProcessingNotification,
} from '../../generic/processing-notification/data/slice';
import { showToastOutsideReact, closeToastOutsideReact } from '../../generic/toast-context';
import { handleResponseErrors } from '../../generic/saving-error-alert';
import { NOTIFICATION_MESSAGES } from '../../constants';
import {
Expand Down Expand Up @@ -45,7 +42,7 @@ export function fetchCertificates(courseId) {
export function createCourseCertificate(courseId, certificate) {
return async (dispatch) => {
dispatch(updateSavingStatus({ status: RequestStatus.PENDING }));
dispatch(showProcessingNotification(NOTIFICATION_MESSAGES.saving));
showToastOutsideReact(NOTIFICATION_MESSAGES.saving);

try {
const certificateValues = await createCertificate(courseId, certificate);
Expand All @@ -55,15 +52,15 @@ export function createCourseCertificate(courseId, certificate) {
} catch (error) {
return handleResponseErrors(error, dispatch, updateSavingStatus);
} finally {
dispatch(hideProcessingNotification());
closeToastOutsideReact();
}
};
}

export function updateCourseCertificate(courseId, certificate) {
return async (dispatch) => {
dispatch(updateSavingStatus({ status: RequestStatus.PENDING }));
dispatch(showProcessingNotification(NOTIFICATION_MESSAGES.saving));
showToastOutsideReact(NOTIFICATION_MESSAGES.saving);

try {
const certificatesValues = await updateCertificate(courseId, certificate);
Expand All @@ -73,15 +70,15 @@ export function updateCourseCertificate(courseId, certificate) {
} catch (error) {
return handleResponseErrors(error, dispatch, updateSavingStatus);
} finally {
dispatch(hideProcessingNotification());
closeToastOutsideReact();
}
};
}

export function deleteCourseCertificate(courseId, certificateId) {
return async (dispatch) => {
dispatch(updateSavingStatus({ status: RequestStatus.PENDING }));
dispatch(showProcessingNotification(NOTIFICATION_MESSAGES.deleting));
showToastOutsideReact(NOTIFICATION_MESSAGES.deleting);

try {
await deleteCertificate(courseId, certificateId);
Expand All @@ -91,18 +88,17 @@ export function deleteCourseCertificate(courseId, certificateId) {
} catch (error) {
return handleResponseErrors(error, dispatch, updateSavingStatus);
} finally {
dispatch(hideProcessingNotification());
closeToastOutsideReact();
}
};
}

export function updateCertificateActiveStatus(courseId, path, activationStatus) {
return async (dispatch) => {
dispatch(updateSavingStatus({ status: RequestStatus.PENDING }));

dispatch(showProcessingNotification(
showToastOutsideReact(
activationStatus ? ACTIVATION_MESSAGES.activating : ACTIVATION_MESSAGES.deactivating,
));
);

try {
await updateActiveStatus(path, activationStatus);
Expand All @@ -112,7 +108,7 @@ export function updateCertificateActiveStatus(courseId, path, activationStatus)
} catch (error) {
return handleResponseErrors(error, dispatch, updateSavingStatus);
} finally {
dispatch(hideProcessingNotification());
closeToastOutsideReact();
}
};
}
7 changes: 0 additions & 7 deletions src/certificates/layout/MainLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Container, Layout } from '@openedx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';

import { SavingErrorAlert } from '../../generic/saving-error-alert';
import ProcessingNotification from '../../generic/processing-notification';
import SubHeader from '../../generic/sub-header/SubHeader';
import messages from '../messages';
import CertificatesSidebar from './certificates-sidebar/CertificatesSidebar';
Expand All @@ -16,8 +15,6 @@ const MainLayout = ({ courseId, showHeaderButtons, children }) => {
const {
errorMessage,
savingStatus,
isShowProcessingNotification,
processingNotificationTitle,
} = useLayout();

return (
Expand Down Expand Up @@ -50,10 +47,6 @@ const MainLayout = ({ courseId, showHeaderButtons, children }) => {
</section>
</Container>
<div className="certificates alert-toast">
<ProcessingNotification
isShow={isShowProcessingNotification}
title={processingNotificationTitle}
/>
<SavingErrorAlert
savingStatus={savingStatus}
errorMessage={errorMessage}
Expand Down
8 changes: 0 additions & 8 deletions src/certificates/layout/hooks/useLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,12 @@ import { useEffect } from 'react';
import { useSelector } from 'react-redux';

import { RequestStatus } from '../../../data/constants';
import { getProcessingNotification } from '../../../generic/processing-notification/data/selectors';
import { getSavingStatus, getErrorMessage } from '../../data/selectors';

const useLayout = () => {
const savingStatus = useSelector(getSavingStatus);
const errorMessage = useSelector(getErrorMessage);

const {
isShow: isShowProcessingNotification,
title: processingNotificationTitle,
} = useSelector(getProcessingNotification);

useEffect(() => {
if (savingStatus === RequestStatus.SUCCESSFUL) {
window.scrollTo({ top: 0, behavior: 'smooth' });
Expand All @@ -23,8 +17,6 @@ const useLayout = () => {
return {
errorMessage,
savingStatus,
isShowProcessingNotification,
processingNotificationTitle,
};
};

Expand Down
41 changes: 20 additions & 21 deletions src/course-outline/CourseOutline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@ import { useLocation } from 'react-router-dom';
import { CourseAuthoringOutlineSidebarSlot } from '@src/plugin-slots/CourseAuthoringOutlineSidebarSlot';

import { LoadingSpinner } from '@src/generic/Loading';
import { getProcessingNotification } from '@src/generic/processing-notification/data/selectors';
import { RequestStatus } from '@src/data/constants';
import SubHeader from '@src/generic/sub-header/SubHeader';
import ProcessingNotification from '@src/generic/processing-notification';
import InternetConnectionAlert from '@src/generic/internet-connection-alert';
import DeleteModal from '@src/generic/delete-modal/DeleteModal';
import ConfigureModal from '@src/generic/configure-modal/ConfigureModal';
Expand All @@ -37,6 +35,7 @@ import { useCourseAuthoringContext } from '@src/CourseAuthoringContext';
import LegacyLibContentBlockAlert from '@src/course-libraries/LegacyLibContentBlockAlert';
import { ContainerType } from '@src/generic/key-utils';
import { useCourseItemData } from '@src/course-outline/data/apiHooks';
import { useToastContext } from '@src/generic/toast-context';
import {
getProctoredExamsFlag,
getTimedExamsFlag,
Expand Down Expand Up @@ -77,6 +76,7 @@ const CourseOutline = () => {
closeUnlinkModal,
currentSelection,
} = useCourseAuthoringContext();
const { showToast } = useToastContext();

const {
courseName,
Expand Down Expand Up @@ -161,11 +161,6 @@ const CourseOutline = () => {
setSections(() => [...sectionsList]);
};

const {
isShow: isShowProcessingNotification,
title: processingNotificationTitle,
} = useSelector(getProcessingNotification);

const { data: currentItemData } = useCourseItemData(currentSelection?.currentId);

const itemCategory = currentItemData?.category || '';
Expand Down Expand Up @@ -235,6 +230,24 @@ const CourseOutline = () => {
setSections(sectionsList);
}, [sectionsList]);

useEffect(() => {
if (handleAddBlock.isPending
|| handleAddAndOpenUnit.isPending
|| isConfigureOpPending
|| isSectionHighlightsUpdatePending
|| isDuplicatingItem
|| isPasting) {
showToast(NOTIFICATION_MESSAGES.saving);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shows the toast, but it doesn't hide it when the processing is done, and I think the code before did that?

For example, if I add a new section to the course, it says "Saving" for 1 second and then I see the new section appear, but it still says "Saving" for a few more seconds, even though the new section is already fully created.

The unit page is working fine though; the problem is only on the outline page.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's true. I refactored that code in dc730de

}
}, [
handleAddBlock.isPending,
handleAddAndOpenUnit.isPending,
isConfigureOpPending,
isSectionHighlightsUpdatePending,
isDuplicatingItem,
isPasting,
]);

if (isLoading) {
// eslint-disable-next-line react/jsx-no-useless-fragment
return (
Expand Down Expand Up @@ -518,20 +531,6 @@ const CourseOutline = () => {
/>
</Container>
<div className="alert-toast">
<ProcessingNotification
// Show processing toast if any mutation is running
isShow={
isShowProcessingNotification
|| handleAddBlock.isPending
|| handleAddAndOpenUnit.isPending
|| isConfigureOpPending
|| isSectionHighlightsUpdatePending
|| isDuplicatingItem
|| isPasting
}
// HACK: Use saving as default title till we have a need for better messages
title={processingNotificationTitle || NOTIFICATION_MESSAGES.saving}
/>
<InternetConnectionAlert
isFailed={isInternetConnectionAlertFailed}
isQueryPending={savingStatus === RequestStatus.PENDING}
Expand Down
22 changes: 10 additions & 12 deletions src/course-outline/data/thunk.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { logError } from '@edx/frontend-platform/logging';
import { RequestStatus } from '@src/data/constants';
import { NOTIFICATION_MESSAGES } from '@src/constants';
import {
hideProcessingNotification,
showProcessingNotification,
} from '@src/generic/processing-notification/data/slice';
import { showToastOutsideReact, closeToastOutsideReact } from '@src/generic/toast-context';
import {
getCourseBestPracticesChecklist,
getCourseLaunchChecklist,
Expand Down Expand Up @@ -142,34 +139,35 @@ export function fetchCourseBestPracticesQuery({
export function enableCourseHighlightsEmailsQuery(courseId: string) {
return async (dispatch) => {
dispatch(updateSavingStatus({ status: RequestStatus.PENDING }));
dispatch(showProcessingNotification(NOTIFICATION_MESSAGES.saving));
showToastOutsideReact(NOTIFICATION_MESSAGES.saving);

try {
await enableCourseHighlightsEmails(courseId);
dispatch(fetchCourseOutlineIndexQuery(courseId));

dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL }));
dispatch(hideProcessingNotification());
} catch {
dispatch(updateSavingStatus({ status: RequestStatus.FAILED }));
} finally {
closeToastOutsideReact();
}
};
}

export function setVideoSharingOptionQuery(courseId: string, option: string) {
return async (dispatch) => {
dispatch(updateSavingStatus({ status: RequestStatus.PENDING }));
dispatch(showProcessingNotification(NOTIFICATION_MESSAGES.saving));
showToastOutsideReact(NOTIFICATION_MESSAGES.saving);

try {
await setVideoSharingOption(courseId, option);
dispatch(updateStatusBar({ videoSharingOptions: option }));

dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL }));
dispatch(hideProcessingNotification());
} catch {
dispatch(updateSavingStatus({ status: RequestStatus.FAILED }));
dispatch(hideProcessingNotification());
} finally {
closeToastOutsideReact();
}
};
}
Expand Down Expand Up @@ -227,20 +225,20 @@ function setBlockOrderListQuery(
) {
return async (dispatch) => {
dispatch(updateSavingStatus({ status: RequestStatus.PENDING }));
dispatch(showProcessingNotification(NOTIFICATION_MESSAGES.saving));
showToastOutsideReact(NOTIFICATION_MESSAGES.saving);

try {
await apiFn(parentId, blockIds).then(async (result) => {
if (result) {
successCallback();
dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL }));
dispatch(hideProcessingNotification());
}
});
} catch {
restoreCallback();
dispatch(hideProcessingNotification());
dispatch(updateSavingStatus({ status: RequestStatus.FAILED }));
} finally {
closeToastOutsideReact();
}
};
}
Expand Down
16 changes: 10 additions & 6 deletions src/course-unit/CourseUnit.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
screen,
} from '@src/testUtils';
import mockResult from '@src/library-authoring/__mocks__/library-search.json';
import { IFRAME_FEATURE_POLICY } from '@src/constants';
import { IFRAME_FEATURE_POLICY, NOTIFICATION_MESSAGES } from '@src/constants';
import { mockWaffleFlags } from '@src/data/apiHooks.mock';
import pasteComponentMessages from '@src/generic/clipboard/paste-component/messages';
import { getClipboardUrl } from '@src/generic/data/api';
Expand Down Expand Up @@ -79,6 +79,8 @@ import messages from './messages';

let axiosMock;
let store;
let mockShowToast;
let mockCloseToast;
const courseId = '123';
const blockId = '567890';
const sequenceId = 'block-v1:edX+DemoX+Demo_Course+type@sequential+block@19a30717eff543078a5d94ae9d6c18a5';
Expand Down Expand Up @@ -147,6 +149,8 @@ describe('<CourseUnit />', () => {
window.scrollTo = jest.fn();
global.localStorage.clear();
store = mocks.reduxStore;
mockShowToast = mocks.mockShowToast;
mockCloseToast = mocks.mockCloseToast;
axiosMock = mocks.axiosMock;
axiosMock
.onGet(getClipboardUrl())
Expand Down Expand Up @@ -2162,26 +2166,26 @@ describe('<CourseUnit />', () => {
});

it('displays processing notification on receiving post message', async () => {
const { getByText, queryByText } = render(<RootWrapper />);
render(<RootWrapper />);

await waitFor(() => {
simulatePostMessageEvent(messageTypes.addNewComponent);
expect(getByText(('Adding'))).toBeInTheDocument();
expect(mockShowToast).toHaveBeenCalledWith(NOTIFICATION_MESSAGES.adding);
});

await waitFor(() => {
simulatePostMessageEvent(messageTypes.hideProcessingNotification);
expect(queryByText(('Adding'))).not.toBeInTheDocument();
expect(mockCloseToast).toHaveBeenCalled();
});

await waitFor(() => {
simulatePostMessageEvent(messageTypes.pasteNewComponent);
expect(getByText(('Pasting'))).toBeInTheDocument();
expect(mockShowToast).toHaveBeenCalledWith(NOTIFICATION_MESSAGES.pasting);
});

await waitFor(() => {
simulatePostMessageEvent(messageTypes.hideProcessingNotification);
expect(queryByText(('Pasting'))).not.toBeInTheDocument();
expect(mockCloseToast).toHaveBeenCalledTimes(2);
});
});

Expand Down
Loading