2017-12-07 16:13:19 -08:00
|
|
|
import _ from 'lodash';
|
2016-06-09 16:02:51 -07:00
|
|
|
import { Observable } from 'rx';
|
2017-07-31 20:04:01 -07:00
|
|
|
import { combineEpics, ofType } from 'redux-epic';
|
|
|
|
|
|
|
|
import {
|
|
|
|
types,
|
|
|
|
|
2017-11-09 17:10:30 -08:00
|
|
|
challengeUpdated,
|
|
|
|
onRouteChallenges,
|
2017-12-07 16:13:19 -08:00
|
|
|
onRouteCurrentChallenge
|
2017-07-31 20:04:01 -07:00
|
|
|
} from './';
|
2018-02-24 08:44:12 +00:00
|
|
|
|
2017-07-31 20:04:01 -07:00
|
|
|
import {
|
|
|
|
createErrorObservable,
|
|
|
|
challengeSelector,
|
2018-02-24 08:44:12 +00:00
|
|
|
nextChallengeSelector
|
2017-07-31 20:04:01 -07:00
|
|
|
} from '../../../redux';
|
2017-11-09 17:10:30 -08:00
|
|
|
import { langSelector } from '../../../Router/redux';
|
2017-07-31 20:04:01 -07:00
|
|
|
import { makeToast } from '../../../Toasts/redux';
|
2016-06-09 16:02:51 -07:00
|
|
|
|
2017-11-09 17:10:30 -08:00
|
|
|
// When we change challenge, update the current challenge
|
|
|
|
// UI data.
|
2017-07-31 20:04:01 -07:00
|
|
|
export function challengeUpdatedEpic(actions, { getState }) {
|
2017-11-09 17:10:30 -08:00
|
|
|
return actions::ofType(types.onRouteChallenges)
|
|
|
|
// prevent subsequent onRouteChallenges to cause UI to refresh
|
|
|
|
.distinctUntilChanged(({ payload: { dashedName }}) => dashedName)
|
|
|
|
.map(() => challengeSelector(getState()))
|
|
|
|
// if the challenge isn't loaded in the current state,
|
|
|
|
// this will be an empty object
|
|
|
|
// We wait instead for the fetchChallenge.complete to complete the UI state
|
|
|
|
.filter(({ dashedName }) => !!dashedName)
|
2017-12-07 16:13:19 -08:00
|
|
|
// send the challenge to update UI and trigger main iframe to update
|
|
|
|
// use unary to prevent index from being passed to func
|
|
|
|
.map(_.unary(challengeUpdated));
|
2017-07-31 20:04:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// used to reset users code on request
|
|
|
|
export function resetChallengeEpic(actions, { getState }) {
|
2017-11-09 17:10:30 -08:00
|
|
|
return actions::ofType(types.clickOnReset)
|
2017-12-07 16:13:19 -08:00
|
|
|
.map(_.flow(getState, challengeSelector, challengeUpdated));
|
2017-07-31 20:04:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
export function nextChallengeEpic(actions, { getState }) {
|
|
|
|
return actions::ofType(types.moveToNextChallenge)
|
2016-06-09 16:02:51 -07:00
|
|
|
.flatMap(() => {
|
2018-02-24 08:44:12 +00:00
|
|
|
const state = getState();
|
|
|
|
const lang = langSelector(state);
|
|
|
|
const { nextChallenge } = nextChallengeSelector(state);
|
|
|
|
if (!nextChallenge) {
|
|
|
|
return createErrorObservable(
|
|
|
|
new Error('Next Challenge could not be found')
|
2016-07-01 19:05:50 -07:00
|
|
|
);
|
2018-02-24 08:44:12 +00:00
|
|
|
}
|
|
|
|
if (nextChallenge.isLocked) {
|
2016-06-10 14:01:13 -07:00
|
|
|
return Observable.of(
|
2018-02-24 08:44:12 +00:00
|
|
|
makeToast({
|
|
|
|
message: 'The next challenge has not been unlocked. ' +
|
|
|
|
'Please revisit the required (*) challenges ' +
|
|
|
|
'that have not been passed yet. ',
|
|
|
|
timeout: 15000
|
|
|
|
}),
|
|
|
|
onRouteCurrentChallenge()
|
2016-06-10 14:01:13 -07:00
|
|
|
);
|
|
|
|
}
|
2018-02-24 08:44:12 +00:00
|
|
|
return Observable.of(
|
|
|
|
// normally we wouldn't need to add the lang as
|
|
|
|
// addLangToRoutesEnhancer should add langs for us, but the way
|
|
|
|
// enhancers/middlewares and RFR orders things this action will not
|
|
|
|
// see addLangToRoutesEnhancer and cause RFR to render NotFound
|
|
|
|
onRouteChallenges({ lang, ...nextChallenge }),
|
|
|
|
makeToast({ message: 'Your next challenge has arrived.' })
|
|
|
|
);
|
2016-06-09 16:02:51 -07:00
|
|
|
});
|
|
|
|
}
|
2017-07-31 20:04:01 -07:00
|
|
|
|
|
|
|
export default combineEpics(
|
|
|
|
challengeUpdatedEpic,
|
|
|
|
nextChallengeEpic,
|
|
|
|
resetChallengeEpic
|
|
|
|
);
|