Files
freeCodeCamp/client/src/templates/Challenges/redux/index.js

331 lines
9.2 KiB
JavaScript
Raw Normal View History

2018-04-06 14:51:52 +01:00
import { createAction, handleActions } from 'redux-actions';
2018-09-21 17:30:16 +03:00
import { reducer as reduxFormReducer } from 'redux-form';
2018-09-27 14:19:03 +03:00
import { createTypes } from '../../../../utils/stateManagement';
import { createAsyncTypes } from '../../../utils/createTypes';
2018-04-06 14:51:52 +01:00
import { createPoly } from '../utils/polyvinyl';
import challengeModalEpic from './challenge-modal-epic';
import completionEpic from './completion-epic';
import codeLockEpic from './code-lock-epic';
import createQuestionEpic from './create-question-epic';
import codeStorageEpic from './code-storage-epic';
2018-04-06 14:51:52 +01:00
import { createIdToNameMapSaga } from './id-to-name-map-saga';
2018-11-26 02:17:38 +03:00
import { createExecuteChallengeSaga } from './execute-challenge-saga';
import { createCurrentChallengeSaga } from './current-challenge-saga';
2018-11-29 12:12:15 +00:00
export const ns = 'challenge';
export const backendNS = 'backendChallenge';
2018-04-06 14:51:52 +01:00
const initialState = {
challengeFiles: {},
challengeIdToNameMap: {},
2018-04-06 14:51:52 +01:00
challengeMeta: {
id: '',
nextChallengePath: '/'
},
challengeTests: [],
consoleOut: '',
isCodeLocked: false,
2018-04-06 14:51:52 +01:00
isJSEnabled: true,
modal: {
completion: false,
help: false,
video: false,
reset: false
2018-04-06 14:51:52 +01:00
},
2018-09-27 14:19:03 +03:00
projectFormValues: {},
2018-04-06 14:51:52 +01:00
successMessage: 'Happy Coding!'
};
export const types = createTypes(
[
'createFiles',
'createQuestion',
2018-04-06 14:51:52 +01:00
'initTests',
'initConsole',
'initLogs',
2018-04-06 14:51:52 +01:00
'updateConsole',
'updateChallengeMeta',
'updateFile',
'updateJSEnabled',
2018-05-24 19:45:38 +01:00
'updateProjectFormValues',
2018-04-06 14:51:52 +01:00
'updateSuccessMessage',
'updateTests',
'updateLogs',
'logsToConsole',
'lockCode',
'unlockCode',
2018-04-06 14:51:52 +01:00
'disableJSOnError',
'storedCodeFound',
'noStoredCodeFound',
2018-04-06 14:51:52 +01:00
'closeModal',
'openModal',
'previewMounted',
'challengeMounted',
2018-04-06 14:51:52 +01:00
'checkChallenge',
'executeChallenge',
'resetChallenge',
'submitChallenge',
'moveToTab',
...createAsyncTypes('fetchIdToNameMap')
2018-04-06 14:51:52 +01:00
],
ns
);
export const epics = [
challengeModalEpic,
codeLockEpic,
completionEpic,
createQuestionEpic,
codeStorageEpic
];
2018-11-26 02:17:38 +03:00
export const sagas = [
...createIdToNameMapSaga(types),
...createExecuteChallengeSaga(types),
...createCurrentChallengeSaga(types)
2018-11-26 02:17:38 +03:00
];
2018-04-06 14:51:52 +01:00
export const createFiles = createAction(types.createFiles, challengeFiles =>
Object.keys(challengeFiles)
.filter(key => challengeFiles[key])
.map(key => challengeFiles[key])
.reduce(
(challengeFiles, file) => ({
...challengeFiles,
[file.key]: {
...createPoly(file),
seed: file.contents.slice(0)
}
}),
{}
)
);
export const fetchIdToNameMap = createAction(types.fetchIdToNameMap);
export const fetchIdToNameMapComplete = createAction(
types.fetchIdToNameMapComplete
);
export const fetchIdToNameMapError = createAction(types.fetchIdToNameMapError);
export const createQuestion = createAction(types.createQuestion);
2018-04-06 14:51:52 +01:00
export const initTests = createAction(types.initTests);
export const updateTests = createAction(types.updateTests);
export const initConsole = createAction(types.initConsole);
export const initLogs = createAction(types.initLogs);
2018-04-06 14:51:52 +01:00
export const updateChallengeMeta = createAction(types.updateChallengeMeta);
export const updateFile = createAction(types.updateFile);
export const updateConsole = createAction(types.updateConsole);
export const updateLogs = createAction(types.updateLogs);
2018-04-06 14:51:52 +01:00
export const updateJSEnabled = createAction(types.updateJSEnabled);
2018-05-24 19:45:38 +01:00
export const updateProjectFormValues = createAction(
types.updateProjectFormValues
);
2018-04-06 14:51:52 +01:00
export const updateSuccessMessage = createAction(types.updateSuccessMessage);
export const logsToConsole = createAction(types.logsToConsole);
export const lockCode = createAction(types.lockCode);
export const unlockCode = createAction(types.unlockCode);
export const disableJSOnError = createAction(types.disableJSOnError);
export const storedCodeFound = createAction(types.storedCodeFound);
export const noStoredCodeFound = createAction(types.noStoredCodeFound);
2018-04-06 14:51:52 +01:00
export const closeModal = createAction(types.closeModal);
export const openModal = createAction(types.openModal);
export const previewMounted = createAction(types.previewMounted);
export const challengeMounted = createAction(types.challengeMounted);
2018-04-06 14:51:52 +01:00
export const checkChallenge = createAction(types.checkChallenge);
export const executeChallenge = createAction(types.executeChallenge);
export const resetChallenge = createAction(types.resetChallenge);
2018-04-06 14:51:52 +01:00
export const submitChallenge = createAction(types.submitChallenge);
export const moveToTab = createAction(types.moveToTab);
export const currentTabSelector = state => state[ns].currentTab;
2018-04-06 14:51:52 +01:00
export const challengeFilesSelector = state => state[ns].challengeFiles;
export const challengeIdToNameMapSelector = state =>
state[ns].challengeIdToNameMap;
2018-04-06 14:51:52 +01:00
export const challengeMetaSelector = state => state[ns].challengeMeta;
export const challengeTestsSelector = state => state[ns].challengeTests;
export const consoleOutputSelector = state => state[ns].consoleOut;
export const isCodeLockedSelector = state => state[ns].isCodeLocked;
2018-04-06 14:51:52 +01:00
export const isCompletionModalOpenSelector = state =>
2018-04-11 14:51:47 +01:00
state[ns].modal.completion;
export const isHelpModalOpenSelector = state => state[ns].modal.help;
export const isVideoModalOpenSelector = state => state[ns].modal.video;
export const isResetModalOpenSelector = state => state[ns].modal.reset;
2018-04-11 14:43:23 +01:00
export const isJSEnabledSelector = state => state[ns].isJSEnabled;
2018-04-06 14:51:52 +01:00
export const successMessageSelector = state => state[ns].successMessage;
2018-05-24 19:45:38 +01:00
export const backendFormValuesSelector = state => state.form[backendNS];
2018-09-27 14:19:03 +03:00
export const projectFormValuesSelector = state =>
state[ns].projectFormValues || {};
2018-05-24 19:45:38 +01:00
2018-04-06 14:51:52 +01:00
export const reducer = handleActions(
{
[types.fetchIdToNameMapComplete]: (state, { payload }) => ({
...state,
challengeIdToNameMap: payload
}),
2018-04-06 14:51:52 +01:00
[types.createFiles]: (state, { payload }) => ({
...state,
challengeFiles: payload
}),
[types.updateFile]: (state, { payload: { key, editorValue } }) => ({
...state,
challengeFiles: {
...state.challengeFiles,
[key]: {
...state.challengeFiles[key],
contents: editorValue
}
}
}),
[types.storedCodeFound]: (state, { payload }) => ({
...state,
challengeFiles: payload
}),
2018-04-06 14:51:52 +01:00
[types.initTests]: (state, { payload }) => ({
...state,
challengeTests: payload
}),
[types.updateTests]: (state, { payload }) => ({
...state,
challengeTests: payload
}),
2018-04-06 14:51:52 +01:00
[types.initConsole]: (state, { payload }) => ({
...state,
consoleOut: payload
}),
[types.updateConsole]: (state, { payload }) => ({
...state,
consoleOut: state.consoleOut + '\n' + payload
}),
[types.initLogs]: state => ({
...state,
logsOut: []
}),
[types.updateLogs]: (state, { payload }) => ({
...state,
logsOut: [...state.logsOut, payload]
}),
[types.logsToConsole]: (state, { payload }) => ({
...state,
consoleOut:
state.consoleOut +
(state.logsOut.length
? '\n' + payload + '\n' + state.logsOut.join('\n')
: '')
}),
[types.updateChallengeMeta]: (state, { payload }) => ({
...state,
challengeMeta: { ...payload }
}),
[types.resetChallenge]: state => ({
2018-04-06 14:51:52 +01:00
...state,
currentTab: 2,
2018-04-06 14:51:52 +01:00
challengeFiles: {
...Object.keys(state.challengeFiles)
.map(key => state.challengeFiles[key])
.reduce(
(files, file) => ({
...files,
[file.key]: {
...file,
contents: file.seed.slice()
}
}),
{}
)
},
challengeTests: state.challengeTests.map(({ text, testString }) => ({
text,
testString
})),
consoleOut: ''
2018-04-06 14:51:52 +01:00
}),
2018-05-24 19:45:38 +01:00
[types.updateProjectFormValues]: (state, { payload }) => ({
...state,
2018-09-27 14:19:03 +03:00
projectFormValues: payload
2018-05-24 19:45:38 +01:00
}),
[types.lockCode]: state => ({
...state,
isCodeLocked: true
}),
[types.unlockCode]: state => ({
...state,
isJSEnabled: true,
isCodeLocked: false
}),
[types.disableJSOnError]: (state, { payload }) => ({
2018-04-06 14:51:52 +01:00
...state,
consoleOut: state.consoleOut + ' \n' + payload,
2018-04-06 14:51:52 +01:00
isJSEnabled: false
}),
2018-04-06 14:51:52 +01:00
[types.updateSuccessMessage]: (state, { payload }) => ({
...state,
successMessage: payload
}),
[types.closeModal]: (state, { payload }) => ({
...state,
modal: {
...state.modal,
[payload]: false
}
}),
[types.openModal]: (state, { payload }) => ({
...state,
modal: {
...state.modal,
[payload]: true
}
}),
[types.moveToTab]: (state, { payload }) => ({
...state,
currentTab: payload
}),
[types.executeChallenge]: state => ({
...state,
currentTab: 3
2018-04-06 14:51:52 +01:00
})
},
initialState
);
2018-09-21 17:30:16 +03:00
const resetProjectFormValues = handleActions(
{
2018-09-21 17:30:16 +03:00
[types.updateProjectFormValues]: (state, { payload: { solution } }) => {
if (!solution) {
return {
...state,
solution: {},
githubLink: {}
};
}
return state;
}
},
{}
);
export const formReducer = reduxFormReducer.plugin({
'frond-end-form': resetProjectFormValues,
'back-end-form': resetProjectFormValues
});