Files
freeCodeCamp/packages/learn/src/templates/Challenges/redux/completion-epic.js

169 lines
4.5 KiB
JavaScript
Raw Normal View History

2018-04-06 14:51:52 +01:00
import { of } from 'rxjs/observable/of';
2018-05-24 19:45:38 +01:00
import { empty } from 'rxjs/observable/empty';
import {
switchMap,
retry,
map,
catchError,
concat,
2018-09-12 15:58:08 +03:00
filter,
tap
2018-05-24 19:45:38 +01:00
} from 'rxjs/operators';
2018-04-06 14:51:52 +01:00
import { ofType } from 'redux-observable';
2018-09-12 15:58:08 +03:00
import { navigate } from 'gatsby';
2018-04-06 14:51:52 +01:00
2018-05-24 19:45:38 +01:00
import { _csrf as csrfToken } from '../../../redux/cookieVaules';
2018-04-06 14:51:52 +01:00
import {
2018-05-24 19:45:38 +01:00
backendFormValuesSelector,
projectFormVaulesSelector,
submitComplete,
2018-04-06 14:51:52 +01:00
types,
2018-05-24 19:45:38 +01:00
challengeMetaSelector,
challengeTestsSelector,
closeModal,
2018-09-21 17:30:16 +03:00
challengeFilesSelector,
updateProjectFormValues
2018-04-06 14:51:52 +01:00
} from './';
import {
userSelector,
isSignedInSelector,
openDonationModal,
shouldShowDonationSelector
} from '../../../redux/app';
2018-04-06 14:51:52 +01:00
2018-05-24 19:45:38 +01:00
import { postJSON$ } from '../utils/ajax-stream';
import { challengeTypes, submitTypes } from '../../../../utils/challengeTypes';
2018-04-06 14:51:52 +01:00
2018-05-24 19:45:38 +01:00
function postChallenge(url, username, _csrf, challengeInfo) {
const body = { ...challengeInfo, _csrf };
const saveChallenge = postJSON$(url, body).pipe(
retry(3),
map(({ points }) =>
submitComplete({
username,
points,
...challengeInfo
})
),
catchError(err => {
console.error(err);
return of({ type: 'here is an error' });
})
);
return saveChallenge;
}
2018-04-06 14:51:52 +01:00
2018-05-24 19:45:38 +01:00
function submitModern(type, state) {
const tests = challengeTestsSelector(state);
if (tests.length > 0 && tests.every(test => test.pass && !test.err)) {
if (type === types.checkChallenge) {
return of({ type: 'this was a check challenge' });
}
2018-04-06 14:51:52 +01:00
2018-05-24 19:45:38 +01:00
if (type === types.submitChallenge) {
const { id } = challengeMetaSelector(state);
const files = challengeFilesSelector(state);
2018-05-24 19:45:38 +01:00
const { username } = userSelector(state);
return postChallenge(
'/external/modern-challenge-completed',
username,
csrfToken,
{
id,
files
2018-05-24 19:45:38 +01:00
}
);
}
}
return empty();
}
2018-04-06 14:51:52 +01:00
2018-05-24 19:45:38 +01:00
function submitProject(type, state) {
if (type === types.checkChallenge) {
return empty();
}
2018-04-06 14:51:52 +01:00
2018-05-24 19:45:38 +01:00
const { solution, githubLink } = projectFormVaulesSelector(state);
const { id, challengeType } = challengeMetaSelector(state);
const { username } = userSelector(state);
const challengeInfo = { id, challengeType, solution };
if (challengeType === challengeTypes.backEndProject) {
challengeInfo.githubLink = githubLink;
}
return postChallenge(
'/external/project-completed',
username,
csrfToken,
challengeInfo
2018-09-21 17:30:16 +03:00
).pipe(
concat(of(updateProjectFormValues({})))
2018-05-24 19:45:38 +01:00
);
}
2018-04-06 14:51:52 +01:00
2018-05-24 19:45:38 +01:00
function submitBackendChallenge(type, state) {
const tests = challengeTestsSelector(state);
if (tests.length > 0 && tests.every(test => test.pass && !test.err)) {
if (type === types.submitChallenge) {
const { id } = challengeMetaSelector(state);
const { username } = userSelector(state);
const { solution: { value: solution } } = backendFormValuesSelector(
state
);
const challengeInfo = { id, solution };
return postChallenge(
'/external/backend-challenge-completed',
username,
csrfToken,
challengeInfo
);
}
}
return empty();
}
2018-04-06 14:51:52 +01:00
2018-05-24 19:45:38 +01:00
const submitters = {
tests: submitModern,
backend: submitBackendChallenge,
'project.frontEnd': submitProject,
'project.backEnd': submitProject
};
2018-04-06 14:51:52 +01:00
function shouldShowDonate(state) {
return shouldShowDonationSelector(state) ? of(openDonationModal()) : empty();
}
2018-04-06 14:51:52 +01:00
export default function completionEpic(action$, { getState }) {
return action$.pipe(
ofType(types.submitChallenge),
switchMap(({ type }) => {
2018-05-24 19:45:38 +01:00
const state = getState();
const meta = challengeMetaSelector(state);
const { isDonating } = userSelector(state);
2018-05-24 19:45:38 +01:00
const { nextChallengePath, introPath, challengeType } = meta;
const showDonate = isDonating ? empty() : shouldShowDonate(state);
2018-05-24 19:45:38 +01:00
const closeChallengeModal = of(closeModal('completion'));
let submitter = () => of({ type: 'no-user-signed-in' });
2018-05-24 19:45:38 +01:00
if (
!(challengeType in submitTypes) ||
!(submitTypes[challengeType] in submitters)
) {
throw new Error(
'Unable to find the correct submit function for challengeType ' +
challengeType
);
}
if (isSignedInSelector(state)) {
submitter = submitters[submitTypes[challengeType]];
}
return submitter(type, state).pipe(
2018-09-12 15:58:08 +03:00
tap(() => navigate(introPath ? introPath : nextChallengePath)),
2018-05-24 19:45:38 +01:00
concat(closeChallengeModal),
concat(showDonate),
2018-05-24 19:45:38 +01:00
filter(Boolean)
);
2018-04-06 14:51:52 +01:00
})
);
}