Files
freeCodeCamp/common/app/routes/challenges/redux/reducer.js

340 lines
8.6 KiB
JavaScript
Raw Normal View History

import { handleActions } from 'redux-actions';
2016-05-11 18:38:08 -07:00
import { createPoly } from '../../../../utils/polyvinyl';
import types from './types';
2016-06-07 20:41:42 -07:00
import { bonfire, html, js } from '../../../utils/challengeTypes';
2016-05-20 12:42:26 -07:00
import {
arrayToString,
buildSeed,
createTests,
getPreFile,
getFileKey,
toggleThisPanel,
collapseAllPanels,
expandAllPanels
2016-05-20 12:42:26 -07:00
} from '../utils';
const initialUiState = {
hintIndex: 0,
// step index tracing
currentIndex: 0,
previousIndex: -1,
// step action
isActionCompleted: false,
isLightBoxOpen: false,
// project is ready to submit
isSubmitting: false,
2016-06-10 14:01:13 -07:00
output: `/**
* Your output will go here.
* Any console.log() - type
2016-06-10 14:01:13 -07:00
* statements will appear in
* your browser\'s DevTools
* JavaScript console as well.
*/`,
// video
// 1 indexed
currentQuestion: 1,
// [ xPosition, yPosition ]
mouse: [ 0, 0 ],
// change in mouse position since pressed
// [ xDelta, yDelta ]
delta: [ 0, 0 ],
isPressed: false,
isCorrect: false,
shouldShakeQuestion: false,
2016-10-17 21:38:16 +01:00
shouldShowQuestions: false,
isChallengeModalOpen: false,
2016-10-22 20:09:23 +01:00
successMessage: 'Happy Coding!',
unlockedSteps: []
};
const initialState = {
isCodeLocked: false,
2016-05-27 22:07:10 -07:00
id: '',
challenge: '',
2016-07-11 17:44:50 -07:00
helpChatRoom: 'Help',
isBugOpen: false,
// old code storage key
2016-05-27 22:07:10 -07:00
legacyKey: '',
files: {},
2016-06-08 18:48:30 -07:00
// map
mapUi: { isAllCollapsed: false },
filter: '',
2016-06-01 15:52:08 -07:00
superBlocks: [],
2016-06-08 18:48:30 -07:00
// misc
toast: 0,
...initialUiState
};
2016-05-11 18:38:08 -07:00
const mainReducer = handleActions(
{
2016-05-10 21:25:12 -07:00
[types.fetchChallengeCompleted]: (state, { payload = '' }) => ({
...state,
challenge: payload
}),
[types.updateCurrentChallenge]: (state, { payload: challenge = {} }) => ({
...state,
2016-05-27 22:07:10 -07:00
id: challenge.id,
// used mainly to find code storage
legacyKey: challenge.name,
challenge: challenge.dashedName,
2016-05-20 12:42:26 -07:00
key: getFileKey(challenge),
tests: createTests(challenge),
2016-07-11 17:44:50 -07:00
helpChatRoom: challenge.helpRoom || 'Help',
numOfHints: Array.isArray(challenge.hints) ? challenge.hints.length : 0
}),
[types.updateTests]: (state, { payload: tests }) => ({
...state,
2016-10-17 21:38:16 +01:00
tests,
isChallengeModalOpen: (
tests.length > 0 &&
tests.every(test => test.pass && !test.err)
)
}),
[types.closeChallengeModal]: state => ({
...state,
isChallengeModalOpen: false
}),
[types.updateSuccessMessage]: (state, { payload }) => ({
...state,
successMessage: payload
}),
[types.updateHint]: state => ({
...state,
hintIndex: state.hintIndex + 1 >= state.numOfHints ?
0 :
state.hintIndex + 1
}),
[types.lockUntrustedCode]: state => ({
...state,
isCodeLocked: true
}),
[types.unlockUntrustedCode]: state => ({
...state,
isCodeLocked: false
}),
[types.executeChallenge]: state => ({
...state,
tests: state.tests.map(test => ({ ...test, err: false, pass: false }))
}),
2016-06-01 15:52:08 -07:00
[types.showChallengeComplete]: (state, { payload: toast }) => ({
...state,
toast
}),
2016-06-07 20:41:42 -07:00
[types.showProjectSubmit]: state => ({
...state,
isSubmitting: true
}),
[types.resetUi]: (state) => ({
...state,
...initialUiState
}),
// map
[types.updateFilter]: (state, { payload = ''}) => ({
...state,
filter: payload
}),
[types.clearFilter]: (state) => ({
...state,
filter: ''
}),
[types.fetchChallengesCompleted]: (state, { payload = [] }) => ({
...state,
superBlocks: payload
}),
// step
2016-10-22 20:09:23 +01:00
[types.goToStep]: (state, { payload: { step = 0, isUnlocked }}) => ({
...state,
2016-06-08 18:48:30 -07:00
currentIndex: step,
previousIndex: state.currentIndex,
2016-10-22 20:09:23 +01:00
isActionCompleted: isUnlocked
2016-06-08 18:48:30 -07:00
}),
[types.completeAction]: state => ({
...state,
isActionCompleted: true
}),
2016-10-22 20:09:23 +01:00
[types.updateUnlockedSteps]: (state, { payload }) => ({
...state,
unlockedSteps: payload
}),
[types.openLightBoxImage]: state => ({
...state,
isLightBoxOpen: true
}),
[types.closeLightBoxImage]: state => ({
...state,
isLightBoxOpen: false
}),
2016-06-08 18:48:30 -07:00
// classic/modern
[types.initOutput]: (state, { payload: output }) => ({
...state,
output
}),
[types.updateOutput]: (state, { payload: output }) => ({
...state,
output: (state.output || '') + output
}),
// video
[types.toggleQuestionView]: state => ({
...state,
shouldShowQuestions: !state.shouldShowQuestions,
currentQuestion: 1
}),
[types.grabQuestion]: (state, { payload: { delta, mouse } }) => ({
...state,
isPressed: true,
delta,
mouse
}),
[types.releaseQuestion]: state => ({
...state,
isPressed: false,
mouse: [ 0, 0 ]
}),
[types.moveQuestion]: (state, { payload: mouse }) => ({ ...state, mouse }),
[types.startShake]: state => ({ ...state, shouldShakeQuestion: true }),
[types.endShake]: state => ({ ...state, shouldShakeQuestion: false }),
[types.primeNextQuestion]: (state, { payload: userAnswer }) => ({
...state,
currentQuestion: state.currentQuestion + 1,
mouse: [ userAnswer ? 1000 : -1000, 0],
isPressed: false
}),
[types.goToNextQuestion]: state => ({
...state,
mouse: [ 0, 0 ]
}),
[types.videoCompleted]: (state, { payload: userAnswer }) => ({
...state,
isCorrect: true,
isPressed: false,
delta: [ 0, 0 ],
mouse: [ userAnswer ? 1000 : -1000, 0]
}),
[types.openBugModal]: state => ({ ...state, isBugOpen: true }),
[types.closeBugModal]: state => ({ ...state, isBugOpen: false })
},
initialState
);
2016-05-11 18:38:08 -07:00
const filesReducer = handleActions(
{
[types.updateFile]: (state, { payload: file }) => ({
...state,
2016-05-20 12:42:26 -07:00
[file.key]: file
2016-05-11 18:38:08 -07:00
}),
[types.updateFiles]: (state, { payload: files }) => {
return files
.reduce((files, file) => {
2016-05-20 12:42:26 -07:00
files[file.key] = file;
2016-05-11 18:38:08 -07:00
return files;
}, { ...state });
},
[types.savedCodeFound]: (state, { payload: { files, challenge } }) => {
if (challenge.type === 'mod') {
// this may need to change to update head/tail
return challenge.files;
}
if (
challenge.challengeType !== html &&
challenge.challengeType !== js &&
challenge.challengeType !== bonfire
) {
return {};
}
// classic challenge to modern format
const preFile = getPreFile(challenge);
return {
[preFile.key]: createPoly({
...files[preFile.key],
// make sure head/tail are always fresh
head: arrayToString(challenge.head),
tail: arrayToString(challenge.tail)
})
};
},
2016-09-09 01:19:21 -07:00
[types.updateCurrentChallenge]: (state, { payload: challenge = {} }) => {
if (challenge.type === 'mod') {
return challenge.files;
}
if (
2016-06-07 20:41:42 -07:00
challenge.challengeType !== html &&
challenge.challengeType !== js &&
challenge.challengeType !== bonfire
) {
return {};
}
// classic challenge to modern format
2016-05-20 12:42:26 -07:00
const preFile = getPreFile(challenge);
2016-05-11 18:38:08 -07:00
return {
2016-05-20 12:42:26 -07:00
[preFile.key]: createPoly({
...preFile,
2016-05-14 16:46:20 -07:00
contents: buildSeed(challenge),
head: arrayToString(challenge.head),
tail: arrayToString(challenge.tail)
})
2016-05-11 18:38:08 -07:00
};
}
},
{}
);
2016-06-21 16:28:13 -07:00
// {
// children: [...{
// name: (superBlock: String),
// isOpen: Boolean,
// isHidden: Boolean,
// children: [...{
// name: (blockName: String),
// isOpen: Boolean,
// isHidden: Boolean,
// children: [...{
// name: (challengeName: String),
// isHidden: Boolean
// }]
// }]
// }
2016-06-21 16:28:13 -07:00
// }
const mapReducer = handleActions(
{
[types.initMap]: (state, { payload }) => payload,
[types.toggleThisPanel]: (state, { payload: name }) => {
return toggleThisPanel(state, name);
},
[types.collapseAll]: state => {
const newState = collapseAllPanels(state);
newState.isAllCollapsed = true;
return newState;
},
[types.expandAll]: state => {
const newState = expandAllPanels(state);
newState.isAllCollapsed = false;
return newState;
}
2016-06-21 16:28:13 -07:00
},
initialState.mapUi
2016-06-21 16:28:13 -07:00
);
2016-05-11 18:38:08 -07:00
export default function challengeReducers(state, action) {
const newState = mainReducer(state, action);
const files = filesReducer(state && state.files || {}, action);
2016-06-21 16:28:13 -07:00
if (newState.files !== files) {
return { ...newState, files };
}
// map actions only effect this reducer;
const mapUi = mapReducer(state && state.mapUi || {}, action);
if (newState.mapUi !== mapUi) {
return { ...newState, mapUi };
2016-06-21 16:28:13 -07:00
}
return newState;
2016-05-11 18:38:08 -07:00
}