Fix next challenge loading logic

This commit is contained in:
Berkeley Martinez
2016-06-10 14:01:13 -07:00
parent c0015a4050
commit 5f5f9e526e
7 changed files with 154 additions and 49 deletions

View File

@ -53,8 +53,8 @@ function cacheScript({ src } = {}) {
const frameRunner$ = cacheScript({ src: '/js/frame-runner.js' });
const htmlCatch = '\n<!-- -->';
const jsCatch = '\n;/* */';
const htmlCatch = '\n<!--fcc-->';
const jsCatch = '\n;/*fcc*/';
export default function executeChallengeSaga(action$, getState) {
return action$

View File

@ -1,16 +1,8 @@
import React from 'react';
import React, { PropTypes } from 'react';
import PureComponent from 'react-pure-render/component';
import NoSSR from 'react-no-ssr';
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 = {
lineNumbers: false,
mode: 'javascript',
@ -21,11 +13,9 @@ const defaultOptions = {
export default class extends PureComponent {
static displayName = 'Output';
static defaultProps = {
output: defaultOutput
static propTypes = {
output: PropTypes.string
};
render() {
const { output } = this.props;
return (
@ -33,7 +23,8 @@ export default class extends PureComponent {
<NoSSR>
<Codemirror
options={ defaultOptions }
value={ output } />
value={ output }
/>
</NoSSR>
</div>
);

View File

@ -22,29 +22,34 @@ export class ToolPanel extends PureComponent {
block={ true }
bsStyle='primary'
className='btn-big'
onClick={ executeChallenge }>
onClick={ executeChallenge }
>
Run tests (ctrl + enter)
</Button>
<div className='button-spacer' />
<ButtonGroup
className='input-group'
justified={ true }>
justified={ true }
>
<Button
bsSize='large'
bsStyle='primary'
componentClass='label'>
componentClass='label'
>
Reset
</Button>
<Button
bsSize='large'
bsStyle='primary'
componentClass='label'>
componentClass='label'
>
Help
</Button>
<Button
bsSize='large'
bsStyle='primary'
componentClass='label'>
componentClass='label'
>
Bug
</Button>
</ButtonGroup>

View File

@ -1,24 +1,68 @@
import { Observable } from 'rx';
import { push } from 'react-router-redux';
import { moveToNextChallenge } from './types';
import { getNextChallenge } from '../utils';
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) {
return actions$
.filter(({ type }) => type === moveToNextChallenge)
.flatMap(() => {
const state = getState();
const nextChallenge = getNextChallenge(
state.challengesApp.challenge,
state.entities,
state.challengesApp.superBlocks
);
return Observable.of(
updateCurrentChallenge(nextChallenge),
resetUi(),
push(`/challenges/${nextChallenge.block}/${nextChallenge.dashedName}`)
);
let nextChallenge;
// let message = '';
// let isNewBlock = false;
// let isNewSuperBlock = false;
try {
const state = getState();
const { challenge, superBlocks } = state.challengesApp;
const { entities } = state;
nextChallenge = getNextChallenge(challenge, entities);
// 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);
}
});
}

View File

@ -15,7 +15,12 @@ const initialUiState = {
currentIndex: 0,
previousIndex: -1,
isActionCompleted: false,
isSubmitting: true
isSubmitting: true,
output: `/**
* Any console.log()
* statements will appear in
* here console.
*/`
};
const initialState = {
id: '',

View File

@ -91,20 +91,80 @@ export function getFirstChallenge(
];
}
export function getNextChallenge(
current,
entites,
superBlocks
) {
export function getNextChallenge(current, entites) {
const { challenge: challengeMap, block: blockMap } = entites;
// find current challenge
// find current block
// find next challenge in block
const currentChallenge = challengeMap[current];
if (currentChallenge) {
const block = blockMap[currentChallenge.block];
const index = block.challenges.indexOf(currentChallenge.dashedName);
return challengeMap[block.challenges[index + 1]];
if (!currentChallenge) {
return null;
}
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;
}

View File

@ -265,7 +265,7 @@
"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($(\"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&#44; i.e. <code>--&#62;</code>.');"
"assert(!/[^fc]-->/gi.test(code.replace(/ *<!--[^fc]*\\n/g,'')), 'message: Be sure to delete all trailing comment tags&#44; i.e. <code>--&#62;</code>.');"
],
"type": "waypoint",
"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(($(\"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(code.match(/-->/g).length > 1, 'message: Be sure to close each of your comments with <code>--&#62;</code>.');",
"assert(code.match(/[^fc]-->/g).length > 1, 'message: Be sure to close each of your comments with <code>--&#62;</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",
@ -1071,8 +1071,8 @@
"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.*,.*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>&#60!--</code> in front of it.');",
"assert(new RegExp(\"-->\", \"gi\").test(code), 'message: Be sure to close your comment by adding <code>--&#62;</code>.');"
"assert(new RegExp(\"<!--[^fc]\", \"gi\").test(code), 'message: Comment out your call to Google for the <code>Lobster</code> font by putting <code>&#60!--</code> in front of it.');",
"assert(new RegExp(\"[^fc]-->\", \"gi\").test(code), 'message: Be sure to close your comment by adding <code>--&#62;</code>.');"
],
"type": "waypoint",
"titleEs": "Especifica cómo deben degradarse los tipos de letra",