Feature(code-uri): Lock untrusted code from playing on first load
This commit is contained in:
@ -53,6 +53,8 @@ function legacyToFile(code, files, key) {
|
|||||||
export function saveCodeSaga(actions, getState) {
|
export function saveCodeSaga(actions, getState) {
|
||||||
return actions
|
return actions
|
||||||
::ofType(types.saveCode)
|
::ofType(types.saveCode)
|
||||||
|
// do not save challenge if code is locked
|
||||||
|
.filter(() => !getState().challengesApp.isCodeLocked)
|
||||||
.map(() => {
|
.map(() => {
|
||||||
const { challengesApp: { id = '', files = {} } } = getState();
|
const { challengesApp: { id = '', files = {} } } = getState();
|
||||||
store.set(id, files);
|
store.set(id, files);
|
||||||
|
@ -91,6 +91,8 @@ export default function executeChallengeSaga(action$, getState) {
|
|||||||
type === types.executeChallenge ||
|
type === types.executeChallenge ||
|
||||||
type === types.updateMain
|
type === types.updateMain
|
||||||
))
|
))
|
||||||
|
// if isCodeLockedTrue do not run challenges
|
||||||
|
.filter(() => !getState().challengesApp.isCodeLocked)
|
||||||
.debounce(750)
|
.debounce(750)
|
||||||
.flatMapLatest(({ type }) => {
|
.flatMapLatest(({ type }) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
@ -2,6 +2,7 @@ import Rx, { Observable, Subject } from 'rx';
|
|||||||
/* eslint-disable import/no-unresolved */
|
/* eslint-disable import/no-unresolved */
|
||||||
import loopProtect from 'loop-protect';
|
import loopProtect from 'loop-protect';
|
||||||
/* eslint-enable import/no-unresolved */
|
/* eslint-enable import/no-unresolved */
|
||||||
|
import { ofType } from '../../common/utils/get-actions-of-type';
|
||||||
import types from '../../common/app/routes/challenges/redux/types';
|
import types from '../../common/app/routes/challenges/redux/types';
|
||||||
import {
|
import {
|
||||||
updateOutput,
|
updateOutput,
|
||||||
@ -89,12 +90,13 @@ export default function frameSaga(actions$, getState, { window, document }) {
|
|||||||
const proxyLogger$ = new Subject();
|
const proxyLogger$ = new Subject();
|
||||||
const runTests$ = window.__common[testId + 'Ready$'] =
|
const runTests$ = window.__common[testId + 'Ready$'] =
|
||||||
new Subject();
|
new Subject();
|
||||||
const result$ = actions$
|
const result$ = actions$::ofType(
|
||||||
.filter(({ type }) => (
|
types.frameMain,
|
||||||
type === types.frameMain ||
|
types.frameTests,
|
||||||
type === types.frameTests ||
|
types.frameOutput
|
||||||
type === types.frameOutput
|
)
|
||||||
))
|
// if isCodeLocked is true do not frame user code
|
||||||
|
.filter(() => !getState().challengesApp.isCodeLocked)
|
||||||
.map(action => {
|
.map(action => {
|
||||||
if (action.type === types.frameMain) {
|
if (action.type === types.frameMain) {
|
||||||
return frameMain(action.payload, document, proxyLogger$);
|
return frameMain(action.payload, document, proxyLogger$);
|
||||||
|
@ -12,7 +12,8 @@ import { challengeSelector } from '../../redux/selectors';
|
|||||||
import {
|
import {
|
||||||
openBugModal,
|
openBugModal,
|
||||||
updateHint,
|
updateHint,
|
||||||
executeChallenge
|
executeChallenge,
|
||||||
|
unlockUntrustedCode
|
||||||
} from '../../redux/actions';
|
} from '../../redux/actions';
|
||||||
import { makeToast } from '../../../../toasts/redux/actions';
|
import { makeToast } from '../../../../toasts/redux/actions';
|
||||||
import { toggleHelpChat } from '../../../../redux/actions';
|
import { toggleHelpChat } from '../../../../redux/actions';
|
||||||
@ -22,7 +23,8 @@ const bindableActions = {
|
|||||||
executeChallenge,
|
executeChallenge,
|
||||||
updateHint,
|
updateHint,
|
||||||
toggleHelpChat,
|
toggleHelpChat,
|
||||||
openBugModal
|
openBugModal,
|
||||||
|
unlockUntrustedCode
|
||||||
};
|
};
|
||||||
const mapStateToProps = createSelector(
|
const mapStateToProps = createSelector(
|
||||||
challengeSelector,
|
challengeSelector,
|
||||||
@ -31,20 +33,23 @@ const mapStateToProps = createSelector(
|
|||||||
state => state.challengesApp.tests,
|
state => state.challengesApp.tests,
|
||||||
state => state.challengesApp.output,
|
state => state.challengesApp.output,
|
||||||
state => state.challengesApp.hintIndex,
|
state => state.challengesApp.hintIndex,
|
||||||
|
state => state.challengesApp.isCodeLocked,
|
||||||
(
|
(
|
||||||
{ challenge: { title, description, hints = [] } = {} },
|
{ challenge: { title, description, hints = [] } = {} },
|
||||||
windowHeight,
|
windowHeight,
|
||||||
navHeight,
|
navHeight,
|
||||||
tests,
|
tests,
|
||||||
output,
|
output,
|
||||||
hintIndex
|
hintIndex,
|
||||||
|
isCodeLocked
|
||||||
) => ({
|
) => ({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
height: windowHeight - navHeight - 20,
|
height: windowHeight - navHeight - 20,
|
||||||
tests,
|
tests,
|
||||||
output,
|
output,
|
||||||
hint: hints[hintIndex]
|
hint: hints[hintIndex],
|
||||||
|
isCodeLocked
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -65,7 +70,8 @@ export class SidePanel extends PureComponent {
|
|||||||
updateHint: PropTypes.func,
|
updateHint: PropTypes.func,
|
||||||
makeToast: PropTypes.func,
|
makeToast: PropTypes.func,
|
||||||
toggleHelpChat: PropTypes.func,
|
toggleHelpChat: PropTypes.func,
|
||||||
openBugModal: PropTypes.func
|
openBugModal: PropTypes.func,
|
||||||
|
unlockUntrustedCode: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
renderDescription(description = [ 'Happy Coding!' ], descriptionRegex) {
|
renderDescription(description = [ 'Happy Coding!' ], descriptionRegex) {
|
||||||
@ -106,7 +112,9 @@ export class SidePanel extends PureComponent {
|
|||||||
updateHint,
|
updateHint,
|
||||||
makeToast,
|
makeToast,
|
||||||
toggleHelpChat,
|
toggleHelpChat,
|
||||||
openBugModal
|
openBugModal,
|
||||||
|
isCodeLocked,
|
||||||
|
unlockUntrustedCode
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const style = {};
|
const style = {};
|
||||||
if (height) {
|
if (height) {
|
||||||
@ -135,9 +143,11 @@ export class SidePanel extends PureComponent {
|
|||||||
<ToolPanel
|
<ToolPanel
|
||||||
executeChallenge={ executeChallenge }
|
executeChallenge={ executeChallenge }
|
||||||
hint={ hint }
|
hint={ hint }
|
||||||
|
isCodeLocked={ isCodeLocked }
|
||||||
makeToast={ makeToast }
|
makeToast={ makeToast }
|
||||||
openBugModal={ openBugModal }
|
openBugModal={ openBugModal }
|
||||||
toggleHelpChat={ toggleHelpChat }
|
toggleHelpChat={ toggleHelpChat }
|
||||||
|
unlockUntrustedCode={ unlockUntrustedCode }
|
||||||
updateHint={ updateHint }
|
updateHint={ updateHint }
|
||||||
/>
|
/>
|
||||||
<Output output={ output }/>
|
<Output output={ output }/>
|
||||||
|
@ -14,8 +14,10 @@ export default class ToolPanel extends PureComponent {
|
|||||||
executeChallenge: PropTypes.func,
|
executeChallenge: PropTypes.func,
|
||||||
updateHint: PropTypes.func,
|
updateHint: PropTypes.func,
|
||||||
hint: PropTypes.string,
|
hint: PropTypes.string,
|
||||||
|
isCodeLocked: PropTypes.bool,
|
||||||
toggleHelpChat: PropTypes.func,
|
toggleHelpChat: PropTypes.func,
|
||||||
openBugModal: PropTypes.func
|
openBugModal: PropTypes.func,
|
||||||
|
unlockUntrustedCode: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
makeHint() {
|
makeHint() {
|
||||||
@ -51,24 +53,50 @@ export default class ToolPanel extends PureComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
renderExecute(isCodeLocked, executeChallenge, unlockUntrustedCode) {
|
||||||
const {
|
if (isCodeLocked) {
|
||||||
hint,
|
return (
|
||||||
executeChallenge,
|
|
||||||
toggleHelpChat,
|
|
||||||
openBugModal
|
|
||||||
} = this.props;
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{ this.renderHint(hint, this.makeHint) }
|
|
||||||
<Button
|
<Button
|
||||||
block={ true }
|
block={ true }
|
||||||
bsStyle='primary'
|
bsStyle='primary'
|
||||||
className='btn-big'
|
className='btn-big'
|
||||||
onClick={ executeChallenge }
|
onClick={ unlockUntrustedCode }
|
||||||
>
|
>
|
||||||
Run tests (ctrl + enter)
|
Code Locked. Unlock?
|
||||||
</Button>
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
block={ true }
|
||||||
|
bsStyle='primary'
|
||||||
|
className='btn-big'
|
||||||
|
onClick={ executeChallenge }
|
||||||
|
>
|
||||||
|
Run tests (ctrl + enter)
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
hint,
|
||||||
|
isCodeLocked,
|
||||||
|
executeChallenge,
|
||||||
|
toggleHelpChat,
|
||||||
|
openBugModal,
|
||||||
|
unlockUntrustedCode
|
||||||
|
} = this.props;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{ this.renderHint(hint, this.makeHint) }
|
||||||
|
{
|
||||||
|
this.renderExecute(
|
||||||
|
isCodeLocked,
|
||||||
|
executeChallenge,
|
||||||
|
unlockUntrustedCode
|
||||||
|
)
|
||||||
|
}
|
||||||
<div className='button-spacer' />
|
<div className='button-spacer' />
|
||||||
<ButtonGroup
|
<ButtonGroup
|
||||||
className='input-group'
|
className='input-group'
|
||||||
|
@ -23,6 +23,10 @@ export const fetchChallengeCompleted = createAction(
|
|||||||
export const resetUi = createAction(types.resetUi);
|
export const resetUi = createAction(types.resetUi);
|
||||||
export const updateHint = createAction(types.updateHint);
|
export const updateHint = createAction(types.updateHint);
|
||||||
export const lockUntrustedCode = createAction(types.lockUntrustedCode);
|
export const lockUntrustedCode = createAction(types.lockUntrustedCode);
|
||||||
|
export const unlockUntrustedCode = createAction(
|
||||||
|
types.unlockUntrustedCode,
|
||||||
|
() => null
|
||||||
|
);
|
||||||
|
|
||||||
export const fetchChallenges = createAction(types.fetchChallenges);
|
export const fetchChallenges = createAction(types.fetchChallenges);
|
||||||
export const fetchChallengesCompleted = createAction(
|
export const fetchChallengesCompleted = createAction(
|
||||||
|
@ -93,7 +93,7 @@ const mainReducer = handleActions(
|
|||||||
...state,
|
...state,
|
||||||
isCodeLocked: true
|
isCodeLocked: true
|
||||||
}),
|
}),
|
||||||
[types.unlockCode]: state => ({
|
[types.unlockUntrustedCode]: state => ({
|
||||||
...state,
|
...state,
|
||||||
isCodeLocked: false
|
isCodeLocked: false
|
||||||
}),
|
}),
|
||||||
|
@ -18,6 +18,7 @@ export default createTypes([
|
|||||||
'resetUi',
|
'resetUi',
|
||||||
'updateHint',
|
'updateHint',
|
||||||
'lockUntrustedCode',
|
'lockUntrustedCode',
|
||||||
|
'unlockUntrustedCode',
|
||||||
|
|
||||||
// map
|
// map
|
||||||
'updateFilter',
|
'updateFilter',
|
||||||
|
Reference in New Issue
Block a user