Move flux contianer to top level hike comp
Removes all the lower level hikes Finishing questions takes you to next hike BUG: sometimes it takes you an incorrect next challenge
This commit is contained in:
@ -1,32 +1,54 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import stampit from 'react-stampit';
|
||||
import { Row } from 'react-bootstrap';
|
||||
import { contain } from 'thundercats-react';
|
||||
// import debugFactory from 'debug';
|
||||
|
||||
import HikesMap from './Map.jsx';
|
||||
|
||||
// const debug = debugFactory('freecc:hikes');
|
||||
|
||||
export default stampit(React, {
|
||||
displayName: 'Hikes',
|
||||
|
||||
propTypes: {
|
||||
children: PropTypes.element
|
||||
export default contain(
|
||||
{
|
||||
store: 'hikesStore',
|
||||
fetchAction: 'hikesActions.fetchHikes',
|
||||
getPayload: ({ hikes, params }) => ({
|
||||
isPrimed: (hikes && !!hikes.length),
|
||||
dashedName: params.dashedName
|
||||
})
|
||||
},
|
||||
stampit(React, {
|
||||
displayName: 'Hikes',
|
||||
|
||||
renderMap() {
|
||||
return (
|
||||
<HikesMap />
|
||||
);
|
||||
},
|
||||
propTypes: {
|
||||
children: PropTypes.element,
|
||||
currentHike: PropTypes.object,
|
||||
hikes: PropTypes.array
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Row>
|
||||
{ this.props.children || this.renderMap() }
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
renderMap(hikes) {
|
||||
return (
|
||||
<HikesMap hikes={ hikes }/>
|
||||
);
|
||||
},
|
||||
|
||||
renderChild(children, hikes, currentHike) {
|
||||
if (!children) {
|
||||
return null;
|
||||
}
|
||||
return React.cloneElement(children, { hikes, currentHike });
|
||||
},
|
||||
|
||||
render() {
|
||||
const { hikes, children, currentHike } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<Row>
|
||||
{ this.renderChild(children, hikes, currentHike) ||
|
||||
this.renderMap(hikes) }
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
@ -1,94 +1,71 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import { Button, Col, Row, Panel } from 'react-bootstrap';
|
||||
import { Navigation } from 'react-router';
|
||||
import { contain } from 'thundercats-react';
|
||||
import stampit from 'react-stampit';
|
||||
import Vimeo from 'react-vimeo';
|
||||
import debugFactory from 'debug';
|
||||
|
||||
const debug = debugFactory('freecc:hikes');
|
||||
|
||||
export default contain(
|
||||
{
|
||||
store: 'hikesStore',
|
||||
fetchAction: 'hikesActions.fetchCurrentHike',
|
||||
getPayload({ currentHike, params: { dashedName } }) {
|
||||
const filterRegex = new RegExp(dashedName, 'i');
|
||||
if (currentHike && filterRegex.test(currentHike.dashedName)) {
|
||||
return {
|
||||
isPrimed: true,
|
||||
dashedName
|
||||
};
|
||||
}
|
||||
return {
|
||||
isPrimed: false,
|
||||
dashedName: dashedName
|
||||
};
|
||||
}
|
||||
export default stampit(React, {
|
||||
displayName: 'Lecture',
|
||||
|
||||
propTypes: {
|
||||
currentHike: PropTypes.object,
|
||||
params: PropTypes.object
|
||||
},
|
||||
stampit(React, {
|
||||
displayName: 'Lecture',
|
||||
|
||||
propTypes: {
|
||||
currentHike: PropTypes.object,
|
||||
dashedName: PropTypes.string,
|
||||
params: PropTypes.object
|
||||
},
|
||||
handleError: debug,
|
||||
|
||||
handleError: debug,
|
||||
handleFinish() {
|
||||
debug('loading questions');
|
||||
const { dashedName } = this.props.params;
|
||||
this.transitionTo(`/hikes/${dashedName}/questions/1`);
|
||||
},
|
||||
|
||||
handleFinish() {
|
||||
debug('loading questions');
|
||||
const { dashedName } = this.props.params;
|
||||
this.transitionTo(`/hikes/${dashedName}/questions/1`);
|
||||
},
|
||||
renderTranscript(transcript, dashedName) {
|
||||
return transcript.map((line, index) => (
|
||||
<p key={ dashedName + index }>{ line }</p>
|
||||
));
|
||||
},
|
||||
|
||||
renderTranscript(transcript) {
|
||||
return transcript.map((line, index) => (
|
||||
<p key={ index }>{ line }</p>
|
||||
)
|
||||
);
|
||||
},
|
||||
render() {
|
||||
const {
|
||||
title,
|
||||
challengeSeed = ['1'],
|
||||
description = []
|
||||
} = this.props.currentHike;
|
||||
const { dashedName } = this.props.params;
|
||||
|
||||
render() {
|
||||
const {
|
||||
title,
|
||||
challengeSeed = ['1'],
|
||||
description = []
|
||||
} = this.props.currentHike;
|
||||
const [ id ] = challengeSeed;
|
||||
|
||||
const [ id ] = challengeSeed;
|
||||
|
||||
const videoTitle = <h2>{ title }</h2>;
|
||||
return (
|
||||
<Col xs={ 12 }>
|
||||
<Row>
|
||||
<Panel className={ 'text-center' } title={ videoTitle }>
|
||||
<Vimeo
|
||||
onError={ this.handleError }
|
||||
onFinish= { ::this.handleFinish }
|
||||
videoId={ id } />
|
||||
const videoTitle = <h2>{ title }</h2>;
|
||||
return (
|
||||
<Col xs={ 12 }>
|
||||
<Row>
|
||||
<Panel className={ 'text-center' } title={ videoTitle }>
|
||||
<Vimeo
|
||||
onError={ this.handleError }
|
||||
onFinish= { ::this.handleFinish }
|
||||
videoId={ id } />
|
||||
</Panel>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col xs={ 12 }>
|
||||
<Panel>
|
||||
{ this.renderTranscript(description, dashedName) }
|
||||
</Panel>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col xs={ 12 }>
|
||||
<Panel>
|
||||
<p>
|
||||
{ this.renderTranscript(description) }
|
||||
</p>
|
||||
</Panel>
|
||||
<Panel>
|
||||
<Button
|
||||
block={ true }
|
||||
bsSize='large'
|
||||
onClick={ ::this.handleFinish }>
|
||||
Take me to the Questions
|
||||
</Button>
|
||||
</Panel>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
}).compose(Navigation)
|
||||
);
|
||||
<Panel>
|
||||
<Button
|
||||
block={ true }
|
||||
bsSize='large'
|
||||
onClick={ ::this.handleFinish }>
|
||||
Take me to the Questions
|
||||
</Button>
|
||||
</Panel>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
}).compose(Navigation);
|
||||
|
@ -1,47 +1,39 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import stampit from 'react-stampit';
|
||||
import { Link } from 'react-router';
|
||||
import { contain } from 'thundercats-react';
|
||||
import { ListGroup, ListGroupItem, Panel } from 'react-bootstrap';
|
||||
|
||||
export default contain(
|
||||
{
|
||||
store: 'hikesStore',
|
||||
fetchAction: 'hikesActions.fetchHikes',
|
||||
getPayload: ({ hikes }) => ({ isPrimed: (hikes && !!hikes.length) })
|
||||
export default stampit(React, {
|
||||
displayName: 'HikesMap',
|
||||
|
||||
propTypes: {
|
||||
hikes: PropTypes.array
|
||||
},
|
||||
stampit(React, {
|
||||
displayName: 'HikesMap',
|
||||
|
||||
propTypes: {
|
||||
hikes: PropTypes.array
|
||||
},
|
||||
|
||||
render() {
|
||||
const {
|
||||
hikes
|
||||
} = this.props;
|
||||
|
||||
const vidElements = hikes.map(({ title, dashedName}) => {
|
||||
return (
|
||||
<ListGroupItem key={ dashedName }>
|
||||
<Link to={ `/hikes/${dashedName}` }>
|
||||
<h3>{ title }</h3>
|
||||
</Link>
|
||||
</ListGroupItem>
|
||||
);
|
||||
});
|
||||
render() {
|
||||
const {
|
||||
hikes
|
||||
} = this.props;
|
||||
|
||||
const vidElements = hikes.map(({ title, dashedName}) => {
|
||||
return (
|
||||
<div>
|
||||
<Panel>
|
||||
<h2>Welcome To Hikes!</h2>
|
||||
</Panel>
|
||||
<ListGroup>
|
||||
{ vidElements }
|
||||
</ListGroup>
|
||||
</div>
|
||||
<ListGroupItem key={ dashedName }>
|
||||
<Link to={ `/hikes/${dashedName}` }>
|
||||
<h3>{ title }</h3>
|
||||
</Link>
|
||||
</ListGroupItem>
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Panel>
|
||||
<h2>Welcome To Hikes!</h2>
|
||||
</Panel>
|
||||
<ListGroup>
|
||||
{ vidElements }
|
||||
</ListGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import { Navigation, TransitionHook } from 'react-router';
|
||||
import stampit from 'react-stampit';
|
||||
import { contain } from 'thundercats-react';
|
||||
import debugFactory from 'debug';
|
||||
import {
|
||||
Button,
|
||||
@ -13,145 +12,121 @@ import {
|
||||
|
||||
const debug = debugFactory('freecc:hikes');
|
||||
|
||||
export default contain(
|
||||
{
|
||||
store: 'hikesStore',
|
||||
map({ currentHike }) {
|
||||
const { tests = [] } = currentHike;
|
||||
export default stampit(React, {
|
||||
state: { showInfo: false },
|
||||
displayName: 'Question',
|
||||
|
||||
return { currentHike, tests };
|
||||
},
|
||||
fetchAction: 'hikesActions.fetchCurrentHike',
|
||||
getPayload({ currentHike, params: { dashedName } }) {
|
||||
const filterRegex = new RegExp(dashedName, 'i');
|
||||
if (currentHike && filterRegex.test(currentHike.dashedName)) {
|
||||
return {
|
||||
isPrimed: true,
|
||||
dashedName
|
||||
};
|
||||
}
|
||||
return {
|
||||
isPrimed: false,
|
||||
dashedName: dashedName
|
||||
};
|
||||
}
|
||||
propTypes: {
|
||||
currentHike: PropTypes.object,
|
||||
dashedName: PropTypes.string,
|
||||
hikes: PropTypes.array,
|
||||
params: PropTypes.object
|
||||
},
|
||||
stampit(React, {
|
||||
state: { showInfo: false },
|
||||
displayName: 'Question',
|
||||
|
||||
propTypes: {
|
||||
currentHike: PropTypes.object,
|
||||
dashedName: PropTypes.string,
|
||||
params: PropTypes.object,
|
||||
tests: PropTypes.array
|
||||
},
|
||||
|
||||
onAnswer(answer, userAnswer, info, e) {
|
||||
if (e && e.preventDefault) {
|
||||
e.preventDefault();
|
||||
}
|
||||
if (answer === userAnswer) {
|
||||
debug('correct answer!');
|
||||
this.setState({ showInfo: true });
|
||||
}
|
||||
return debug('incorrect');
|
||||
},
|
||||
|
||||
onCorrectAnswer() {
|
||||
const { hikes, currentHike } = this.props;
|
||||
const { dashedName, number } = this.props.params;
|
||||
const { difficulty, tests } = currentHike;
|
||||
const nextQuestionIndex = +number;
|
||||
this.setState({ showInfo: false }, () => {
|
||||
if (tests[nextQuestionIndex]) {
|
||||
return this.transitionTo(
|
||||
`/hikes/${ dashedName }/questions/${ nextQuestionIndex + 1 }`
|
||||
);
|
||||
}
|
||||
// next questions does not exit
|
||||
// find next hike
|
||||
//
|
||||
const nextHike = hikes
|
||||
// hikes is in oder of difficulty, lets get reverse order
|
||||
.reverse()
|
||||
// now lets find the hike with the difficulty right above this one
|
||||
.reduce((lowerHike, hike) => {
|
||||
if (hike.difficulty > difficulty) {
|
||||
return hike;
|
||||
}
|
||||
return lowerHike;
|
||||
}, null);
|
||||
|
||||
if (nextHike) {
|
||||
return this.transitionTo(`${ nextHike.dashedName }`);
|
||||
}
|
||||
debug('next Hike was not found');
|
||||
});
|
||||
},
|
||||
|
||||
routerWillLeave(/* nextState, router, cb[optional] */) {
|
||||
// TODO(berks): do animated transitions here stuff here
|
||||
},
|
||||
|
||||
renderInfo(showInfo, info) {
|
||||
return (
|
||||
<Modal
|
||||
backdrop={ false }
|
||||
onHide={ ::this.onCorrectAnswer }
|
||||
show={ showInfo }>
|
||||
<Modal.Body>
|
||||
<h3>
|
||||
{ info || 'correct!' }
|
||||
</h3>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button
|
||||
block={ true }
|
||||
bsSize='large'
|
||||
onClick={ ::this.onCorrectAnswer }>
|
||||
To next questions
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
);
|
||||
},
|
||||
|
||||
render() {
|
||||
const { showInfo } = this.state;
|
||||
const { tests } = this.props;
|
||||
const { number = '1' } = this.props.params;
|
||||
|
||||
const [question, answer, info] = tests[number - 1] || [];
|
||||
|
||||
return (
|
||||
<Col
|
||||
xs={ 8 }
|
||||
xsOffset={ 2 }>
|
||||
<Row>
|
||||
<Panel>
|
||||
<p>{ question }</p>
|
||||
</Panel>
|
||||
{ this.renderInfo(showInfo, info) }
|
||||
<Panel>
|
||||
<Button
|
||||
bsSize='large'
|
||||
className='pull-left'
|
||||
onClick={ this.onAnswer.bind(this, answer, false, info) }>
|
||||
false
|
||||
</Button>
|
||||
<Button
|
||||
bsSize='large'
|
||||
className='pull-right'
|
||||
onClick={ this.onAnswer.bind(this, answer, true, info) }>
|
||||
true
|
||||
</Button>
|
||||
</Panel>
|
||||
</Row>
|
||||
</Col>
|
||||
);
|
||||
onAnswer(answer, userAnswer, info, e) {
|
||||
if (e && e.preventDefault) {
|
||||
e.preventDefault();
|
||||
}
|
||||
})
|
||||
.compose(Navigation)
|
||||
.compose(TransitionHook)
|
||||
);
|
||||
if (answer === userAnswer) {
|
||||
debug('correct answer!');
|
||||
this.setState({ showInfo: true });
|
||||
}
|
||||
return debug('incorrect');
|
||||
},
|
||||
|
||||
onCorrectAnswer() {
|
||||
const { hikes, currentHike } = this.props;
|
||||
const { dashedName, number } = this.props.params;
|
||||
const { difficulty, tests } = currentHike;
|
||||
const nextQuestionIndex = +number;
|
||||
this.setState({ showInfo: false }, () => {
|
||||
if (tests[nextQuestionIndex]) {
|
||||
return this.transitionTo(
|
||||
`/hikes/${ dashedName }/questions/${ nextQuestionIndex + 1 }`
|
||||
);
|
||||
}
|
||||
// next questions does not exit
|
||||
// find next hike
|
||||
//
|
||||
const nextHike = hikes
|
||||
// hikes is in oder of difficulty, lets get reverse order
|
||||
.reverse()
|
||||
// now lets find the hike with the difficulty right above this one
|
||||
.reduce((lowerHike, hike) => {
|
||||
if (hike.difficulty > difficulty) {
|
||||
return hike;
|
||||
}
|
||||
return lowerHike;
|
||||
}, null);
|
||||
|
||||
if (nextHike) {
|
||||
return this.transitionTo(`/hikes/${ nextHike.dashedName }`);
|
||||
}
|
||||
debug('next Hike was not found');
|
||||
});
|
||||
},
|
||||
|
||||
routerWillLeave(/* nextState, router, cb[optional] */) {
|
||||
// TODO(berks): do animated transitions here stuff here
|
||||
},
|
||||
|
||||
renderInfo(showInfo, info) {
|
||||
return (
|
||||
<Modal
|
||||
backdrop={ false }
|
||||
onHide={ ::this.onCorrectAnswer }
|
||||
show={ showInfo }>
|
||||
<Modal.Body>
|
||||
<h3>
|
||||
{ info || 'correct!' }
|
||||
</h3>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button
|
||||
block={ true }
|
||||
bsSize='large'
|
||||
onClick={ ::this.onCorrectAnswer }>
|
||||
To next questions
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
);
|
||||
},
|
||||
|
||||
render() {
|
||||
const { showInfo } = this.state;
|
||||
const { currentHike: { tests = [] } } = this.props;
|
||||
const { number = '1' } = this.props.params;
|
||||
|
||||
const [question, answer, info] = tests[number - 1] || [];
|
||||
|
||||
return (
|
||||
<Col
|
||||
xs={ 8 }
|
||||
xsOffset={ 2 }>
|
||||
<Row>
|
||||
<Panel>
|
||||
<p>{ question }</p>
|
||||
</Panel>
|
||||
{ this.renderInfo(showInfo, info) }
|
||||
<Panel>
|
||||
<Button
|
||||
bsSize='large'
|
||||
className='pull-left'
|
||||
onClick={ this.onAnswer.bind(this, answer, false, info) }>
|
||||
false
|
||||
</Button>
|
||||
<Button
|
||||
bsSize='large'
|
||||
className='pull-right'
|
||||
onClick={ this.onAnswer.bind(this, answer, true, info) }>
|
||||
true
|
||||
</Button>
|
||||
</Panel>
|
||||
</Row>
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
})
|
||||
.compose(Navigation)
|
||||
.compose(TransitionHook);
|
||||
|
@ -8,28 +8,42 @@ const service = new Fetchr({
|
||||
xhrPath: '/services'
|
||||
});
|
||||
|
||||
function getCurrentHike(hikes =[{}], dashedName, currentHike) {
|
||||
if (!dashedName) {
|
||||
return hikes[0];
|
||||
}
|
||||
const filterRegex = new RegExp(dashedName, 'i');
|
||||
|
||||
return hikes
|
||||
.filter(({ dashedName }) => {
|
||||
return filterRegex.test(dashedName);
|
||||
})
|
||||
.reduce((throwAway, hike) => {
|
||||
return hike;
|
||||
}, currentHike || {});
|
||||
}
|
||||
|
||||
export default Actions({
|
||||
// start fetching hikes
|
||||
fetchHikes: null,
|
||||
// set hikes on store
|
||||
setHikes: null,
|
||||
|
||||
fetchCurrentHike: null,
|
||||
setCurrentHike: null
|
||||
setHikes: null
|
||||
})
|
||||
.refs({ displayName: 'HikesActions' })
|
||||
.init(({ instance }) => {
|
||||
// set up hikes fetching
|
||||
instance.fetchHikes.subscribe(
|
||||
({ isPrimed }) => {
|
||||
({ isPrimed, dashedName }) => {
|
||||
if (isPrimed) {
|
||||
return instance.setHikes({
|
||||
transform: (oldState) => {
|
||||
const { hikes } = oldState;
|
||||
const newState = {
|
||||
currentHike: (oldState.currentHike || hikes[0] || {})
|
||||
};
|
||||
return assign({}, oldState, newState);
|
||||
const { hikes, currentContext } = oldState;
|
||||
const currentHike = getCurrentHike(
|
||||
hikes,
|
||||
dashedName,
|
||||
currentContext
|
||||
);
|
||||
return assign({}, oldState, { currentHike });
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -40,38 +54,10 @@ export default Actions({
|
||||
instance.setHikes({
|
||||
set: {
|
||||
hikes: hikes,
|
||||
currentHike: hikes[0] || {}
|
||||
currentHike: getCurrentHike(hikes, dashedName)
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
instance.fetchCurrentHike.subscribe(({ isPrimed, dashedName }) => {
|
||||
if (isPrimed) {
|
||||
return instance.setCurrentHike({
|
||||
transform: (oldState) => {
|
||||
const { hikes } = oldState;
|
||||
const filterRegex = new RegExp(dashedName, 'i');
|
||||
const potentialHike = hikes
|
||||
.filter(({ dashedName }) => {
|
||||
return filterRegex.test(dashedName);
|
||||
})
|
||||
.reduce((throwAway, hike) => {
|
||||
return hike;
|
||||
});
|
||||
|
||||
// TODO(berks): do something when potential hike does not exist
|
||||
return assign({}, oldState, { currentHike: potentialHike });
|
||||
}
|
||||
});
|
||||
}
|
||||
service.read('hikes', { dashedName }, null, (err, hikes) => {
|
||||
if (err) {
|
||||
debug('error occurred fetching hike', err);
|
||||
}
|
||||
const [currentHike] = hikes;
|
||||
return instance.setCurrentHike({ set: { currentHike } });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { Store } from 'thundercats';
|
||||
|
||||
const { fromMany } = Store;
|
||||
const initialValue = {
|
||||
hikes: [],
|
||||
currentHike: {}
|
||||
@ -10,11 +9,9 @@ export default Store(initialValue)
|
||||
.refs({ displayName: 'HikesStore'})
|
||||
.init(({ instance, args }) => {
|
||||
const [cat] = args;
|
||||
let {
|
||||
setHikes,
|
||||
setCurrentHike
|
||||
} = cat.getActions('hikesActions');
|
||||
instance.register(fromMany(setHikes, setCurrentHike));
|
||||
|
||||
let { setHikes } = cat.getActions('hikesActions');
|
||||
instance.register(setHikes);
|
||||
|
||||
return instance;
|
||||
});
|
||||
|
Reference in New Issue
Block a user