From 93a569f9ec48dcf713a5db71140479f8aeef7fdb Mon Sep 17 00:00:00 2001 From: Adam Butterworth Date: Tue, 14 Jan 2020 15:32:58 -0500 Subject: [PATCH] fix: initial sequence component set --- .../LearningSequencePage.jsx | 159 +++++++++++++++++- .../sequence/CompleteIcon.jsx | 7 + src/learning-sequence/sequence/Sequence.jsx | 139 +++++++++++++++ .../sequence/SequenceNavigation.jsx | 35 ++++ src/learning-sequence/sequence/Unit.jsx | 22 +++ src/learning-sequence/sequence/UnitButton.jsx | 29 ++++ src/learning-sequence/sequence/UnitIcon.jsx | 35 ++++ src/learning-sequence/sequence/api.js | 50 ++++++ .../sequence/content-lock/ContentLock.jsx | 37 ++++ .../sequence/content-lock/index.js | 1 + .../sequence/content-lock/messages.js | 21 +++ src/learning-sequence/sequence/messages.js | 11 ++ 12 files changed, 545 insertions(+), 1 deletion(-) create mode 100644 src/learning-sequence/sequence/CompleteIcon.jsx create mode 100644 src/learning-sequence/sequence/Sequence.jsx create mode 100644 src/learning-sequence/sequence/SequenceNavigation.jsx create mode 100644 src/learning-sequence/sequence/Unit.jsx create mode 100644 src/learning-sequence/sequence/UnitButton.jsx create mode 100644 src/learning-sequence/sequence/UnitIcon.jsx create mode 100644 src/learning-sequence/sequence/api.js create mode 100644 src/learning-sequence/sequence/content-lock/ContentLock.jsx create mode 100644 src/learning-sequence/sequence/content-lock/index.js create mode 100644 src/learning-sequence/sequence/content-lock/messages.js create mode 100644 src/learning-sequence/sequence/messages.js diff --git a/src/learning-sequence/LearningSequencePage.jsx b/src/learning-sequence/LearningSequencePage.jsx index de5e84ee..445f107d 100644 --- a/src/learning-sequence/LearningSequencePage.jsx +++ b/src/learning-sequence/LearningSequencePage.jsx @@ -9,6 +9,7 @@ import CourseStructureContext from './CourseStructureContext'; import { useLoadCourseStructure, useMissingSubSectionRedirect } from './data/hooks'; import SubSection from './sub-section/SubSection'; import { history } from '@edx/frontend-platform'; +import Sequence from './sequence/Sequence'; function LearningSequencePage({ match, intl }) { const { @@ -37,7 +38,163 @@ function LearningSequencePage({ match, intl }) { />} {loaded && unitId && } - {subSectionId && } + {subSectionId && ( + Lesson 1 - Getting Started > Getting Started", + "bookmarked": false, + "graded": false, + "page_title": "Getting Started", + "href": "", + "complete": true, + "content": "", + "id": "block-v1:edX+DemoX+Demo_Course+type@vertical+block@867dddb6f55d410caaa9c1eb9c6743ec" + }, + "block-v1:edX+DemoX+Demo_Course+type@vertical+block@3e3f9b5199ba4e96b2fc6539087cfe2c": { + "type": "other", + "path": "Example Week 1: Getting Started > Lesson 1 - Getting Started > MY unit", + "bookmarked": false, + "graded": false, + "page_title": "MY unit", + "href": "", + "complete": false, + "content": "", + "id": "block-v1:edX+DemoX+Demo_Course+type@vertical+block@3e3f9b5199ba4e96b2fc6539087cfe2c" + }, + "block-v1:edX+DemoX+Demo_Course+type@vertical+block@4f6c1b4e316a419ab5b6bf30e6c708e9": { + "type": "video", + "path": "Example Week 1: Getting Started > Lesson 1 - Getting Started > Working with Videos", + "bookmarked": false, + "graded": false, + "page_title": "Working with Videos", + "href": "", + "complete": false, + "content": "", + "id": "block-v1:edX+DemoX+Demo_Course+type@vertical+block@4f6c1b4e316a419ab5b6bf30e6c708e9" + }, + "block-v1:edX+DemoX+Demo_Course+type@vertical+block@3dc16db8d14842e38324e95d4030b8a0": { + "type": "video", + "path": "Example Week 1: Getting Started > Lesson 1 - Getting Started > Videos on edX", + "bookmarked": false, + "graded": false, + "page_title": "Videos on edX", + "href": "", + "complete": false, + "content": "", + "id": "block-v1:edX+DemoX+Demo_Course+type@vertical+block@3dc16db8d14842e38324e95d4030b8a0" + }, + "block-v1:edX+DemoX+Demo_Course+type@vertical+block@4a1bba2a403f40bca5ec245e945b0d76": { + "type": "other", + "path": "Example Week 1: Getting Started > Lesson 1 - Getting Started > Video Demonstrations", + "bookmarked": false, + "graded": false, + "page_title": "Video Demonstrations", + "href": "", + "complete": false, + "content": "", + "id": "block-v1:edX+DemoX+Demo_Course+type@vertical+block@4a1bba2a403f40bca5ec245e945b0d76" + }, + "block-v1:edX+DemoX+Demo_Course+type@vertical+block@f0e6d90842c44cc7a50fd1a18a7dd982": { + "type": "video", + "path": "Example Week 1: Getting Started > Lesson 1 - Getting Started > Video Demonstrations", + "bookmarked": false, + "graded": false, + "page_title": "Video Demonstrations", + "href": "", + "complete": false, + "content": "", + "id": "block-v1:edX+DemoX+Demo_Course+type@vertical+block@f0e6d90842c44cc7a50fd1a18a7dd982" + }, + "block-v1:edX+DemoX+Demo_Course+type@vertical+block@256f17a44983429fb1a60802203ee4e0": { + "type": "video", + "path": "Example Week 1: Getting Started > Lesson 1 - Getting Started > Video Presentation Styles", + "bookmarked": false, + "graded": false, + "page_title": "Video Presentation Styles", + "href": "", + "complete": false, + "content": "", + "id": "block-v1:edX+DemoX+Demo_Course+type@vertical+block@256f17a44983429fb1a60802203ee4e0" + }, + "block-v1:edX+DemoX+Demo_Course+type@vertical+block@e3601c0abee6427d8c17e6d6f8fdddd1": { + "type": "problem", + "path": "Example Week 1: Getting Started > Lesson 1 - Getting Started > Interactive Questions", + "bookmarked": false, + "graded": false, + "page_title": "Interactive Questions", + "href": "", + "complete": false, + "content": "", + "id": "block-v1:edX+DemoX+Demo_Course+type@vertical+block@e3601c0abee6427d8c17e6d6f8fdddd1" + }, + "block-v1:edX+DemoX+Demo_Course+type@vertical+block@a79d59cd72034188a71d388f4954a606": { + "type": "other", + "path": "Example Week 1: Getting Started > Lesson 1 - Getting Started > Exciting Labs and Tools", + "bookmarked": false, + "graded": false, + "page_title": "Exciting Labs and Tools", + "href": "", + "complete": false, + "content": "", + "id": "block-v1:edX+DemoX+Demo_Course+type@vertical+block@a79d59cd72034188a71d388f4954a606" + }, + "block-v1:edX+DemoX+Demo_Course+type@vertical+block@134df56c516a4a0dbb24dd5facef746e": { + "type": "problem", + "path": "Example Week 1: Getting Started > Lesson 1 - Getting Started > Reading Assignments", + "bookmarked": false, + "graded": false, + "page_title": "Reading Assignments", + "href": "", + "complete": false, + "content": "", + "id": "block-v1:edX+DemoX+Demo_Course+type@vertical+block@134df56c516a4a0dbb24dd5facef746e" + }, + "block-v1:edX+DemoX+Demo_Course+type@vertical+block@d91b9e5d8bc64d57a1332d06bf2f2193": { + "type": "other", + "path": "Example Week 1: Getting Started > Lesson 1 - Getting Started > When Are Your Exams? ", + "bookmarked": false, + "graded": false, + "page_title": "When Are Your Exams? ", + "href": "", + "complete": false, + "content": "", + "id": "block-v1:edX+DemoX+Demo_Course+type@vertical+block@d91b9e5d8bc64d57a1332d06bf2f2193" + }, + }} + displayName="Sequence Name" + activeUnitId="block-v1:edX+DemoX+Demo_Course+type@vertical+block@867dddb6f55d410caaa9c1eb9c6743ec" + showCompletion={true} + isTimeLimited={false} + bannerText={null} + onNext={() => {}} + onPrevious={() => {}} + onNavigateUnit={() => {}} + isGated={false} + prerequisite={{ + name: 'Prerequisite name', + url: 'url? or id', + id: 'asdasd', + }} + savePosition={true} + /> + )} diff --git a/src/learning-sequence/sequence/CompleteIcon.jsx b/src/learning-sequence/sequence/CompleteIcon.jsx new file mode 100644 index 00000000..6bc7a0ef --- /dev/null +++ b/src/learning-sequence/sequence/CompleteIcon.jsx @@ -0,0 +1,7 @@ +import React from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faCheckCircle } from '@fortawesome/free-solid-svg-icons'; + +export default function CompleteIcon(props) { + return +} diff --git a/src/learning-sequence/sequence/Sequence.jsx b/src/learning-sequence/sequence/Sequence.jsx new file mode 100644 index 00000000..7e9075e4 --- /dev/null +++ b/src/learning-sequence/sequence/Sequence.jsx @@ -0,0 +1,139 @@ +import React, { useState, useEffect, Suspense } from 'react'; +import PropTypes from 'prop-types'; +import Unit from './Unit'; +import SequenceNavigation from './SequenceNavigation'; +import PageLoading from '../PageLoading'; +import { getBlockCompletion, saveSequencePosition } from './api'; +const ContentLock = React.lazy(() => import('./content-lock')); +import messages from './messages'; +import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; + +function Sequence({ + courseId, + id, + unitIds, + units: initialUnits, + displayName, + showCompletion, + isTimeLimited, + bannerText, + onNext, + onPrevious, + onNavigateUnit, + isGated, + prerequisite, + savePosition, + activeUnitId: initialActiveUnitId, + intl, +}) { + const [units, setUnits] = useState(initialUnits); + const [activeUnitId, setActiveUnitId] = useState(initialActiveUnitId); + + const activeUnitIndex = unitIds.indexOf(activeUnitId); + const activeUnit = units[activeUnitId]; + const unitsArr = unitIds.map((unitId) => ({ + ...units[unitId], + id: unitId, + isActive: unitId === activeUnitId, + })); + + const updateUnitCompletion = (unitId) => { + // If the unit is already complete, don't check. + if (units[unitId].complete) { + return; + } + + getBlockCompletion(courseId, id, unitId).then((isComplete) => { + if (isComplete) { + setUnits({ + ...units, + [unitId]: { ...units[unitId], complete: isComplete }, + }); + } + }) + }; + + const handleNext = () => { + if (activeUnitIndex < unitIds.length - 1) { + handleNavigate(activeUnitIndex + 1); + } else { + onNext(); + } + }; + + const handlePrevious = () => { + if (activeUnitIndex > 0) { + handleNavigate(activeUnitIndex - 1); + } else { + onPrevious(); + } + }; + + const handleNavigate = (unitIndex) => { + const newUnitId = unitIds[unitIndex]; + if (showCompletion) { + updateUnitCompletion(activeUnitId); + } + setActiveUnitId(newUnitId); + onNavigateUnit(newUnitId, units[newUnitId]); + }; + + useEffect(() => { + if (savePosition) { + saveSequencePosition(courseId, id, activeUnitIndex); + } + }, [activeUnitId]); + + return ( +
+ + {isGated? ( + }> + + + ): ( + + )} +
+ ); +} + +Sequence.propTypes = { + id: PropTypes.string.isRequired, + courseId: PropTypes.string.isRequired, + unitIds: PropTypes.arrayOf(PropTypes.string).isRequired, + units: PropTypes.objectOf(PropTypes.shape({ + })), + displayName: PropTypes.string.isRequired, + activeUnitId: PropTypes.string.isRequired, + showCompletion: PropTypes.bool.isRequired, + isTimeLimited: PropTypes.bool.isRequired, + bannerText: PropTypes.string, + onNext: PropTypes.func.isRequired, + onPrevious: PropTypes.func.isRequired, + onNavigateUnit: PropTypes.func.isRequired, + isGated: PropTypes.bool.isRequired, + prerequisite: PropTypes.shape({ + name: PropTypes.string.isRequired, + url: PropTypes.string.isRequired, + id: PropTypes.string.isRequired, + }), + savePosition: PropTypes.bool.isRequired, + intl: intlShape.isRequired, +}; + +export default injectIntl(Sequence); diff --git a/src/learning-sequence/sequence/SequenceNavigation.jsx b/src/learning-sequence/sequence/SequenceNavigation.jsx new file mode 100644 index 00000000..fab7c5f5 --- /dev/null +++ b/src/learning-sequence/sequence/SequenceNavigation.jsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { Button } from '@edx/paragon'; +import UnitButton from './UnitButton'; + +export default function SequenceNavigation({ + onNext, + onPrevious, + onNavigate, + units, + isLocked, + showCompletion, +}) { + const unitButtons = units.map((unit, index) => ( + + )); + + return ( + + ); +} diff --git a/src/learning-sequence/sequence/Unit.jsx b/src/learning-sequence/sequence/Unit.jsx new file mode 100644 index 00000000..207e87ae --- /dev/null +++ b/src/learning-sequence/sequence/Unit.jsx @@ -0,0 +1,22 @@ +import React, { useRef } from 'react'; +import PropTypes from 'prop-types'; +import { getConfig } from '@edx/frontend-platform'; + +export default function Unit({ id, title }) { + const iframeRef = useRef(null); + const iframeUrl = `${getConfig().LMS_BASE_URL}/xblock/${id}`; + + return ( +