Fix next challenge loading logic
This commit is contained in:
@ -53,8 +53,8 @@ function cacheScript({ src } = {}) {
|
|||||||
|
|
||||||
const frameRunner$ = cacheScript({ src: '/js/frame-runner.js' });
|
const frameRunner$ = cacheScript({ src: '/js/frame-runner.js' });
|
||||||
|
|
||||||
const htmlCatch = '\n<!-- -->';
|
const htmlCatch = '\n<!--fcc-->';
|
||||||
const jsCatch = '\n;/* */';
|
const jsCatch = '\n;/*fcc*/';
|
||||||
|
|
||||||
export default function executeChallengeSaga(action$, getState) {
|
export default function executeChallengeSaga(action$, getState) {
|
||||||
return action$
|
return action$
|
||||||
|
@ -1,16 +1,8 @@
|
|||||||
import React from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import PureComponent from 'react-pure-render/component';
|
import PureComponent from 'react-pure-render/component';
|
||||||
import NoSSR from 'react-no-ssr';
|
import NoSSR from 'react-no-ssr';
|
||||||
import Codemirror from 'react-codemirror';
|
import Codemirror from 'react-codemirror';
|
||||||
|
|
||||||
const defaultOutput = `/**
|
|
||||||
* Your output will go here.
|
|
||||||
* Any console.log() -type
|
|
||||||
* statements will appear in
|
|
||||||
* your browser\'s DevTools
|
|
||||||
* JavaScript console.
|
|
||||||
*/`;
|
|
||||||
|
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
lineNumbers: false,
|
lineNumbers: false,
|
||||||
mode: 'javascript',
|
mode: 'javascript',
|
||||||
@ -21,11 +13,9 @@ const defaultOptions = {
|
|||||||
|
|
||||||
export default class extends PureComponent {
|
export default class extends PureComponent {
|
||||||
static displayName = 'Output';
|
static displayName = 'Output';
|
||||||
|
static propTypes = {
|
||||||
static defaultProps = {
|
output: PropTypes.string
|
||||||
output: defaultOutput
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { output } = this.props;
|
const { output } = this.props;
|
||||||
return (
|
return (
|
||||||
@ -33,7 +23,8 @@ export default class extends PureComponent {
|
|||||||
<NoSSR>
|
<NoSSR>
|
||||||
<Codemirror
|
<Codemirror
|
||||||
options={ defaultOptions }
|
options={ defaultOptions }
|
||||||
value={ output } />
|
value={ output }
|
||||||
|
/>
|
||||||
</NoSSR>
|
</NoSSR>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -22,29 +22,34 @@ export class ToolPanel extends PureComponent {
|
|||||||
block={ true }
|
block={ true }
|
||||||
bsStyle='primary'
|
bsStyle='primary'
|
||||||
className='btn-big'
|
className='btn-big'
|
||||||
onClick={ executeChallenge }>
|
onClick={ executeChallenge }
|
||||||
|
>
|
||||||
Run tests (ctrl + enter)
|
Run tests (ctrl + enter)
|
||||||
</Button>
|
</Button>
|
||||||
<div className='button-spacer' />
|
<div className='button-spacer' />
|
||||||
<ButtonGroup
|
<ButtonGroup
|
||||||
className='input-group'
|
className='input-group'
|
||||||
justified={ true }>
|
justified={ true }
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
bsSize='large'
|
bsSize='large'
|
||||||
bsStyle='primary'
|
bsStyle='primary'
|
||||||
componentClass='label'>
|
componentClass='label'
|
||||||
|
>
|
||||||
Reset
|
Reset
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
bsSize='large'
|
bsSize='large'
|
||||||
bsStyle='primary'
|
bsStyle='primary'
|
||||||
componentClass='label'>
|
componentClass='label'
|
||||||
|
>
|
||||||
Help
|
Help
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
bsSize='large'
|
bsSize='large'
|
||||||
bsStyle='primary'
|
bsStyle='primary'
|
||||||
componentClass='label'>
|
componentClass='label'
|
||||||
|
>
|
||||||
Bug
|
Bug
|
||||||
</Button>
|
</Button>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
|
@ -1,24 +1,68 @@
|
|||||||
import { Observable } from 'rx';
|
import { Observable } from 'rx';
|
||||||
import { push } from 'react-router-redux';
|
import { push } from 'react-router-redux';
|
||||||
import { moveToNextChallenge } from './types';
|
import { moveToNextChallenge } from './types';
|
||||||
import { getNextChallenge } from '../utils';
|
|
||||||
import { resetUi, updateCurrentChallenge } from './actions';
|
import { resetUi, updateCurrentChallenge } from './actions';
|
||||||
// import { createErrorObservable, makeToast } from '../../../redux/actions';
|
import { createErrorObservable, makeToast } from '../../../redux/actions';
|
||||||
|
import {
|
||||||
|
getNextChallenge,
|
||||||
|
getFirstChallengeOfNextBlock,
|
||||||
|
getFirstChallengeOfNextSuperBlock
|
||||||
|
} from '../utils';
|
||||||
|
import { getRandomVerb } from '../../../utils/get-words';
|
||||||
|
|
||||||
export default function nextChallengeSaga(actions$, getState) {
|
export default function nextChallengeSaga(actions$, getState) {
|
||||||
return actions$
|
return actions$
|
||||||
.filter(({ type }) => type === moveToNextChallenge)
|
.filter(({ type }) => type === moveToNextChallenge)
|
||||||
.flatMap(() => {
|
.flatMap(() => {
|
||||||
const state = getState();
|
let nextChallenge;
|
||||||
const nextChallenge = getNextChallenge(
|
// let message = '';
|
||||||
state.challengesApp.challenge,
|
// let isNewBlock = false;
|
||||||
state.entities,
|
// let isNewSuperBlock = false;
|
||||||
state.challengesApp.superBlocks
|
try {
|
||||||
);
|
const state = getState();
|
||||||
return Observable.of(
|
const { challenge, superBlocks } = state.challengesApp;
|
||||||
updateCurrentChallenge(nextChallenge),
|
const { entities } = state;
|
||||||
resetUi(),
|
nextChallenge = getNextChallenge(challenge, entities);
|
||||||
push(`/challenges/${nextChallenge.block}/${nextChallenge.dashedName}`)
|
// block completed.
|
||||||
);
|
if (!nextChallenge) {
|
||||||
|
// isNewBlock = true;
|
||||||
|
nextChallenge = getFirstChallengeOfNextBlock(challenge, entities);
|
||||||
|
}
|
||||||
|
// superBlock completed
|
||||||
|
if (!nextChallenge) {
|
||||||
|
// isNewSuperBlock = true;
|
||||||
|
nextChallenge = getFirstChallengeOfNextSuperBlock(
|
||||||
|
challenge,
|
||||||
|
entities,
|
||||||
|
superBlocks
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/* this requires user data not available yet
|
||||||
|
if (isNewSuperBlock || isNewBlock) {
|
||||||
|
const getName = isNewSuperBlock ?
|
||||||
|
getCurrentSuperBlockName :
|
||||||
|
getCurrentBlockName;
|
||||||
|
const blockType = isNewSuperBlock ? 'SuperBlock' : 'Block';
|
||||||
|
message =
|
||||||
|
`You've competed the ${getName(challenge, entities)} ${blockType}!`;
|
||||||
|
}
|
||||||
|
message += ' Your next challenge has arrived.';
|
||||||
|
const toast = {
|
||||||
|
// title: isNewSuperBlock || isNewBlock ? getRandomVerb() : null,
|
||||||
|
message
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
return Observable.of(
|
||||||
|
updateCurrentChallenge(nextChallenge),
|
||||||
|
resetUi(),
|
||||||
|
makeToast({
|
||||||
|
title: getRandomVerb(),
|
||||||
|
message: 'Your next challenge has arrived.'
|
||||||
|
}),
|
||||||
|
push(`/challenges/${nextChallenge.block}/${nextChallenge.dashedName}`)
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
return createErrorObservable(err);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,12 @@ const initialUiState = {
|
|||||||
currentIndex: 0,
|
currentIndex: 0,
|
||||||
previousIndex: -1,
|
previousIndex: -1,
|
||||||
isActionCompleted: false,
|
isActionCompleted: false,
|
||||||
isSubmitting: true
|
isSubmitting: true,
|
||||||
|
output: `/**
|
||||||
|
* Any console.log()
|
||||||
|
* statements will appear in
|
||||||
|
* here console.
|
||||||
|
*/`
|
||||||
};
|
};
|
||||||
const initialState = {
|
const initialState = {
|
||||||
id: '',
|
id: '',
|
||||||
|
@ -91,20 +91,80 @@ export function getFirstChallenge(
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNextChallenge(
|
export function getNextChallenge(current, entites) {
|
||||||
current,
|
|
||||||
entites,
|
|
||||||
superBlocks
|
|
||||||
) {
|
|
||||||
const { challenge: challengeMap, block: blockMap } = entites;
|
const { challenge: challengeMap, block: blockMap } = entites;
|
||||||
// find current challenge
|
// find current challenge
|
||||||
// find current block
|
// find current block
|
||||||
// find next challenge in block
|
// find next challenge in block
|
||||||
const currentChallenge = challengeMap[current];
|
const currentChallenge = challengeMap[current];
|
||||||
if (currentChallenge) {
|
if (!currentChallenge) {
|
||||||
const block = blockMap[currentChallenge.block];
|
return null;
|
||||||
const index = block.challenges.indexOf(currentChallenge.dashedName);
|
|
||||||
return challengeMap[block.challenges[index + 1]];
|
|
||||||
}
|
}
|
||||||
return getFirstChallenge(entites, superBlocks);
|
const block = blockMap[currentChallenge.block];
|
||||||
|
const index = block.challenges.indexOf(currentChallenge.dashedName);
|
||||||
|
return challengeMap[block.challenges[index + 1]];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFirstChallengeOfNextBlock(current, entites) {
|
||||||
|
const {
|
||||||
|
challenge: challengeMap,
|
||||||
|
block: blockMap,
|
||||||
|
superBlock: SuperBlockMap
|
||||||
|
} = entites;
|
||||||
|
const currentChallenge = challengeMap[current];
|
||||||
|
if (!currentChallenge) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const block = blockMap[currentChallenge.block];
|
||||||
|
if (!block) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const superBlock = SuperBlockMap[block.superBlock];
|
||||||
|
const index = superBlock.blocks.indexOf(block.dashedName);
|
||||||
|
const newBlock = superBlock.blocks[ index + 1 ];
|
||||||
|
if (!newBlock) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return challengeMap[newBlock.challenges[0]];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFirstChallengeOfNextSuperBlock(
|
||||||
|
current,
|
||||||
|
entites,
|
||||||
|
superBlocks
|
||||||
|
) {
|
||||||
|
const {
|
||||||
|
challenge: challengeMap,
|
||||||
|
block: blockMap,
|
||||||
|
superBlock: SuperBlockMap
|
||||||
|
} = entites;
|
||||||
|
const currentChallenge = challengeMap[current];
|
||||||
|
if (!currentChallenge) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const block = blockMap[currentChallenge.block];
|
||||||
|
if (!block) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const superBlock = SuperBlockMap[block.superBlock];
|
||||||
|
const index = superBlocks.indexOf(superBlock.dashedName);
|
||||||
|
const newSuperBlock = SuperBlockMap[superBlocks[ index + 1]];
|
||||||
|
if (!newSuperBlock) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const newBlock = blockMap[newSuperBlock.blocks[0]];
|
||||||
|
return challengeMap[newBlock.challenges[0]];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCurrentBlockName(current, entities) {
|
||||||
|
const { challenge: challengeMap } = entities;
|
||||||
|
const challenge = challengeMap[current];
|
||||||
|
return challenge.block;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCurrentSuperBlockName(current, entities) {
|
||||||
|
const { challenge: challengeMap, block: blockMap } = entities;
|
||||||
|
const challenge = challengeMap[current];
|
||||||
|
const block = blockMap[challenge.block];
|
||||||
|
return block.superBlock;
|
||||||
}
|
}
|
||||||
|
@ -265,7 +265,7 @@
|
|||||||
"assert($(\"h1\").length > 0, 'message: Make your <code>h1</code> element visible on your page by uncommenting it.');",
|
"assert($(\"h1\").length > 0, 'message: Make your <code>h1</code> element visible on your page by uncommenting it.');",
|
||||||
"assert($(\"h2\").length > 0, 'message: Make your <code>h2</code> element visible on your page by uncommenting it.');",
|
"assert($(\"h2\").length > 0, 'message: Make your <code>h2</code> element visible on your page by uncommenting it.');",
|
||||||
"assert($(\"p\").length > 0, 'message: Make your <code>p</code> element visible on your page by uncommenting it.');",
|
"assert($(\"p\").length > 0, 'message: Make your <code>p</code> element visible on your page by uncommenting it.');",
|
||||||
"assert(!/-->/gi.test(code.replace(/ *<!--.*\\n/g,'')), 'message: Be sure to delete all trailing comment tags, i.e. <code>--></code>.');"
|
"assert(!/[^fc]-->/gi.test(code.replace(/ *<!--[^fc]*\\n/g,'')), 'message: Be sure to delete all trailing comment tags, i.e. <code>--></code>.');"
|
||||||
],
|
],
|
||||||
"type": "waypoint",
|
"type": "waypoint",
|
||||||
"titleEs": "Quita comentarios HTML",
|
"titleEs": "Quita comentarios HTML",
|
||||||
@ -322,7 +322,7 @@
|
|||||||
"assert(($(\"h1\").length === 0), 'message: Comment out your <code>h1</code> element so that it is not visible on your page.');",
|
"assert(($(\"h1\").length === 0), 'message: Comment out your <code>h1</code> element so that it is not visible on your page.');",
|
||||||
"assert(($(\"h2\").length > 0), 'message: Leave your <code>h2</code> element uncommented so that it is visible on your page.');",
|
"assert(($(\"h2\").length > 0), 'message: Leave your <code>h2</code> element uncommented so that it is visible on your page.');",
|
||||||
"assert(($(\"p\").length === 0), 'message: Comment out your <code>p</code> element so that it is not visible on your page.');",
|
"assert(($(\"p\").length === 0), 'message: Comment out your <code>p</code> element so that it is not visible on your page.');",
|
||||||
"assert(code.match(/-->/g).length > 1, 'message: Be sure to close each of your comments with <code>--></code>.');",
|
"assert(code.match(/[^fc]-->/g).length > 1, 'message: Be sure to close each of your comments with <code>--></code>.');",
|
||||||
"assert((code.match(/<([a-z0-9]){1,2}>/g)[0]===\"<h1>\" && code.match(/<([a-z0-9]){1,2}>/g)[1]===\"<h2>\" && code.match(/<([a-z0-9]){1,2}>/g)[2]===\"<p>\") , 'message: Do not change the order of the <code>h1</code> <code>h2</code> or <code>p</code> in the code.');"
|
"assert((code.match(/<([a-z0-9]){1,2}>/g)[0]===\"<h1>\" && code.match(/<([a-z0-9]){1,2}>/g)[1]===\"<h2>\" && code.match(/<([a-z0-9]){1,2}>/g)[2]===\"<p>\") , 'message: Do not change the order of the <code>h1</code> <code>h2</code> or <code>p</code> in the code.');"
|
||||||
],
|
],
|
||||||
"type": "waypoint",
|
"type": "waypoint",
|
||||||
@ -1071,8 +1071,8 @@
|
|||||||
"tests": [
|
"tests": [
|
||||||
"assert($(\"h2\").css(\"font-family\").match(/^\"?lobster/i), 'message: Your h2 element should use the font <code>Lobster</code>.');",
|
"assert($(\"h2\").css(\"font-family\").match(/^\"?lobster/i), 'message: Your h2 element should use the font <code>Lobster</code>.');",
|
||||||
"assert($(\"h2\").css(\"font-family\").match(/lobster.*,.*monospace/i), 'message: Your h2 element should degrade to the font <code>Monospace</code> when <code>Lobster</code> is not available.');",
|
"assert($(\"h2\").css(\"font-family\").match(/lobster.*,.*monospace/i), 'message: Your h2 element should degrade to the font <code>Monospace</code> when <code>Lobster</code> is not available.');",
|
||||||
"assert(new RegExp(\"<!--\", \"gi\").test(code), 'message: Comment out your call to Google for the <code>Lobster</code> font by putting <code><!--</code> in front of it.');",
|
"assert(new RegExp(\"<!--[^fc]\", \"gi\").test(code), 'message: Comment out your call to Google for the <code>Lobster</code> font by putting <code><!--</code> in front of it.');",
|
||||||
"assert(new RegExp(\"-->\", \"gi\").test(code), 'message: Be sure to close your comment by adding <code>--></code>.');"
|
"assert(new RegExp(\"[^fc]-->\", \"gi\").test(code), 'message: Be sure to close your comment by adding <code>--></code>.');"
|
||||||
],
|
],
|
||||||
"type": "waypoint",
|
"type": "waypoint",
|
||||||
"titleEs": "Especifica cómo deben degradarse los tipos de letra",
|
"titleEs": "Especifica cómo deben degradarse los tipos de letra",
|
||||||
|
Reference in New Issue
Block a user