Locking the current unit and sub section nav when we’re on gated content.
Will follow up with a “locked content” UI in the next commit.
This commit is contained in:
@@ -6,7 +6,7 @@ import PageLoading from './PageLoading';
|
||||
import messages from './messages';
|
||||
import CourseBreadcrumbs from './CourseBreadcrumbs';
|
||||
import CourseStructureContext from './CourseStructureContext';
|
||||
import { useCourseStructure } from './data/hooks';
|
||||
import { useLoadCourseStructure } from './data/hooks';
|
||||
import SubSection from './sub-section/SubSection';
|
||||
|
||||
function LearningSequencePage({ match, intl }) {
|
||||
@@ -16,7 +16,7 @@ function LearningSequencePage({ match, intl }) {
|
||||
unitId,
|
||||
} = match.params;
|
||||
|
||||
const { blocks, loaded, courseBlockId } = useCourseStructure(courseId);
|
||||
const { blocks, loaded, courseBlockId } = useLoadCourseStructure(courseId);
|
||||
|
||||
return (
|
||||
<main className="container-fluid d-flex flex-column flex-grow-1">
|
||||
|
||||
@@ -18,7 +18,7 @@ export function useBlockAncestry(blockId) {
|
||||
}, [blocks, blockId, loaded]);
|
||||
}
|
||||
|
||||
export function useCourseStructure(courseId) {
|
||||
export function useLoadCourseStructure(courseId) {
|
||||
const { authenticatedUser } = useContext(AppContext);
|
||||
|
||||
const [blocks, setBlocks] = useState(null);
|
||||
|
||||
@@ -4,10 +4,11 @@ import SubSectionNavigation from './SubSectionNavigation';
|
||||
import CourseStructureContext from '../CourseStructureContext';
|
||||
import Unit from './Unit';
|
||||
import {
|
||||
useSubSectionMetadata,
|
||||
useLoadSubSectionMetadata,
|
||||
useExamRedirect,
|
||||
usePersistentUnitPosition
|
||||
usePersistentUnitPosition,
|
||||
} from './data/hooks';
|
||||
import SubSectionMetadataContext from './SubSectionMetadataContext';
|
||||
|
||||
export default function SubSection() {
|
||||
const {
|
||||
@@ -16,16 +17,26 @@ export default function SubSection() {
|
||||
unitId,
|
||||
blocks,
|
||||
} = useContext(CourseStructureContext);
|
||||
const { metadata } = useSubSectionMetadata(courseId, subSectionId);
|
||||
const { metadata } = useLoadSubSectionMetadata(courseId, subSectionId);
|
||||
usePersistentUnitPosition(courseId, subSectionId, unitId, metadata);
|
||||
|
||||
useExamRedirect(metadata, blocks);
|
||||
|
||||
const ready = blocks !== null && metadata !== null;
|
||||
|
||||
return ready && (
|
||||
<section className="d-flex flex-column flex-grow-1">
|
||||
<SubSectionNavigation />
|
||||
<Unit id={unitId} unit={blocks[unitId]} />
|
||||
</section>
|
||||
if (!ready) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isGated = metadata.gatedContent.gated;
|
||||
|
||||
return (
|
||||
<SubSectionMetadataContext.Provider value={metadata}>
|
||||
<section className="d-flex flex-column flex-grow-1">
|
||||
<SubSectionNavigation />
|
||||
{isGated && <div>This is gated content.</div>}
|
||||
{!isGated && <Unit id={unitId} unit={blocks[unitId]} />}
|
||||
</section>
|
||||
</SubSectionMetadataContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
const SubSectionMetadataContext = React.createContext({});
|
||||
|
||||
export default SubSectionMetadataContext;
|
||||
@@ -3,10 +3,11 @@ import PropTypes from 'prop-types';
|
||||
import { history } from '@edx/frontend-platform';
|
||||
import { Button } from '@edx/paragon';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faFilm, faBook, faPencilAlt, faTasks } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faFilm, faBook, faPencilAlt, faTasks, faLock } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import { useCurrentSubSection, usePreviousUnit, useNextUnit, useCurrentSubSectionUnits, useCurrentUnit } from '../data/hooks';
|
||||
import { usePreviousUnit, useNextUnit, useCurrentSubSectionUnits, useCurrentUnit } from '../data/hooks';
|
||||
import CourseStructureContext from '../CourseStructureContext';
|
||||
import SubSectionMetadataContext from './SubSectionMetadataContext';
|
||||
|
||||
function UnitIcon({ type }) {
|
||||
let icon = null;
|
||||
@@ -23,6 +24,9 @@ function UnitIcon({ type }) {
|
||||
case 'problem':
|
||||
icon = faPencilAlt;
|
||||
break;
|
||||
case 'lock':
|
||||
icon = faLock;
|
||||
break;
|
||||
default:
|
||||
icon = faBook;
|
||||
}
|
||||
@@ -32,6 +36,10 @@ function UnitIcon({ type }) {
|
||||
);
|
||||
}
|
||||
|
||||
UnitIcon.propTypes = {
|
||||
type: PropTypes.oneOf(['video', 'other', 'vertical', 'problem', 'lock']).isRequired,
|
||||
};
|
||||
|
||||
export default function SubSectionNavigation() {
|
||||
const { courseId } = useContext(CourseStructureContext);
|
||||
const previousUnit = usePreviousUnit();
|
||||
@@ -76,12 +84,16 @@ export default function SubSectionNavigation() {
|
||||
function UnitNavigation({ clickHandler }) {
|
||||
const units = useCurrentSubSectionUnits();
|
||||
const currentUnit = useCurrentUnit();
|
||||
const metadata = useContext(SubSectionMetadataContext);
|
||||
|
||||
const isGated = metadata.gatedContent.gated;
|
||||
|
||||
return (
|
||||
<div className="btn-group ml-2 mr-2 flex-grow-1 d-flex" role="group">
|
||||
{units.map(unit => (
|
||||
{!isGated && units.map(unit => (
|
||||
<UnitButton key={unit.id} unit={unit} disabled={unit.id === currentUnit.id} clickHandler={clickHandler} />
|
||||
))}
|
||||
{isGated && <UnitButton key={currentUnit.id} unit={currentUnit} disabled locked />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -90,10 +102,14 @@ UnitNavigation.propTypes = {
|
||||
clickHandler: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
function UnitButton({ unit, disabled, clickHandler }) {
|
||||
function UnitButton({
|
||||
unit, disabled, locked, clickHandler,
|
||||
}) {
|
||||
const { id, type } = unit;
|
||||
const handleClick = useCallback(() => {
|
||||
clickHandler(unit);
|
||||
if (clickHandler !== null) {
|
||||
clickHandler(unit);
|
||||
}
|
||||
}, [unit]);
|
||||
|
||||
return (
|
||||
@@ -103,7 +119,7 @@ function UnitButton({ unit, disabled, clickHandler }) {
|
||||
onClick={handleClick}
|
||||
disabled={disabled}
|
||||
>
|
||||
<UnitIcon type={type} />
|
||||
<UnitIcon type={locked ? 'lock' : type} />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -113,6 +129,12 @@ UnitButton.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
type: PropTypes.oneOf(['video', 'other', 'vertical', 'problem']).isRequired,
|
||||
}).isRequired,
|
||||
disabled: PropTypes.bool.isRequired,
|
||||
clickHandler: PropTypes.func.isRequired,
|
||||
disabled: PropTypes.bool.isRequired, // Whether or not the button will function.
|
||||
locked: PropTypes.bool, // Whether the unit is semantically "locked" and unnavigable.
|
||||
clickHandler: PropTypes.func,
|
||||
};
|
||||
|
||||
UnitButton.defaultProps = {
|
||||
clickHandler: null,
|
||||
locked: false,
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import { camelCaseObject } from '@edx/frontend-platform';
|
||||
|
||||
import { getSubSectionMetadata, saveSubSectionPosition } from './api';
|
||||
|
||||
export function useSubSectionMetadata(courseId, subSectionId) {
|
||||
export function useLoadSubSectionMetadata(courseId, subSectionId) {
|
||||
const [metadata, setMetadata] = useState(null);
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user