2016-06-01 15:52:08 -07:00
|
|
|
import { Observable } from 'rx';
|
2016-07-06 11:47:16 -07:00
|
|
|
|
2016-06-01 15:52:08 -07:00
|
|
|
import types from './types';
|
2016-08-18 19:33:24 -07:00
|
|
|
import {
|
|
|
|
moveToNextChallenge,
|
|
|
|
clearSavedCode
|
|
|
|
} from './actions';
|
2016-06-01 15:52:08 -07:00
|
|
|
|
|
|
|
import { challengeSelector } from './selectors';
|
2016-06-08 11:11:13 -07:00
|
|
|
import { randomCompliment } from '../../../utils/get-words';
|
2016-08-11 16:41:03 -07:00
|
|
|
import {
|
|
|
|
createErrorObservable,
|
|
|
|
updateUserPoints,
|
|
|
|
updateUserChallenge
|
|
|
|
} from '../../../redux/actions';
|
2016-07-08 12:06:02 -07:00
|
|
|
import { backEndProject } from '../../../utils/challengeTypes';
|
2016-08-11 16:41:03 -07:00
|
|
|
import { makeToast } from '../../../toasts/redux/actions';
|
2016-06-01 15:52:08 -07:00
|
|
|
import { postJSON$ } from '../../../../utils/ajax-stream';
|
|
|
|
|
2016-08-11 16:41:03 -07:00
|
|
|
function postChallenge(url, username, _csrf, challengeInfo) {
|
|
|
|
const body = { ...challengeInfo, _csrf };
|
2016-06-20 21:25:27 -07:00
|
|
|
const saveChallenge$ = postJSON$(url, body)
|
2016-06-01 15:52:08 -07:00
|
|
|
.retry(3)
|
2016-08-11 16:41:03 -07:00
|
|
|
.flatMap(({ points, lastUpdated, completedDate }) => {
|
|
|
|
return Observable.of(
|
|
|
|
updateUserPoints(username, points),
|
|
|
|
updateUserChallenge(
|
|
|
|
username,
|
|
|
|
{ ...challengeInfo, lastUpdated, completedDate }
|
2016-08-18 19:33:24 -07:00
|
|
|
),
|
|
|
|
clearSavedCode()
|
2016-08-11 16:41:03 -07:00
|
|
|
);
|
2016-06-01 15:52:08 -07:00
|
|
|
})
|
|
|
|
.catch(createErrorObservable);
|
2016-07-08 12:06:02 -07:00
|
|
|
const challengeCompleted$ = Observable.of(moveToNextChallenge());
|
2016-06-01 15:52:08 -07:00
|
|
|
return Observable.merge(saveChallenge$, challengeCompleted$);
|
|
|
|
}
|
|
|
|
|
2016-06-07 20:41:42 -07:00
|
|
|
function submitModern(type, state) {
|
|
|
|
const { tests } = state.challengesApp;
|
|
|
|
if (tests.length > 0 && tests.every(test => test.pass && !test.err)) {
|
|
|
|
if (type === types.checkChallenge) {
|
|
|
|
return Observable.of(
|
2016-07-06 11:47:16 -07:00
|
|
|
makeToast({
|
2016-07-08 12:06:02 -07:00
|
|
|
message: `${randomCompliment()} Go to next challenge.`,
|
2016-07-06 11:47:16 -07:00
|
|
|
action: 'Submit',
|
2016-07-08 12:06:02 -07:00
|
|
|
actionCreator: 'submitChallenge',
|
|
|
|
timeout: 10000
|
2016-07-06 11:47:16 -07:00
|
|
|
})
|
2016-06-07 20:41:42 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type === types.submitChallenge) {
|
2016-06-20 21:25:27 -07:00
|
|
|
const { challenge: { id } } = challengeSelector(state);
|
|
|
|
const {
|
|
|
|
app: { user, csrfToken },
|
|
|
|
challengesApp: { files }
|
|
|
|
} = state;
|
2016-08-11 16:41:03 -07:00
|
|
|
const challengeInfo = { id, files };
|
|
|
|
return postChallenge(
|
|
|
|
'/modern-challenge-completed',
|
|
|
|
user,
|
|
|
|
csrfToken,
|
|
|
|
challengeInfo
|
|
|
|
);
|
2016-06-07 20:41:42 -07:00
|
|
|
}
|
|
|
|
}
|
2016-09-08 22:22:45 -07:00
|
|
|
return Observable.just(
|
|
|
|
makeToast({ message: 'Keep trying.' })
|
|
|
|
);
|
2016-06-07 20:41:42 -07:00
|
|
|
}
|
|
|
|
|
2016-06-08 11:11:13 -07:00
|
|
|
function submitProject(type, state, { solution, githubLink }) {
|
|
|
|
const {
|
|
|
|
challenge: { id, challengeType }
|
|
|
|
} = challengeSelector(state);
|
|
|
|
const {
|
2016-06-20 11:35:19 -07:00
|
|
|
app: { user, csrfToken }
|
2016-06-08 11:11:13 -07:00
|
|
|
} = state;
|
2016-08-11 16:41:03 -07:00
|
|
|
const challengeInfo = { id, challengeType, solution };
|
2016-06-08 11:11:13 -07:00
|
|
|
if (challengeType === backEndProject) {
|
2016-08-11 16:41:03 -07:00
|
|
|
challengeInfo.githubLink = githubLink;
|
2016-06-08 11:11:13 -07:00
|
|
|
}
|
2016-08-11 16:41:03 -07:00
|
|
|
return postChallenge(
|
|
|
|
'/project-completed',
|
|
|
|
user,
|
|
|
|
csrfToken,
|
|
|
|
challengeInfo
|
|
|
|
);
|
2016-06-07 20:41:42 -07:00
|
|
|
}
|
|
|
|
|
2016-06-08 15:40:32 -07:00
|
|
|
function submitSimpleChallenge(type, state) {
|
2016-06-08 11:26:33 -07:00
|
|
|
const {
|
|
|
|
challenge: { id }
|
|
|
|
} = challengeSelector(state);
|
|
|
|
const {
|
2016-06-20 11:35:19 -07:00
|
|
|
app: { user, csrfToken }
|
2016-06-08 11:26:33 -07:00
|
|
|
} = state;
|
2016-08-11 16:41:03 -07:00
|
|
|
const challengeInfo = { id };
|
|
|
|
return postChallenge(
|
|
|
|
'/challenge-completed',
|
|
|
|
user,
|
|
|
|
csrfToken,
|
|
|
|
challengeInfo
|
|
|
|
);
|
2016-06-08 11:26:33 -07:00
|
|
|
}
|
|
|
|
|
2016-06-07 20:41:42 -07:00
|
|
|
const submitTypes = {
|
|
|
|
tests: submitModern,
|
2016-06-08 15:40:32 -07:00
|
|
|
step: submitSimpleChallenge,
|
2016-06-13 12:26:30 -07:00
|
|
|
video: submitSimpleChallenge,
|
2016-06-08 11:11:13 -07:00
|
|
|
'project.frontEnd': submitProject,
|
2016-06-08 11:26:33 -07:00
|
|
|
'project.backEnd': submitProject,
|
2016-06-08 15:40:32 -07:00
|
|
|
'project.simple': submitSimpleChallenge
|
2016-06-07 20:41:42 -07:00
|
|
|
};
|
|
|
|
|
2016-06-01 15:52:08 -07:00
|
|
|
export default function completionSaga(actions$, getState) {
|
|
|
|
return actions$
|
|
|
|
.filter(({ type }) => (
|
|
|
|
type === types.checkChallenge ||
|
2016-06-09 16:02:51 -07:00
|
|
|
type === types.submitChallenge
|
2016-06-01 15:52:08 -07:00
|
|
|
))
|
2016-06-07 20:41:42 -07:00
|
|
|
.flatMap(({ type, payload }) => {
|
2016-06-01 15:52:08 -07:00
|
|
|
const state = getState();
|
2016-06-07 20:41:42 -07:00
|
|
|
const { submitType } = challengeSelector(state);
|
|
|
|
const submitter = submitTypes[submitType] ||
|
|
|
|
(() => Observable.just(null));
|
|
|
|
return submitter(type, state, payload);
|
2016-06-01 15:52:08 -07:00
|
|
|
});
|
|
|
|
}
|