Absorbing work from douglashall/learning_sequence
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
NODE_ENV='development'
|
||||
PORT=8080
|
||||
PORT=2000
|
||||
ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload'
|
||||
BASE_URL='localhost:8080'
|
||||
BASE_URL='localhost:2000'
|
||||
CREDENTIALS_BASE_URL='http://localhost:18150'
|
||||
CSRF_TOKEN_API_PATH='/csrf/api/v1/token'
|
||||
ECOMMERCE_BASE_URL='http://localhost:18130'
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function ExamplePage() {
|
||||
return (
|
||||
<main>
|
||||
<div className="container-fluid">
|
||||
<h1>Example Page</h1>
|
||||
<p>Hello world!</p>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
describe('example', () => {
|
||||
it('will pass because it is an example', () => {
|
||||
|
||||
});
|
||||
});
|
||||
@@ -1,4 +0,0 @@
|
||||
data folder
|
||||
===========
|
||||
|
||||
This folder is the home for non-component files, such as redux reducers, actions, selectors, API client services, etc. See `Feature-based Application Organization <https://github.com/edx/frontend-template-application/blob/master/docs/decisions/0002-feature-based-application-organization.rst>`_. for more detail.
|
||||
@@ -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 } 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,9 @@ subscribe(APP_READY, () => {
|
||||
ReactDOM.render(
|
||||
<AppProvider>
|
||||
<Header />
|
||||
<ExamplePage />
|
||||
<Switch>
|
||||
<Route path="/course/:courseRunId/:sequenceBlockId" component={LearningSequencePage} />
|
||||
</Switch>
|
||||
<Footer />
|
||||
</AppProvider>,
|
||||
document.getElementById('root'),
|
||||
|
||||
@@ -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";
|
||||
|
||||
119
src/learning-sequence/LearningSequencePage.jsx
Normal file
119
src/learning-sequence/LearningSequencePage.jsx
Normal file
@@ -0,0 +1,119 @@
|
||||
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, {
|
||||
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;
|
||||
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
|
||||
// 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=course-v1%3AedX%2BDemoX%2BDemo_Course&username=staff&depth=all&block_types_filter=sequential&requested_fields=children`, {}),
|
||||
{
|
||||
keepDataIfFailed: false,
|
||||
refreshParams: [
|
||||
props.match.params.courseRunId,
|
||||
props.match.params.sequenceBlockId,
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
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>
|
||||
<p>Hello world!</p>
|
||||
Params: {props.match.params.courseRunId}/{props.match.params.sequenceBlockId}
|
||||
State:
|
||||
{loaded && data.blocks ? (
|
||||
<iframe
|
||||
title="yus"
|
||||
ref={iframeRef}
|
||||
src={Object.values(data.blocks)[0].studentViewUrl}
|
||||
onLoad={handleResizeIframe}
|
||||
height={500}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
LearningSequencePage.propTypes = {
|
||||
match: PropTypes.shape({
|
||||
params: PropTypes.shape({
|
||||
courseRunId: PropTypes.string.isRequired,
|
||||
sequenceBlockId: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
}).isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(LearningSequencePage);
|
||||
37
src/learning-sequence/PageLoading.jsx
Normal file
37
src/learning-sequence/PageLoading.jsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import React, { Component } from 'react';
|
||||
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,
|
||||
};
|
||||
4
src/learning-sequence/index.scss
Normal file
4
src/learning-sequence/index.scss
Normal file
@@ -0,0 +1,4 @@
|
||||
iframe {
|
||||
border: 0;
|
||||
width: 100%;
|
||||
}
|
||||
16
src/learning-sequence/messages.js
Normal file
16
src/learning-sequence/messages.js
Normal file
@@ -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;
|
||||
Reference in New Issue
Block a user