Files
freeCodeCamp/common/app/routes/Hikes/redux/answer-saga.js

143 lines
3.5 KiB
JavaScript
Raw Normal View History

2016-01-27 11:34:44 -08:00
import { Observable } from 'rx';
2016-02-05 16:40:02 -08:00
import { push } from 'react-router-redux';
2016-01-27 11:34:44 -08:00
import types from './types';
import { getMouse } from './utils';
import { makeToast, updatePoints } from '../../../redux/actions';
import { hikeCompleted, goToNextHike } from './actions';
import { postJSON$ } from '../../../../utils/ajax-stream';
2016-02-05 16:40:02 -08:00
import { getCurrentHike } from './selectors';
2016-01-27 11:34:44 -08:00
2016-04-24 21:54:48 -07:00
function handleAnswer(action, getState) {
2016-02-05 16:40:02 -08:00
const {
e,
answer,
userAnswer,
info,
threshold
} = action.payload;
2016-01-27 11:34:44 -08:00
2016-02-05 16:40:02 -08:00
const state = getState();
const { id, name, challengeType, tests } = getCurrentHike(state);
const {
2016-05-09 10:13:02 -07:00
app: { isSignedIn, csrfToken },
2016-02-05 16:40:02 -08:00
hikesApp: {
currentQuestion,
delta = [ 0, 0 ]
}
} = state;
2016-01-27 11:34:44 -08:00
2016-02-05 16:40:02 -08:00
let finalAnswer;
// drag answer, compute response
if (typeof userAnswer === 'undefined') {
const [positionX] = getMouse(e, delta);
2016-01-27 11:34:44 -08:00
2016-02-05 16:40:02 -08:00
// question released under threshold
if (Math.abs(positionX) < threshold) {
2016-04-24 21:54:48 -07:00
return Observable.just(null);
2016-01-27 11:34:44 -08:00
}
2016-02-05 16:40:02 -08:00
if (positionX >= threshold) {
finalAnswer = true;
}
2016-01-27 11:34:44 -08:00
2016-02-05 16:40:02 -08:00
if (positionX <= -threshold) {
finalAnswer = false;
2016-01-27 11:34:44 -08:00
}
2016-02-05 16:40:02 -08:00
} else {
finalAnswer = userAnswer;
}
2016-01-27 11:34:44 -08:00
2016-02-05 16:40:02 -08:00
// incorrect question
if (answer !== finalAnswer) {
2016-04-24 21:54:48 -07:00
let infoAction;
2016-02-05 16:40:02 -08:00
if (info) {
2016-04-24 21:54:48 -07:00
infoAction = makeToast({
2016-02-05 16:40:02 -08:00
title: 'Hint',
message: info,
type: 'info'
2016-04-24 21:54:48 -07:00
});
2016-01-27 11:34:44 -08:00
}
2016-02-05 16:40:02 -08:00
return Observable
.just({ type: types.endShake })
.delay(500)
2016-04-24 21:54:48 -07:00
.startWith(infoAction, { type: types.startShake });
2016-02-05 16:40:02 -08:00
}
if (tests[currentQuestion]) {
return Observable
.just({ type: types.goToNextQuestion })
.delay(300)
2016-04-24 21:54:48 -07:00
.startWith({ type: types.primeNextQuestion });
2016-02-05 16:40:02 -08:00
}
let updateUser$;
if (isSignedIn) {
2016-05-09 10:13:02 -07:00
const body = { id, name, challengeType: +challengeType, _csrf: csrfToken };
2016-02-05 16:40:02 -08:00
updateUser$ = postJSON$('/completed-challenge', body)
// if post fails, will retry once
.retry(3)
.flatMap(({ alreadyCompleted, points }) => {
return Observable.of(
makeToast({
message:
'Challenge saved.' +
(alreadyCompleted ? '' : ' First time Completed!'),
title: 'Saved',
type: 'info'
}),
updatePoints(points),
);
})
.catch(error => {
return Observable.just({
type: 'app.error',
error
2016-01-27 11:34:44 -08:00
});
2016-02-05 16:40:02 -08:00
});
} else {
updateUser$ = Observable.empty();
}
const challengeCompleted$ = Observable.of(
goToNextHike(),
makeToast({
title: 'Congratulations!',
message: 'Hike completed.' + (isSignedIn ? ' Saving...' : ''),
type: 'success'
})
);
return Observable.merge(challengeCompleted$, updateUser$)
.delay(300)
.startWith(hikeCompleted(finalAnswer))
.catch(error => Observable.just({
type: 'error',
error
}))
// end with action so we know it is ok to transition
2016-04-24 21:54:48 -07:00
.concat(Observable.just({ type: types.transitionHike }));
2016-02-05 16:40:02 -08:00
}
2016-04-24 21:54:48 -07:00
export default function answerSaga(action$, getState) {
return action$
.filter(action => {
return action.type === types.answerQuestion ||
action.type === types.transitionHike;
})
.flatMap(action => {
if (action.type === types.answerQuestion) {
return handleAnswer(action, getState);
}
2016-01-27 11:34:44 -08:00
2016-02-05 16:40:02 -08:00
const { hikesApp: { currentHike } } = getState();
// if no next hike currentHike will equal '' which is falsy
if (currentHike) {
2016-04-24 21:54:48 -07:00
return Observable.just(push(`/videos/${currentHike}`));
2016-02-05 16:40:02 -08:00
}
2016-04-24 21:54:48 -07:00
return Observable.just(push('/map'));
});
}