Make structure changes to hikes

This commit is contained in:
Berkeley Martinez
2015-12-22 19:33:25 -08:00
parent fe16a74faa
commit af950779a4
11 changed files with 154 additions and 116 deletions

View File

@ -4,46 +4,42 @@ import debugFactory from 'debug';
const debug = debugFactory('freecc:app:actions'); const debug = debugFactory('freecc:app:actions');
export default Actions({ export default Actions({
shouldBindMethods: true,
refs: { displayName: 'AppActions' },
setTitle(title = 'Learn To Code') { setTitle(title = 'Learn To Code') {
return { title: title + '| Free Code Camp' }; return { title: title + ' | Free Code Camp' };
}, },
setUser({ getUser({ isPrimed }) {
username, if (isPrimed) {
picture, return null;
progressTimestamps = [], }
isFrontEndCert,
isFullStackCert debug('fetching user data');
}) { return this.readService$('user', null, null)
return { .map(function({
username, username,
picture, picture,
points: progressTimestamps.length, progressTimestamps = [],
isFrontEndCert, isFrontEndCert,
isFullStackCert isFullStackCert
}; }) {
return {
username,
picture,
points: progressTimestamps.length,
isFrontEndCert,
isFullStackCert
};
})
.catch(err => {
console.error(err);
});
}, },
getUser: null,
updateRoute(route) { updateRoute(route) {
return { route }; return { route };
}, },
goBack: null goBack: null
}) });
.refs({ displayName: 'AppActions' })
.init(({ instance: appActions, args: [services] }) => {
appActions.getUser.subscribe(({ isPrimed }) => {
if (isPrimed) {
debug('isPrimed');
return;
}
services.read('user', null, null, (err, user) => {
if (err) {
return debug('user service error');
}
debug('user service returned successful');
return appActions.setUser(user);
});
});
return appActions;
});

View File

@ -18,15 +18,15 @@ export default Store({
value: initValue value: initValue
}, },
init({ instance: appStore, args: [cat] }) { init({ instance: appStore, args: [cat] }) {
const { updateRoute, setUser, setTitle } = cat.getActions('appActions'); const { updateRoute, getUser, setTitle } = cat.getActions('appActions');
const register = createRegistrar(appStore); const register = createRegistrar(appStore);
let { setHikes } = cat.getActions('hikesActions'); const { fetchHikes } = cat.getActions('hikesActions');
// app // app
register(setter(fromMany(setUser, setTitle, updateRoute))); register(setter(fromMany(getUser, setTitle, updateRoute)));
// hikes // hikes
register(setHikes); register(fetchHikes);
return appStore; return appStore;
} }

View File

@ -0,0 +1,56 @@
import React, { PropTypes } from 'react';
import {
Col,
Panel,
Row
} from 'react-bootstrap';
import Lecture from './Lecture.jsx';
import Questions from './Questions.jsx';
export default React.createClass({
displayName: 'Hike',
propTypes: {
showQuestions: PropTypes.bool,
currentHike: PropTypes.object
},
renderBody(showQuestions, currentHike) {
if (showQuestions) {
return (
<Questions hike={ currentHike }/>
);
}
const {
challengeSeed: [ id ] = ['1'],
description = []
} = currentHike;
return (
<Lecture
description={ description }
id={ id } />
);
},
render() {
const { currentHike, showQuestions } = this.props;
const { title } = currentHike;
const videoTitle = <h2>{ title }</h2>;
return (
<Col xs={ 12 }>
<Row>
<Panel
className={ 'text-center' }
title={ videoTitle }>
{ this.renderBody(showQuestions, currentHike) }
</Panel>
</Row>
</Col>
);
}
});

View File

@ -9,7 +9,10 @@ import HikesMap from './Map.jsx';
export default contain( export default contain(
{ {
store: 'hikesStore', store: 'appStore',
map(state) {
return state.hikesApp;
},
actions: ['appActions'], actions: ['appActions'],
fetchAction: 'hikesActions.fetchHikes', fetchAction: 'hikesActions.fetchHikes',
getPayload: ({ hikes, params }) => ({ getPayload: ({ hikes, params }) => ({
@ -54,8 +57,12 @@ export default contain(
return ( return (
<div> <div>
<Row style={ preventOverflow }> <Row style={ preventOverflow }>
{ this.renderChild(children, hikes, currentHike) || {
this.renderMap(hikes) } // render sub-route
this.renderChild(children, hikes, currentHike) ||
// if no sub-route render hikes map
this.renderMap(hikes)
}
</Row> </Row>
</div> </div>
); );

View File

@ -1,5 +1,5 @@
import React, { PropTypes } from 'react'; import React, { PropTypes } from 'react';
import { Button, Col, Row, Panel } from 'react-bootstrap'; import { Button, Col, Row } from 'react-bootstrap';
import { History } from 'react-router'; import { History } from 'react-router';
import Vimeo from 'react-vimeo'; import Vimeo from 'react-vimeo';
import debugFactory from 'debug'; import debugFactory from 'debug';
@ -19,8 +19,6 @@ export default React.createClass({
handleFinish() { handleFinish() {
debug('loading questions'); debug('loading questions');
const { dashedName } = this.props.params;
this.history.pushState(null, `/hikes/${dashedName}/questions/1`);
}, },
renderTranscript(transcript, dashedName) { renderTranscript(transcript, dashedName) {
@ -31,7 +29,6 @@ export default React.createClass({
render() { render() {
const { const {
title,
challengeSeed = ['1'], challengeSeed = ['1'],
description = [] description = []
} = this.props.currentHike; } = this.props.currentHike;
@ -39,31 +36,22 @@ export default React.createClass({
const [ id ] = challengeSeed; const [ id ] = challengeSeed;
const videoTitle = <h2>{ title }</h2>;
return ( return (
<Col xs={ 12 }> <Col xs={ 12 }>
<Row> <Row>
<Panel className={ 'text-center' } title={ videoTitle }> <Vimeo
<Vimeo onError={ this.handleError }
onError={ this.handleError } onFinish= { this.handleFinish }
onFinish= { this.handleFinish } videoId={ id } />
videoId={ id } />
</Panel>
</Row> </Row>
<Row> <Row>
<Col xs={ 12 }> { this.renderTranscript(description, dashedName) }
<Panel> <Button
{ this.renderTranscript(description, dashedName) } block={ true }
</Panel> bsSize='large'
<Panel> onClick={ this.handleFinish }>
<Button Take me to the Questions
block={ true } </Button>
bsSize='large'
onClick={ this.handleFinish }>
Take me to the Questions
</Button>
</Panel>
</Col>
</Row> </Row>
</Col> </Col>
); );

View File

@ -11,7 +11,7 @@ export default React.createClass({
render() { render() {
const { const {
hikes hikes = [{}]
} = this.props; } = this.props;
const vidElements = hikes.map(({ title, dashedName}) => { const vidElements = hikes.map(({ title, dashedName}) => {

View File

@ -16,7 +16,7 @@ const debug = debugFactory('freecc:hikes');
const ANSWER_THRESHOLD = 200; const ANSWER_THRESHOLD = 200;
export default React.createClass({ export default React.createClass({
displayName: 'Question', displayName: 'Questions',
mixins: [ mixins: [
History, History,

View File

@ -25,49 +25,41 @@ function getCurrentHike(hikes = [{}], dashedName, currentHike) {
} }
export default Actions({ export default Actions({
// start fetching hikes refs: { displayName: 'HikesActions' },
fetchHikes: null, shouldBindMethods: true,
// set hikes on store fetchHikes({ isPrimed, dashedName }) {
setHikes: null if (isPrimed) {
}) return {
.refs({ displayName: 'HikesActions' }) transform: (state) => {
.init(({ instance: hikeActions, args: [services] }) => {
// set up hikes fetching
hikeActions.fetchHikes.subscribe(
({ isPrimed, dashedName }) => {
if (isPrimed) {
return hikeActions.setHikes({
transform: (state) => {
const { hikesApp: oldState } = state; const { hikesApp: oldState } = state;
const currentHike = getCurrentHike( const currentHike = getCurrentHike(
oldState.hikes, oldState.hikes,
dashedName, dashedName,
oldState.currentHike oldState.currentHike
); );
const hikesApp = { ...oldState, currentHike }; const hikesApp = { ...oldState, currentHike };
return Object.assign({}, state, { hikesApp }); return Object.assign({}, state, { hikesApp });
}
});
} }
};
}
services.read('hikes', null, null, (err, hikes) => { return this.readService$('hikes', null, null)
if (err) { .map(hikes => {
return console.error(err); const hikesApp = {
hikes,
currentHike: getCurrentHike(hikes, dashedName)
};
return {
transform(oldState) {
return Object.assign({}, oldState, { hikesApp });
} }
};
const hikesApp = { })
hikes, .catch(err => {
currentHike: getCurrentHike(hikes, dashedName) console.error(err);
}; });
}
hikeActions.setHikes({ });
transform(oldState) {
return Object.assign({}, oldState, { hikesApp });
}
});
});
}
);
});

View File

@ -1,2 +1 @@
export { default as HikesActions } from './Actions'; export { default as HikesActions } from './Actions';
export { default as HikesStore } from './Store';

View File

@ -1,6 +1,5 @@
import Hikes from './components/Hikes.jsx'; import Hikes from './components/Hikes.jsx';
import Lecture from './components/Lecture.jsx'; import Hike from './components/Hike.jsx';
import Question from './components/Question.jsx';
/* /*
* show video /hikes/someVideo * show video /hikes/someVideo
@ -12,9 +11,6 @@ export default {
component: Hikes, component: Hikes,
childRoutes: [{ childRoutes: [{
path: ':dashedName', path: ':dashedName',
component: Lecture component: Hike
}, {
path: ':dashedName/questions/:number',
component: Question
}] }]
}; };

View File

@ -1,9 +1,13 @@
import helmet from 'helmet'; import helmet from 'helmet';
const trusted = [ let trusted = [
"'self'" "'self'"
]; ];
if (process.env.NODE_ENV !== 'production') {
trusted.push('ws://localhost:3001');
}
export default function csp() { export default function csp() {
return helmet.csp({ return helmet.csp({
defaultSrc: trusted, defaultSrc: trusted,