fix(challenge): Handle slow network connections gracefully
This commit is contained in:
@ -196,13 +196,12 @@ export default composeReducers(
|
|||||||
app.fetchChallenges.complete,
|
app.fetchChallenges.complete,
|
||||||
map.fetchMapUi.complete
|
map.fetchMapUi.complete
|
||||||
)
|
)
|
||||||
]: (state, { payload }) => {
|
]: (state, { payload: { entities } }) => merge({}, state, entities),
|
||||||
const {entities: { block } } = payload;
|
[app.fetchChallenges.complete]:
|
||||||
return {
|
(state, { payload: { entities: { block }}}) => ({
|
||||||
...merge(state, payload.entities),
|
...state,
|
||||||
fullBlocks: union(state.fullBlocks, [ Object.keys(block)[0] ])
|
fullBlocks: union(state.fullBlocks, [ Object.keys(block)[0] ])
|
||||||
};
|
}),
|
||||||
},
|
|
||||||
[
|
[
|
||||||
challenges.submitChallenge.complete
|
challenges.submitChallenge.complete
|
||||||
]: (state, { payload: { username, points, challengeInfo } }) => ({
|
]: (state, { payload: { username, points, challengeInfo } }) => ({
|
||||||
|
@ -27,20 +27,20 @@ LoaderCircle.displayName = 'LoaderCircle';
|
|||||||
|
|
||||||
const animationProps = [
|
const animationProps = [
|
||||||
{
|
{
|
||||||
delay: '-1.5s',
|
delay: '0.24s',
|
||||||
origin: '1% 1%'
|
origin: '0% 0%'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
delay: '-1s',
|
delay: '0.95s',
|
||||||
origin: '1% 99%'
|
origin: '0% 100%'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
delay: '-0.5s',
|
delay: '0.67s',
|
||||||
origin: '99% 1%'
|
origin: '100% 0%'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
delay: '0s',
|
delay: '1.33s',
|
||||||
origin: '99% 99%'
|
origin: '100% 100%'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -28,13 +28,18 @@ export default `
|
|||||||
transform: scale(0.1);
|
transform: scale(0.1);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
50% {
|
||||||
|
-webkit-transform: scale(0.8);
|
||||||
|
transform: scale(0.8);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
70% {
|
70% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
opacity: 0.0;
|
opacity: 0;
|
||||||
-webkit-transform: scale(1);
|
-webkit-transform: scale(1.2);
|
||||||
transform: scale(1);
|
transform: scale(1.2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,14 +60,15 @@ export default `
|
|||||||
}
|
}
|
||||||
|
|
||||||
.innerCircle {
|
.innerCircle {
|
||||||
-webkit-animation-duration: 2s;
|
-webkit-animation-duration: 2s;
|
||||||
animation-duration: 2s;
|
animation-duration: 2s;
|
||||||
-webkit-animation-iteration-count: infinite;
|
-webkit-animation-iteration-count: infinite;
|
||||||
animation-iteration-count: infinite;
|
animation-iteration-count: infinite;
|
||||||
-webkit-animation-name: overlay-loader;
|
-webkit-animation-name: overlay-loader;
|
||||||
animation-name: overlay-loader;
|
animation-name: overlay-loader;
|
||||||
-webkit-animation-timing-function: ease-out;
|
-webkit-animation-timing-function: ease-out;
|
||||||
animation-timing-function: ease-out;
|
animation-timing-function: ease-out;
|
||||||
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
@ -45,7 +45,7 @@ const mapStateToProps = createSelector(
|
|||||||
paramsSelector,
|
paramsSelector,
|
||||||
fullBlocksSelector,
|
fullBlocksSelector,
|
||||||
(
|
(
|
||||||
{ dashedName, isTranslated },
|
{ dashedName, isTranslated, description },
|
||||||
{ viewType, title },
|
{ viewType, title },
|
||||||
params,
|
params,
|
||||||
blocks
|
blocks
|
||||||
@ -54,6 +54,7 @@ const mapStateToProps = createSelector(
|
|||||||
challenge: dashedName,
|
challenge: dashedName,
|
||||||
isTranslated,
|
isTranslated,
|
||||||
params,
|
params,
|
||||||
|
showLoading: !description || description.length === 0,
|
||||||
title,
|
title,
|
||||||
viewType
|
viewType
|
||||||
})
|
})
|
||||||
@ -71,6 +72,7 @@ const propTypes = {
|
|||||||
dashedName: PropTypes.string,
|
dashedName: PropTypes.string,
|
||||||
lang: PropTypes.string.isRequired
|
lang: PropTypes.string.isRequired
|
||||||
}),
|
}),
|
||||||
|
showLoading: PropTypes.bool,
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
updateSuccessMessage: PropTypes.func.isRequired,
|
updateSuccessMessage: PropTypes.func.isRequired,
|
||||||
updateTitle: PropTypes.func.isRequired,
|
updateTitle: PropTypes.func.isRequired,
|
||||||
@ -113,9 +115,9 @@ export class Show extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { viewType } = this.props;
|
const { viewType, showLoading } = this.props;
|
||||||
const View = views[viewType] || Classic;
|
const View = views[viewType] || Classic;
|
||||||
return <View />;
|
return <View showLoading={ showLoading } />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,13 +30,6 @@ import { descriptionRegex } from './utils';
|
|||||||
import { challengeSelector } from '../../redux';
|
import { challengeSelector } from '../../redux';
|
||||||
import { makeToast } from '../../Toasts/redux';
|
import { makeToast } from '../../Toasts/redux';
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
makeToast,
|
|
||||||
executeChallenge,
|
|
||||||
updateHint,
|
|
||||||
openHelpModal,
|
|
||||||
unlockUntrustedCode
|
|
||||||
};
|
|
||||||
const mapStateToProps = createSelector(
|
const mapStateToProps = createSelector(
|
||||||
challengeSelector,
|
challengeSelector,
|
||||||
challengeMetaSelector,
|
challengeMetaSelector,
|
||||||
@ -62,6 +55,15 @@ const mapStateToProps = createSelector(
|
|||||||
isCodeLocked
|
isCodeLocked
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
makeToast,
|
||||||
|
executeChallenge,
|
||||||
|
updateHint,
|
||||||
|
openHelpModal,
|
||||||
|
unlockUntrustedCode
|
||||||
|
};
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
description: PropTypes.arrayOf(PropTypes.string),
|
description: PropTypes.arrayOf(PropTypes.string),
|
||||||
executeChallenge: PropTypes.func,
|
executeChallenge: PropTypes.func,
|
||||||
@ -95,7 +97,8 @@ export class SidePanel extends PureComponent {
|
|||||||
this.descriptionTop = node;
|
this.descriptionTop = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderDescription(description = [ 'Happy Coding!' ]) {
|
renderDescription() {
|
||||||
|
const { description = [ 'Happy Coding!' ] } = this.props;
|
||||||
return description.map((line, index) => {
|
return description.map((line, index) => {
|
||||||
if (descriptionRegex.test(line)) {
|
if (descriptionRegex.test(line)) {
|
||||||
return (
|
return (
|
||||||
@ -118,7 +121,6 @@ export class SidePanel extends PureComponent {
|
|||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
title,
|
title,
|
||||||
description,
|
|
||||||
tests = [],
|
tests = [],
|
||||||
output,
|
output,
|
||||||
hint,
|
hint,
|
||||||
@ -142,7 +144,7 @@ export class SidePanel extends PureComponent {
|
|||||||
{ title }
|
{ title }
|
||||||
</ChallengeTitle>
|
</ChallengeTitle>
|
||||||
<ChallengeDescription>
|
<ChallengeDescription>
|
||||||
{ this.renderDescription(description) }
|
{ this.renderDescription() }
|
||||||
</ChallengeDescription>
|
</ChallengeDescription>
|
||||||
</div>
|
</div>
|
||||||
<ToolPanel
|
<ToolPanel
|
||||||
|
@ -7,6 +7,7 @@ import { addNS } from 'berkeleys-redux-utils';
|
|||||||
|
|
||||||
import ns from './ns.json';
|
import ns from './ns.json';
|
||||||
import Editor from './Editor.jsx';
|
import Editor from './Editor.jsx';
|
||||||
|
import { OverlayLoader } from '../../../../helperComponents';
|
||||||
import ChildContainer from '../../Child-Container.jsx';
|
import ChildContainer from '../../Child-Container.jsx';
|
||||||
import { showPreviewSelector, types } from '../../redux';
|
import { showPreviewSelector, types } from '../../redux';
|
||||||
import SidePanel from '../../Side-Panel.jsx';
|
import SidePanel from '../../Side-Panel.jsx';
|
||||||
@ -21,7 +22,8 @@ const createModernEditorToggleType = fileKey =>
|
|||||||
const getFirstFileKey = _.flow(_.values, _.first, _.property('key'));
|
const getFirstFileKey = _.flow(_.values, _.first, _.property('key'));
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
nameToFileKey: PropTypes.object
|
nameToFileKey: PropTypes.object,
|
||||||
|
showLoading: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = createSelector(
|
const mapStateToProps = createSelector(
|
||||||
@ -83,9 +85,12 @@ const nameToComponent = {
|
|||||||
Preview: Preview
|
Preview: Preview
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ShowModern({ nameToFileKey }) {
|
export function ShowModern({ nameToFileKey, showLoading }) {
|
||||||
return (
|
return (
|
||||||
<ChildContainer isFullWidth={ true }>
|
<ChildContainer isFullWidth={ true }>
|
||||||
|
{
|
||||||
|
showLoading ? <OverlayLoader /> : null
|
||||||
|
}
|
||||||
<Panes
|
<Panes
|
||||||
render={ name => {
|
render={ name => {
|
||||||
const Comp = nameToComponent[name];
|
const Comp = nameToComponent[name];
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { addNS } from 'berkeleys-redux-utils';
|
import { addNS } from 'berkeleys-redux-utils';
|
||||||
|
|
||||||
|
import { OverlayLoader } from '../../../../helperComponents';
|
||||||
import ChildContainer from '../../Child-Container.jsx';
|
import ChildContainer from '../../Child-Container.jsx';
|
||||||
import BackEnd from './Back-End.jsx';
|
import BackEnd from './Back-End.jsx';
|
||||||
import { types } from '../../redux';
|
import { types } from '../../redux';
|
||||||
import Panes from '../../../../Panes';
|
import Panes from '../../../../Panes';
|
||||||
import _Map from '../../../../Map';
|
import _Map from '../../../../Map';
|
||||||
|
|
||||||
const propTypes = {};
|
const propTypes = {
|
||||||
|
showLoading: PropTypes.bool
|
||||||
|
};
|
||||||
|
|
||||||
export const mapStateToPanes = addNS(
|
export const mapStateToPanes = addNS(
|
||||||
'backend',
|
'backend',
|
||||||
@ -27,9 +31,12 @@ const renderPane = name => {
|
|||||||
return Comp ? <Comp /> : <span>Pane { name } not found</span>;
|
return Comp ? <Comp /> : <span>Pane { name } not found</span>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ShowBackEnd() {
|
export default function ShowBackEnd({ showLoading }) {
|
||||||
return (
|
return (
|
||||||
<ChildContainer isFullWidth={ true }>
|
<ChildContainer isFullWidth={ true }>
|
||||||
|
{
|
||||||
|
showLoading ? <OverlayLoader /> : null
|
||||||
|
}
|
||||||
<Panes render={ renderPane } />
|
<Panes render={ renderPane } />
|
||||||
</ChildContainer>
|
</ChildContainer>
|
||||||
);
|
);
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { addNS } from 'berkeleys-redux-utils';
|
import { addNS } from 'berkeleys-redux-utils';
|
||||||
|
|
||||||
import Editor from './Editor.jsx';
|
import Editor from './Editor.jsx';
|
||||||
import ChildContainer from '../../Child-Container.jsx';
|
import ChildContainer from '../../Child-Container.jsx';
|
||||||
|
import { OverlayLoader } from '../../../../helperComponents';
|
||||||
import { types, showPreviewSelector } from '../../redux';
|
import { types, showPreviewSelector } from '../../redux';
|
||||||
import Preview from '../../Preview.jsx';
|
import Preview from '../../Preview.jsx';
|
||||||
import SidePanel from '../../Side-Panel.jsx';
|
import SidePanel from '../../Side-Panel.jsx';
|
||||||
import Panes from '../../../../Panes';
|
import Panes from '../../../../Panes';
|
||||||
import _Map from '../../../../Map';
|
import _Map from '../../../../Map';
|
||||||
|
|
||||||
const propTypes = {};
|
const propTypes = {
|
||||||
|
showLoading: PropTypes.bool
|
||||||
|
};
|
||||||
|
|
||||||
export const mapStateToPanes = addNS(
|
export const mapStateToPanes = addNS(
|
||||||
'classic',
|
'classic',
|
||||||
@ -39,9 +43,12 @@ const renderPane = name => {
|
|||||||
return Comp ? <Comp /> : <span>Pane for { name } not found</span>;
|
return Comp ? <Comp /> : <span>Pane for { name } not found</span>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ShowClassic() {
|
export default function ShowClassic({ showLoading }) {
|
||||||
return (
|
return (
|
||||||
<ChildContainer isFullWidth={ true }>
|
<ChildContainer isFullWidth={ true }>
|
||||||
|
{
|
||||||
|
showLoading ? <OverlayLoader /> : null
|
||||||
|
}
|
||||||
<Panes render={ renderPane }/>
|
<Panes render={ renderPane }/>
|
||||||
</ChildContainer>
|
</ChildContainer>
|
||||||
);
|
);
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { addNS } from 'berkeleys-redux-utils';
|
import { addNS } from 'berkeleys-redux-utils';
|
||||||
|
|
||||||
import ns from './ns.json';
|
import ns from './ns.json';
|
||||||
import Main from './Project.jsx';
|
import Main from './Project.jsx';
|
||||||
|
import { OverlayLoader } from '../../../../helperComponents';
|
||||||
import ChildContainer from '../../Child-Container.jsx';
|
import ChildContainer from '../../Child-Container.jsx';
|
||||||
import { types } from '../../redux';
|
import { types } from '../../redux';
|
||||||
import Panes from '../../../../Panes';
|
import Panes from '../../../../Panes';
|
||||||
import _Map from '../../../../Map';
|
import _Map from '../../../../Map';
|
||||||
|
|
||||||
const propTypes = {};
|
const propTypes = {
|
||||||
|
showLoading: PropTypes.bool
|
||||||
|
};
|
||||||
export const mapStateToPanes = addNS(
|
export const mapStateToPanes = addNS(
|
||||||
ns,
|
ns,
|
||||||
() => ({
|
() => ({
|
||||||
@ -27,9 +31,12 @@ const renderPane = name => {
|
|||||||
return Comp ? <Comp /> : <span>Pane { name } not found</span>;
|
return Comp ? <Comp /> : <span>Pane { name } not found</span>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ShowProject() {
|
export default function ShowProject({ showLoading }) {
|
||||||
return (
|
return (
|
||||||
<ChildContainer isFullWidth={ true }>
|
<ChildContainer isFullWidth={ true }>
|
||||||
|
{
|
||||||
|
showLoading ? <OverlayLoader /> : null
|
||||||
|
}
|
||||||
<Panes render={ renderPane }/>
|
<Panes render={ renderPane }/>
|
||||||
</ChildContainer>
|
</ChildContainer>
|
||||||
);
|
);
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { addNS } from 'berkeleys-redux-utils';
|
import { addNS } from 'berkeleys-redux-utils';
|
||||||
|
|
||||||
import ns from './ns.json';
|
import ns from './ns.json';
|
||||||
import Main from './Quiz.jsx';
|
import Main from './Quiz.jsx';
|
||||||
|
import { OverlayLoader } from '../../../../helperComponents';
|
||||||
import ChildContainer from '../../Child-Container.jsx';
|
import ChildContainer from '../../Child-Container.jsx';
|
||||||
import { types } from '../../redux';
|
import { types } from '../../redux';
|
||||||
import Panes from '../../../../Panes';
|
import Panes from '../../../../Panes';
|
||||||
import _Map from '../../../../Map';
|
import _Map from '../../../../Map';
|
||||||
|
|
||||||
const propTypes = {};
|
const propTypes = {
|
||||||
|
showLoading: PropTypes.bool
|
||||||
|
};
|
||||||
export const mapStateToPanes = addNS(
|
export const mapStateToPanes = addNS(
|
||||||
ns,
|
ns,
|
||||||
() => ({
|
() => ({
|
||||||
@ -27,9 +31,12 @@ const renderPane = name => {
|
|||||||
return Comp ? <Comp /> : <span>Pane { name } not found</span>;
|
return Comp ? <Comp /> : <span>Pane { name } not found</span>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ShowQuiz() {
|
export default function ShowQuiz({ showLoading }) {
|
||||||
return (
|
return (
|
||||||
<ChildContainer isFullWidth={ true }>
|
<ChildContainer isFullWidth={ true }>
|
||||||
|
{
|
||||||
|
showLoading ? <OverlayLoader /> : null
|
||||||
|
}
|
||||||
<Panes render={ renderPane }/>
|
<Panes render={ renderPane }/>
|
||||||
</ChildContainer>
|
</ChildContainer>
|
||||||
);
|
);
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { addNS } from 'berkeleys-redux-utils';
|
import { addNS } from 'berkeleys-redux-utils';
|
||||||
|
|
||||||
import ns from './ns.json';
|
import ns from './ns.json';
|
||||||
import Step from './Step.jsx';
|
import Step from './Step.jsx';
|
||||||
|
import { OverlayLoader } from '../../../../helperComponents';
|
||||||
import ChildContainer from '../../Child-Container.jsx';
|
import ChildContainer from '../../Child-Container.jsx';
|
||||||
import { types } from '../../redux';
|
import { types } from '../../redux';
|
||||||
import Panes from '../../../../Panes';
|
import Panes from '../../../../Panes';
|
||||||
import _Map from '../../../../Map';
|
import _Map from '../../../../Map';
|
||||||
|
|
||||||
const propTypes = {};
|
const propTypes = {
|
||||||
|
showLoading: PropTypes.bool
|
||||||
|
};
|
||||||
export const mapStateToPanes = addNS(
|
export const mapStateToPanes = addNS(
|
||||||
ns,
|
ns,
|
||||||
() => ({
|
() => ({
|
||||||
@ -27,9 +31,12 @@ const renderPane = name => {
|
|||||||
return Comp ? <Comp /> : <span>Pane { name } not found</span>;
|
return Comp ? <Comp /> : <span>Pane { name } not found</span>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ShowStep() {
|
export default function ShowStep({ showLoading }) {
|
||||||
return (
|
return (
|
||||||
<ChildContainer isFullWidth={ true }>
|
<ChildContainer isFullWidth={ true }>
|
||||||
|
{
|
||||||
|
showLoading ? <OverlayLoader /> : null
|
||||||
|
}
|
||||||
<Panes render={ renderPane }/>
|
<Panes render={ renderPane }/>
|
||||||
</ChildContainer>
|
</ChildContainer>
|
||||||
);
|
);
|
||||||
|
@ -31,7 +31,7 @@ const mapStateToProps = createSelector(
|
|||||||
actionCompletedSelector,
|
actionCompletedSelector,
|
||||||
lightBoxSelector,
|
lightBoxSelector,
|
||||||
(
|
(
|
||||||
{ description = [] },
|
{ description = [['', '', 'Happy Coding!', '']] },
|
||||||
currentIndex,
|
currentIndex,
|
||||||
previousIndex,
|
previousIndex,
|
||||||
isActionCompleted,
|
isActionCompleted,
|
||||||
|
@ -58,7 +58,8 @@ export default function mapUiService(app) {
|
|||||||
block,
|
block,
|
||||||
isLocked,
|
isLocked,
|
||||||
isComingSoon,
|
isComingSoon,
|
||||||
isBeta
|
isBeta,
|
||||||
|
challengeType
|
||||||
} = challenge;
|
} = challenge;
|
||||||
map[dashedName] = {
|
map[dashedName] = {
|
||||||
dashedName,
|
dashedName,
|
||||||
@ -68,7 +69,8 @@ export default function mapUiService(app) {
|
|||||||
block,
|
block,
|
||||||
isLocked,
|
isLocked,
|
||||||
isComingSoon,
|
isComingSoon,
|
||||||
isBeta
|
isBeta,
|
||||||
|
challengeType
|
||||||
};
|
};
|
||||||
return map;
|
return map;
|
||||||
}, {});
|
}, {});
|
||||||
|
Reference in New Issue
Block a user