diff --git a/src/components/asset-panel/selector.jsx b/src/components/asset-panel/selector.jsx index d0d076bab5b9be6ecc64a152cb8e3f6954021106..0ddc2bb51202771fc9861ac603174bb28716773f 100644 --- a/src/components/asset-panel/selector.jsx +++ b/src/components/asset-panel/selector.jsx @@ -27,6 +27,7 @@ const Selector = props => { <Box className={styles.listArea}> {items.map((item, index) => ( <SpriteSelectorItem + assetId={item.assetId} className={styles.listItem} costumeURL={item.url} id={index} diff --git a/src/components/sprite-selector/sprite-selector.jsx b/src/components/sprite-selector/sprite-selector.jsx index df48343745100b55e7f2a1334681b0aec65c3e58..3410c75809e683e67f664d11b01049f003fd0855 100644 --- a/src/components/sprite-selector/sprite-selector.jsx +++ b/src/components/sprite-selector/sprite-selector.jsx @@ -53,17 +53,15 @@ const SpriteSelectorComponent = function (props) { {Object.keys(sprites) // Re-order by list order .sort((id1, id2) => sprites[id1].order - sprites[id2].order) - .map(id => ( + .map(id => sprites[id]) + .map(sprite => ( <SpriteSelectorItem + assetId={sprite.costume && sprite.costume.assetId} className={styles.sprite} - costumeURL={ - sprites[id].costume && - sprites[id].costume.url - } - id={id} - key={id} - name={sprites[id].name} - selected={id === selectedId} + id={sprite.id} + key={sprite.id} + name={sprite.name} + selected={sprite.id === selectedId} onClick={onSelectSprite} onDeleteButtonClick={onDeleteSprite} /> diff --git a/src/components/target-pane/target-pane.jsx b/src/components/target-pane/target-pane.jsx index 5d8fc1915cd185125ad12104a80c341590406895..c779f274713413e13d62851dae45c54223caadaa 100644 --- a/src/components/target-pane/target-pane.jsx +++ b/src/components/target-pane/target-pane.jsx @@ -1,5 +1,3 @@ -const isEqual = require('lodash.isequal'); -const omit = require('lodash.omit'); const classNames = require('classnames'); const PropTypes = require('prop-types'); const React = require('react'); @@ -23,119 +21,106 @@ const addIcon = require('./icon--add.svg'); * @param {object} props Props for the component * @returns {React.Component} rendered component */ -class TargetPane extends React.Component { - shouldComponentUpdate (nextProps) { - return ( - // Do a normal shallow compare on all props except sprites - Object.keys(omit(nextProps, ['sprites'])) - .reduce((all, k) => all || nextProps[k] !== this.props[k], false) || - // Deep compare on sprites object - !isEqual(this.props.sprites, nextProps.sprites) - ); - } - render () { - const { - editingTarget, - backdropLibraryVisible, - costumeLibraryVisible, - soundLibraryVisible, - spriteLibraryVisible, - onChangeSpriteDirection, - onChangeSpriteName, - onChangeSpriteRotationStyle, - onChangeSpriteVisibility, - onChangeSpriteX, - onChangeSpriteY, - onDeleteSprite, - onNewSpriteClick, - onNewBackdropClick, - onRequestCloseBackdropLibrary, - onRequestCloseCostumeLibrary, - onRequestCloseSoundLibrary, - onRequestCloseSpriteLibrary, - onSelectSprite, - stage, - sprites, - vm, - ...componentProps - } = this.props; - return ( - <Box - className={styles.targetPane} - {...componentProps} - > +const TargetPane = ({ + editingTarget, + backdropLibraryVisible, + costumeLibraryVisible, + soundLibraryVisible, + spriteLibraryVisible, + onChangeSpriteDirection, + onChangeSpriteName, + onChangeSpriteRotationStyle, + onChangeSpriteVisibility, + onChangeSpriteX, + onChangeSpriteY, + onDeleteSprite, + onNewSpriteClick, + onNewBackdropClick, + onRequestCloseBackdropLibrary, + onRequestCloseCostumeLibrary, + onRequestCloseSoundLibrary, + onRequestCloseSpriteLibrary, + onSelectSprite, + stage, + sprites, + vm, + ...componentProps +}) => ( + <Box + className={styles.targetPane} + {...componentProps} + > - <SpriteSelectorComponent - selectedId={editingTarget} - sprites={sprites} - onChangeSpriteDirection={onChangeSpriteDirection} - onChangeSpriteName={onChangeSpriteName} - onChangeSpriteRotationStyle={onChangeSpriteRotationStyle} - onChangeSpriteVisibility={onChangeSpriteVisibility} - onChangeSpriteX={onChangeSpriteX} - onChangeSpriteY={onChangeSpriteY} - onDeleteSprite={onDeleteSprite} - onSelectSprite={onSelectSprite} - /> - <Box className={styles.stageSelectorWrapper}> - {stage.id && <StageSelector - backdropCount={stage.costumeCount} - id={stage.id} - selected={stage.id === editingTarget} - url={ - stage.costume && - stage.costume.url - } - onSelect={onSelectSprite} - />} - <Box> + <SpriteSelectorComponent + selectedId={editingTarget} + sprites={sprites} + onChangeSpriteDirection={onChangeSpriteDirection} + onChangeSpriteName={onChangeSpriteName} + onChangeSpriteRotationStyle={onChangeSpriteRotationStyle} + onChangeSpriteVisibility={onChangeSpriteVisibility} + onChangeSpriteX={onChangeSpriteX} + onChangeSpriteY={onChangeSpriteY} + onDeleteSprite={onDeleteSprite} + onSelectSprite={onSelectSprite} + /> + <Box className={styles.stageSelectorWrapper}> + {stage.id && <StageSelector + assetId={ + stage.costume && + stage.costume.assetId + } + backdropCount={stage.costumeCount} + id={stage.id} + selected={stage.id === editingTarget} + onSelect={onSelectSprite} + />} + <Box> - <button - className={classNames(styles.addButtonWrapper, styles.addButtonWrapperSprite)} - onClick={onNewSpriteClick} - > - <img - className={styles.addButton} - src={addIcon} - /> - </button> + <button + className={classNames(styles.addButtonWrapper, styles.addButtonWrapperSprite)} + onClick={onNewSpriteClick} + > + <img + className={styles.addButton} + src={addIcon} + /> + </button> - <button - className={classNames(styles.addButtonWrapper, styles.addButtonWrapperStage)} - onClick={onNewBackdropClick} - > - <img - className={styles.addButton} - src={addIcon} - /> - </button> + <button + className={classNames(styles.addButtonWrapper, styles.addButtonWrapperStage)} + onClick={onNewBackdropClick} + > + <img + className={styles.addButton} + src={addIcon} + /> + </button> - <SpriteLibrary - visible={spriteLibraryVisible} - vm={vm} - onRequestClose={onRequestCloseSpriteLibrary} - /> - <CostumeLibrary - visible={costumeLibraryVisible} - vm={vm} - onRequestClose={onRequestCloseCostumeLibrary} - /> - <SoundLibrary - visible={soundLibraryVisible} - vm={vm} - onRequestClose={onRequestCloseSoundLibrary} - /> - <BackdropLibrary - visible={backdropLibraryVisible} - vm={vm} - onRequestClose={onRequestCloseBackdropLibrary} - /> - </Box> - </Box> + <SpriteLibrary + visible={spriteLibraryVisible} + vm={vm} + onRequestClose={onRequestCloseSpriteLibrary} + /> + <CostumeLibrary + visible={costumeLibraryVisible} + vm={vm} + onRequestClose={onRequestCloseCostumeLibrary} + /> + <SoundLibrary + visible={soundLibraryVisible} + vm={vm} + onRequestClose={onRequestCloseSoundLibrary} + /> + <BackdropLibrary + visible={backdropLibraryVisible} + vm={vm} + onRequestClose={onRequestCloseBackdropLibrary} + /> </Box> - ); - } -} + </Box> + </Box> +); + const spriteShape = PropTypes.shape({ costume: PropTypes.shape({ url: PropTypes.string, diff --git a/src/containers/sprite-selector-item.jsx b/src/containers/sprite-selector-item.jsx index a9182996e171321066dc8cf52233816b32b6aa4f..d6c738cb3002688b3d812367f30b8cd8e27a1406 100644 --- a/src/containers/sprite-selector-item.jsx +++ b/src/containers/sprite-selector-item.jsx @@ -2,6 +2,8 @@ const bindAll = require('lodash.bindall'); const PropTypes = require('prop-types'); const React = require('react'); +const {connect} = require('react-redux'); + const SpriteSelectorItemComponent = require('../components/sprite-selector-item/sprite-selector-item.jsx'); class SpriteSelectorItem extends React.Component { @@ -24,9 +26,12 @@ class SpriteSelectorItem extends React.Component { } render () { const { - id, // eslint-disable-line no-unused-vars - onClick, // eslint-disable-line no-unused-vars - onDeleteButtonClick, // eslint-disable-line no-unused-vars + /* eslint-disable no-unused-vars */ + assetId, + id, + onClick, + onDeleteButtonClick, + /* eslint-enable no-unused-vars */ ...props } = this.props; return ( @@ -40,6 +45,7 @@ class SpriteSelectorItem extends React.Component { } SpriteSelectorItem.propTypes = { + assetId: PropTypes.string, costumeURL: PropTypes.string, id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), name: PropTypes.string, @@ -48,4 +54,10 @@ SpriteSelectorItem.propTypes = { selected: PropTypes.bool }; -module.exports = SpriteSelectorItem; +const mapStateToProps = (state, {assetId, costumeURL}) => ({ + costumeURL: costumeURL || (assetId && state.vm.runtime.storage.get(assetId).encodeDataURI()) +}); + +module.exports = connect( + mapStateToProps +)(SpriteSelectorItem); diff --git a/src/containers/stage-selector.jsx b/src/containers/stage-selector.jsx index d37a4437c51d2c6c8b6b631005fc5a938e3f7256..9b4b83d0d7a3efcdd98697915af2da8c51ba6a62 100644 --- a/src/containers/stage-selector.jsx +++ b/src/containers/stage-selector.jsx @@ -2,6 +2,8 @@ const bindAll = require('lodash.bindall'); const PropTypes = require('prop-types'); const React = require('react'); +const {connect} = require('react-redux'); + const StageSelectorComponent = require('../components/stage-selector/stage-selector.jsx'); class StageSelector extends React.Component { @@ -18,6 +20,7 @@ class StageSelector extends React.Component { render () { const { /* eslint-disable no-unused-vars */ + assetId, id, onSelect, /* eslint-enable no-unused-vars */ @@ -36,4 +39,12 @@ StageSelector.propTypes = { id: PropTypes.string, onSelect: PropTypes.func }; -module.exports = StageSelector; + +const mapStateToProps = (state, {assetId}) => ({ + url: assetId && state.vm.runtime.storage.get(assetId).encodeDataURI() +}); + +module.exports = connect( + mapStateToProps, + () => ({}) // omit dispatch prop +)(StageSelector); diff --git a/src/lib/vm-listener-hoc.jsx b/src/lib/vm-listener-hoc.jsx index 8c80ae5d9694711a50a3974aa2704883d57de76e..58ea1ca716b83195733d946c452c5eece51bfc8c 100644 --- a/src/lib/vm-listener-hoc.jsx +++ b/src/lib/vm-listener-hoc.jsx @@ -3,8 +3,6 @@ const PropTypes = require('prop-types'); const React = require('react'); const VM = require('scratch-vm'); -const Storage = require('./storage'); - const {connect} = require('react-redux'); const targets = require('../reducers/targets'); @@ -92,13 +90,12 @@ const vmListenerHOC = function (WrappedComponent) { onTargetsUpdate: PropTypes.func, vm: PropTypes.instanceOf(VM).isRequired }; - const defaultVM = new VM('vm-listener-hoc'); - defaultVM.attachStorage(new Storage()); VMListener.defaultProps = { - attachKeyboardEvents: true, - vm: defaultVM + attachKeyboardEvents: true }; - const mapStateToProps = () => ({}); + const mapStateToProps = state => ({ + vm: state.vm + }); const mapDispatchToProps = dispatch => ({ onTargetsUpdate: data => { dispatch(targets.updateEditingTarget(data.editingTarget)); diff --git a/src/reducers/gui.js b/src/reducers/gui.js index bde1a2f3f1c6a02d9a8e1a7131754ef91ee3f33d..9b1cf60671fd255e31ba620dbdff2dd1d6f280ef 100644 --- a/src/reducers/gui.js +++ b/src/reducers/gui.js @@ -2,6 +2,7 @@ const {combineReducers} = require('redux'); module.exports = combineReducers({ modals: require('./modals'), + monitors: require('./monitors'), targets: require('./targets'), - monitors: require('./monitors') + vm: require('./vm') }); diff --git a/src/reducers/vm.js b/src/reducers/vm.js new file mode 100644 index 0000000000000000000000000000000000000000..1fd0eb4a2f199b7329783d825445adcf550b6176 --- /dev/null +++ b/src/reducers/vm.js @@ -0,0 +1,24 @@ +const VM = require('scratch-vm'); +const Storage = require('../lib/storage'); + +const SET_VM = 'scratch-gui/vm/SET_VM'; +const defaultVM = new VM(); +defaultVM.attachStorage(new Storage()); +const initialState = defaultVM; + +const reducer = function (state, action) { + if (typeof state === 'undefined') state = initialState; + switch (action.type) { + case SET_VM: + return action.vm; + default: + return state; + } +}; +reducer.setVM = function (vm) { + return { + type: SET_VM, + vm: vm + }; +}; +module.exports = reducer;