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,
|
2018-06-12 16:42:39 +01:00
|
|
|
closeModal,
|
2018-09-21 17:30:16 +03:00
|
|
|
challengeFilesSelector,
|
|
|
|
updateProjectFormValues
|
2018-04-06 14:51:52 +01:00
|
|
|
} from './';
|
2018-06-07 23:13:33 +01:00
|
|
|
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);
|
2018-06-12 16:42:39 +01:00
|
|
|
const files = challengeFilesSelector(state);
|
2018-05-24 19:45:38 +01:00
|
|
|
const { username } = userSelector(state);
|
|
|
|
return postChallenge(
|
|
|
|
'/external/modern-challenge-completed',
|
|
|
|
username,
|
|
|
|
csrfToken,
|
|
|
|
{
|
2018-06-12 16:42:39 +01:00
|
|
|
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
|
|
|
|
2018-06-07 23:13:33 +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);
|
2018-06-07 23:13:33 +01:00
|
|
|
const { isDonating } = userSelector(state);
|
2018-05-24 19:45:38 +01:00
|
|
|
const { nextChallengePath, introPath, challengeType } = meta;
|
2018-06-07 23:13:33 +01:00
|
|
|
const showDonate = isDonating ? empty() : shouldShowDonate(state);
|
2018-05-24 19:45:38 +01:00
|
|
|
const closeChallengeModal = of(closeModal('completion'));
|
2018-05-26 21:32:40 +01:00
|
|
|
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),
|
2018-06-07 23:13:33 +01:00
|
|
|
concat(showDonate),
|
2018-05-24 19:45:38 +01:00
|
|
|
filter(Boolean)
|
|
|
|
);
|
2018-04-06 14:51:52 +01:00
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|