Merge pull request #38 from Bouncey/feat/codeStorage
Feat: Add code storage and lesson reset modal
This commit is contained in:
committed by
Mrugesh Mohapatra
parent
68eefa5565
commit
bb6b3869ed
@ -46,6 +46,7 @@
|
|||||||
"redux-observable": "^0.18.0",
|
"redux-observable": "^0.18.0",
|
||||||
"reselect": "^3.0.1",
|
"reselect": "^3.0.1",
|
||||||
"rxjs": "^5.5.7",
|
"rxjs": "^5.5.7",
|
||||||
|
"store": "^2.0.12",
|
||||||
"uglifyjs-webpack-plugin": "^1.2.4",
|
"uglifyjs-webpack-plugin": "^1.2.4",
|
||||||
"validator": "^9.4.1"
|
"validator": "^9.4.1"
|
||||||
},
|
},
|
||||||
|
@ -8,16 +8,16 @@ const clientID = AUTH0_CLIENT_ID;
|
|||||||
|
|
||||||
class Auth {
|
class Auth {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.auth0 = new auth0.WebAuth({
|
this.auth0 = new auth0.WebAuth({
|
||||||
domain,
|
domain,
|
||||||
clientID,
|
clientID,
|
||||||
redirectUri: `${
|
redirectUri: `${
|
||||||
typeof window !== 'undefined' ? window.location.origin : ''
|
typeof window !== 'undefined' ? window.location.origin : ''
|
||||||
}/auth-callback`,
|
}/auth-callback`,
|
||||||
audience: `https://${domain}/api/v2/`,
|
audience: `https://${domain}/api/v2/`,
|
||||||
responseType: 'token id_token',
|
responseType: 'token id_token',
|
||||||
scope: `openid profile email ${namespace + 'accountLinkId'}`
|
scope: `openid profile email ${namespace + 'accountLinkId'}`
|
||||||
});
|
});
|
||||||
|
|
||||||
this.getUser = this.getUser.bind(this);
|
this.getUser = this.getUser.bind(this);
|
||||||
this.getToken = this.getToken.bind(this);
|
this.getToken = this.getToken.bind(this);
|
||||||
|
@ -82,7 +82,6 @@ class Editor extends PureComponent {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { contents, ext } = this.props;
|
const { contents, ext } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='classic-editor editor'>
|
<div className='classic-editor editor'>
|
||||||
<base href='/' />
|
<base href='/' />
|
||||||
|
@ -12,6 +12,7 @@ import Preview from '../components/Preview';
|
|||||||
import SidePanel from '../components/Side-Panel';
|
import SidePanel from '../components/Side-Panel';
|
||||||
import CompletionModal from '../components/CompletionModal';
|
import CompletionModal from '../components/CompletionModal';
|
||||||
import HelpModal from '../components/HelpModal';
|
import HelpModal from '../components/HelpModal';
|
||||||
|
import ResetModal from '../components/ResetModal';
|
||||||
|
|
||||||
import { challengeTypes } from '../../../../utils/challengeTypes';
|
import { challengeTypes } from '../../../../utils/challengeTypes';
|
||||||
import { ChallengeNode } from '../../../redux/propTypes';
|
import { ChallengeNode } from '../../../redux/propTypes';
|
||||||
@ -19,7 +20,8 @@ import {
|
|||||||
createFiles,
|
createFiles,
|
||||||
challengeFilesSelector,
|
challengeFilesSelector,
|
||||||
initTests,
|
initTests,
|
||||||
updateChallengeMeta
|
updateChallengeMeta,
|
||||||
|
challengeMounted
|
||||||
} from '../redux';
|
} from '../redux';
|
||||||
|
|
||||||
import './classic.css';
|
import './classic.css';
|
||||||
@ -29,9 +31,13 @@ const mapStateToProps = createSelector(challengeFilesSelector, files => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch =>
|
const mapDispatchToProps = dispatch =>
|
||||||
bindActionCreators({ createFiles, initTests, updateChallengeMeta }, dispatch);
|
bindActionCreators(
|
||||||
|
{ createFiles, initTests, updateChallengeMeta, challengeMounted },
|
||||||
|
dispatch
|
||||||
|
);
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
|
challengeMounted: PropTypes.func.isRequired,
|
||||||
createFiles: PropTypes.func.isRequired,
|
createFiles: PropTypes.func.isRequired,
|
||||||
data: PropTypes.shape({
|
data: PropTypes.shape({
|
||||||
challengeNode: ChallengeNode
|
challengeNode: ChallengeNode
|
||||||
@ -51,6 +57,7 @@ const propTypes = {
|
|||||||
class ShowClassic extends PureComponent {
|
class ShowClassic extends PureComponent {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const {
|
const {
|
||||||
|
challengeMounted,
|
||||||
createFiles,
|
createFiles,
|
||||||
initTests,
|
initTests,
|
||||||
updateChallengeMeta,
|
updateChallengeMeta,
|
||||||
@ -60,11 +67,13 @@ class ShowClassic extends PureComponent {
|
|||||||
createFiles(files);
|
createFiles(files);
|
||||||
initTests(tests);
|
initTests(tests);
|
||||||
updateChallengeMeta({ ...challengeMeta, title });
|
updateChallengeMeta({ ...challengeMeta, title });
|
||||||
|
challengeMounted(challengeMeta.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
const { data: { challengeNode: { title: prevTitle } } } = prevProps;
|
const { data: { challengeNode: { title: prevTitle } } } = prevProps;
|
||||||
const {
|
const {
|
||||||
|
challengeMounted,
|
||||||
createFiles,
|
createFiles,
|
||||||
initTests,
|
initTests,
|
||||||
updateChallengeMeta,
|
updateChallengeMeta,
|
||||||
@ -77,6 +86,7 @@ class ShowClassic extends PureComponent {
|
|||||||
createFiles(files);
|
createFiles(files);
|
||||||
initTests(tests);
|
initTests(tests);
|
||||||
updateChallengeMeta({ ...challengeMeta, title: currentTitle });
|
updateChallengeMeta({ ...challengeMeta, title: currentTitle });
|
||||||
|
challengeMounted(challengeMeta.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,6 +145,7 @@ class ShowClassic extends PureComponent {
|
|||||||
</ReflexContainer>
|
</ReflexContainer>
|
||||||
<CompletionModal />
|
<CompletionModal />
|
||||||
<HelpModal />
|
<HelpModal />
|
||||||
|
<ResetModal />
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -28,12 +28,10 @@ const mapDispatchToProps = function(dispatch) {
|
|||||||
close: () => dispatch(closeModal('completion')),
|
close: () => dispatch(closeModal('completion')),
|
||||||
handleKeypress: e => {
|
handleKeypress: e => {
|
||||||
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
|
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
|
||||||
console.log('dispatching');
|
|
||||||
dispatch(submitChallenge());
|
dispatch(submitChallenge());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
submitChallenge: () => {
|
submitChallenge: () => {
|
||||||
console.log('dispatching');
|
|
||||||
dispatch(submitChallenge());
|
dispatch(submitChallenge());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { bindActionCreators } from 'redux';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import { Button, Modal } from 'react-bootstrap';
|
||||||
|
|
||||||
|
import { isResetModalOpenSelector, closeModal, resetChallenge } from '../redux';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
close: PropTypes.func.isRequired,
|
||||||
|
isOpen: PropTypes.bool.isRequired,
|
||||||
|
reset: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = createSelector(isResetModalOpenSelector, isOpen => ({
|
||||||
|
isOpen
|
||||||
|
}));
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch =>
|
||||||
|
bindActionCreators(
|
||||||
|
{ close: () => closeModal('reset'), reset: () => resetChallenge() },
|
||||||
|
dispatch
|
||||||
|
);
|
||||||
|
|
||||||
|
function withActions(...fns) {
|
||||||
|
return () => fns.forEach(fn => fn());
|
||||||
|
}
|
||||||
|
|
||||||
|
function ResetModal({ reset, close, isOpen }) {
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
animation={false}
|
||||||
|
dialogClassName={'reset-modal'}
|
||||||
|
keyboard={true}
|
||||||
|
onHide={close}
|
||||||
|
show={isOpen}
|
||||||
|
>
|
||||||
|
<Modal.Header className={'challenge-list-header'} closeButton={true}>
|
||||||
|
<Modal.Title>Reset this lesson?</Modal.Title>
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Body>
|
||||||
|
<div className='text-center'>
|
||||||
|
<p>
|
||||||
|
Are you sure you wish to reset this lesson? The editors and tests
|
||||||
|
will be reset.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<em>This cannot be undone</em>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Modal.Body>
|
||||||
|
<Modal.Footer>
|
||||||
|
<Button
|
||||||
|
block={true}
|
||||||
|
bsSize='large'
|
||||||
|
bsStyle='danger'
|
||||||
|
onClick={withActions(reset, close)}
|
||||||
|
>
|
||||||
|
Reset this Lesson
|
||||||
|
</Button>
|
||||||
|
</Modal.Footer>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResetModal.displayName = 'ResetModal';
|
||||||
|
ResetModal.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(ResetModal);
|
@ -17,7 +17,6 @@ import {
|
|||||||
consoleOutputSelector,
|
consoleOutputSelector,
|
||||||
challengeTestsSelector,
|
challengeTestsSelector,
|
||||||
executeChallenge,
|
executeChallenge,
|
||||||
resetChallenge,
|
|
||||||
initConsole,
|
initConsole,
|
||||||
openModal
|
openModal
|
||||||
} from '../redux';
|
} from '../redux';
|
||||||
@ -32,9 +31,9 @@ const mapDispatchToProps = dispatch =>
|
|||||||
bindActionCreators(
|
bindActionCreators(
|
||||||
{
|
{
|
||||||
executeChallenge,
|
executeChallenge,
|
||||||
resetChallenge,
|
|
||||||
initConsole,
|
initConsole,
|
||||||
openHelpModal: () => openModal('help')
|
openHelpModal: () => openModal('help'),
|
||||||
|
openResetModal: () => openModal('reset')
|
||||||
},
|
},
|
||||||
dispatch
|
dispatch
|
||||||
);
|
);
|
||||||
@ -45,8 +44,8 @@ const propTypes = {
|
|||||||
guideUrl: PropTypes.string,
|
guideUrl: PropTypes.string,
|
||||||
initConsole: PropTypes.func.isRequired,
|
initConsole: PropTypes.func.isRequired,
|
||||||
openHelpModal: PropTypes.func.isRequired,
|
openHelpModal: PropTypes.func.isRequired,
|
||||||
|
openResetModal: PropTypes.func.isRequired,
|
||||||
output: PropTypes.string,
|
output: PropTypes.string,
|
||||||
resetChallenge: PropTypes.func.isRequired,
|
|
||||||
tests: PropTypes.arrayOf(
|
tests: PropTypes.arrayOf(
|
||||||
PropTypes.shape({
|
PropTypes.shape({
|
||||||
text: PropTypes.string,
|
text: PropTypes.string,
|
||||||
@ -89,7 +88,7 @@ export class SidePanel extends PureComponent {
|
|||||||
output = '',
|
output = '',
|
||||||
guideUrl,
|
guideUrl,
|
||||||
executeChallenge,
|
executeChallenge,
|
||||||
resetChallenge,
|
openResetModal,
|
||||||
openHelpModal
|
openHelpModal
|
||||||
} = this.props;
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
@ -103,7 +102,7 @@ export class SidePanel extends PureComponent {
|
|||||||
executeChallenge={executeChallenge}
|
executeChallenge={executeChallenge}
|
||||||
guideUrl={guideUrl}
|
guideUrl={guideUrl}
|
||||||
openHelpModal={openHelpModal}
|
openHelpModal={openHelpModal}
|
||||||
reset={resetChallenge}
|
openResetModal={openResetModal}
|
||||||
/>
|
/>
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<Output
|
<Output
|
||||||
|
@ -7,10 +7,15 @@ const propTypes = {
|
|||||||
executeChallenge: PropTypes.func.isRequired,
|
executeChallenge: PropTypes.func.isRequired,
|
||||||
guideUrl: PropTypes.string,
|
guideUrl: PropTypes.string,
|
||||||
openHelpModal: PropTypes.func.isRequired,
|
openHelpModal: PropTypes.func.isRequired,
|
||||||
reset: PropTypes.func.isRequired
|
openResetModal: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
function ToolPanel({ executeChallenge, guideUrl, reset, openHelpModal }) {
|
function ToolPanel({
|
||||||
|
executeChallenge,
|
||||||
|
guideUrl,
|
||||||
|
openResetModal,
|
||||||
|
openHelpModal
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
@ -26,7 +31,7 @@ function ToolPanel({ executeChallenge, guideUrl, reset, openHelpModal }) {
|
|||||||
block={true}
|
block={true}
|
||||||
bsStyle='primary'
|
bsStyle='primary'
|
||||||
className='btn-big'
|
className='btn-big'
|
||||||
onClick={reset}
|
onClick={openResetModal}
|
||||||
>
|
>
|
||||||
Reset this lesson
|
Reset this lesson
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -0,0 +1,120 @@
|
|||||||
|
import { of } from 'rxjs/observable/of';
|
||||||
|
import { filter } from 'rxjs/operators/filter';
|
||||||
|
import { switchMap } from 'rxjs/operators/switchMap';
|
||||||
|
import { tap } from 'rxjs/operators/tap';
|
||||||
|
import { ignoreElements } from 'rxjs/operators/ignoreElements';
|
||||||
|
import { combineEpics, ofType } from 'redux-observable';
|
||||||
|
import store from 'store';
|
||||||
|
|
||||||
|
import {
|
||||||
|
types,
|
||||||
|
storedCodeFound,
|
||||||
|
noStoredCodeFound,
|
||||||
|
isCodeLockedSelector,
|
||||||
|
challengeFilesSelector,
|
||||||
|
challengeMetaSelector
|
||||||
|
} from './';
|
||||||
|
|
||||||
|
import { setContent, isPoly } from '../utils/polyvinyl';
|
||||||
|
|
||||||
|
const legacyPrefixes = [
|
||||||
|
'Bonfire: ',
|
||||||
|
'Waypoint: ',
|
||||||
|
'Zipline: ',
|
||||||
|
'Basejump: ',
|
||||||
|
'Checkpoint: '
|
||||||
|
];
|
||||||
|
const legacyPostfix = 'Val';
|
||||||
|
|
||||||
|
function getCode(id) {
|
||||||
|
const code = store.get(id);
|
||||||
|
return code ? code : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLegacyCode(legacy) {
|
||||||
|
const key = legacy + legacyPostfix;
|
||||||
|
let code = null;
|
||||||
|
const maybeCode = store.get(key);
|
||||||
|
if (maybeCode) {
|
||||||
|
code = '' + maybeCode;
|
||||||
|
store.remove(key);
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
return legacyPrefixes.reduce((code, prefix) => {
|
||||||
|
if (code) {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
return store.get(prefix + key);
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function legacyToFile(code, files, key) {
|
||||||
|
if (isFilesAllPoly(files)) {
|
||||||
|
return { [key]: setContent(code, files[key]) };
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFilesAllPoly(files) {
|
||||||
|
return Object.keys(files)
|
||||||
|
.map(key => files[key])
|
||||||
|
.every(file => isPoly(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearCodeEpic(action$, { getState }) {
|
||||||
|
return action$.pipe(
|
||||||
|
ofType(types.submitComplete, types.resetChallenge),
|
||||||
|
tap(() => {
|
||||||
|
const { id } = challengeMetaSelector(getState());
|
||||||
|
store.remove(id);
|
||||||
|
}),
|
||||||
|
ignoreElements()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveCodeEpic(action$, { getState }) {
|
||||||
|
return action$.pipe(
|
||||||
|
tap(console.info),
|
||||||
|
ofType(types.executeChallenge),
|
||||||
|
// do not save challenge if code is locked
|
||||||
|
filter(() => !isCodeLockedSelector(getState())),
|
||||||
|
tap(() => {
|
||||||
|
const state = getState();
|
||||||
|
const { id } = challengeMetaSelector(state);
|
||||||
|
const files = challengeFilesSelector(state);
|
||||||
|
store.set(id, files);
|
||||||
|
}),
|
||||||
|
ignoreElements()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadCodeEpic(action$, { getState }) {
|
||||||
|
return action$.pipe(
|
||||||
|
ofType(types.challengeMounted),
|
||||||
|
switchMap(({ payload: id }) => {
|
||||||
|
let finalFiles;
|
||||||
|
const state = getState();
|
||||||
|
const challenge = challengeMetaSelector(state);
|
||||||
|
const files = challengeFilesSelector(state);
|
||||||
|
const fileKeys = Object.keys(files);
|
||||||
|
const invalidForLegacy = fileKeys.length > 1;
|
||||||
|
const { title: legacyKey } = challenge;
|
||||||
|
|
||||||
|
const codeFound = getCode(id);
|
||||||
|
if (codeFound && isFilesAllPoly(codeFound)) {
|
||||||
|
finalFiles = codeFound;
|
||||||
|
} else {
|
||||||
|
const legacyCode = getLegacyCode(legacyKey);
|
||||||
|
if (legacyCode && !invalidForLegacy) {
|
||||||
|
finalFiles = legacyToFile(legacyCode, files, fileKeys[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (finalFiles) {
|
||||||
|
return of(storedCodeFound(finalFiles));
|
||||||
|
}
|
||||||
|
return of(noStoredCodeFound());
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default combineEpics(saveCodeEpic, loadCodeEpic, clearCodeEpic);
|
@ -5,7 +5,8 @@ import {
|
|||||||
challengeFilesSelector,
|
challengeFilesSelector,
|
||||||
challengeMetaSelector
|
challengeMetaSelector
|
||||||
} from '../redux';
|
} from '../redux';
|
||||||
import { tap, mapTo } from 'rxjs/operators';
|
import { tap } from 'rxjs/operators/tap';
|
||||||
|
import { mapTo } from 'rxjs/operators/mapTo';
|
||||||
|
|
||||||
function filesToMarkdown(files = {}) {
|
function filesToMarkdown(files = {}) {
|
||||||
const moreThenOneFile = Object.keys(files).length > 1;
|
const moreThenOneFile = Object.keys(files).length > 1;
|
||||||
|
@ -7,6 +7,7 @@ import completionEpic from './completion-epic';
|
|||||||
import executeChallengeEpic from './execute-challenge-epic';
|
import executeChallengeEpic from './execute-challenge-epic';
|
||||||
import codeLockEpic from './code-lock-epic';
|
import codeLockEpic from './code-lock-epic';
|
||||||
import createQuestionEpic from './create-question-epic';
|
import createQuestionEpic from './create-question-epic';
|
||||||
|
import codeStorageEpic from './code-storage-epic';
|
||||||
|
|
||||||
const ns = 'challenge';
|
const ns = 'challenge';
|
||||||
export const backendNS = 'backendChallenge';
|
export const backendNS = 'backendChallenge';
|
||||||
@ -19,10 +20,12 @@ const initialState = {
|
|||||||
},
|
},
|
||||||
challengeTests: [],
|
challengeTests: [],
|
||||||
consoleOut: '',
|
consoleOut: '',
|
||||||
|
isCodeLocked: false,
|
||||||
isJSEnabled: true,
|
isJSEnabled: true,
|
||||||
modal: {
|
modal: {
|
||||||
completion: false,
|
completion: false,
|
||||||
help: false
|
help: false,
|
||||||
|
reset: false
|
||||||
},
|
},
|
||||||
successMessage: 'Happy Coding!'
|
successMessage: 'Happy Coding!'
|
||||||
};
|
};
|
||||||
@ -32,7 +35,8 @@ export const epics = [
|
|||||||
codeLockEpic,
|
codeLockEpic,
|
||||||
completionEpic,
|
completionEpic,
|
||||||
createQuestionEpic,
|
createQuestionEpic,
|
||||||
executeChallengeEpic
|
executeChallengeEpic,
|
||||||
|
codeStorageEpic
|
||||||
];
|
];
|
||||||
|
|
||||||
export const types = createTypes(
|
export const types = createTypes(
|
||||||
@ -48,16 +52,21 @@ export const types = createTypes(
|
|||||||
'updateSuccessMessage',
|
'updateSuccessMessage',
|
||||||
'updateTests',
|
'updateTests',
|
||||||
|
|
||||||
|
'lockCode',
|
||||||
'unlockCode',
|
'unlockCode',
|
||||||
'disableJSOnError',
|
'disableJSOnError',
|
||||||
|
'storedCodeFound',
|
||||||
|
'noStoredCodeFound',
|
||||||
|
|
||||||
'closeModal',
|
'closeModal',
|
||||||
'openModal',
|
'openModal',
|
||||||
|
|
||||||
|
'challengeMounted',
|
||||||
'checkChallenge',
|
'checkChallenge',
|
||||||
'executeChallenge',
|
'executeChallenge',
|
||||||
'resetChallenge',
|
'resetChallenge',
|
||||||
'submitChallenge'
|
'submitChallenge',
|
||||||
|
'submitComplete'
|
||||||
],
|
],
|
||||||
ns
|
ns
|
||||||
);
|
);
|
||||||
@ -88,28 +97,35 @@ export const updateConsole = createAction(types.updateConsole);
|
|||||||
export const updateJSEnabled = createAction(types.updateJSEnabled);
|
export const updateJSEnabled = createAction(types.updateJSEnabled);
|
||||||
export const updateSuccessMessage = createAction(types.updateSuccessMessage);
|
export const updateSuccessMessage = createAction(types.updateSuccessMessage);
|
||||||
|
|
||||||
|
export const lockCode = createAction(types.lockCode);
|
||||||
export const unlockCode = createAction(types.unlockCode);
|
export const unlockCode = createAction(types.unlockCode);
|
||||||
export const disableJSOnError = createAction(types.disableJSOnError, err => {
|
export const disableJSOnError = createAction(types.disableJSOnError, err => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
|
export const storedCodeFound = createAction(types.storedCodeFound);
|
||||||
|
export const noStoredCodeFound = createAction(types.noStoredCodeFound);
|
||||||
|
|
||||||
export const closeModal = createAction(types.closeModal);
|
export const closeModal = createAction(types.closeModal);
|
||||||
export const openModal = createAction(types.openModal);
|
export const openModal = createAction(types.openModal);
|
||||||
|
|
||||||
|
export const challengeMounted = createAction(types.challengeMounted);
|
||||||
export const checkChallenge = createAction(types.checkChallenge);
|
export const checkChallenge = createAction(types.checkChallenge);
|
||||||
export const executeChallenge = createAction(types.executeChallenge);
|
export const executeChallenge = createAction(types.executeChallenge);
|
||||||
export const resetChallenge = createAction(types.resetChallenge);
|
export const resetChallenge = createAction(types.resetChallenge);
|
||||||
export const submitChallenge = createAction(types.submitChallenge);
|
export const submitChallenge = createAction(types.submitChallenge);
|
||||||
|
export const submitComplete = createAction(types.submitComplete);
|
||||||
|
|
||||||
export const backendFormValuesSelector = state => state.form[backendNS];
|
export const backendFormValuesSelector = state => state.form[backendNS];
|
||||||
export const challengeFilesSelector = state => state[ns].challengeFiles;
|
export const challengeFilesSelector = state => state[ns].challengeFiles;
|
||||||
export const challengeMetaSelector = state => state[ns].challengeMeta;
|
export const challengeMetaSelector = state => state[ns].challengeMeta;
|
||||||
export const challengeTestsSelector = state => state[ns].challengeTests;
|
export const challengeTestsSelector = state => state[ns].challengeTests;
|
||||||
export const consoleOutputSelector = state => state[ns].consoleOut;
|
export const consoleOutputSelector = state => state[ns].consoleOut;
|
||||||
|
export const isCodeLockedSelector = state => state[ns].isCodeLocked;
|
||||||
export const isCompletionModalOpenSelector = state =>
|
export const isCompletionModalOpenSelector = state =>
|
||||||
state[ns].modal.completion;
|
state[ns].modal.completion;
|
||||||
export const isHelpModalOpenSelector = state => state[ns].modal.help;
|
export const isHelpModalOpenSelector = state => state[ns].modal.help;
|
||||||
|
export const isResetModalOpenSelector = state => state[ns].modal.reset;
|
||||||
export const isJSEnabledSelector = state => state[ns].isJSEnabled;
|
export const isJSEnabledSelector = state => state[ns].isJSEnabled;
|
||||||
export const successMessageSelector = state => state[ns].successMessage;
|
export const successMessageSelector = state => state[ns].successMessage;
|
||||||
|
|
||||||
@ -129,6 +145,11 @@ export const reducer = handleActions(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
[types.storedCodeFound]: (state, { payload }) => ({
|
||||||
|
...state,
|
||||||
|
challengeFiles: payload
|
||||||
|
}),
|
||||||
|
|
||||||
[types.initTests]: (state, { payload }) => ({
|
[types.initTests]: (state, { payload }) => ({
|
||||||
...state,
|
...state,
|
||||||
challengeTests: payload
|
challengeTests: payload
|
||||||
@ -137,6 +158,7 @@ export const reducer = handleActions(
|
|||||||
...state,
|
...state,
|
||||||
challengeTests: payload
|
challengeTests: payload
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[types.initConsole]: (state, { payload }) => ({
|
[types.initConsole]: (state, { payload }) => ({
|
||||||
...state,
|
...state,
|
||||||
consoleOut: payload
|
consoleOut: payload
|
||||||
@ -173,14 +195,21 @@ export const reducer = handleActions(
|
|||||||
})),
|
})),
|
||||||
consoleOut: ''
|
consoleOut: ''
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
[types.lockCode]: state => ({
|
||||||
|
...state,
|
||||||
|
isCodeLocked: true
|
||||||
|
}),
|
||||||
[types.unlockCode]: state => ({
|
[types.unlockCode]: state => ({
|
||||||
...state,
|
...state,
|
||||||
isJSEnabled: true
|
isJSEnabled: true,
|
||||||
|
isCodeLocked: false
|
||||||
}),
|
}),
|
||||||
[types.disableJSOnError]: state => ({
|
[types.disableJSOnError]: state => ({
|
||||||
...state,
|
...state,
|
||||||
isJSEnabled: false
|
isJSEnabled: false
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[types.updateSuccessMessage]: (state, { payload }) => ({
|
[types.updateSuccessMessage]: (state, { payload }) => ({
|
||||||
...state,
|
...state,
|
||||||
successMessage: payload
|
successMessage: payload
|
||||||
|
@ -10044,6 +10044,10 @@ steno@^0.4.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
graceful-fs "^4.1.3"
|
graceful-fs "^4.1.3"
|
||||||
|
|
||||||
|
store@^2.0.12:
|
||||||
|
version "2.0.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/store/-/store-2.0.12.tgz#8c534e2a0b831f72b75fc5f1119857c44ef5d593"
|
||||||
|
|
||||||
stream-browserify@^2.0.1:
|
stream-browserify@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db"
|
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db"
|
||||||
|
Reference in New Issue
Block a user