diff --git a/common/app/flux/Actions.js b/common/app/flux/Actions.js
index 52334c4876..83196b90f0 100644
--- a/common/app/flux/Actions.js
+++ b/common/app/flux/Actions.js
@@ -4,46 +4,42 @@ import debugFactory from 'debug';
const debug = debugFactory('freecc:app:actions');
export default Actions({
+ shouldBindMethods: true,
+ refs: { displayName: 'AppActions' },
+
setTitle(title = 'Learn To Code') {
- return { title: title + '| Free Code Camp' };
+ return { title: title + ' | Free Code Camp' };
},
- setUser({
- username,
- picture,
- progressTimestamps = [],
- isFrontEndCert,
- isFullStackCert
- }) {
- return {
- username,
- picture,
- points: progressTimestamps.length,
- isFrontEndCert,
- isFullStackCert
- };
+ getUser({ isPrimed }) {
+ if (isPrimed) {
+ return null;
+ }
+
+ debug('fetching user data');
+ return this.readService$('user', null, null)
+ .map(function({
+ username,
+ picture,
+ progressTimestamps = [],
+ isFrontEndCert,
+ isFullStackCert
+ }) {
+ return {
+ username,
+ picture,
+ points: progressTimestamps.length,
+ isFrontEndCert,
+ isFullStackCert
+ };
+ })
+ .catch(err => {
+ console.error(err);
+ });
},
- getUser: null,
updateRoute(route) {
return { route };
},
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;
- });
+});
diff --git a/common/app/flux/Store.js b/common/app/flux/Store.js
index f174314f5a..59f2f7e959 100644
--- a/common/app/flux/Store.js
+++ b/common/app/flux/Store.js
@@ -18,15 +18,15 @@ export default Store({
value: initValue
},
init({ instance: appStore, args: [cat] }) {
- const { updateRoute, setUser, setTitle } = cat.getActions('appActions');
+ const { updateRoute, getUser, setTitle } = cat.getActions('appActions');
const register = createRegistrar(appStore);
- let { setHikes } = cat.getActions('hikesActions');
+ const { fetchHikes } = cat.getActions('hikesActions');
// app
- register(setter(fromMany(setUser, setTitle, updateRoute)));
+ register(setter(fromMany(getUser, setTitle, updateRoute)));
// hikes
- register(setHikes);
+ register(fetchHikes);
return appStore;
}
diff --git a/common/app/routes/Hikes/components/Hike.jsx b/common/app/routes/Hikes/components/Hike.jsx
new file mode 100644
index 0000000000..dbf00314d8
--- /dev/null
+++ b/common/app/routes/Hikes/components/Hike.jsx
@@ -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 (
+
+ );
+ }
+
+ const {
+ challengeSeed: [ id ] = ['1'],
+ description = []
+ } = currentHike;
+
+ return (
+
+ );
+ },
+
+ render() {
+ const { currentHike, showQuestions } = this.props;
+ const { title } = currentHike;
+
+ const videoTitle =
{ title }
;
+
+ return (
+
+
+
+ { this.renderBody(showQuestions, currentHike) }
+
+
+
+ );
+ }
+});
diff --git a/common/app/routes/Hikes/components/Hikes.jsx b/common/app/routes/Hikes/components/Hikes.jsx
index 76632a1456..7943b49320 100644
--- a/common/app/routes/Hikes/components/Hikes.jsx
+++ b/common/app/routes/Hikes/components/Hikes.jsx
@@ -9,7 +9,10 @@ import HikesMap from './Map.jsx';
export default contain(
{
- store: 'hikesStore',
+ store: 'appStore',
+ map(state) {
+ return state.hikesApp;
+ },
actions: ['appActions'],
fetchAction: 'hikesActions.fetchHikes',
getPayload: ({ hikes, params }) => ({
@@ -54,8 +57,12 @@ export default contain(
return (
- { 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)
+ }
);
diff --git a/common/app/routes/Hikes/components/Lecture.jsx b/common/app/routes/Hikes/components/Lecture.jsx
index e47dc0fc98..b70c50d250 100644
--- a/common/app/routes/Hikes/components/Lecture.jsx
+++ b/common/app/routes/Hikes/components/Lecture.jsx
@@ -1,5 +1,5 @@
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 Vimeo from 'react-vimeo';
import debugFactory from 'debug';
@@ -19,8 +19,6 @@ export default React.createClass({
handleFinish() {
debug('loading questions');
- const { dashedName } = this.props.params;
- this.history.pushState(null, `/hikes/${dashedName}/questions/1`);
},
renderTranscript(transcript, dashedName) {
@@ -31,7 +29,6 @@ export default React.createClass({
render() {
const {
- title,
challengeSeed = ['1'],
description = []
} = this.props.currentHike;
@@ -39,31 +36,22 @@ export default React.createClass({
const [ id ] = challengeSeed;
- const videoTitle = { title }
;
return (
-
-
-
+
-
-
- { this.renderTranscript(description, dashedName) }
-
-
-
-
-
+ { this.renderTranscript(description, dashedName) }
+
);
diff --git a/common/app/routes/Hikes/components/Map.jsx b/common/app/routes/Hikes/components/Map.jsx
index 830f74a7ef..64e837603b 100644
--- a/common/app/routes/Hikes/components/Map.jsx
+++ b/common/app/routes/Hikes/components/Map.jsx
@@ -11,7 +11,7 @@ export default React.createClass({
render() {
const {
- hikes
+ hikes = [{}]
} = this.props;
const vidElements = hikes.map(({ title, dashedName}) => {
diff --git a/common/app/routes/Hikes/components/Question.jsx b/common/app/routes/Hikes/components/Questions.jsx
similarity index 99%
rename from common/app/routes/Hikes/components/Question.jsx
rename to common/app/routes/Hikes/components/Questions.jsx
index 63dd15407d..d426b14916 100644
--- a/common/app/routes/Hikes/components/Question.jsx
+++ b/common/app/routes/Hikes/components/Questions.jsx
@@ -16,7 +16,7 @@ const debug = debugFactory('freecc:hikes');
const ANSWER_THRESHOLD = 200;
export default React.createClass({
- displayName: 'Question',
+ displayName: 'Questions',
mixins: [
History,
diff --git a/common/app/routes/Hikes/flux/Actions.js b/common/app/routes/Hikes/flux/Actions.js
index 3eed55159f..3f5e262f31 100644
--- a/common/app/routes/Hikes/flux/Actions.js
+++ b/common/app/routes/Hikes/flux/Actions.js
@@ -25,49 +25,41 @@ function getCurrentHike(hikes = [{}], dashedName, currentHike) {
}
export default Actions({
- // start fetching hikes
- fetchHikes: null,
- // set hikes on store
- setHikes: null
-})
- .refs({ displayName: 'HikesActions' })
- .init(({ instance: hikeActions, args: [services] }) => {
- // set up hikes fetching
- hikeActions.fetchHikes.subscribe(
- ({ isPrimed, dashedName }) => {
- if (isPrimed) {
- return hikeActions.setHikes({
- transform: (state) => {
+ refs: { displayName: 'HikesActions' },
+ shouldBindMethods: true,
+ fetchHikes({ isPrimed, dashedName }) {
+ if (isPrimed) {
+ return {
+ transform: (state) => {
- const { hikesApp: oldState } = state;
- const currentHike = getCurrentHike(
- oldState.hikes,
- dashedName,
- oldState.currentHike
- );
+ const { hikesApp: oldState } = state;
+ const currentHike = getCurrentHike(
+ oldState.hikes,
+ dashedName,
+ oldState.currentHike
+ );
- const hikesApp = { ...oldState, currentHike };
- return Object.assign({}, state, { hikesApp });
- }
- });
+ const hikesApp = { ...oldState, currentHike };
+ return Object.assign({}, state, { hikesApp });
}
+ };
+ }
- services.read('hikes', null, null, (err, hikes) => {
- if (err) {
- return console.error(err);
+ return this.readService$('hikes', null, null)
+ .map(hikes => {
+ const hikesApp = {
+ hikes,
+ currentHike: getCurrentHike(hikes, dashedName)
+ };
+
+ return {
+ transform(oldState) {
+ return Object.assign({}, oldState, { hikesApp });
}
-
- const hikesApp = {
- hikes,
- currentHike: getCurrentHike(hikes, dashedName)
- };
-
- hikeActions.setHikes({
- transform(oldState) {
- return Object.assign({}, oldState, { hikesApp });
- }
- });
- });
- }
- );
- });
+ };
+ })
+ .catch(err => {
+ console.error(err);
+ });
+ }
+});
diff --git a/common/app/routes/Hikes/flux/index.js b/common/app/routes/Hikes/flux/index.js
index 05980cb3ff..336f72d297 100644
--- a/common/app/routes/Hikes/flux/index.js
+++ b/common/app/routes/Hikes/flux/index.js
@@ -1,2 +1 @@
export { default as HikesActions } from './Actions';
-export { default as HikesStore } from './Store';
diff --git a/common/app/routes/Hikes/index.js b/common/app/routes/Hikes/index.js
index 203380153e..a53ac3193d 100644
--- a/common/app/routes/Hikes/index.js
+++ b/common/app/routes/Hikes/index.js
@@ -1,6 +1,5 @@
import Hikes from './components/Hikes.jsx';
-import Lecture from './components/Lecture.jsx';
-import Question from './components/Question.jsx';
+import Hike from './components/Hike.jsx';
/*
* show video /hikes/someVideo
@@ -12,9 +11,6 @@ export default {
component: Hikes,
childRoutes: [{
path: ':dashedName',
- component: Lecture
- }, {
- path: ':dashedName/questions/:number',
- component: Question
+ component: Hike
}]
};
diff --git a/server/middlewares/csp.js b/server/middlewares/csp.js
index 80b41e5fbe..21e542dd01 100644
--- a/server/middlewares/csp.js
+++ b/server/middlewares/csp.js
@@ -1,9 +1,13 @@
import helmet from 'helmet';
-const trusted = [
+let trusted = [
"'self'"
];
+if (process.env.NODE_ENV !== 'production') {
+ trusted.push('ws://localhost:3001');
+}
+
export default function csp() {
return helmet.csp({
defaultSrc: trusted,