-
Notifications
You must be signed in to change notification settings - Fork 333
Absorbing work from douglashall/learning_sequence #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,12 +4,13 @@ import { APP_INIT_ERROR, APP_READY, subscribe, initialize } from '@edx/frontend- | |
| import { AppProvider, ErrorPage } from '@edx/frontend-platform/react'; | ||
| import React from 'react'; | ||
| import ReactDOM from 'react-dom'; | ||
| import { Route, Switch, Link } from 'react-router-dom'; | ||
|
|
||
| import Header, { messages as headerMessages } from '@edx/frontend-component-header'; | ||
| import Footer, { messages as footerMessages } from '@edx/frontend-component-footer'; | ||
|
|
||
| import appMessages from './i18n'; | ||
| import ExamplePage from './example/ExamplePage'; | ||
| import LearningSequencePage from './learning-sequence/LearningSequencePage'; | ||
|
|
||
| import './index.scss'; | ||
| import './assets/favicon.ico'; | ||
|
|
@@ -18,7 +19,14 @@ subscribe(APP_READY, () => { | |
| ReactDOM.render( | ||
| <AppProvider> | ||
| <Header /> | ||
| <ExamplePage /> | ||
| <Switch> | ||
| <Route | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This route is a temporary helper for developers to help them find a useful URL.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good thinking |
||
| exact | ||
| path="/" | ||
| render={() => <Link to="/course/course-v1%3AedX%2BDemoX%2BDemo_Course/0">Visit Demo Course</Link>} | ||
| /> | ||
| <Route path="/course/:courseId/:blockIndex" component={LearningSequencePage} /> | ||
| </Switch> | ||
| <Footer /> | ||
| </AppProvider>, | ||
| document.getElementById('root'), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| @import '~@edx/paragon/scss/edx/theme.scss'; | ||
|
|
||
| @import './example/index.scss'; | ||
| @import './learning-sequence/index'; | ||
|
|
||
| @import "~@edx/frontend-component-header/dist/index"; | ||
| @import "~@edx/frontend-component-footer/dist/footer"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| import React, { useState, useEffect, useCallback, useRef } from 'react'; | ||
| import PropTypes from 'prop-types'; | ||
| import { camelCaseObject, getConfig } from '@edx/frontend-platform'; | ||
| import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; | ||
| import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; | ||
|
|
||
| import PageLoading from './PageLoading'; | ||
|
|
||
| import messages from './messages'; | ||
|
|
||
| function useApi(apiFunction, { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I intend that this |
||
| format = true, keepDataIfFailed = false, loadedIfFailed = false, refreshParams = [], | ||
| }) { | ||
| const [data, setData] = useState(null); | ||
| const [loading, setLoading] = useState(false); | ||
| const [loaded, setLoaded] = useState(false); | ||
| const [failed, setFailed] = useState(false); | ||
| const [error, setError] = useState(null); | ||
|
|
||
| useEffect(() => { | ||
| setLoading(true); | ||
| apiFunction().then((response) => { | ||
| const result = format ? camelCaseObject(response.data) : response.data; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice |
||
| setData(result); | ||
| setLoaded(true); | ||
| setLoading(false); | ||
| setError(null); | ||
| setFailed(false); | ||
| }) | ||
| .catch((e) => { | ||
| if (keepDataIfFailed) { | ||
| setData(null); | ||
| } | ||
| setFailed(true); | ||
| setLoading(false); | ||
| if (loadedIfFailed) { | ||
| setLoaded(true); | ||
| } | ||
| setError(e); | ||
| }); | ||
| }, refreshParams); | ||
|
|
||
| return { | ||
| data, | ||
| loading, | ||
| loaded, | ||
| failed, | ||
| error, | ||
| }; | ||
| } | ||
|
|
||
| function LearningSequencePage(props) { | ||
| const iframeRef = useRef(null); | ||
|
|
||
| const handleResizeIframe = useCallback(() => { | ||
| // TODO: This won't work because of crossdomain issues. Leaving here for reference once we're | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note this TODO. |
||
| // able to have the iFrame content publish resize events through postMessage | ||
| console.log('**** Resizing iframe...'); | ||
| const iframe = iframeRef.current; | ||
| const contentHeight = iframe.contentWindow.document.body.scrollHeight; | ||
| console.log(`**** Height is: ${contentHeight}`); | ||
| iframe.height = contentHeight + 20; | ||
| }); | ||
|
|
||
| const { | ||
| data, | ||
| loading, | ||
| loaded, | ||
| } = useApi( | ||
| async () => getAuthenticatedHttpClient().get(`${getConfig().LMS_BASE_URL}/api/courses/v1/blocks/?course_id=${props.match.params.courseId}&username=staff&depth=all&block_types_filter=sequential&requested_fields=children`, {}), | ||
| { | ||
| keepDataIfFailed: false, | ||
| refreshParams: [ | ||
| props.match.params.courseId, | ||
| props.match.params.blockIndex, | ||
| ], | ||
| }, | ||
| ); | ||
|
|
||
| console.log(data); | ||
|
|
||
| if (loading) { | ||
| return ( | ||
| <PageLoading srMessage={props.intl.formatMessage(messages['learn.loading.learning.sequence'])} /> | ||
| ); | ||
| } | ||
|
|
||
| return ( | ||
| <main> | ||
| <div className="container-fluid"> | ||
| <h1>Learning Sequence Page</h1> | ||
| {loaded && data.blocks ? ( | ||
| <iframe | ||
| title="yus" | ||
| ref={iframeRef} | ||
| src={Object.values(data.blocks)[parseInt(props.match.params.blockIndex, 10)].studentViewUrl} | ||
| onLoad={handleResizeIframe} | ||
| height={500} | ||
| /> | ||
| ) : null} | ||
| </div> | ||
| </main> | ||
| ); | ||
| } | ||
|
|
||
| LearningSequencePage.propTypes = { | ||
| match: PropTypes.shape({ | ||
| params: PropTypes.shape({ | ||
| courseId: PropTypes.string.isRequired, | ||
| blockIndex: PropTypes.number.isRequired, | ||
| }).isRequired, | ||
| }).isRequired, | ||
| intl: intlShape.isRequired, | ||
| }; | ||
|
|
||
| export default injectIntl(LearningSequencePage); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| import React, { Component } from 'react'; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just snagged this component from a different MFE. We should have one of these in Paragon. |
||
| import PropTypes from 'prop-types'; | ||
|
|
||
| export default class PageLoading extends Component { | ||
| renderSrMessage() { | ||
| if (!this.props.srMessage) { | ||
| return null; | ||
| } | ||
|
|
||
| return ( | ||
| <span className="sr-only"> | ||
| {this.props.srMessage} | ||
| </span> | ||
| ); | ||
| } | ||
|
|
||
| render() { | ||
| return ( | ||
| <div> | ||
| <div | ||
| className="d-flex justify-content-center align-items-center flex-column" | ||
| style={{ | ||
| height: '50vh', | ||
| }} | ||
| > | ||
| <div className="spinner-border text-primary" role="status"> | ||
| {this.renderSrMessage()} | ||
| </div> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| PageLoading.propTypes = { | ||
| srMessage: PropTypes.string.isRequired, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| iframe { | ||
| border: 0; | ||
| width: 100%; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import { defineMessages } from '@edx/frontend-platform/i18n'; | ||
|
|
||
| const messages = defineMessages({ | ||
| 'learn.loading.learning.sequence': { | ||
| id: 'learn.loading.learning.sequence', | ||
| defaultMessage: 'Loading learning sequence...', | ||
| description: 'Message when learning sequence is being loaded', | ||
| }, | ||
| 'learn.loading.error': { | ||
| id: 'learn.loading.error', | ||
| defaultMessage: 'Error: {error}', | ||
| description: 'Message when learning sequence fails to load', | ||
| }, | ||
| }); | ||
|
|
||
| export default messages; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean, why not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎈