From 37aa5538590532c1cf050b82f452991e58cd5665 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 27 Nov 2018 11:22:53 -0500 Subject: [PATCH 1/6] Remove unused containers. The functionality was all moved to the Controls container, these containers are no longer used --- src/containers/green-flag.jsx | 60 ------------------- src/containers/stop-all.jsx | 55 ----------------- .../__snapshots__/green-flag.test.jsx.snap | 21 ------- test/unit/containers/green-flag.test.jsx | 49 --------------- 4 files changed, 185 deletions(-) delete mode 100644 src/containers/green-flag.jsx delete mode 100644 src/containers/stop-all.jsx delete mode 100644 test/unit/containers/__snapshots__/green-flag.test.jsx.snap delete mode 100644 test/unit/containers/green-flag.test.jsx diff --git a/src/containers/green-flag.jsx b/src/containers/green-flag.jsx deleted file mode 100644 index a725b7e1b..000000000 --- a/src/containers/green-flag.jsx +++ /dev/null @@ -1,60 +0,0 @@ -import bindAll from 'lodash.bindall'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import VM from 'scratch-vm'; - -import GreenFlagComponent from '../components/green-flag/green-flag.jsx'; - -class GreenFlag extends React.Component { - constructor (props) { - super(props); - bindAll(this, [ - 'handleClick', - 'onProjectRunStart', - 'onProjectRunStop' - ]); - this.state = {projectRunning: false}; - } - componentDidMount () { - this.props.vm.addListener('PROJECT_RUN_START', this.onProjectRunStart); - this.props.vm.addListener('PROJECT_RUN_STOP', this.onProjectRunStop); - } - componentWillUnmount () { - this.props.vm.removeListener('PROJECT_RUN_START', this.onProjectRunStart); - this.props.vm.removeListener('PROJECT_RUN_STOP', this.onProjectRunStop); - } - onProjectRunStart () { - this.setState({projectRunning: true}); - } - onProjectRunStop () { - this.setState({projectRunning: false}); - } - handleClick (e) { - e.preventDefault(); - if (e.shiftKey) { - this.props.vm.setTurboMode(!this.props.vm.runtime.turboMode); - } else { - this.props.vm.greenFlag(); - } - } - render () { - const { - vm, // eslint-disable-line no-unused-vars - ...props - } = this.props; - return ( - - ); - } -} - -GreenFlag.propTypes = { - vm: PropTypes.instanceOf(VM) -}; - -export default GreenFlag; diff --git a/src/containers/stop-all.jsx b/src/containers/stop-all.jsx deleted file mode 100644 index 0f7641a20..000000000 --- a/src/containers/stop-all.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import bindAll from 'lodash.bindall'; -import PropTypes from 'prop-types'; -import React from 'react'; -import VM from 'scratch-vm'; - -import StopAllComponent from '../components/stop-all/stop-all.jsx'; - -class StopAll extends React.Component { - constructor (props) { - super(props); - bindAll(this, [ - 'handleClick', - 'onProjectRunStart', - 'onProjectRunStop' - ]); - this.state = {projectRunning: false}; - } - componentDidMount () { - this.props.vm.addListener('PROJECT_RUN_START', this.onProjectRunStart); - this.props.vm.addListener('PROJECT_RUN_STOP', this.onProjectRunStop); - } - componentWillUnmount () { - this.props.vm.removeListener('PROJECT_RUN_START', this.onProjectRunStart); - this.props.vm.removeListener('PROJECT_RUN_STOP', this.onProjectRunStop); - } - onProjectRunStart () { - this.setState({projectRunning: true}); - } - onProjectRunStop () { - this.setState({projectRunning: false}); - } - handleClick (e) { - e.preventDefault(); - this.props.vm.stopAll(); - } - render () { - const { - vm, // eslint-disable-line no-unused-vars - ...props - } = this.props; - return ( - - ); - } -} - -StopAll.propTypes = { - vm: PropTypes.instanceOf(VM) -}; - -export default StopAll; diff --git a/test/unit/containers/__snapshots__/green-flag.test.jsx.snap b/test/unit/containers/__snapshots__/green-flag.test.jsx.snap deleted file mode 100644 index f8dbdbf82..000000000 --- a/test/unit/containers/__snapshots__/green-flag.test.jsx.snap +++ /dev/null @@ -1,21 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`GreenFlag Container renders active state 1`] = ` - -`; - -exports[`GreenFlag Container renders inactive state 1`] = ` - -`; diff --git a/test/unit/containers/green-flag.test.jsx b/test/unit/containers/green-flag.test.jsx deleted file mode 100644 index 4744ebaab..000000000 --- a/test/unit/containers/green-flag.test.jsx +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import {shallow} from 'enzyme'; -import GreenFlag from '../../../src/containers/green-flag'; -import renderer from 'react-test-renderer'; -import VM from 'scratch-vm'; - -describe('GreenFlag Container', () => { - let vm; - beforeEach(() => { - vm = new VM(); - }); - - test('renders active state', () => { - const component = renderer.create( - - ); - expect(component.toJSON()).toMatchSnapshot(); - }); - - test('renders inactive state', () => { - const component = renderer.create( - - ); - expect(component.toJSON()).toMatchSnapshot(); - }); - - test('triggers onClick when active', () => { - const onClick = jest.fn(); - const componentShallowWrapper = shallow( - - ); - componentShallowWrapper.simulate('click'); - expect(onClick).toHaveBeenCalled(); - }); - - // @todo: Test for handles key events. - // @todo: Test project run start. - // @todo: Test project run stop. -}); From 59df9b6ca53288e88ab56f7517a5719eb596ba80 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 27 Nov 2018 11:25:04 -0500 Subject: [PATCH 2/6] Do not auto-start the VM in player mode. Always start before green flag. --- src/containers/controls.jsx | 3 ++ src/lib/vm-manager-hoc.jsx | 25 ++++++++---- test/unit/util/vm-manager-hoc.test.jsx | 56 +++++++++++++++++++++++--- 3 files changed, 72 insertions(+), 12 deletions(-) diff --git a/src/containers/controls.jsx b/src/containers/controls.jsx index 2869a047a..1d4a666d2 100644 --- a/src/containers/controls.jsx +++ b/src/containers/controls.jsx @@ -20,6 +20,9 @@ class Controls extends React.Component { if (e.shiftKey) { this.props.vm.setTurboMode(!this.props.turbo); } else { + if (!this.props.vm.started) { + this.props.vm.start(); + } this.props.vm.greenFlag(); analytics.event({ category: 'general', diff --git a/src/lib/vm-manager-hoc.jsx b/src/lib/vm-manager-hoc.jsx index 404b7e3c8..159e5c5c0 100644 --- a/src/lib/vm-manager-hoc.jsx +++ b/src/lib/vm-manager-hoc.jsx @@ -27,12 +27,16 @@ const vmManagerHOC = function (WrappedComponent) { ]); } componentDidMount () { - if (this.props.vm.initialized) return; - this.audioEngine = new AudioEngine(); - this.props.vm.attachAudioEngine(this.audioEngine); - this.props.vm.setCompatibilityMode(true); - this.props.vm.start(); - this.props.vm.initialized = true; + if (!this.props.vm.initialized) { + this.audioEngine = new AudioEngine(); + this.props.vm.attachAudioEngine(this.audioEngine); + this.props.vm.setCompatibilityMode(true); + this.props.vm.initialized = true; + } + if (!this.props.isPlayerOnly && !this.props.vm.started) { + this.props.vm.start(); + this.props.vm.started = true; + } } componentDidUpdate (prevProps) { // if project is in loading state, AND fonts are loaded, @@ -41,6 +45,11 @@ const vmManagerHOC = function (WrappedComponent) { (!prevProps.isLoadingWithId || !prevProps.fontsLoaded)) { this.loadProject(); } + // Start the VM if entering editor mode with an unstarted vm + if (!this.props.isPlayerOnly && !this.props.vm.started) { + this.props.vm.start(); + this.props.vm.started = true; + } } loadProject () { return this.props.vm.loadProject(this.props.projectData) @@ -79,6 +88,7 @@ const vmManagerHOC = function (WrappedComponent) { cloudHost: PropTypes.string, fontsLoaded: PropTypes.bool, isLoadingWithId: PropTypes.bool, + isPlayerOnly: PropTypes.bool, loadingState: PropTypes.oneOf(LoadingStates), onError: PropTypes.func, onLoadedProject: PropTypes.func, @@ -94,7 +104,8 @@ const vmManagerHOC = function (WrappedComponent) { isLoadingWithId: getIsLoadingWithId(loadingState), projectData: state.scratchGui.projectState.projectData, projectId: state.scratchGui.projectState.projectId, - loadingState: loadingState + loadingState: loadingState, + isPlayerOnly: state.scratchGui.mode.isPlayerOnly }; }; diff --git a/test/unit/util/vm-manager-hoc.test.jsx b/test/unit/util/vm-manager-hoc.test.jsx index 8c065d83d..4cee7a49c 100644 --- a/test/unit/util/vm-manager-hoc.test.jsx +++ b/test/unit/util/vm-manager-hoc.test.jsx @@ -16,7 +16,8 @@ describe('VMManagerHOC', () => { beforeEach(() => { store = mockStore({ scratchGui: { - projectState: {} + projectState: {}, + mode: {} // Mocked by using override props in tests when needed } }); vm = new VM(); @@ -24,34 +25,79 @@ describe('VMManagerHOC', () => { vm.setCompatibilityMode = jest.fn(); vm.start = jest.fn(); }); - test('when it mounts, the vm is initialized', () => { + test('when it mounts in player mode, the vm is initialized but not started', () => { const Component = () => (
); const WrappedComponent = vmManagerHOC(Component); mount( ); expect(vm.attachAudioEngine.mock.calls.length).toBe(1); expect(vm.setCompatibilityMode.mock.calls.length).toBe(1); - expect(vm.start.mock.calls.length).toBe(1); expect(vm.initialized).toBe(true); + + // But vm should not be started automatically + expect(vm.start.mock.calls.length).toBe(0); + expect(vm.started).not.toBe(true); }); - test('if it mounts with an initialized vm, it does not reinitialize the vm', () => { + test('when it mounts in editor mode, the vm is initialized and started', () => { + const Component = () => (
); + const WrappedComponent = vmManagerHOC(Component); + mount( + + ); + expect(vm.attachAudioEngine.mock.calls.length).toBe(1); + expect(vm.setCompatibilityMode.mock.calls.length).toBe(1); + expect(vm.initialized).toBe(true); + + expect(vm.start.mock.calls.length).toBe(1); + expect(vm.started).toBe(true); + }); + test('if it mounts with an initialized vm, it does not reinitialize the vm but will start it', () => { const Component = () =>
; const WrappedComponent = vmManagerHOC(Component); vm.initialized = true; + vm.started = false; mount( ); expect(vm.attachAudioEngine.mock.calls.length).toBe(0); expect(vm.setCompatibilityMode.mock.calls.length).toBe(0); - expect(vm.start.mock.calls.length).toBe(0); expect(vm.initialized).toBe(true); + + expect(vm.start.mock.calls.length).toBe(1); + expect(vm.started).toBe(true); + }); + + test('if it mounts without starting the VM, it can be started by switching to editor mode', () => { + const Component = () =>
; + const WrappedComponent = vmManagerHOC(Component); + vm.initialized = true; + vm.started = false; + const mounted = mount( + + ); + expect(vm.start.mock.calls.length).toBe(0); + mounted.setProps({ + isPlayerOnly: false + }); + expect(vm.start.mock.calls.length).toBe(1); + expect(vm.started).toBe(true); }); test('if the isLoadingWithId prop becomes true, it loads project data into the vm', () => { vm.loadProject = jest.fn(() => Promise.resolve()); From f659dfbc3cad4eaa927273516a6b6176292b7c51 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 27 Nov 2018 11:59:23 -0500 Subject: [PATCH 3/6] Move the started flag to the vmStatus reducer --- src/containers/controls.jsx | 5 +++- src/lib/vm-listener-hoc.jsx | 6 +++- src/lib/vm-manager-hoc.jsx | 10 +++---- src/reducers/vm-status.js | 15 ++++++++++ test/unit/util/vm-manager-hoc.test.jsx | 41 ++++++++++++++++++-------- 5 files changed, 58 insertions(+), 19 deletions(-) diff --git a/src/containers/controls.jsx b/src/containers/controls.jsx index 1d4a666d2..db052b494 100644 --- a/src/containers/controls.jsx +++ b/src/containers/controls.jsx @@ -20,7 +20,7 @@ class Controls extends React.Component { if (e.shiftKey) { this.props.vm.setTurboMode(!this.props.turbo); } else { - if (!this.props.vm.started) { + if (!this.props.isStarted) { this.props.vm.start(); } this.props.vm.greenFlag(); @@ -41,6 +41,7 @@ class Controls extends React.Component { render () { const { vm, // eslint-disable-line no-unused-vars + isStarted, // eslint-disable-line no-unused-vars projectRunning, turbo, ...props @@ -58,12 +59,14 @@ class Controls extends React.Component { } Controls.propTypes = { + isStarted: PropTypes.bool.isRequired, projectRunning: PropTypes.bool.isRequired, turbo: PropTypes.bool.isRequired, vm: PropTypes.instanceOf(VM) }; const mapStateToProps = state => ({ + isStarted: state.scratchGui.vmStatus.running, projectRunning: state.scratchGui.vmStatus.running, turbo: state.scratchGui.vmStatus.turbo }); diff --git a/src/lib/vm-listener-hoc.jsx b/src/lib/vm-listener-hoc.jsx index 9dee35d75..58f45e79f 100644 --- a/src/lib/vm-listener-hoc.jsx +++ b/src/lib/vm-listener-hoc.jsx @@ -8,7 +8,7 @@ import {connect} from 'react-redux'; import {updateTargets} from '../reducers/targets'; import {updateBlockDrag} from '../reducers/block-drag'; import {updateMonitors} from '../reducers/monitors'; -import {setRunningState, setTurboState} from '../reducers/vm-status'; +import {setRunningState, setTurboState, setStartedState} from '../reducers/vm-status'; import {showExtensionAlert} from '../reducers/alerts'; import {updateMicIndicator} from '../reducers/mic-indicator'; @@ -39,6 +39,7 @@ const vmListenerHOC = function (WrappedComponent) { this.props.vm.on('TURBO_MODE_OFF', this.props.onTurboModeOff); this.props.vm.on('PROJECT_RUN_START', this.props.onProjectRunStart); this.props.vm.on('PROJECT_RUN_STOP', this.props.onProjectRunStop); + this.props.vm.on('RUNTIME_STARTED', this.props.onRuntimeStarted); this.props.vm.on('PERIPHERAL_DISCONNECT_ERROR', this.props.onShowExtensionAlert); this.props.vm.on('MIC_LISTENING', this.props.onMicListeningUpdate); @@ -110,6 +111,7 @@ const vmListenerHOC = function (WrappedComponent) { onTargetsUpdate, onProjectRunStart, onProjectRunStop, + onRuntimeStarted, onTurboModeOff, onTurboModeOn, onShowExtensionAlert, @@ -128,6 +130,7 @@ const vmListenerHOC = function (WrappedComponent) { onMonitorsUpdate: PropTypes.func.isRequired, onProjectRunStart: PropTypes.func.isRequired, onProjectRunStop: PropTypes.func.isRequired, + onRuntimeStarted: PropTypes.func.isRequired, onShowExtensionAlert: PropTypes.func.isRequired, onTargetsUpdate: PropTypes.func.isRequired, onTurboModeOff: PropTypes.func.isRequired, @@ -158,6 +161,7 @@ const vmListenerHOC = function (WrappedComponent) { }, onProjectRunStart: () => dispatch(setRunningState(true)), onProjectRunStop: () => dispatch(setRunningState(false)), + onRuntimeStarted: () => dispatch(setStartedState(true)), onTurboModeOn: () => dispatch(setTurboState(true)), onTurboModeOff: () => dispatch(setTurboState(false)), onShowExtensionAlert: data => { diff --git a/src/lib/vm-manager-hoc.jsx b/src/lib/vm-manager-hoc.jsx index 159e5c5c0..cbbf10385 100644 --- a/src/lib/vm-manager-hoc.jsx +++ b/src/lib/vm-manager-hoc.jsx @@ -33,9 +33,8 @@ const vmManagerHOC = function (WrappedComponent) { this.props.vm.setCompatibilityMode(true); this.props.vm.initialized = true; } - if (!this.props.isPlayerOnly && !this.props.vm.started) { + if (!this.props.isPlayerOnly && !this.props.isStarted) { this.props.vm.start(); - this.props.vm.started = true; } } componentDidUpdate (prevProps) { @@ -46,9 +45,8 @@ const vmManagerHOC = function (WrappedComponent) { this.loadProject(); } // Start the VM if entering editor mode with an unstarted vm - if (!this.props.isPlayerOnly && !this.props.vm.started) { + if (!this.props.isPlayerOnly && !this.props.isStarted) { this.props.vm.start(); - this.props.vm.started = true; } } loadProject () { @@ -65,6 +63,7 @@ const vmManagerHOC = function (WrappedComponent) { /* eslint-disable no-unused-vars */ fontsLoaded, loadingState, + isStarted, onError: onErrorProp, onLoadedProject: onLoadedProjectProp, projectData, @@ -105,7 +104,8 @@ const vmManagerHOC = function (WrappedComponent) { projectData: state.scratchGui.projectState.projectData, projectId: state.scratchGui.projectState.projectId, loadingState: loadingState, - isPlayerOnly: state.scratchGui.mode.isPlayerOnly + isPlayerOnly: state.scratchGui.mode.isPlayerOnly, + isStarted: state.scratchGui.vmStatus.started }; }; diff --git a/src/reducers/vm-status.js b/src/reducers/vm-status.js index 8e9651992..e99b80368 100644 --- a/src/reducers/vm-status.js +++ b/src/reducers/vm-status.js @@ -1,14 +1,20 @@ const SET_RUNNING_STATE = 'scratch-gui/vm-status/SET_RUNNING_STATE'; const SET_TURBO_STATE = 'scratch-gui/vm-status/SET_TURBO_STATE'; +const SET_STARTED_STATE = 'scratch-gui/vm-status/SET_STARTED_STATE'; const initialState = { running: false, + started: false, turbo: false }; const reducer = function (state, action) { if (typeof state === 'undefined') state = initialState; switch (action.type) { + case SET_STARTED_STATE: + return Object.assign({}, state, { + started: action.started + }); case SET_RUNNING_STATE: return Object.assign({}, state, { running: action.running @@ -22,6 +28,14 @@ const reducer = function (state, action) { } }; +const setStartedState = function (started) { + return { + type: SET_STARTED_STATE, + started: started + }; +}; + + const setRunningState = function (running) { return { type: SET_RUNNING_STATE, @@ -40,5 +54,6 @@ export { reducer as default, initialState as vmStatusInitialState, setRunningState, + setStartedState, setTurboState }; diff --git a/test/unit/util/vm-manager-hoc.test.jsx b/test/unit/util/vm-manager-hoc.test.jsx index 4cee7a49c..798e3a7e4 100644 --- a/test/unit/util/vm-manager-hoc.test.jsx +++ b/test/unit/util/vm-manager-hoc.test.jsx @@ -17,7 +17,8 @@ describe('VMManagerHOC', () => { store = mockStore({ scratchGui: { projectState: {}, - mode: {} // Mocked by using override props in tests when needed + mode: {}, + vmStatus: {} } }); vm = new VM(); @@ -31,6 +32,7 @@ describe('VMManagerHOC', () => { mount( @@ -40,8 +42,7 @@ describe('VMManagerHOC', () => { expect(vm.initialized).toBe(true); // But vm should not be started automatically - expect(vm.start.mock.calls.length).toBe(0); - expect(vm.started).not.toBe(true); + expect(vm.start).not.toHaveBeenCalled(); }); test('when it mounts in editor mode, the vm is initialized and started', () => { const Component = () => (
); @@ -49,6 +50,7 @@ describe('VMManagerHOC', () => { mount( @@ -57,17 +59,16 @@ describe('VMManagerHOC', () => { expect(vm.setCompatibilityMode.mock.calls.length).toBe(1); expect(vm.initialized).toBe(true); - expect(vm.start.mock.calls.length).toBe(1); - expect(vm.started).toBe(true); + expect(vm.start).toHaveBeenCalled(); }); test('if it mounts with an initialized vm, it does not reinitialize the vm but will start it', () => { const Component = () =>
; const WrappedComponent = vmManagerHOC(Component); vm.initialized = true; - vm.started = false; mount( @@ -76,28 +77,44 @@ describe('VMManagerHOC', () => { expect(vm.setCompatibilityMode.mock.calls.length).toBe(0); expect(vm.initialized).toBe(true); - expect(vm.start.mock.calls.length).toBe(1); - expect(vm.started).toBe(true); + expect(vm.start).toHaveBeenCalled(); }); test('if it mounts without starting the VM, it can be started by switching to editor mode', () => { const Component = () =>
; const WrappedComponent = vmManagerHOC(Component); vm.initialized = true; - vm.started = false; const mounted = mount( ); - expect(vm.start.mock.calls.length).toBe(0); + expect(vm.start).not.toHaveBeenCalled(); mounted.setProps({ isPlayerOnly: false }); - expect(vm.start.mock.calls.length).toBe(1); - expect(vm.started).toBe(true); + expect(vm.start).toHaveBeenCalled(); + }); + test('if it mounts with an initialized and started VM, it does not start again', () => { + const Component = () =>
; + const WrappedComponent = vmManagerHOC(Component); + vm.initialized = true; + const mounted = mount( + + ); + expect(vm.start).not.toHaveBeenCalled(); + mounted.setProps({ + isPlayerOnly: false + }); + expect(vm.start).not.toHaveBeenCalled(); }); test('if the isLoadingWithId prop becomes true, it loads project data into the vm', () => { vm.loadProject = jest.fn(() => Promise.resolve()); From 9e783d8aeb0315324e1ede2963074fdc1e75cc09 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 27 Nov 2018 14:09:22 -0500 Subject: [PATCH 4/6] Use a big green flag overlay to start the VM if needed --- src/components/stage/stage.css | 15 +++++++- src/components/stage/stage.jsx | 6 +++ src/containers/green-flag-overlay.jsx | 55 +++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 src/containers/green-flag-overlay.jsx diff --git a/src/components/stage/stage.css b/src/components/stage/stage.css index 9264c094e..97f7e1ebf 100644 --- a/src/components/stage/stage.css +++ b/src/components/stage/stage.css @@ -89,7 +89,10 @@ to adjust for the border using a different method */ padding-bottom: calc($stage-full-screen-stage-padding + $stage-full-screen-border-width); } -.monitor-wrapper, .color-picker-wrapper, .frame-wrapper { +.monitor-wrapper, +.color-picker-wrapper, +.frame-wrapper, +.green-flag-overlay-wrapper { position: absolute; top: 0; left: 0; @@ -138,6 +141,16 @@ to adjust for the border using a different method */ animation-fill-mode: forwards; /* Leave at 0 opacity after animation */ } +.green-flag-overlay { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + pointer-events: all; + cursor: pointer; +} + @keyframes flash { 0% { opacity: 1; } 100% { opacity: 0; } diff --git a/src/components/stage/stage.jsx b/src/components/stage/stage.jsx index ede5048ea..6d81443f8 100644 --- a/src/components/stage/stage.jsx +++ b/src/components/stage/stage.jsx @@ -7,6 +7,7 @@ import DOMElementRenderer from '../../containers/dom-element-renderer.jsx'; import Loupe from '../loupe/loupe.jsx'; import MonitorList from '../../containers/monitor-list.jsx'; import TargetHighlight from '../../containers/target-highlight.jsx'; +import GreenFlagOverlay from '../../containers/green-flag-overlay.jsx'; import Question from '../../containers/question.jsx'; import MicIndicator from '../mic-indicator/mic-indicator.jsx'; import {STAGE_DISPLAY_SIZES} from '../../lib/layout-constants.js'; @@ -71,6 +72,11 @@ const StageComponent = props => { stageWidth={stageDimensions.width} /> + + + {isColorPicking && colorInfo ? ( diff --git a/src/containers/green-flag-overlay.jsx b/src/containers/green-flag-overlay.jsx new file mode 100644 index 000000000..2d0e4135f --- /dev/null +++ b/src/containers/green-flag-overlay.jsx @@ -0,0 +1,55 @@ +import bindAll from 'lodash.bindall'; +import React from 'react'; +import PropTypes from 'prop-types'; + +import {connect} from 'react-redux'; +import VM from 'scratch-vm'; +import greenFlag from '../components/green-flag/icon--green-flag.svg'; + +class GreenFlagOverlay extends React.Component { + constructor (props) { + super(props); + bindAll(this, [ + 'handleClick' + ]); + } + + handleClick () { + this.props.vm.start(); + this.props.vm.greenFlag(); + } + + render () { + if (this.props.isStarted) return null; + + return ( +
+ +
+ ); + } +} + +GreenFlagOverlay.propTypes = { + className: PropTypes.string, + isStarted: PropTypes.bool, + vm: PropTypes.instanceOf(VM) +}; + +const mapStateToProps = state => ({ + isStarted: state.scratchGui.vmStatus.started, + vm: state.scratchGui.vm +}); + +const mapDispatchToProps = () => ({}); + +export default connect( + mapStateToProps, + mapDispatchToProps +)(GreenFlagOverlay); From 43b1ae85c7cd2874e75720963492572c619ff849 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 27 Nov 2018 14:59:39 -0500 Subject: [PATCH 5/6] Call renderer.draw after project loads to show the initial state --- src/containers/stage.jsx | 6 ++++++ src/lib/vm-manager-hoc.jsx | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/containers/stage.jsx b/src/containers/stage.jsx index 888dfa1c6..077006fcc 100644 --- a/src/containers/stage.jsx +++ b/src/containers/stage.jsx @@ -59,6 +59,12 @@ class Stage extends React.Component { this.canvas = document.createElement('canvas'); this.renderer = new Renderer(this.canvas); this.props.vm.attachRenderer(this.renderer); + + // Calling draw a single time before any project is loaded just makes + // the canvas white instead of solid black–needed because it is not + // possible to use CSS to style the canvas to have a different + // default color + this.props.vm.renderer.draw(); } this.props.vm.attachV2SVGAdapter(new V2SVGAdapter()); this.props.vm.attachV2BitmapAdapter(new V2BitmapAdapter()); diff --git a/src/lib/vm-manager-hoc.jsx b/src/lib/vm-manager-hoc.jsx index cbbf10385..981fe8326 100644 --- a/src/lib/vm-manager-hoc.jsx +++ b/src/lib/vm-manager-hoc.jsx @@ -53,6 +53,17 @@ const vmManagerHOC = function (WrappedComponent) { return this.props.vm.loadProject(this.props.projectData) .then(() => { this.props.onLoadedProject(this.props.loadingState, this.props.canSave); + + // If the vm is not running, call draw on the renderer manually + // This draws the state of the loaded project with no blocks running + // which closely matches the 2.0 behavior, except for monitors– + // 2.0 runs monitors and shows updates (e.g. timer monitor) + // before the VM starts running other hat blocks. + if (!this.props.isStarted) { + // Wrap in a setTimeout because skin loading in + // the renderer can be async. + setTimeout(() => this.props.vm.renderer.draw()); + } }) .catch(e => { this.props.onError(e); From 4d74ed036c3213a6a4f3f90ab7de7827575542bb Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 27 Nov 2018 15:33:23 -0500 Subject: [PATCH 6/6] Better rendering for green flag overlay --- src/components/stage/stage.css | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/components/stage/stage.css b/src/components/stage/stage.css index 97f7e1ebf..7e0a59c2e 100644 --- a/src/components/stage/stage.css +++ b/src/components/stage/stage.css @@ -141,16 +141,36 @@ to adjust for the border using a different method */ animation-fill-mode: forwards; /* Leave at 0 opacity after animation */ } -.green-flag-overlay { - width: 100%; - height: 100%; +.green-flag-overlay-wrapper { display: flex; justify-content: center; align-items: center; + background: rgba(0,0,0,0.25); + border-radius: 0.5rem; +} + +.green-flag-overlay { + padding: 1rem; + border-radius: 100%; + background: rgba(255,255,255,0.75); + border: 3px solid $ui-white; pointer-events: all; cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + box-sizing: border-box; + width: 5rem; + height: 5rem; } +.green-flag-overlay > img { + width: 100%; + object-fit: contain; +} + + + @keyframes flash { 0% { opacity: 1; } 100% { opacity: 0; }