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