Show message when there are no units in a sequence. (#60)
TNL-7191 - We didn’t fully protect against sequences with no units. The next/previous buttons now check whether there is a unit ID and construct a URL without if one doesn’t exist. When we load a sequence without units, we now show a message to the user so the page doesn’t look broken.
This commit is contained in:
@@ -55,8 +55,13 @@ function useNextSequenceHandler(courseId, sequenceId) {
|
||||
const sequenceStatus = useSelector(state => state.courseware.sequenceStatus);
|
||||
return useCallback(() => {
|
||||
if (nextSequence !== null) {
|
||||
const nextUnitId = nextSequence.unitIds[0];
|
||||
history.push(`/course/${courseId}/${nextSequence.id}/${nextUnitId}`);
|
||||
if (nextSequence.unitIds.length > 0) {
|
||||
const nextUnitId = nextSequence.unitIds[0];
|
||||
history.push(`/course/${courseId}/${nextSequence.id}/${nextUnitId}`);
|
||||
} else {
|
||||
// Some sequences have no units. This will show a blank page with prev/next buttons.
|
||||
history.push(`/course/${courseId}/${nextSequence.id}`);
|
||||
}
|
||||
}
|
||||
}, [courseStatus, sequenceStatus, sequenceId]);
|
||||
}
|
||||
@@ -67,8 +72,13 @@ function usePreviousSequenceHandler(courseId, sequenceId) {
|
||||
const sequenceStatus = useSelector(state => state.courseware.sequenceStatus);
|
||||
return useCallback(() => {
|
||||
if (previousSequence !== null) {
|
||||
const previousUnitId = previousSequence.unitIds[previousSequence.unitIds.length - 1];
|
||||
history.push(`/course/${courseId}/${previousSequence.id}/${previousUnitId}`);
|
||||
if (previousSequence.unitIds.length > 0) {
|
||||
const previousUnitId = previousSequence.unitIds[previousSequence.unitIds.length - 1];
|
||||
history.push(`/course/${courseId}/${previousSequence.id}/${previousUnitId}`);
|
||||
} else {
|
||||
// Some sequences have no units. This will show a blank page with prev/next buttons.
|
||||
history.push(`/course/${courseId}/${previousSequence.id}`);
|
||||
}
|
||||
}
|
||||
}, [courseStatus, sequenceStatus, sequenceId]);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { connect } from 'react-redux';
|
||||
import { Collapsible } from '@edx/paragon';
|
||||
|
||||
function InstructorToolbar(props) {
|
||||
// TODO: Only render this toolbar if the user is course staff
|
||||
if (!props.activeUnitLmsWebUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
/* eslint-disable no-use-before-define */
|
||||
import React, {
|
||||
useEffect, useContext, Suspense, useState,
|
||||
useEffect, useContext, useState,
|
||||
} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { useSelector } from 'react-redux';
|
||||
import Unit from './Unit';
|
||||
import { SequenceNavigation, UnitNavigation } from './sequence-navigation';
|
||||
|
||||
import PageLoading from '../../../PageLoading';
|
||||
import messages from './messages';
|
||||
import { UserMessagesContext, ALERT_TYPES } from '../../../user-messages';
|
||||
import { useModel } from '../../../model-store';
|
||||
|
||||
const ContentLock = React.lazy(() => import('./content-lock'));
|
||||
import messages from './messages';
|
||||
import { SequenceNavigation, UnitNavigation } from './sequence-navigation';
|
||||
import SequenceContent from './SequenceContent';
|
||||
|
||||
function Sequence({
|
||||
unitId,
|
||||
@@ -132,30 +131,13 @@ function Sequence({
|
||||
}}
|
||||
/>
|
||||
<div className="unit-container flex-grow-1">
|
||||
{gated && (
|
||||
<Suspense
|
||||
fallback={(
|
||||
<PageLoading
|
||||
srMessage={intl.formatMessage(messages['learn.loading.content.lock'])}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
<ContentLock
|
||||
courseId={courseId}
|
||||
sequenceTitle={sequence.title}
|
||||
prereqSectionName={sequence.gatedContent.gatedSectionName}
|
||||
prereqId={sequence.gatedContent.prereqId}
|
||||
/>
|
||||
</Suspense>
|
||||
)}
|
||||
{!gated && unitId !== null && (
|
||||
<Unit
|
||||
courseId={courseId}
|
||||
key={unitId}
|
||||
id={unitId}
|
||||
onLoaded={handleUnitLoaded}
|
||||
/>
|
||||
)}
|
||||
<SequenceContent
|
||||
courseId={courseId}
|
||||
gated={gated}
|
||||
sequenceId={sequenceId}
|
||||
unitId={unitId}
|
||||
unitLoadedHandler={handleUnitLoaded}
|
||||
/>
|
||||
{unitHasLoaded && (
|
||||
<UnitNavigation
|
||||
sequenceId={sequenceId}
|
||||
|
||||
67
src/courseware/course/sequence/SequenceContent.jsx
Normal file
67
src/courseware/course/sequence/SequenceContent.jsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import React, { Suspense } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import PageLoading from '../../../PageLoading';
|
||||
import { useModel } from '../../../model-store';
|
||||
|
||||
import messages from './messages';
|
||||
import Unit from './Unit';
|
||||
|
||||
const ContentLock = React.lazy(() => import('./content-lock'));
|
||||
|
||||
function SequenceContent({
|
||||
gated, intl, courseId, sequenceId, unitId, unitLoadedHandler,
|
||||
}) {
|
||||
const sequence = useModel('sequences', sequenceId);
|
||||
|
||||
if (gated) {
|
||||
return (
|
||||
<Suspense
|
||||
fallback={(
|
||||
<PageLoading
|
||||
srMessage={intl.formatMessage(messages['learn.loading.content.lock'])}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
<ContentLock
|
||||
courseId={courseId}
|
||||
sequenceTitle={sequence.title}
|
||||
prereqSectionName={sequence.gatedContent.gatedSectionName}
|
||||
prereqId={sequence.gatedContent.prereqId}
|
||||
/>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
if (unitId === null) {
|
||||
return (
|
||||
<div>
|
||||
{intl.formatMessage(messages['learn.sequence.no.content'])}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Unit
|
||||
courseId={courseId}
|
||||
key={unitId}
|
||||
id={unitId}
|
||||
onLoaded={unitLoadedHandler}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
SequenceContent.propTypes = {
|
||||
gated: PropTypes.bool.isRequired,
|
||||
courseId: PropTypes.string.isRequired,
|
||||
sequenceId: PropTypes.string.isRequired,
|
||||
unitId: PropTypes.string,
|
||||
unitLoadedHandler: PropTypes.func.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
SequenceContent.defaultProps = {
|
||||
unitId: null,
|
||||
};
|
||||
|
||||
export default injectIntl(SequenceContent);
|
||||
@@ -16,6 +16,11 @@ const messages = defineMessages({
|
||||
defaultMessage: 'There was an error loading this course.',
|
||||
description: 'Message when a course fails to load',
|
||||
},
|
||||
'learn.sequence.no.content': {
|
||||
id: 'learn.sequence.no.content',
|
||||
defaultMessage: 'There is no content here.',
|
||||
description: 'Message shown when there is no content to show a user inside a learning sequence.',
|
||||
},
|
||||
});
|
||||
|
||||
export default messages;
|
||||
|
||||
Reference in New Issue
Block a user