diff --git a/package.json b/package.json index d8b102fe61a9c7694a77aab92711a489b2203046..38bd0168678c7b649c705dee4695825b3dd1943d 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "react-ga": "2.4.1", "react-intl": "2.4.0", "react-intl-redux": "0.7.0", - "react-modal": "3.3.1", + "react-modal": "3.3.2", "react-redux": "5.0.7", "react-responsive": "4.0.5", "react-style-proptype": "3.2.1", @@ -89,9 +89,9 @@ "redux-throttle": "0.1.1", "rimraf": "^2.6.1", "scratch-audio": "0.1.0-prerelease.1516198804", - "scratch-blocks": "0.1.0-prerelease.1520519504", + "scratch-blocks": "0.1.0-prerelease.1521811349", "scratch-l10n": "2.0.20180108132626", - "scratch-paint": "0.2.0-prerelease.20180309170325", + "scratch-paint": "0.2.0-prerelease.20180320181154", "scratch-render": "0.1.0-prerelease.1516837442", "scratch-storage": "0.4.0", "scratch-vm": "0.1.0-prerelease.1521814818-prerelease.1521814838", diff --git a/src/components/asset-panel/selector.jsx b/src/components/asset-panel/selector.jsx index f2ab58cae0dc4c084cc525ffc50252eaad777b8e..cc5abed4d55d1cb4e1af2b441dfefc90e40aecd5 100644 --- a/src/components/asset-panel/selector.jsx +++ b/src/components/asset-panel/selector.jsx @@ -59,9 +59,9 @@ const Selector = props => { Selector.propTypes = { buttons: PropTypes.arrayOf(PropTypes.shape({ - message: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, img: PropTypes.string.isRequired, - onClick: PropTypes.func.isRequired + onClick: PropTypes.func })), items: PropTypes.arrayOf(PropTypes.shape({ url: PropTypes.string, diff --git a/src/components/browser-modal/browser-modal.jsx b/src/components/browser-modal/browser-modal.jsx index c410f7f62d26e359a300e2bb80072e8ed9bcf248..9052d5f57075796500df46e8f172cca3405e0f8f 100644 --- a/src/components/browser-modal/browser-modal.jsx +++ b/src/components/browser-modal/browser-modal.jsx @@ -9,7 +9,7 @@ import styles from './browser-modal.css'; const messages = defineMessages({ label: { id: 'gui.unsupportedBrowser.label', - defaultMessage: 'Internet Explorer is not supported', + defaultMessage: 'Browser is not supported', description: '' } }); @@ -31,7 +31,7 @@ const BrowserModal = ({intl, ...props}) => ( <p> { /* eslint-disable max-len */ } <FormattedMessage - defaultMessage="We're very sorry, but Scratch 3.0 does not support Internet Explorer. We recommend trying a newer browser such as Google Chrome, Mozilla Firefox, or Microsoft Edge." + defaultMessage="We're very sorry, but Scratch 3.0 does not support Internet Explorer, Opera or Silk. We recommend trying a newer browser such as Google Chrome, Mozilla Firefox, or Microsoft Edge." description="Unsupported browser description" id="gui.unsupportedBrowser.description" /> diff --git a/src/containers/backdrop-library.jsx b/src/containers/backdrop-library.jsx index 68f373ece16c7181c259a7b0c35db5d2442752b1..8f796225e81a8e8740b4100b408269fbbc22b2b2 100644 --- a/src/containers/backdrop-library.jsx +++ b/src/containers/backdrop-library.jsx @@ -23,11 +23,7 @@ class BackdropLibrary extends React.Component { bitmapResolution: item.info.length > 2 ? item.info[2] : 1, skinId: null }; - this.props.vm.addBackdrop(item.md5, vmBackdrop).then(() => { - if (this.props.onNewBackdrop) { - this.props.onNewBackdrop(); - } - }); + this.props.vm.addBackdrop(item.md5, vmBackdrop); analytics.event({ category: 'library', action: 'Select Backdrop', @@ -47,7 +43,6 @@ class BackdropLibrary extends React.Component { } BackdropLibrary.propTypes = { - onNewBackdrop: PropTypes.func.isRequired, onRequestClose: PropTypes.func, vm: PropTypes.instanceOf(VM).isRequired }; diff --git a/src/containers/blocks.jsx b/src/containers/blocks.jsx index bedbbdbade90c98fe17572495521ca28478cdef9..504291036f35d8f99d61c062b53c31d7e0315dbe 100644 --- a/src/containers/blocks.jsx +++ b/src/containers/blocks.jsx @@ -95,9 +95,11 @@ class Blocks extends React.Component { } if (prevProps.toolboxXML !== this.props.toolboxXML) { - const selectedCategoryName = this.workspace.toolbox_.getSelectedItem().name_; + const categoryName = this.workspace.toolbox_.getSelectedCategoryName(); + const offset = this.workspace.toolbox_.getCategoryScrollOffset(); this.workspace.updateToolbox(this.props.toolboxXML); - this.workspace.toolbox_.setSelectedCategoryByName(selectedCategoryName); + const currentCategoryPos = this.workspace.toolbox_.getCategoryPositionByName(categoryName); + this.workspace.toolbox_.setFlyoutScrollPos(currentCategoryPos + offset); } if (this.props.isVisible === prevProps.isVisible) { return; diff --git a/src/containers/costume-library.jsx b/src/containers/costume-library.jsx index 2e631bd95e4339344eac40cbfc3f691f4c2cd87b..afdd56a5d292a714fa81d954f6ee27fede66ab29 100644 --- a/src/containers/costume-library.jsx +++ b/src/containers/costume-library.jsx @@ -23,9 +23,7 @@ class CostumeLibrary extends React.PureComponent { bitmapResolution: item.info.length > 2 ? item.info[2] : 1, skinId: null }; - this.props.vm.addCostume(item.md5, vmCostume).then(() => { - this.props.onNewCostume(); - }); + this.props.vm.addCostume(item.md5, vmCostume); analytics.event({ category: 'library', action: 'Select Costume', @@ -45,7 +43,6 @@ class CostumeLibrary extends React.PureComponent { } CostumeLibrary.propTypes = { - onNewCostume: PropTypes.func.isRequired, onRequestClose: PropTypes.func, vm: PropTypes.instanceOf(VM).isRequired }; diff --git a/src/containers/costume-tab.jsx b/src/containers/costume-tab.jsx index 3668a94fb1bc57fc86f19beb97f3f2bd9af86371..37fd8a12e1e490933ec5f843eb12e667c2316b10 100644 --- a/src/containers/costume-tab.jsx +++ b/src/containers/costume-tab.jsx @@ -67,7 +67,6 @@ class CostumeTab extends React.Component { 'handleSelectCostume', 'handleDeleteCostume', 'handleDuplicateCostume', - 'handleNewCostume', 'handleNewBlankCostume', 'handleSurpriseCostume', 'handleSurpriseBackdrop' @@ -96,11 +95,20 @@ class CostumeTab extends React.Component { return; } - // If switching editing targets, update the costume index - if (this.props.editingTarget !== editingTarget) { + if (this.props.editingTarget === editingTarget) { + // If costumes have been added or removed, change costumes to the editing target's + // current costume. + const oldTarget = this.props.sprites[editingTarget] ? + this.props.sprites[editingTarget] : this.props.stage; + // @todo: Find and switch to the index of the costume that is new. This is blocked by + // https://github.com/LLK/scratch-vm/issues/967 + // Right now, you can land on the wrong costume if a costume changing script is running. + if (oldTarget.costumeCount !== target.costumeCount) { + this.setState({selectedCostumeIndex: target.currentCostume}); + } + } else { + // If switching editing targets, update the costume index this.setState({selectedCostumeIndex: target.currentCostume}); - } else if (this.state.selectedCostumeIndex > target.costumes.length - 1) { - this.setState({selectedCostumeIndex: target.costumes.length - 1}); } } handleSelectCostume (costumeIndex) { @@ -111,14 +119,7 @@ class CostumeTab extends React.Component { this.props.vm.deleteCostume(costumeIndex); } handleDuplicateCostume (costumeIndex) { - this.props.vm.duplicateCostume(costumeIndex).then(() => { - this.setState({selectedCostumeIndex: costumeIndex + 1}); - }); - } - handleNewCostume () { - if (!this.props.vm.editingTarget) return; - const costumes = this.props.vm.editingTarget.getCostumes() || []; - this.setState({selectedCostumeIndex: Math.max(costumes.length - 1, 0)}); + this.props.vm.duplicateCostume(costumeIndex); } handleNewBlankCostume () { const emptyItem = costumeLibraryContent.find(item => ( @@ -133,9 +134,7 @@ class CostumeTab extends React.Component { skinId: null }; - this.props.vm.addCostume(emptyItem.md5, vmCostume).then(() => { - this.handleNewCostume(); - }); + this.props.vm.addCostume(emptyItem.md5, vmCostume); } handleSurpriseCostume () { const item = costumeLibraryContent[Math.floor(Math.random() * costumeLibraryContent.length)]; @@ -146,9 +145,7 @@ class CostumeTab extends React.Component { bitmapResolution: item.info.length > 2 ? item.info[2] : 1, skinId: null }; - this.props.vm.addCostume(item.md5, vmCostume).then(() => { - this.handleNewCostume(); - }); + this.props.vm.addCostume(item.md5, vmCostume); } handleSurpriseBackdrop () { const item = backdropLibraryContent[Math.floor(Math.random() * backdropLibraryContent.length)]; @@ -159,12 +156,9 @@ class CostumeTab extends React.Component { bitmapResolution: item.info.length > 2 ? item.info[2] : 1, skinId: null }; - this.props.vm.addCostume(item.md5, vmCostume).then(() => { - this.handleNewCostume(); - }); + this.props.vm.addCostume(item.md5, vmCostume); } render () { - // For paint wrapper const { intl, onNewLibraryBackdropClick, @@ -173,15 +167,11 @@ class CostumeTab extends React.Component { costumeLibraryVisible, onRequestCloseBackdropLibrary, onRequestCloseCostumeLibrary, - ...props - } = this.props; - - const { editingTarget, sprites, stage, vm - } = props; + } = this.props; const target = editingTarget && sprites[editingTarget] ? sprites[editingTarget] : stage; @@ -236,14 +226,12 @@ class CostumeTab extends React.Component { {costumeLibraryVisible ? ( <CostumeLibrary vm={vm} - onNewCostume={this.handleNewCostume} onRequestClose={onRequestCloseCostumeLibrary} /> ) : null} {backdropLibraryVisible ? ( <BackdropLibrary vm={vm} - onNewBackdrop={this.handleNewCostume} onRequestClose={onRequestCloseBackdropLibrary} /> ) : null} diff --git a/src/containers/error-boundary.jsx b/src/containers/error-boundary.jsx index d7083f05273c2b1003551f5059995e2a448ecb05..4c473a7374a05e14b4f99772a499e72a1f8f8eb3 100644 --- a/src/containers/error-boundary.jsx +++ b/src/containers/error-boundary.jsx @@ -35,7 +35,12 @@ class ErrorBoundary extends React.Component { render () { if (this.state.hasError) { - if (platform.name === 'IE') { + // don't use array.includes because that's something that causes IE to crash. + if ( + platform.name === 'IE' || + platform.name === 'Opera' || + platform.name === 'Opera Mini' || + platform.name === 'Silk') { return <BrowserModalComponent onBack={this.handleBack} />; } return <CrashMessageComponent onReload={this.handleReload} />; diff --git a/src/containers/preview-modal.jsx b/src/containers/preview-modal.jsx index 1209f251f0445da6a5aa0d65f8fa6d343ce5654e..3fa6c8f03f97eea04d8e1ed3b69c50ca2e070b0a 100644 --- a/src/containers/preview-modal.jsx +++ b/src/containers/preview-modal.jsx @@ -36,10 +36,7 @@ class PreviewModal extends React.Component { this.props.onViewProject(); } supportedBrowser () { - if (platform.name === 'IE') { - return false; - } - return true; + return !['IE', 'Opera', 'Opera Mini', 'Silk', 'Vivaldi'].includes(platform.name); } render () { return (this.supportedBrowser() ? diff --git a/src/containers/target-pane.jsx b/src/containers/target-pane.jsx index 317cfcbb32b5d0dfff2403ee406381036477f8cf..97d6dffaa31e4c89e9f09a02d0e7acda5bb8fe76 100644 --- a/src/containers/target-pane.jsx +++ b/src/containers/target-pane.jsx @@ -89,6 +89,7 @@ class TargetPane extends React.Component { render () { const { onActivateTab, // eslint-disable-line no-unused-vars + onReceivedBlocks, // eslint-disable-line no-unused-vars ...componentProps } = this.props; return ( diff --git a/src/lib/make-toolbox-xml.js b/src/lib/make-toolbox-xml.js index 677dc2efad0b1c2d3b7ddb650d998ca9db441258..4bfb8b2e5f93e287bfd35c9fbe63a7ec8a328b06 100644 --- a/src/lib/make-toolbox-xml.js +++ b/src/lib/make-toolbox-xml.js @@ -310,13 +310,17 @@ const sound = function () { `; }; -const events = function () { +const events = function (isStage) { return ` <category name="Events" colour="#FFD500" secondaryColour="#CC9900"> <block type="event_whenflagclicked"/> <block type="event_whenkeypressed"> </block> - <block type="event_whenthisspriteclicked"/> + ${isStage ? ` + <block type="event_whenstageclicked"/> + ` : ` + <block type="event_whenthisspriteclicked"/> + `} <block type="event_whenbackdropswitchesto"> </block> ${blockSeparator} @@ -430,7 +434,11 @@ const sensing = function (isStage) { </block> <block id="answer" type="sensing_answer"/> ${blockSeparator} - <block type="sensing_keypressed"/> + <block type="sensing_keypressed"> + <value name="KEY_OPTION"> + <shadow type="sensing_keyoptions"/> + </value> + </block> <block type="sensing_mousedown"/> <block type="sensing_mousex"/> <block type="sensing_mousey"/> diff --git a/src/lib/vm-listener-hoc.jsx b/src/lib/vm-listener-hoc.jsx index 8e7a0ca17d1062bb41491e2bbac5cb1b63dffeab..3f1453b548daf4aa77493a0330fbf349d1397c28 100644 --- a/src/lib/vm-listener-hoc.jsx +++ b/src/lib/vm-listener-hoc.jsx @@ -52,11 +52,6 @@ const vmListenerHOC = function (WrappedComponent) { keyCode: e.keyCode, isDown: true }); - - // Don't stop browser keyboard shortcuts - if (e.metaKey || e.altKey || e.ctrlKey) return; - - e.preventDefault(); } handleKeyUp (e) { // Always capture up events, @@ -99,9 +94,7 @@ const vmListenerHOC = function (WrappedComponent) { attachKeyboardEvents: true }; const mapStateToProps = state => ({ - vm: state.vm, - hoveredSprite: state.hoveredTarget.sprite, - editingTarget: state.targets.editingTarget + vm: state.vm }); const mapDispatchToProps = dispatch => ({ onTargetsUpdate: data => {