From 2973614e3b130fdb38d44ea04c2564b030d948df Mon Sep 17 00:00:00 2001 From: Muhammad Anas <88967643+Anas12091101@users.noreply.github.com> Date: Wed, 9 Jul 2025 21:35:58 +0500 Subject: [PATCH] fix: loading unit page directly from link after logging in in Teak (#2246) This is a simple version of the fix for Teak; on master it was fixed with https://github.com/openedx/frontend-app-authoring/pull/1867 --- src/course-unit/CourseUnit.jsx | 2 ++ src/course-unit/course-sequence/hooks.js | 13 ++++++---- .../SequenceNavigation.jsx | 4 +-- src/course-unit/data/selectors.js | 2 +- src/course-unit/hooks.jsx | 3 +++ src/course-unit/sidebar/utils.js | 2 +- .../xblock-container-iframe/index.tsx | 26 ++++++++++++++++++- .../xblock-container-iframe/types.ts | 5 ++++ 8 files changed, 47 insertions(+), 10 deletions(-) diff --git a/src/course-unit/CourseUnit.jsx b/src/course-unit/CourseUnit.jsx index 10a9ad25b..0054a77e2 100644 --- a/src/course-unit/CourseUnit.jsx +++ b/src/course-unit/CourseUnit.jsx @@ -41,6 +41,7 @@ const CourseUnit = ({ courseId }) => { courseUnit, isLoading, sequenceId, + courseUnitLoadingStatus, unitTitle, unitCategory, errorMessage, @@ -210,6 +211,7 @@ const CourseUnit = ({ courseId }) => { courseId={courseId} blockId={blockId} isUnitVerticalType={isUnitVerticalType} + courseUnitLoadingStatus={courseUnitLoadingStatus} unitXBlockActions={unitXBlockActions} courseVerticalChildren={courseVerticalChildren.children} handleConfigureSubmit={handleConfigureSubmit} diff --git a/src/course-unit/course-sequence/hooks.js b/src/course-unit/course-sequence/hooks.js index 28035e1af..cb541f1ab 100644 --- a/src/course-unit/course-sequence/hooks.js +++ b/src/course-unit/course-sequence/hooks.js @@ -12,16 +12,19 @@ export function useSequenceNavigationMetadata(courseId, currentSequenceId, curre const isLastUnit = !nextUrl; const sequenceIds = useSelector(getSequenceIds); const sequenceIndex = sequenceIds.indexOf(currentSequenceId); - const unitIndex = sequence.unitIds.indexOf(currentUnitId); + let unitIndex = sequence?.unitIds.indexOf(currentUnitId); const nextSequenceId = sequenceIndex < sequenceIds.length - 1 ? sequenceIds[sequenceIndex + 1] : null; const previousSequenceId = sequenceIndex > 0 ? sequenceIds[sequenceIndex - 1] : null; - + if (!unitIndex) { + // Handle case where unitIndex is not found + unitIndex = 0; + } let nextLink; const nextIndex = unitIndex + 1; - if (nextIndex < sequence.unitIds.length) { - const nextUnitId = sequence.unitIds[nextIndex]; + if (nextIndex < sequence?.unitIds.length) { + const nextUnitId = sequence?.unitIds[nextIndex]; nextLink = `/course/${courseId}/container/${nextUnitId}/${currentSequenceId}`; } else if (nextSequenceId) { const pathToNextUnit = decodeURIComponent(nextUrl); @@ -32,7 +35,7 @@ export function useSequenceNavigationMetadata(courseId, currentSequenceId, curre const previousIndex = unitIndex - 1; if (previousIndex >= 0) { - const previousUnitId = sequence.unitIds[previousIndex]; + const previousUnitId = sequence?.unitIds[previousIndex]; previousLink = `/course/${courseId}/container/${previousUnitId}/${currentSequenceId}`; } else if (previousSequenceId) { const pathToPreviousUnit = decodeURIComponent(prevUrl); diff --git a/src/course-unit/course-sequence/sequence-navigation/SequenceNavigation.jsx b/src/course-unit/course-sequence/sequence-navigation/SequenceNavigation.jsx index 0fa15fa29..0af7ef63b 100644 --- a/src/course-unit/course-sequence/sequence-navigation/SequenceNavigation.jsx +++ b/src/course-unit/course-sequence/sequence-navigation/SequenceNavigation.jsx @@ -35,7 +35,7 @@ const SequenceNavigation = ({ const shouldDisplayNotificationTriggerInSequence = useWindowSize().width < breakpoints.small.minWidth; const renderUnitButtons = () => { - if (sequence.unitIds?.length === 0 || unitId === null) { + if (sequence?.unitIds?.length === 0 || unitId === null) { return (
); @@ -43,7 +43,7 @@ const SequenceNavigation = ({ return ( state.courseUnit.courseVerti export const getCourseOutlineInfo = (state) => state.courseUnit.courseOutlineInfo; export const getCourseOutlineInfoLoadingStatus = (state) => state.courseUnit.courseOutlineInfoLoadingStatus; export const getMovedXBlockParams = (state) => state.courseUnit.movedXBlockParams; -const getLoadingStatuses = (state) => state.courseUnit.loadingStatus; +export const getLoadingStatuses = (state) => state.courseUnit.loadingStatus; export const getIsLoading = createSelector( [getLoadingStatuses], loadingStatus => Object.values(loadingStatus) diff --git a/src/course-unit/hooks.jsx b/src/course-unit/hooks.jsx index fc8fe092e..7ab5eb591 100644 --- a/src/course-unit/hooks.jsx +++ b/src/course-unit/hooks.jsx @@ -35,6 +35,7 @@ import { getSavingStatus, getSequenceStatus, getStaticFileNotices, + getLoadingStatuses, } from './data/selectors'; import { changeEditTitleFormOpen, @@ -51,6 +52,7 @@ export const useCourseUnit = ({ courseId, blockId }) => { const [isMoveModalOpen, openMoveModal, closeMoveModal] = useToggle(false); const courseUnit = useSelector(getCourseUnitData); + const courseUnitLoadingStatus = useSelector(getLoadingStatuses); const savingStatus = useSelector(getSavingStatus); const isLoading = useSelector(getIsLoading); const errorMessage = useSelector(getErrorMessage); @@ -218,6 +220,7 @@ export const useCourseUnit = ({ courseId, blockId }) => { return { sequenceId, courseUnit, + courseUnitLoadingStatus, unitTitle, unitCategory, errorMessage, diff --git a/src/course-unit/sidebar/utils.js b/src/course-unit/sidebar/utils.js index af3263861..390d9a316 100644 --- a/src/course-unit/sidebar/utils.js +++ b/src/course-unit/sidebar/utils.js @@ -99,4 +99,4 @@ export const getIconVariant = (visibilityState, published, hasChanges) => { * @param {string} id - The course unit ID. * @returns {string} The clear course unit ID extracted from the provided data. */ -export const extractCourseUnitId = (id) => id.match(/block@(.+)$/)[1]; +export const extractCourseUnitId = (id) => id?.match(/block@(.+)$/)[1]; diff --git a/src/course-unit/xblock-container-iframe/index.tsx b/src/course-unit/xblock-container-iframe/index.tsx index 48be568b2..ac6fc9293 100644 --- a/src/course-unit/xblock-container-iframe/index.tsx +++ b/src/course-unit/xblock-container-iframe/index.tsx @@ -37,9 +37,16 @@ import { useIframeContent } from '../../generic/hooks/useIframeContent'; import { useIframeMessages } from '../../generic/hooks/useIframeMessages'; import VideoSelectorPage from '../../editors/VideoSelectorPage'; import EditorPage from '../../editors/EditorPage'; +import { RequestStatus } from '../../data/constants'; const XBlockContainerIframe: FC = ({ - courseId, blockId, unitXBlockActions, courseVerticalChildren, handleConfigureSubmit, isUnitVerticalType, + courseId, + blockId, + unitXBlockActions, + courseVerticalChildren, + handleConfigureSubmit, + isUnitVerticalType, + courseUnitLoadingStatus, }) => { const intl = useIntl(); const dispatch = useDispatch(); @@ -70,6 +77,23 @@ const XBlockContainerIframe: FC = ({ setIframeRef(iframeRef); }, [setIframeRef]); + useEffect(() => { + const iframe = iframeRef?.current; + if (!iframe) { return undefined; } + + const handleIframeLoad = () => { + if (courseUnitLoadingStatus.fetchUnitLoadingStatus === RequestStatus.FAILED) { + window.location.reload(); + } + }; + + iframe.addEventListener('load', handleIframeLoad); + + return () => { + iframe.removeEventListener('load', handleIframeLoad); + }; + }, [iframeRef]); + const onXBlockSave = useCallback(/* istanbul ignore next */ () => { closeXBlockEditorModal(); closeVideoSelectorModal(); diff --git a/src/course-unit/xblock-container-iframe/types.ts b/src/course-unit/xblock-container-iframe/types.ts index 084577d16..e83d5f759 100644 --- a/src/course-unit/xblock-container-iframe/types.ts +++ b/src/course-unit/xblock-container-iframe/types.ts @@ -42,6 +42,11 @@ export interface XBlockContainerIframeProps { courseId: string; blockId: string; isUnitVerticalType: boolean, + courseUnitLoadingStatus: { + fetchUnitLoadingStatus: string; + fetchVerticalChildrenLoadingStatus: string; + fetchXBlockDataLoadingStatus: string; + }; unitXBlockActions: { handleDelete: (XBlockId: string | null) => void; handleDuplicate: (XBlockId: string | null) => void;