Files
freeCodeCamp/common/app/redux/fetch-challenges-epic.js
2018-02-24 08:51:35 +00:00

120 lines
3.1 KiB
JavaScript

import { Observable } from 'rx';
import { combineEpics, ofType } from 'redux-epic';
import debug from 'debug';
import {
types,
createErrorObservable,
delayedRedirect,
fetchChallengeCompleted,
fetchChallengesCompleted,
fetchNewBlock,
challengeSelector,
nextChallengeSelector
} from './';
import {
isChallengeLoaded,
fullBlocksSelector
} from '../entities';
import { shapeChallenges } from './utils';
import { types as challenge } from '../routes/Challenges/redux';
import { langSelector } from '../Router/redux';
const isDev = debug.enabled('fcc:*');
function fetchChallengeEpic(actions, { getState }, { services }) {
return actions::ofType(challenge.onRouteChallenges)
.filter(({ payload }) => !isChallengeLoaded(getState(), payload))
.flatMapLatest(({ payload: params }) => {
const options = {
service: 'challenge',
params
};
return services.readService$(options)
.retry(3)
.map(({ entities, ...rest }) => ({
entities: shapeChallenges(entities, isDev),
...rest
}))
.flatMap(({ entities, result, redirect } = {}) => {
const actions = [
fetchChallengeCompleted({
entities,
currentChallenge: result.challenge,
challenge: entities.challenge[result.challenge],
result
}),
redirect ? delayedRedirect(redirect) : null
];
return Observable.from(actions).filter(Boolean);
})
.catch(createErrorObservable);
});
}
export function fetchChallengesForBlockEpic(
actions,
{ getState },
{ services }
) {
return actions::ofType(
types.appMounted,
types.updateChallenges,
types.fetchNewBlock.start
)
.flatMapLatest(({ type, payload }) => {
const fetchAnotherBlock = type === types.fetchNewBlock.start;
const state = getState();
let { block: blockName } = challengeSelector(state);
const lang = langSelector(state);
if (fetchAnotherBlock) {
const fullBlocks = fullBlocksSelector(state);
if (fullBlocks.includes(payload)) {
return Observable.of(null);
}
blockName = payload;
}
const options = {
params: { lang, blockName },
service: 'challenge'
};
return services.readService$(options)
.retry(3)
.map(fetchChallengesCompleted)
.startWith({ type: types.fetchChallenges.start })
.catch(createErrorObservable);
})
.filter(Boolean);
}
function fetchChallengesForNextBlockEpic(action$, { getState }) {
return action$::ofType(challenge.checkForNextBlock)
.map(() => {
const {
nextChallenge,
isNewBlock,
isNewSuperBlock
} = nextChallengeSelector(getState());
const isNewBlockRequired = (
(isNewBlock || isNewSuperBlock) &&
nextChallenge &&
!nextChallenge.description
);
return isNewBlockRequired ?
fetchNewBlock(nextChallenge.block) :
null;
})
.filter(Boolean);
}
export default combineEpics(
fetchChallengeEpic,
fetchChallengesForBlockEpic,
fetchChallengesForNextBlockEpic
);