From 6da8ce7b510211da84eb051612640c26151fe28b Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2019 21:39:58 +0000 Subject: [PATCH 01/43] chore(package): update babel-eslint to version 10.0.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c56904828..a04231e83 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "autoprefixer": "6.3.6", "babel-cli": "6.26.0", "babel-core": "6.23.1", - "babel-eslint": "10.0.2", + "babel-eslint": "10.0.3", "babel-loader": "7.1.0", "babel-plugin-transform-object-rest-spread": "6.26.0", "babel-plugin-transform-require-context": "0.1.1", From ef5bbd8af785c92c9a18ce5f1bdd866115869a6f Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2019 21:40:20 +0000 Subject: [PATCH 02/43] chore(package): update lockfile package-lock.json --- package-lock.json | 49 ++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/package-lock.json b/package-lock.json index ba6fe1528..208aed211 100644 --- a/package-lock.json +++ b/package-lock.json @@ -91,9 +91,9 @@ } }, "chokidar": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz", - "integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", "dev": true, "optional": true, "requires": { @@ -1741,17 +1741,28 @@ } }, "babel-eslint": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.2.tgz", - "integrity": "sha512-UdsurWPtgiPgpJ06ryUnuaSXC2s0WoSZnQmEpbAH65XZSdwowgN5MvyP7e88nW07FYXv72erVtpBkxyDVKhH1Q==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.3.tgz", + "integrity": "sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "@babel/parser": "^7.0.0", "@babel/traverse": "^7.0.0", "@babel/types": "^7.0.0", - "eslint-scope": "3.7.1", - "eslint-visitor-keys": "^1.0.0" + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } } }, "babel-generator": { @@ -5037,16 +5048,6 @@ } } }, - "eslint-scope": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", - "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, "eslint-utils": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.0.tgz", @@ -15421,15 +15422,15 @@ } }, "scratch-gui": { - "version": "0.1.0-prerelease.20190820191527", - "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20190820191527.tgz", - "integrity": "sha512-i8q02twmI4WM69zBPMekhCG0JV7bqFi7F3l8iIpQA4DMZw8q2lw8hdsVqHEVhSSww8CL2S2iZs2Q6OtePhq5rA==", + "version": "0.1.0-prerelease.20190823155639", + "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20190823155639.tgz", + "integrity": "sha512-ITWoNgaXfN8uISThFlgLs9yNhucQrnmph5WYjAJER9KgDQxh7MBw1EFQyhZgcGU7W2TbUpeBtJRIeylwacpaVA==", "dev": true }, "scratch-l10n": { - "version": "3.5.20190813223429", - "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.5.20190813223429.tgz", - "integrity": "sha512-rSxUSwv0RgZTXUknAWuc7BFZWewiNhrgyPUMos/qAw4GgVMdY1ZRSIHBEIItpCXXYLOzw4ObcNafIim6Taq9NA==", + "version": "3.5.20190820223503", + "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.5.20190820223503.tgz", + "integrity": "sha512-FXuoED7ksWORYYYeprPPz0zRM2HGHLtZamDLkZkqi9rFmSRAbRgVA/SlVx1zz/8fng4BzTQMzNiDwW9PQhrNtQ==", "dev": true, "requires": { "@babel/cli": "^7.1.2", From 5d1c46d24fd54eb30eb9a22e5871f368d6b6a5ad Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 16 Sep 2019 22:41:28 -0400 Subject: [PATCH 03/43] slower spinner animation, with bezier timing --- src/components/spinner/spinner.scss | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/components/spinner/spinner.scss b/src/components/spinner/spinner.scss index 4aba6ad98..51cf2ffff 100644 --- a/src/components/spinner/spinner.scss +++ b/src/components/spinner/spinner.scss @@ -2,18 +2,13 @@ /* This class can be used on an icon that should spin. It first plays the intro animation, then spins forever. */ animation-name: intro, spin; - animation-duration: .25s, .5s; - animation-timing-function: cubic-bezier(.3, -3, .6, 3), linear; + animation-duration: .25s, 1s; + animation-timing-function: cubic-bezier(.3, -3, .6, 3), cubic-bezier(0.4, 0.1, 0.4, 1); animation-delay: 0s, .25s; animation-iteration-count: 1, infinite; animation-direction: normal; width: 1.4rem; /* standard is 1.4 rem but can be overwritten by parent */ height: 1.4rem; - -webkit-animation-name: intro, spin; - -webkit-animation-duration: .25s, .5s; - -webkit-animation-iteration-count: 1, infinite; - -webkit-animation-delay: 0s, .25s; - -webkit-animation-timing-function: cubic-bezier(.3, -3, .6, 3), linear; transform-origin: center; } @@ -21,24 +16,20 @@ 0% { transform: scale(0); opacity: 0; - -webkit-transform: scale(0); } 100% { transform: scale(1); opacity: 1; - -webkit-transform: scale(1); } } @keyframes spin { 0% { transform: rotate(0); - -webkit-transform: rotate(0); } 100% { transform: rotate(359deg); - -webkit-transform: rotate(359deg); } } From d2bfa13d0a63a8f6f2a96e8ce14a0f3dcc960e7a Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 16 Sep 2019 22:41:46 -0400 Subject: [PATCH 04/43] set size of join flow next step spinner --- src/components/join-flow/next-step-button.jsx | 16 ++++++++-------- src/components/join-flow/next-step-button.scss | 5 +++++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/components/join-flow/next-step-button.jsx b/src/components/join-flow/next-step-button.jsx index 16a447688..04b30ac43 100644 --- a/src/components/join-flow/next-step-button.jsx +++ b/src/components/join-flow/next-step-button.jsx @@ -16,14 +16,14 @@ const NextStepButton = props => ( type="submit" {...omit(props, ['intl', 'text', 'waiting'])} > - {props.waiting ? - : ( - - ) - } + {props.waiting ? ( + + ) : ( + + )} ); diff --git a/src/components/join-flow/next-step-button.scss b/src/components/join-flow/next-step-button.scss index 0b4bfc7ad..366972f11 100644 --- a/src/components/join-flow/next-step-button.scss +++ b/src/components/join-flow/next-step-button.scss @@ -22,3 +22,8 @@ justify-content: center; align-items: center; } + +.next-step-spinner { + width: 2.625rem; + height: 2.625rem; +} From de5e712305b1658f33bf4029de580021b0deb6c0 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Thu, 19 Sep 2019 12:36:25 -0400 Subject: [PATCH 05/43] when looking for latest fastly VCL to clone, use only active VCLs --- bin/configure-fastly.js | 2 +- bin/lib/fastly-extended.js | 13 ++++++-- test/unit/lib/fastly-extended.test.js | 44 +++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 test/unit/lib/fastly-extended.test.js diff --git a/bin/configure-fastly.js b/bin/configure-fastly.js index c6f76411d..c3eb4cca2 100644 --- a/bin/configure-fastly.js +++ b/bin/configure-fastly.js @@ -24,7 +24,7 @@ var routes = routeJson.map(function (route) { async.auto({ version: function (cb) { - fastly.getLatestVersion(function (err, response) { + fastly.getLatestActiveVersion(function (err, response) { if (err) return cb(err); // Validate latest version before continuing if (response.active || response.locked) { diff --git a/bin/lib/fastly-extended.js b/bin/lib/fastly-extended.js index 0e4467221..fbad130d5 100644 --- a/bin/lib/fastly-extended.js +++ b/bin/lib/fastly-extended.js @@ -24,11 +24,11 @@ module.exports = function (apiKey, serviceId) { }; /* - * getLatestVersion: Get the most recent version for the configured service + * getLatestActiveVersion: Get the most recent version for the configured service * * @param {callback} Callback with signature *err, latestVersion) */ - fastly.getLatestVersion = function (cb) { + fastly.getLatestActiveVersion = function (cb) { if (!this.serviceId) { return cb('Failed to get latest version. No serviceId configured'); } @@ -39,7 +39,14 @@ module.exports = function (apiKey, serviceId) { } var latestVersion = versions.reduce(function (lateVersion, version) { if (!lateVersion) return version; - if (version.number > lateVersion.number) return version; + // if versions we're comparing are both active, or both inactive, prefer + // whichever has a higher version number + if (lateVersion.active === version.active) { + if (version.number > lateVersion.number) return version; + return lateVersion; + } + // if only one of the versions is active, prefer that one + if (version.active === true) return version; return lateVersion; }); return cb(null, latestVersion); diff --git a/test/unit/lib/fastly-extended.test.js b/test/unit/lib/fastly-extended.test.js new file mode 100644 index 000000000..edc5e78f1 --- /dev/null +++ b/test/unit/lib/fastly-extended.test.js @@ -0,0 +1,44 @@ +describe('fastly library', () => { + test('getLatestActiveVersion returns largest active VCL number', done => { + + const mockedFastlyRequest = jest.fn((method, url, cb) => { + cb(null, [ + { + number: 4, + active: false + }, + { + number: 1, + active: true + }, + { + number: 2, + active: true + }, + { + number: 3, + active: false + } + ]); + }); + + jest.mock('fastly', () => (() => ({ + request: mockedFastlyRequest + }))); + + const fastlyExtended = require('../../../bin/lib/fastly-extended'); // eslint-disable-line global-require + const fastlyInstance = fastlyExtended('api_key', 'service_id'); + + fastlyInstance.getLatestActiveVersion((err, response) => { + expect(err).toBe(null); + expect(response).toEqual({ + number: 2, + active: true + }); + expect(mockedFastlyRequest).toHaveBeenCalledWith( + 'GET', '/service/service_id/version', expect.any(Function) + ); + done(); + }); + }); +}); From 775173661fade65b7f8ef0d65a79d6514ecc1de5 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Tue, 17 Sep 2019 21:49:48 -0400 Subject: [PATCH 06/43] embed view with minimal functionality, route --- src/routes.json | 10 +- src/views/preview/embed-view.jsx | 154 +++++++++++++++++++++++++++++ src/views/preview/embed.jsx | 29 ++++++ src/views/preview/project-view.jsx | 5 +- 4 files changed, 193 insertions(+), 5 deletions(-) create mode 100644 src/views/preview/embed-view.jsx create mode 100644 src/views/preview/embed.jsx diff --git a/src/routes.json b/src/routes.json index c1494768f..4cfeb3963 100644 --- a/src/routes.json +++ b/src/routes.json @@ -177,12 +177,20 @@ }, { "name": "projects", - "pattern": "^/projects(/editor|(/\\d+(/editor|/fullscreen|/embed)?)?)?/?(\\?.*)?$", + "pattern": "^/projects(/editor|(/\\d+(/editor|/fullscreen)?)?)?/?(\\?.*)?$", "routeAlias": "/projects/?$", "view": "preview/preview", "title": "Scratch Project", "dynamicMetaTags": true }, + { + "name": "projects", + "pattern": "^/projects/\\d+/embed/?(\\?.*)?$", + "routeAlias": "/projects/?$", + "view": "preview/embed", + "title": "Scratch Project", + "dynamicMetaTags": true + }, { "name": "parents", "pattern": "^/parents/?(\\?.*)?$", diff --git a/src/views/preview/embed-view.jsx b/src/views/preview/embed-view.jsx new file mode 100644 index 000000000..94d6afe5c --- /dev/null +++ b/src/views/preview/embed-view.jsx @@ -0,0 +1,154 @@ +// embed view + +const bindAll = require('lodash.bindall'); +const React = require('react'); +const PropTypes = require('prop-types'); +const connect = require('react-redux').connect; +const injectIntl = require('react-intl').injectIntl; + +const Page = require('../../components/page/www/page.jsx'); +const storage = require('../../lib/storage.js').default; +const jar = require('../../lib/jar.js'); +const projectShape = require('./projectshape.jsx').projectShape; +const NotAvailable = require('../../components/not-available/not-available.jsx'); +const Meta = require('./meta.jsx'); + +const sessionActions = require('../../redux/session.js'); +const previewActions = require('../../redux/preview.js'); + +const GUI = require('scratch-gui'); +const IntlGUI = injectIntl(GUI.default); + +const Sentry = require('@sentry/browser'); +if (`${process.env.SENTRY_DSN}` !== '') { + Sentry.init({ + dsn: `${process.env.SENTRY_DSN}`, + // Do not collect global onerror, only collect specifically from React error boundaries. + // TryCatch plugin also includes errors from setTimeouts (i.e. the VM) + integrations: integrations => integrations.filter(i => + !(i.name === 'GlobalHandlers' || i.name === 'TryCatch')) + }); + window.Sentry = Sentry; // Allow GUI access to Sentry via window +} + +class EmbedView extends React.Component { + constructor (props) { + super(props); + bindAll(this, [ + ]); + const pathname = window.location.pathname.toLowerCase(); + const parts = pathname.split('/').filter(Boolean); + this.state = { + extensions: [], + invalidProject: parts.length === 1, + projectId: parts[1] + }; + } + componentDidUpdate (prevProps) { + if (this.state.projectId > 0 && + ((this.props.sessionStatus !== prevProps.sessionStatus && + this.props.sessionStatus === sessionActions.Status.FETCHED))) { + this.props.getProjectInfo(this.state.projectId); + this.getProjectData(this.state.projectId, true /* Show cloud/username alerts */); + } + } + getProjectData (projectId) { + if (projectId <= 0) return 0; + storage + .load(storage.AssetType.Project, projectId, storage.DataFormat.JSON) + .then(projectAsset => { // NOTE: this is turning up null, breaking the line below. + let input = projectAsset.data; + if (typeof input === 'object' && !(input instanceof ArrayBuffer) && + !ArrayBuffer.isView(input)) { // taken from scratch-vm + // If the input is an object and not any ArrayBuffer + // or an ArrayBuffer view (this includes all typed arrays and DataViews) + // turn the object into a JSON string, because we suspect + // this is a project.json as an object + // validate expects a string or buffer as input + // TODO not sure if we need to check that it also isn't a data view + input = JSON.stringify(input); + } + }); + } + handleSetLanguage (locale) { + jar.set('scratchlanguage', locale); + } + render () { + if (this.props.projectNotAvailable || this.state.invalidProject) { + return ( + +
+ +
+
+ ); + } + + return ( + + + + + + + ); + } +} + +EmbedView.propTypes = { + assetHost: PropTypes.string.isRequired, + getProjectInfo: PropTypes.func.isRequired, + projectHost: PropTypes.string.isRequired, + projectInfo: projectShape, + projectNotAvailable: PropTypes.bool, + sessionStatus: PropTypes.string +}; + +EmbedView.defaultProps = { + assetHost: process.env.ASSET_HOST, + projectHost: process.env.PROJECT_HOST +}; + +const mapStateToProps = state => ({ + projectInfo: state.preview.projectInfo, + projectNotAvailable: state.preview.projectNotAvailable +}); + +const mapDispatchToProps = dispatch => ({ + getProjectInfo: (id, token) => { + dispatch(previewActions.getProjectInfo(id, token)); + } +}); + +module.exports.View = connect( + mapStateToProps, + mapDispatchToProps +)(EmbedView); + +// initialize GUI by calling its reducer functions depending on URL +GUI.setAppElement(document.getElementById('app')); +module.exports.initGuiState = guiInitialState => { + const pathname = window.location.pathname.toLowerCase(); + const parts = pathname.split('/').filter(Boolean); + // parts[0]: 'projects' + // parts[1]: either :id or 'editor' + // parts[2]: undefined if no :id, otherwise either 'editor', 'fullscreen' or 'embed' + if (parts.indexOf('embed') !== -1) { + guiInitialState = GUI.initEmbedded(guiInitialState); + } + return guiInitialState; +}; + +module.exports.guiReducers = GUI.guiReducers; +module.exports.guiInitialState = GUI.guiInitialState; +module.exports.guiMiddleware = GUI.guiMiddleware; +module.exports.initLocale = GUI.initLocale; +module.exports.localesInitialState = GUI.localesInitialState; diff --git a/src/views/preview/embed.jsx b/src/views/preview/embed.jsx new file mode 100644 index 000000000..278fe3591 --- /dev/null +++ b/src/views/preview/embed.jsx @@ -0,0 +1,29 @@ +// preview view can show either project page or editor page; +// idea is that we shouldn't require a page reload to switch back and forth +const React = require('react'); +const Page = require('../../components/page/www/page.jsx'); +const render = require('../../lib/render.jsx'); + +const previewActions = require('../../redux/preview.js'); + +const isSupportedBrowser = require('../../lib/supported-browser').default; +const UnsupportedBrowser = require('./unsupported-browser.jsx'); + +if (isSupportedBrowser()) { + const EmbedView = require('./embed-view.jsx'); + render( + , + document.getElementById('app'), + { + preview: previewActions.previewReducer, + ...EmbedView.guiReducers + }, + { + locales: EmbedView.initLocale(EmbedView.localesInitialState, window._locale), + scratchGui: EmbedView.initGuiState(EmbedView.guiInitialState) + }, + EmbedView.guiMiddleware + ); +} else { + render(, document.getElementById('app')); +} diff --git a/src/views/preview/project-view.jsx b/src/views/preview/project-view.jsx index f31353bc1..97ffd7fd3 100644 --- a/src/views/preview/project-view.jsx +++ b/src/views/preview/project-view.jsx @@ -1092,16 +1092,13 @@ module.exports.initGuiState = guiInitialState => { const parts = pathname.split('/').filter(Boolean); // parts[0]: 'projects' // parts[1]: either :id or 'editor' - // parts[2]: undefined if no :id, otherwise either 'editor', 'fullscreen' or 'embed' + // parts[2]: undefined if no :id, otherwise either 'editor' or 'fullscreen' if (parts.indexOf('editor') === -1) { guiInitialState = GUI.initPlayer(guiInitialState); } if (parts.indexOf('fullscreen') !== -1) { guiInitialState = GUI.initFullScreen(guiInitialState); } - if (parts.indexOf('embed') !== -1) { - guiInitialState = GUI.initEmbedded(guiInitialState); - } return guiInitialState; }; From 58baa25d9f5d89b601a75c35ad7e5743a994cec4 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Fri, 20 Sep 2019 17:50:33 -0400 Subject: [PATCH 07/43] scratch3 join flow redirects to /join, outside editor --- src/components/navigation/www/navigation.jsx | 32 +++++++++++--------- src/redux/navigation.js | 4 +++ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/components/navigation/www/navigation.jsx b/src/components/navigation/www/navigation.jsx index 90d34bcad..28e71aed5 100644 --- a/src/components/navigation/www/navigation.jsx +++ b/src/components/navigation/www/navigation.jsx @@ -18,7 +18,6 @@ const LoginDropdown = require('../../login/login-dropdown.jsx'); const CanceledDeletionModal = require('../../login/canceled-deletion-modal.jsx'); const NavigationBox = require('../base/navigation.jsx'); const Registration = require('../../registration/registration.jsx'); -const Scratch3Registration = require('../../registration/scratch3-registration.jsx'); const AccountNav = require('./accountnav.jsx'); require('./navigation.scss'); @@ -28,6 +27,7 @@ class Navigation extends React.Component { super(props); bindAll(this, [ 'getProfileUrl', + 'handleClickRegistration', 'handleSearchSubmit' ]); this.state = { @@ -78,6 +78,13 @@ class Navigation extends React.Component { if (!this.props.user) return; return `/users/${this.props.user.username}/`; } + handleClickRegistration (event) { + if (this.props.useScratch3Registration) { + this.props.navigateToRegistration(event); + } else { + this.props.handleOpenRegistration(event); + } + } handleSearchSubmit (formData) { let targetUrl = '/search/projects'; if (formData.q) { @@ -191,7 +198,7 @@ class Navigation extends React.Component { > @@ -214,18 +221,10 @@ class Navigation extends React.Component { ]) : [] } - {this.props.registrationOpen && ( - this.props.useScratch3Registration ? ( - - ) : ( - - ) + {this.props.registrationOpen && !this.props.useScratch3Registration && ( + )} @@ -243,6 +242,7 @@ Navigation.propTypes = { handleToggleAccountNav: PropTypes.func, handleToggleLoginOpen: PropTypes.func, intl: intlShape, + navigateToRegistration: PropTypes.func, permissions: PropTypes.shape({ admin: PropTypes.bool, social: PropTypes.bool, @@ -305,6 +305,10 @@ const mapDispatchToProps = dispatch => ({ event.preventDefault(); dispatch(navigationActions.toggleLoginOpen()); }, + navigateToRegistration: event => { + event.preventDefault(); + dispatch(navigationActions.navigateToRegistration()); + }, setMessageCount: newCount => { dispatch(messageCountActions.setCount(newCount)); } diff --git a/src/redux/navigation.js b/src/redux/navigation.js index a7ff091a8..591833208 100644 --- a/src/redux/navigation.js +++ b/src/redux/navigation.js @@ -92,6 +92,10 @@ module.exports.setSearchTerm = searchTerm => ({ searchTerm: searchTerm }); +module.exports.navigateToRegistration = () => { + window.location = '/join'; +}; + module.exports.handleCompleteRegistration = createProject => (dispatch => { if (createProject) { window.location = '/projects/editor/?tutorial=getStarted'; From 6ff1a1c4f132fcaa300f7a0c7214b63fd2a23a36 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Thu, 26 Sep 2019 09:29:02 -0400 Subject: [PATCH 08/43] add navigation test --- src/components/navigation/www/navigation.jsx | 13 +++- test/unit/components/navigation.test.jsx | 69 ++++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 test/unit/components/navigation.test.jsx diff --git a/src/components/navigation/www/navigation.jsx b/src/components/navigation/www/navigation.jsx index 28e71aed5..98f68cead 100644 --- a/src/components/navigation/www/navigation.jsx +++ b/src/components/navigation/www/navigation.jsx @@ -196,7 +196,10 @@ class Navigation extends React.Component { className="link right join" key="join" > + {/* there's no css class registrationLink -- this is + just to make the link findable for testing */} @@ -307,16 +310,22 @@ const mapDispatchToProps = dispatch => ({ }, navigateToRegistration: event => { event.preventDefault(); - dispatch(navigationActions.navigateToRegistration()); + navigationActions.navigateToRegistration(); }, setMessageCount: newCount => { dispatch(messageCountActions.setCount(newCount)); } }); +// Allow incoming props to override redux-provided props. Used to mock in tests. +const mergeProps = (stateProps, dispatchProps, ownProps) => Object.assign( + {}, stateProps, dispatchProps, ownProps +); + const ConnectedNavigation = connect( mapStateToProps, - mapDispatchToProps + mapDispatchToProps, + mergeProps )(Navigation); module.exports = injectIntl(ConnectedNavigation); diff --git a/test/unit/components/navigation.test.jsx b/test/unit/components/navigation.test.jsx new file mode 100644 index 000000000..727f48239 --- /dev/null +++ b/test/unit/components/navigation.test.jsx @@ -0,0 +1,69 @@ +const React = require('react'); +const {shallowWithIntl} = require('../../helpers/intl-helpers.jsx'); +import configureStore from 'redux-mock-store'; +const Navigation = require('../../../src/components/navigation/www/navigation.jsx'); +const sessionActions = require('../../../src/redux/session.js'); + +describe('Navigation', () => { + const mockStore = configureStore(); + let store; + + beforeEach(() => { + }); + + const getNavigationWrapper = props => { + const wrapper = shallowWithIntl( + + , {context: {store}} + ); + return wrapper + .dive() // unwrap redux connect(injectIntl(JoinFlow)) + .dive(); // unwrap injectIntl(JoinFlow) + }; + + test('when using old join flow, clicking Join Scratch attemps to open registration', () => { + store = mockStore({ + navigation: { + useScratch3Registration: false + }, + session: { + status: sessionActions.Status.FETCHED + }, + messageCount: { + messageCount: 0 + } + }); + const props = { + handleOpenRegistration: jest.fn() + }; + const navWrapper = getNavigationWrapper(props); + const navInstance = navWrapper.instance(); + + navWrapper.find('a.registrationLink').simulate('click'); + expect(navInstance.props.handleOpenRegistration).toHaveBeenCalled(); + }); + + test('when using new join flow, clicking Join Scratch attemps to navigate to registration', () => { + store = mockStore({ + navigation: { + useScratch3Registration: true + }, + session: { + status: sessionActions.Status.FETCHED + }, + messageCount: { + messageCount: 0 + } + }); + const props = { + navigateToRegistration: jest.fn() + }; + const navWrapper = getNavigationWrapper(props); + const navInstance = navWrapper.instance(); + + navWrapper.find('a.registrationLink').simulate('click'); + expect(navInstance.props.navigateToRegistration).toHaveBeenCalled(); + }); +}); From 88abed34e9785333ad2581d283a80cf53bbd27dd Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Thu, 26 Sep 2019 10:49:12 -0400 Subject: [PATCH 09/43] revised fastly tests, added more --- bin/lib/fastly-extended.js | 21 ++-- test/unit/lib/fastly-extended.test.js | 134 ++++++++++++++++++++++++-- 2 files changed, 134 insertions(+), 21 deletions(-) diff --git a/bin/lib/fastly-extended.js b/bin/lib/fastly-extended.js index fbad130d5..ffac40acc 100644 --- a/bin/lib/fastly-extended.js +++ b/bin/lib/fastly-extended.js @@ -37,18 +37,15 @@ module.exports = function (apiKey, serviceId) { if (err) { return cb('Failed to fetch versions: ' + err); } - var latestVersion = versions.reduce(function (lateVersion, version) { - if (!lateVersion) return version; - // if versions we're comparing are both active, or both inactive, prefer - // whichever has a higher version number - if (lateVersion.active === version.active) { - if (version.number > lateVersion.number) return version; - return lateVersion; - } - // if only one of the versions is active, prefer that one - if (version.active === true) return version; - return lateVersion; - }); + var latestVersion = versions.reduce((latestActiveSoFar, cur) => { + // if one of [latestActiveSoFar, cur] is active and the other isn't, + // return whichever is active. If both are not active, return + // latestActiveSoFar. + if (!cur || !cur.active) return latestActiveSoFar; + if (!latestActiveSoFar || !latestActiveSoFar.active) return cur; + // when both are active, prefer whichever has a higher version number. + return (cur.number > latestActiveSoFar.number) ? cur : latestActiveSoFar; + }, null); return cb(null, latestVersion); }); }; diff --git a/test/unit/lib/fastly-extended.test.js b/test/unit/lib/fastly-extended.test.js index edc5e78f1..451902c16 100644 --- a/test/unit/lib/fastly-extended.test.js +++ b/test/unit/lib/fastly-extended.test.js @@ -1,7 +1,50 @@ describe('fastly library', () => { - test('getLatestActiveVersion returns largest active VCL number', done => { + let mockedFastlyRequest = {}; - const mockedFastlyRequest = jest.fn((method, url, cb) => { + jest.mock('fastly', () => (() => ({ + request: mockedFastlyRequest + }))); + const fastlyExtended = require('../../../bin/lib/fastly-extended'); // eslint-disable-line global-require + + test('getLatestActiveVersion returns largest active VCL number, ' + + 'when called with VCLs in sequential order', done => { + mockedFastlyRequest = jest.fn((method, url, cb) => { + cb(null, [ + { + number: 1, + active: false + }, + { + number: 2, + active: false + }, + { + number: 3, + active: true + }, + { + number: 4, + active: false + } + ]); + }); + const fastlyInstance = fastlyExtended('api_key', 'service_id'); + + fastlyInstance.getLatestActiveVersion((err, response) => { + expect(err).toBe(null); + expect(response).toEqual({ + number: 3, + active: true + }); + expect(mockedFastlyRequest).toHaveBeenCalledWith( + 'GET', '/service/service_id/version', expect.any(Function) + ); + done(); + }); + }); + + test('getLatestActiveVersion returns largest active VCL number, when called with VCLs in mixed up order', done => { + mockedFastlyRequest = jest.fn((method, url, cb) => { cb(null, [ { number: 4, @@ -9,7 +52,7 @@ describe('fastly library', () => { }, { number: 1, - active: true + active: false }, { number: 2, @@ -21,12 +64,6 @@ describe('fastly library', () => { } ]); }); - - jest.mock('fastly', () => (() => ({ - request: mockedFastlyRequest - }))); - - const fastlyExtended = require('../../../bin/lib/fastly-extended'); // eslint-disable-line global-require const fastlyInstance = fastlyExtended('api_key', 'service_id'); fastlyInstance.getLatestActiveVersion((err, response) => { @@ -41,4 +78,83 @@ describe('fastly library', () => { done(); }); }); + + test('getLatestActiveVersion returns null, when none of the VCL versions are active', done => { + mockedFastlyRequest = jest.fn((method, url, cb) => { + cb(null, [ + { + number: 4, + active: false + }, + { + number: 1, + active: false + }, + { + number: 2, + active: false + }, + { + number: 3, + active: false + } + ]); + }); + const fastlyInstance = fastlyExtended('api_key', 'service_id'); + + fastlyInstance.getLatestActiveVersion((err, response) => { + expect(err).toBe(null); + expect(response).toEqual(null); + expect(mockedFastlyRequest).toHaveBeenCalledWith( + 'GET', '/service/service_id/version', expect.any(Function) + ); + done(); + }); + }); + + test('getLatestActiveVersion returns largest active VCL number, ' + + 'when called with a single active VCL', done => { + mockedFastlyRequest = jest.fn((method, url, cb) => { + cb(null, [ + { + number: 1, + active: true + } + ]); + }); + const fastlyInstance = fastlyExtended('api_key', 'service_id'); + + fastlyInstance.getLatestActiveVersion((err, response) => { + expect(err).toBe(null); + expect(response).toEqual({ + number: 1, + active: true + }); + expect(mockedFastlyRequest).toHaveBeenCalledWith( + 'GET', '/service/service_id/version', expect.any(Function) + ); + done(); + }); + }); + + test('getLatestActiveVersion returns null, when called with a single inactive VCL', done => { + mockedFastlyRequest = jest.fn((method, url, cb) => { + cb(null, [ + { + number: 1, + active: false + } + ]); + }); + const fastlyInstance = fastlyExtended('api_key', 'service_id'); + + fastlyInstance.getLatestActiveVersion((err, response) => { + expect(err).toBe(null); + expect(response).toEqual(null); + expect(mockedFastlyRequest).toHaveBeenCalledWith( + 'GET', '/service/service_id/version', expect.any(Function) + ); + done(); + }); + }); }); From 2efb3a7bfa859cf208a50d3d671258c33374424a Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Thu, 26 Sep 2019 14:46:42 -0400 Subject: [PATCH 10/43] join flow bold text and related design tweaks --- src/components/formik-forms/formik-checkbox.scss | 5 +++++ src/components/join-flow/join-flow-step.scss | 6 +++++- src/components/join-flow/username-step.jsx | 13 ++++++------- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/components/formik-forms/formik-checkbox.scss b/src/components/formik-forms/formik-checkbox.scss index 9dd11b106..0c0e56302 100644 --- a/src/components/formik-forms/formik-checkbox.scss +++ b/src/components/formik-forms/formik-checkbox.scss @@ -26,3 +26,8 @@ input[type="checkbox"].formik-checkbox { background-position: center; } } + +.formik-checkbox-label { + padding-top: .0625rem; + display: block; +} diff --git a/src/components/join-flow/join-flow-step.scss b/src/components/join-flow/join-flow-step.scss index e7de82007..5ae543ca9 100644 --- a/src/components/join-flow/join-flow-step.scss +++ b/src/components/join-flow/join-flow-step.scss @@ -53,7 +53,11 @@ padding: 0.875rem 1.5rem; background-color: $ui-blue-25percent; font-size: .75rem; - font-weight: 600; + font-weight: 500; text-align: center; color: $ui-blue; } + +.join-flow-footer-message a { + font-weight: 500; +} diff --git a/src/components/join-flow/username-step.jsx b/src/components/join-flow/username-step.jsx index a1783a958..a4537e935 100644 --- a/src/components/join-flow/username-step.jsx +++ b/src/components/join-flow/username-step.jsx @@ -237,13 +237,12 @@ class UsernameStep extends React.Component { onFocus={() => this.handleFocused('passwordConfirm')} /* eslint-enable react/jsx-no-bind */ /> -
- -
+ From 51c816c5cfdceb9621f1483920ec9fb5cc68bcdc Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Thu, 26 Sep 2019 14:49:42 -0400 Subject: [PATCH 11/43] make validations semi-bold --- src/components/forms/validation-message.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/forms/validation-message.scss b/src/components/forms/validation-message.scss index 5cbbcb12b..3a217ed99 100644 --- a/src/components/forms/validation-message.scss +++ b/src/components/forms/validation-message.scss @@ -18,6 +18,7 @@ overflow: visible; color: $type-white; z-index: 1; + font-weight: 500; &:before { display: block; @@ -82,7 +83,6 @@ .validation-info { background-color: $ui-blue; box-shadow: 0 0 4px 2px rgba(0, 0, 0, .15); - font-weight: 500; &:before { background-color: $ui-blue; From 3bee3fae4bfdc0d10428e66d19f1b6924af9e95a Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Thu, 26 Sep 2019 14:51:33 -0400 Subject: [PATCH 12/43] change password default; remove unused state var --- src/components/join-flow/username-step.jsx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/components/join-flow/username-step.jsx b/src/components/join-flow/username-step.jsx index a1783a958..60cecc0b5 100644 --- a/src/components/join-flow/username-step.jsx +++ b/src/components/join-flow/username-step.jsx @@ -20,7 +20,6 @@ class UsernameStep extends React.Component { constructor (props) { super(props); bindAll(this, [ - 'handleChangeShowPassword', 'handleFocused', 'handleSetUsernameRef', 'handleValidSubmit', @@ -30,17 +29,13 @@ class UsernameStep extends React.Component { 'validateForm' ]); this.state = { - focused: null, - showPassword: false + focused: null }; } componentDidMount () { // automatically start with focus on username field if (this.usernameInput) this.usernameInput.focus(); } - handleChangeShowPassword () { - this.setState({showPassword: !this.state.showPassword}); - } // track the currently focused input field, to determine whether each field should // display a tooltip. (We only display it if a field is focused and has never been touched.) handleFocused (fieldName) { @@ -114,7 +109,7 @@ class UsernameStep extends React.Component { username: '', password: '', passwordConfirm: '', - showPassword: false + showPassword: true }} validate={this.validateForm} validateOnBlur={false} From ae661a48c7ce16297f3ba0e34bdba1d8dfc4a6bc Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Sat, 28 Sep 2019 10:17:59 -0400 Subject: [PATCH 13/43] corrected embed route name --- src/routes.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes.json b/src/routes.json index 4cfeb3963..d90277313 100644 --- a/src/routes.json +++ b/src/routes.json @@ -184,7 +184,7 @@ "dynamicMetaTags": true }, { - "name": "projects", + "name": "embed", "pattern": "^/projects/\\d+/embed/?(\\?.*)?$", "routeAlias": "/projects/?$", "view": "preview/embed", From ca4a4bcbb32106f0efe2a00299518842b911dfc4 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Sat, 28 Sep 2019 10:18:19 -0400 Subject: [PATCH 14/43] added project view hoc, not hooked up yes --- src/views/preview/project-view-hoc.jsx | 88 ++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 src/views/preview/project-view-hoc.jsx diff --git a/src/views/preview/project-view-hoc.jsx b/src/views/preview/project-view-hoc.jsx new file mode 100644 index 000000000..aa0b388c8 --- /dev/null +++ b/src/views/preview/project-view-hoc.jsx @@ -0,0 +1,88 @@ +const bindAll = require('lodash.bindall'); +const React = require('react'); +const PropTypes = require('prop-types'); +const connect = require('react-redux').connect; + +const storage = require('../../lib/storage.js').default; +const previewActions = require('../../redux/preview.js'); + +const Sentry = require('@sentry/browser'); +if (`${process.env.SENTRY_DSN}` !== '') { + Sentry.init({ + dsn: `${process.env.SENTRY_DSN}`, + // Do not collect global onerror, only collect specifically from React error boundaries. + // TryCatch plugin also includes errors from setTimeouts (i.e. the VM) + integrations: integrations => integrations.filter(i => + !(i.name === 'GlobalHandlers' || i.name === 'TryCatch')) + }); + window.Sentry = Sentry; // Allow GUI access to Sentry via window +} + +/** + * Higher-order component for presenting a project view. + * @param {React.Component} Component a project view component + * @return {React.Component} a wrapped project view component + */ + +const ProjectViewHOC = Component => { + class WrappedComponent extends React.Component { + constructor (props) { + super(props); + bindAll(this, [ + 'fetchProjectData' + ]); + } + fetchProjectData (projectId) { + if (projectId <= 0) return Promise.reject(null); + return storage + .load(storage.AssetType.Project, projectId, storage.DataFormat.JSON) + .then(projectAsset => { // NOTE: this is turning up null, breaking the line below. + let input = projectAsset.data; + if (typeof input === 'object' && !(input instanceof ArrayBuffer) && + !ArrayBuffer.isView(input)) { // taken from scratch-vm + // If the input is an object and not any ArrayBuffer + // or an ArrayBuffer view (this includes all typed arrays and DataViews) + // turn the object into a JSON string, because we suspect + // this is a project.json as an object + // validate expects a string or buffer as input + // TODO not sure if we need to check that it also isn't a data view + input = JSON.stringify(input); // NOTE: what is the point of doing this?? + } + }); + } + render () { + return (); + } + } + + WrappedComponent.propTypes = { + assetHost: PropTypes.string.isRequired, + projectHost: PropTypes.string.isRequired + }; + + WrappedComponent.defaultProps = { + assetHost: process.env.ASSET_HOST, + projectHost: process.env.PROJECT_HOST + }; + + const mapStateToProps = state => ({ + projectInfo: state.preview.projectInfo, + projectNotAvailable: state.preview.projectNotAvailable + }); + + const mapDispatchToProps = dispatch => ({ + getProjectInfo: (id, token) => { + dispatch(previewActions.getProjectInfo(id, token)); + } + }); + + return connect( + mapStateToProps, + mapDispatchToProps + )(WrappedComponent); +}; + +module.exports = ProjectViewHOC; From 01a273b1f86c46348680729974e32dddb3b5441f Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Sat, 28 Sep 2019 10:19:06 -0400 Subject: [PATCH 15/43] refactored project view and embed view to use hoc --- src/views/preview/embed-view.jsx | 84 +++-------------- src/views/preview/embed.jsx | 6 +- src/views/preview/project-view.jsx | 139 ++++++++++++----------------- 3 files changed, 72 insertions(+), 157 deletions(-) diff --git a/src/views/preview/embed-view.jsx b/src/views/preview/embed-view.jsx index 94d6afe5c..19594da80 100644 --- a/src/views/preview/embed-view.jsx +++ b/src/views/preview/embed-view.jsx @@ -3,19 +3,14 @@ const bindAll = require('lodash.bindall'); const React = require('react'); const PropTypes = require('prop-types'); -const connect = require('react-redux').connect; const injectIntl = require('react-intl').injectIntl; -const Page = require('../../components/page/www/page.jsx'); -const storage = require('../../lib/storage.js').default; -const jar = require('../../lib/jar.js'); +const ProjectViewHOC = require('./project-view-hoc.jsx'); +const ErrorBoundary = require('../../components/errorboundary/errorboundary.jsx'); const projectShape = require('./projectshape.jsx').projectShape; const NotAvailable = require('../../components/not-available/not-available.jsx'); const Meta = require('./meta.jsx'); -const sessionActions = require('../../redux/session.js'); -const previewActions = require('../../redux/preview.js'); - const GUI = require('scratch-gui'); const IntlGUI = injectIntl(GUI.default); @@ -44,43 +39,18 @@ class EmbedView extends React.Component { projectId: parts[1] }; } - componentDidUpdate (prevProps) { - if (this.state.projectId > 0 && - ((this.props.sessionStatus !== prevProps.sessionStatus && - this.props.sessionStatus === sessionActions.Status.FETCHED))) { - this.props.getProjectInfo(this.state.projectId); - this.getProjectData(this.state.projectId, true /* Show cloud/username alerts */); - } - } - getProjectData (projectId) { - if (projectId <= 0) return 0; - storage - .load(storage.AssetType.Project, projectId, storage.DataFormat.JSON) - .then(projectAsset => { // NOTE: this is turning up null, breaking the line below. - let input = projectAsset.data; - if (typeof input === 'object' && !(input instanceof ArrayBuffer) && - !ArrayBuffer.isView(input)) { // taken from scratch-vm - // If the input is an object and not any ArrayBuffer - // or an ArrayBuffer view (this includes all typed arrays and DataViews) - // turn the object into a JSON string, because we suspect - // this is a project.json as an object - // validate expects a string or buffer as input - // TODO not sure if we need to check that it also isn't a data view - input = JSON.stringify(input); - } - }); - } - handleSetLanguage (locale) { - jar.set('scratchlanguage', locale); + componentDidMount () { + this.props.getProjectInfo(this.state.projectId); + this.props.fetchProjectData(this.state.projectId); } render () { if (this.props.projectNotAvailable || this.state.invalidProject) { return ( - +
-
+ ); } @@ -95,7 +65,6 @@ class EmbedView extends React.Component { projectHost={this.props.projectHost} projectId={this.state.projectId} projectTitle={this.props.projectInfo.title} - onSetLanguage={this.handleSetLanguage} /> @@ -105,48 +74,17 @@ class EmbedView extends React.Component { EmbedView.propTypes = { assetHost: PropTypes.string.isRequired, + fetchProjectData: PropTypes.func.isRequired, getProjectInfo: PropTypes.func.isRequired, projectHost: PropTypes.string.isRequired, projectInfo: projectShape, - projectNotAvailable: PropTypes.bool, - sessionStatus: PropTypes.string + projectNotAvailable: PropTypes.bool }; -EmbedView.defaultProps = { - assetHost: process.env.ASSET_HOST, - projectHost: process.env.PROJECT_HOST -}; +module.exports.View = ProjectViewHOC(EmbedView); -const mapStateToProps = state => ({ - projectInfo: state.preview.projectInfo, - projectNotAvailable: state.preview.projectNotAvailable -}); - -const mapDispatchToProps = dispatch => ({ - getProjectInfo: (id, token) => { - dispatch(previewActions.getProjectInfo(id, token)); - } -}); - -module.exports.View = connect( - mapStateToProps, - mapDispatchToProps -)(EmbedView); - -// initialize GUI by calling its reducer functions depending on URL GUI.setAppElement(document.getElementById('app')); -module.exports.initGuiState = guiInitialState => { - const pathname = window.location.pathname.toLowerCase(); - const parts = pathname.split('/').filter(Boolean); - // parts[0]: 'projects' - // parts[1]: either :id or 'editor' - // parts[2]: undefined if no :id, otherwise either 'editor', 'fullscreen' or 'embed' - if (parts.indexOf('embed') !== -1) { - guiInitialState = GUI.initEmbedded(guiInitialState); - } - return guiInitialState; -}; - +module.exports.initGuiState = GUI.initEmbedded; module.exports.guiReducers = GUI.guiReducers; module.exports.guiInitialState = GUI.guiInitialState; module.exports.guiMiddleware = GUI.guiMiddleware; diff --git a/src/views/preview/embed.jsx b/src/views/preview/embed.jsx index 278fe3591..cb2d9ba9b 100644 --- a/src/views/preview/embed.jsx +++ b/src/views/preview/embed.jsx @@ -1,7 +1,5 @@ -// preview view can show either project page or editor page; -// idea is that we shouldn't require a page reload to switch back and forth const React = require('react'); -const Page = require('../../components/page/www/page.jsx'); +const ErrorBoundary = require('../../components/errorboundary/errorboundary.jsx'); const render = require('../../lib/render.jsx'); const previewActions = require('../../redux/preview.js'); @@ -25,5 +23,5 @@ if (isSupportedBrowser()) { EmbedView.guiMiddleware ); } else { - render(, document.getElementById('app')); + render(, document.getElementById('app')); } diff --git a/src/views/preview/project-view.jsx b/src/views/preview/project-view.jsx index 97ffd7fd3..3d6e671fb 100644 --- a/src/views/preview/project-view.jsx +++ b/src/views/preview/project-view.jsx @@ -10,10 +10,10 @@ const injectIntl = require('react-intl').injectIntl; const parser = require('scratch-parser'); const Page = require('../../components/page/www/page.jsx'); -const storage = require('../../lib/storage.js').default; const log = require('../../lib/log'); const jar = require('../../lib/jar.js'); const thumbnailUrl = require('../../lib/user-thumbnail'); +const ProjectViewHOC = require('./project-view-hoc.jsx'); const ProjectInfo = require('../../lib/project-info'); const PreviewPresentation = require('./presentation.jsx'); const projectShape = require('./projectshape.jsx').projectShape; @@ -35,18 +35,6 @@ const IntlGUI = injectIntl(GUI.default); const localStorageAvailable = 'localStorage' in window && window.localStorage !== null; -const Sentry = require('@sentry/browser'); -if (`${process.env.SENTRY_DSN}` !== '') { - Sentry.init({ - dsn: `${process.env.SENTRY_DSN}`, - // Do not collect global onerror, only collect specifically from React error boundaries. - // TryCatch plugin also includes errors from setTimeouts (i.e. the VM) - integrations: integrations => integrations.filter(i => - !(i.name === 'GlobalHandlers' || i.name === 'TryCatch')) - }); - window.Sentry = Sentry; // Allow GUI access to Sentry via window -} - class Preview extends React.Component { constructor (props) { super(props); @@ -134,12 +122,17 @@ class Preview extends React.Component { } componentDidMount () { this.addEventListeners(); + console.log(`componentDidMount: session status: ${this.props.sessionStatus}`); + } componentDidUpdate (prevProps, prevState) { + console.log(`componentDidUpdate: session status: ${this.props.sessionStatus}`); if (this.state.projectId > 0 && ((this.props.sessionStatus !== prevProps.sessionStatus && this.props.sessionStatus === sessionActions.Status.FETCHED) || (this.state.projectId !== prevState.projectId))) { + console.log("got here A"); + console.log(`this: ${this.state.projectId} prev: ${prevState.projectId}`); this.fetchCommunityData(); this.getProjectData(this.state.projectId, true /* Show cloud/username alerts */); if (this.state.justShared) { @@ -245,67 +238,53 @@ class Preview extends React.Component { } } getProjectData (projectId, showAlerts) { - if (projectId <= 0) return 0; - storage - .load(storage.AssetType.Project, projectId, storage.DataFormat.JSON) - .then(projectAsset => { // NOTE: this is turning up null, breaking the line below. - let input = projectAsset.data; - if (typeof input === 'object' && !(input instanceof ArrayBuffer) && - !ArrayBuffer.isView(input)) { // taken from scratch-vm - // If the input is an object and not any ArrayBuffer - // or an ArrayBuffer view (this includes all typed arrays and DataViews) - // turn the object into a JSON string, because we suspect - // this is a project.json as an object - // validate expects a string or buffer as input - // TODO not sure if we need to check that it also isn't a data view - input = JSON.stringify(input); + this.props.fetchProjectData(projectId).then(projectAsset => { + parser(projectAsset.data, false, (err, projectData) => { + if (err) { + log.error(`Unhandled project parsing error: ${err}`); + return; } - parser(projectAsset.data, false, (err, projectData) => { - if (err) { - log.error(`Unhandled project parsing error: ${err}`); - return; - } - const newState = { - modInfo: {} // Filled in below - }; + const newState = { + modInfo: {} // Filled in below + }; - const helpers = ProjectInfo[projectData[0].projectVersion]; - if (!helpers) return; // sb1 not handled - newState.extensions = Array.from(helpers.extensions(projectData[0])); - newState.modInfo.scriptCount = helpers.scriptCount(projectData[0]); - newState.modInfo.spriteCount = helpers.spriteCount(projectData[0]); - const hasCloudData = helpers.cloudData(projectData[0]); - if (hasCloudData) { - if (this.props.isLoggedIn) { - // show cloud variables log link if logged in - newState.extensions.push({ - action: { - l10nId: 'project.cloudDataLink', - uri: `/cloudmonitor/${projectId}/` - }, - icon: 'clouddata.svg', - l10nId: 'project.cloudVariables', - linked: true - }); - } else { - newState.extensions.push({ - icon: 'clouddata.svg', - l10nId: 'project.cloudVariables' - }); - } + const helpers = ProjectInfo[projectData[0].projectVersion]; + if (!helpers) return; // sb1 not handled + newState.extensions = Array.from(helpers.extensions(projectData[0])); + newState.modInfo.scriptCount = helpers.scriptCount(projectData[0]); + newState.modInfo.spriteCount = helpers.spriteCount(projectData[0]); + const hasCloudData = helpers.cloudData(projectData[0]); + if (hasCloudData) { + if (this.props.isLoggedIn) { + // show cloud variables log link if logged in + newState.extensions.push({ + action: { + l10nId: 'project.cloudDataLink', + uri: `/cloudmonitor/${projectId}/` + }, + icon: 'clouddata.svg', + l10nId: 'project.cloudVariables', + linked: true + }); + } else { + newState.extensions.push({ + icon: 'clouddata.svg', + l10nId: 'project.cloudVariables' + }); } + } - if (showAlerts) { - // Check for username block only if user is logged in - if (this.props.isLoggedIn) { - newState.showUsernameBlockAlert = helpers.usernameBlock(projectData[0]); - } else { // Check for cloud vars only if user is logged out - newState.showCloudDataAlert = hasCloudData; - } + if (showAlerts) { + // Check for username block only if user is logged in + if (this.props.isLoggedIn) { + newState.showUsernameBlockAlert = helpers.usernameBlock(projectData[0]); + } else { // Check for cloud vars only if user is logged out + newState.showCloudDataAlert = hasCloudData; } - this.setState(newState); - }); + } + this.setState(newState); }); + }); } handleToggleComments () { this.props.updateProject( @@ -792,6 +771,7 @@ Preview.propTypes = { enableCommunity: PropTypes.bool, faved: PropTypes.bool, favedLoaded: PropTypes.bool, + fetchProjectData: PropTypes.func.isRequired, fullScreen: PropTypes.bool, getCommentById: PropTypes.func.isRequired, getCuratedStudios: PropTypes.func.isRequired, @@ -879,17 +859,17 @@ Preview.defaultProps = { userPresent: false }; -const mapStateToProps = state => { - const projectInfoPresent = state.preview.projectInfo && - Object.keys(state.preview.projectInfo).length > 0 && state.preview.projectInfo.id > 0; +const mapStateToProps = (state, ownProps) => { + const projectInfoPresent = ownProps.projectInfo && + Object.keys(ownProps.projectInfo).length > 0 && ownProps.projectInfo.id > 0; const userPresent = state.session.session.user !== null && typeof state.session.session.user !== 'undefined' && Object.keys(state.session.session.user).length > 0; const isLoggedIn = state.session.status === sessionActions.Status.FETCHED && userPresent; const isAdmin = isLoggedIn && state.session.session.permissions.admin; - const author = projectInfoPresent && state.preview.projectInfo.author; - const authorPresent = author && Object.keys(state.preview.projectInfo.author).length > 0; + const author = projectInfoPresent && ownProps.projectInfo.author; + const authorPresent = author && Object.keys(ownProps.projectInfo.author).length > 0; const authorId = authorPresent && author.id && author.id.toString(); const authorUsername = authorPresent && author.username; const userOwnsProject = isLoggedIn && authorPresent && @@ -899,7 +879,7 @@ const mapStateToProps = state => { state.permissions.admin === true); // if we don't have projectInfo, assume it's shared until we know otherwise - const isShared = !projectInfoPresent || state.preview.projectInfo.is_published; + const isShared = !projectInfoPresent || ownProps.projectInfo.is_published; return { authorId: authorId, @@ -935,8 +915,8 @@ const mapStateToProps = state => { original: state.preview.original, parent: state.preview.parent, playerMode: state.scratchGui.mode.isPlayerOnly, - projectInfo: state.preview.projectInfo, - projectNotAvailable: state.preview.projectNotAvailable, + projectInfo: ownProps.projectInfo, + projectNotAvailable: ownProps.projectNotAvailable, projectStudios: state.preview.projectStudios, registrationOpen: state.navigation.registrationOpen, remixes: state.preview.remixes, @@ -991,9 +971,6 @@ const mapDispatchToProps = dispatch => ({ getParentInfo: id => { dispatch(previewActions.getParentInfo(id)); }, - getProjectInfo: (id, token) => { - dispatch(previewActions.getProjectInfo(id, token)); - }, getRemixes: id => { dispatch(previewActions.getRemixes(id)); }, @@ -1064,11 +1041,13 @@ const mapDispatchToProps = dispatch => ({ } }); -module.exports.View = connect( +const ConnectedPreview = connect( mapStateToProps, mapDispatchToProps )(Preview); +module.exports.View = ProjectViewHOC(ConnectedPreview); + // replace old Scratch 2.0-style hashtag URLs with updated format if (window.location.hash) { let pathname = window.location.pathname; From a2e045f8ef857fb592068efd65ce81fd67343700 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Sat, 28 Sep 2019 13:59:43 -0400 Subject: [PATCH 16/43] removed extra sentry init --- src/views/preview/embed-view.jsx | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/views/preview/embed-view.jsx b/src/views/preview/embed-view.jsx index 19594da80..2a17b2a66 100644 --- a/src/views/preview/embed-view.jsx +++ b/src/views/preview/embed-view.jsx @@ -14,18 +14,6 @@ const Meta = require('./meta.jsx'); const GUI = require('scratch-gui'); const IntlGUI = injectIntl(GUI.default); -const Sentry = require('@sentry/browser'); -if (`${process.env.SENTRY_DSN}` !== '') { - Sentry.init({ - dsn: `${process.env.SENTRY_DSN}`, - // Do not collect global onerror, only collect specifically from React error boundaries. - // TryCatch plugin also includes errors from setTimeouts (i.e. the VM) - integrations: integrations => integrations.filter(i => - !(i.name === 'GlobalHandlers' || i.name === 'TryCatch')) - }); - window.Sentry = Sentry; // Allow GUI access to Sentry via window -} - class EmbedView extends React.Component { constructor (props) { super(props); From ae591857a4d306d1149f2cf60779b718fd33fd2b Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 30 Sep 2019 10:04:44 -0400 Subject: [PATCH 17/43] removed unnecessary React.Fragment --- src/views/preview/embed-view.jsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/views/preview/embed-view.jsx b/src/views/preview/embed-view.jsx index 2a17b2a66..5adc54b41 100644 --- a/src/views/preview/embed-view.jsx +++ b/src/views/preview/embed-view.jsx @@ -45,16 +45,14 @@ class EmbedView extends React.Component { return ( - - - + ); } From df95f4f1165c7a495f4102b0893c5c622a8a87bc Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 30 Sep 2019 10:06:06 -0400 Subject: [PATCH 18/43] refactored getProjectData back into only project-view --- src/views/preview/embed-view.jsx | 2 - src/views/preview/project-view-hoc.jsx | 21 ------ src/views/preview/project-view.jsx | 100 ++++++++++++++----------- 3 files changed, 57 insertions(+), 66 deletions(-) diff --git a/src/views/preview/embed-view.jsx b/src/views/preview/embed-view.jsx index 5adc54b41..251ab0267 100644 --- a/src/views/preview/embed-view.jsx +++ b/src/views/preview/embed-view.jsx @@ -29,7 +29,6 @@ class EmbedView extends React.Component { } componentDidMount () { this.props.getProjectInfo(this.state.projectId); - this.props.fetchProjectData(this.state.projectId); } render () { if (this.props.projectNotAvailable || this.state.invalidProject) { @@ -60,7 +59,6 @@ class EmbedView extends React.Component { EmbedView.propTypes = { assetHost: PropTypes.string.isRequired, - fetchProjectData: PropTypes.func.isRequired, getProjectInfo: PropTypes.func.isRequired, projectHost: PropTypes.string.isRequired, projectInfo: projectShape, diff --git a/src/views/preview/project-view-hoc.jsx b/src/views/preview/project-view-hoc.jsx index aa0b388c8..08da7d221 100644 --- a/src/views/preview/project-view-hoc.jsx +++ b/src/views/preview/project-view-hoc.jsx @@ -3,7 +3,6 @@ const React = require('react'); const PropTypes = require('prop-types'); const connect = require('react-redux').connect; -const storage = require('../../lib/storage.js').default; const previewActions = require('../../redux/preview.js'); const Sentry = require('@sentry/browser'); @@ -29,30 +28,10 @@ const ProjectViewHOC = Component => { constructor (props) { super(props); bindAll(this, [ - 'fetchProjectData' ]); } - fetchProjectData (projectId) { - if (projectId <= 0) return Promise.reject(null); - return storage - .load(storage.AssetType.Project, projectId, storage.DataFormat.JSON) - .then(projectAsset => { // NOTE: this is turning up null, breaking the line below. - let input = projectAsset.data; - if (typeof input === 'object' && !(input instanceof ArrayBuffer) && - !ArrayBuffer.isView(input)) { // taken from scratch-vm - // If the input is an object and not any ArrayBuffer - // or an ArrayBuffer view (this includes all typed arrays and DataViews) - // turn the object into a JSON string, because we suspect - // this is a project.json as an object - // validate expects a string or buffer as input - // TODO not sure if we need to check that it also isn't a data view - input = JSON.stringify(input); // NOTE: what is the point of doing this?? - } - }); - } render () { return (); } diff --git a/src/views/preview/project-view.jsx b/src/views/preview/project-view.jsx index 3d6e671fb..f98fe1083 100644 --- a/src/views/preview/project-view.jsx +++ b/src/views/preview/project-view.jsx @@ -10,6 +10,7 @@ const injectIntl = require('react-intl').injectIntl; const parser = require('scratch-parser'); const Page = require('../../components/page/www/page.jsx'); +const storage = require('../../lib/storage.js').default; const log = require('../../lib/log'); const jar = require('../../lib/jar.js'); const thumbnailUrl = require('../../lib/user-thumbnail'); @@ -238,53 +239,67 @@ class Preview extends React.Component { } } getProjectData (projectId, showAlerts) { - this.props.fetchProjectData(projectId).then(projectAsset => { - parser(projectAsset.data, false, (err, projectData) => { - if (err) { - log.error(`Unhandled project parsing error: ${err}`); - return; + if (projectId <= 0) return 0; + return storage + .load(storage.AssetType.Project, projectId, storage.DataFormat.JSON) + .then(projectAsset => { // NOTE: this is turning up null, breaking the line below. + let input = projectAsset.data; + if (typeof input === 'object' && !(input instanceof ArrayBuffer) && + !ArrayBuffer.isView(input)) { // taken from scratch-vm + // If the input is an object and not any ArrayBuffer + // or an ArrayBuffer view (this includes all typed arrays and DataViews) + // turn the object into a JSON string, because we suspect + // this is a project.json as an object + // validate expects a string or buffer as input + // TODO not sure if we need to check that it also isn't a data view + input = JSON.stringify(input); // NOTE: what is the point of doing this?? } - const newState = { - modInfo: {} // Filled in below - }; - - const helpers = ProjectInfo[projectData[0].projectVersion]; - if (!helpers) return; // sb1 not handled - newState.extensions = Array.from(helpers.extensions(projectData[0])); - newState.modInfo.scriptCount = helpers.scriptCount(projectData[0]); - newState.modInfo.spriteCount = helpers.spriteCount(projectData[0]); - const hasCloudData = helpers.cloudData(projectData[0]); - if (hasCloudData) { - if (this.props.isLoggedIn) { - // show cloud variables log link if logged in - newState.extensions.push({ - action: { - l10nId: 'project.cloudDataLink', - uri: `/cloudmonitor/${projectId}/` - }, - icon: 'clouddata.svg', - l10nId: 'project.cloudVariables', - linked: true - }); - } else { - newState.extensions.push({ - icon: 'clouddata.svg', - l10nId: 'project.cloudVariables' - }); + parser(projectAsset.data, false, (err, projectData) => { + if (err) { + log.error(`Unhandled project parsing error: ${err}`); + return; } - } + const newState = { + modInfo: {} // Filled in below + }; - if (showAlerts) { - // Check for username block only if user is logged in - if (this.props.isLoggedIn) { - newState.showUsernameBlockAlert = helpers.usernameBlock(projectData[0]); - } else { // Check for cloud vars only if user is logged out - newState.showCloudDataAlert = hasCloudData; + const helpers = ProjectInfo[projectData[0].projectVersion]; + if (!helpers) return; // sb1 not handled + newState.extensions = Array.from(helpers.extensions(projectData[0])); + newState.modInfo.scriptCount = helpers.scriptCount(projectData[0]); + newState.modInfo.spriteCount = helpers.spriteCount(projectData[0]); + const hasCloudData = helpers.cloudData(projectData[0]); + if (hasCloudData) { + if (this.props.isLoggedIn) { + // show cloud variables log link if logged in + newState.extensions.push({ + action: { + l10nId: 'project.cloudDataLink', + uri: `/cloudmonitor/${projectId}/` + }, + icon: 'clouddata.svg', + l10nId: 'project.cloudVariables', + linked: true + }); + } else { + newState.extensions.push({ + icon: 'clouddata.svg', + l10nId: 'project.cloudVariables' + }); + } } - } - this.setState(newState); + + if (showAlerts) { + // Check for username block only if user is logged in + if (this.props.isLoggedIn) { + newState.showUsernameBlockAlert = helpers.usernameBlock(projectData[0]); + } else { // Check for cloud vars only if user is logged out + newState.showCloudDataAlert = hasCloudData; + } + } + this.setState(newState); + }); }); - }); } handleToggleComments () { this.props.updateProject( @@ -771,7 +786,6 @@ Preview.propTypes = { enableCommunity: PropTypes.bool, faved: PropTypes.bool, favedLoaded: PropTypes.bool, - fetchProjectData: PropTypes.func.isRequired, fullScreen: PropTypes.bool, getCommentById: PropTypes.func.isRequired, getCuratedStudios: PropTypes.func.isRequired, From afb7957d04d82d248bdb9f875d5c9d344abc7244 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 30 Sep 2019 10:06:33 -0400 Subject: [PATCH 19/43] =?UTF-8?q?moved=20Sentry=20init=20into=20ProjectVie?= =?UTF-8?q?wHOC=20wrapper=E2=80=99s=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/preview/project-view-hoc.jsx | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/views/preview/project-view-hoc.jsx b/src/views/preview/project-view-hoc.jsx index 08da7d221..7bae86eff 100644 --- a/src/views/preview/project-view-hoc.jsx +++ b/src/views/preview/project-view-hoc.jsx @@ -5,18 +5,6 @@ const connect = require('react-redux').connect; const previewActions = require('../../redux/preview.js'); -const Sentry = require('@sentry/browser'); -if (`${process.env.SENTRY_DSN}` !== '') { - Sentry.init({ - dsn: `${process.env.SENTRY_DSN}`, - // Do not collect global onerror, only collect specifically from React error boundaries. - // TryCatch plugin also includes errors from setTimeouts (i.e. the VM) - integrations: integrations => integrations.filter(i => - !(i.name === 'GlobalHandlers' || i.name === 'TryCatch')) - }); - window.Sentry = Sentry; // Allow GUI access to Sentry via window -} - /** * Higher-order component for presenting a project view. * @param {React.Component} Component a project view component @@ -24,6 +12,19 @@ if (`${process.env.SENTRY_DSN}` !== '') { */ const ProjectViewHOC = Component => { + // initialize Sentry instance, making sure it hasn't been initialized already + if (!window.Sentry && `${process.env.SENTRY_DSN}` !== '') { + const Sentry = require('@sentry/browser'); + Sentry.init({ + dsn: `${process.env.SENTRY_DSN}`, + // Do not collect global onerror, only collect specifically from React error boundaries. + // TryCatch plugin also includes errors from setTimeouts (i.e. the VM) + integrations: integrations => integrations.filter(i => + !(i.name === 'GlobalHandlers' || i.name === 'TryCatch')) + }); + window.Sentry = Sentry; // Allow GUI access to Sentry via window + } + class WrappedComponent extends React.Component { constructor (props) { super(props); From 10e6685a159032aa65486699f9aabb72cb01652f Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 30 Sep 2019 10:07:18 -0400 Subject: [PATCH 20/43] remove console.log statements --- src/views/preview/project-view.jsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/views/preview/project-view.jsx b/src/views/preview/project-view.jsx index f98fe1083..7ffb8621a 100644 --- a/src/views/preview/project-view.jsx +++ b/src/views/preview/project-view.jsx @@ -123,17 +123,12 @@ class Preview extends React.Component { } componentDidMount () { this.addEventListeners(); - console.log(`componentDidMount: session status: ${this.props.sessionStatus}`); - } componentDidUpdate (prevProps, prevState) { - console.log(`componentDidUpdate: session status: ${this.props.sessionStatus}`); if (this.state.projectId > 0 && ((this.props.sessionStatus !== prevProps.sessionStatus && this.props.sessionStatus === sessionActions.Status.FETCHED) || (this.state.projectId !== prevState.projectId))) { - console.log("got here A"); - console.log(`this: ${this.state.projectId} prev: ${prevState.projectId}`); this.fetchCommunityData(); this.getProjectData(this.state.projectId, true /* Show cloud/username alerts */); if (this.state.justShared) { From 4a2553e8194ce92db215ef40a96b410cb7250b96 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 30 Sep 2019 10:48:10 -0400 Subject: [PATCH 21/43] create sentry library for initing Sentry --- src/lib/sentry.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/lib/sentry.js diff --git a/src/lib/sentry.js b/src/lib/sentry.js new file mode 100644 index 000000000..b16cd03a9 --- /dev/null +++ b/src/lib/sentry.js @@ -0,0 +1,18 @@ +const initSentry = () => { + // initialize Sentry instance, making sure it hasn't been initialized already + if (!window.Sentry && `${process.env.SENTRY_DSN}` !== '') { + const Sentry = require('@sentry/browser'); + + Sentry.init({ + dsn: `${process.env.SENTRY_DSN}`, + // Do not collect global onerror, only collect specifically from React error boundaries. + // TryCatch plugin also includes errors from setTimeouts (i.e. the VM) + integrations: integrations => integrations.filter(i => + !(i.name === 'GlobalHandlers' || i.name === 'TryCatch')) + }); + + window.Sentry = Sentry; // Allow GUI access to Sentry via window + } +}; + +module.exports = initSentry; From 6772dc3b6d2a5ea836c2c6c5e9c912dd26c947b8 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 30 Sep 2019 10:49:19 -0400 Subject: [PATCH 22/43] refactor project view and embed view to not use shared hoc --- src/views/preview/embed-view.jsx | 31 ++++++++++-- src/views/preview/project-view-hoc.jsx | 68 -------------------------- src/views/preview/project-view.jsx | 27 +++++----- 3 files changed, 41 insertions(+), 85 deletions(-) delete mode 100644 src/views/preview/project-view-hoc.jsx diff --git a/src/views/preview/embed-view.jsx b/src/views/preview/embed-view.jsx index 251ab0267..be3184d5b 100644 --- a/src/views/preview/embed-view.jsx +++ b/src/views/preview/embed-view.jsx @@ -1,24 +1,26 @@ // embed view -const bindAll = require('lodash.bindall'); const React = require('react'); const PropTypes = require('prop-types'); +const connect = require('react-redux').connect; const injectIntl = require('react-intl').injectIntl; -const ProjectViewHOC = require('./project-view-hoc.jsx'); const ErrorBoundary = require('../../components/errorboundary/errorboundary.jsx'); const projectShape = require('./projectshape.jsx').projectShape; const NotAvailable = require('../../components/not-available/not-available.jsx'); const Meta = require('./meta.jsx'); +const previewActions = require('../../redux/preview.js'); + const GUI = require('scratch-gui'); const IntlGUI = injectIntl(GUI.default); +const initSentry = require('../../lib/sentry.js'); +initSentry(); + class EmbedView extends React.Component { constructor (props) { super(props); - bindAll(this, [ - ]); const pathname = window.location.pathname.toLowerCase(); const parts = pathname.split('/').filter(Boolean); this.state = { @@ -65,7 +67,26 @@ EmbedView.propTypes = { projectNotAvailable: PropTypes.bool }; -module.exports.View = ProjectViewHOC(EmbedView); +EmbedView.defaultProps = { + assetHost: process.env.ASSET_HOST, + projectHost: process.env.PROJECT_HOST +}; + +const mapStateToProps = state => ({ + projectInfo: state.preview.projectInfo, + projectNotAvailable: state.preview.projectNotAvailable +}); + +const mapDispatchToProps = dispatch => ({ + getProjectInfo: (id, token) => { + dispatch(previewActions.getProjectInfo(id, token)); + } +}); + +module.exports.View = connect( + mapStateToProps, + mapDispatchToProps +)(EmbedView); GUI.setAppElement(document.getElementById('app')); module.exports.initGuiState = GUI.initEmbedded; diff --git a/src/views/preview/project-view-hoc.jsx b/src/views/preview/project-view-hoc.jsx deleted file mode 100644 index 7bae86eff..000000000 --- a/src/views/preview/project-view-hoc.jsx +++ /dev/null @@ -1,68 +0,0 @@ -const bindAll = require('lodash.bindall'); -const React = require('react'); -const PropTypes = require('prop-types'); -const connect = require('react-redux').connect; - -const previewActions = require('../../redux/preview.js'); - -/** - * Higher-order component for presenting a project view. - * @param {React.Component} Component a project view component - * @return {React.Component} a wrapped project view component - */ - -const ProjectViewHOC = Component => { - // initialize Sentry instance, making sure it hasn't been initialized already - if (!window.Sentry && `${process.env.SENTRY_DSN}` !== '') { - const Sentry = require('@sentry/browser'); - Sentry.init({ - dsn: `${process.env.SENTRY_DSN}`, - // Do not collect global onerror, only collect specifically from React error boundaries. - // TryCatch plugin also includes errors from setTimeouts (i.e. the VM) - integrations: integrations => integrations.filter(i => - !(i.name === 'GlobalHandlers' || i.name === 'TryCatch')) - }); - window.Sentry = Sentry; // Allow GUI access to Sentry via window - } - - class WrappedComponent extends React.Component { - constructor (props) { - super(props); - bindAll(this, [ - ]); - } - render () { - return (); - } - } - - WrappedComponent.propTypes = { - assetHost: PropTypes.string.isRequired, - projectHost: PropTypes.string.isRequired - }; - - WrappedComponent.defaultProps = { - assetHost: process.env.ASSET_HOST, - projectHost: process.env.PROJECT_HOST - }; - - const mapStateToProps = state => ({ - projectInfo: state.preview.projectInfo, - projectNotAvailable: state.preview.projectNotAvailable - }); - - const mapDispatchToProps = dispatch => ({ - getProjectInfo: (id, token) => { - dispatch(previewActions.getProjectInfo(id, token)); - } - }); - - return connect( - mapStateToProps, - mapDispatchToProps - )(WrappedComponent); -}; - -module.exports = ProjectViewHOC; diff --git a/src/views/preview/project-view.jsx b/src/views/preview/project-view.jsx index 7ffb8621a..cc192af46 100644 --- a/src/views/preview/project-view.jsx +++ b/src/views/preview/project-view.jsx @@ -14,7 +14,6 @@ const storage = require('../../lib/storage.js').default; const log = require('../../lib/log'); const jar = require('../../lib/jar.js'); const thumbnailUrl = require('../../lib/user-thumbnail'); -const ProjectViewHOC = require('./project-view-hoc.jsx'); const ProjectInfo = require('../../lib/project-info'); const PreviewPresentation = require('./presentation.jsx'); const projectShape = require('./projectshape.jsx').projectShape; @@ -36,6 +35,9 @@ const IntlGUI = injectIntl(GUI.default); const localStorageAvailable = 'localStorage' in window && window.localStorage !== null; +const initSentry = require('../../lib/sentry.js'); +initSentry(); + class Preview extends React.Component { constructor (props) { super(props); @@ -868,17 +870,17 @@ Preview.defaultProps = { userPresent: false }; -const mapStateToProps = (state, ownProps) => { - const projectInfoPresent = ownProps.projectInfo && - Object.keys(ownProps.projectInfo).length > 0 && ownProps.projectInfo.id > 0; +const mapStateToProps = state => { + const projectInfoPresent = state.preview.projectInfo && + Object.keys(state.preview.projectInfo).length > 0 && state.preview.projectInfo.id > 0; const userPresent = state.session.session.user !== null && typeof state.session.session.user !== 'undefined' && Object.keys(state.session.session.user).length > 0; const isLoggedIn = state.session.status === sessionActions.Status.FETCHED && userPresent; const isAdmin = isLoggedIn && state.session.session.permissions.admin; - const author = projectInfoPresent && ownProps.projectInfo.author; - const authorPresent = author && Object.keys(ownProps.projectInfo.author).length > 0; + const author = projectInfoPresent && state.preview.projectInfo.author; + const authorPresent = author && Object.keys(state.preview.projectInfo.author).length > 0; const authorId = authorPresent && author.id && author.id.toString(); const authorUsername = authorPresent && author.username; const userOwnsProject = isLoggedIn && authorPresent && @@ -888,7 +890,7 @@ const mapStateToProps = (state, ownProps) => { state.permissions.admin === true); // if we don't have projectInfo, assume it's shared until we know otherwise - const isShared = !projectInfoPresent || ownProps.projectInfo.is_published; + const isShared = !projectInfoPresent || state.preview.projectInfo.is_published; return { authorId: authorId, @@ -924,8 +926,8 @@ const mapStateToProps = (state, ownProps) => { original: state.preview.original, parent: state.preview.parent, playerMode: state.scratchGui.mode.isPlayerOnly, - projectInfo: ownProps.projectInfo, - projectNotAvailable: ownProps.projectNotAvailable, + projectInfo: state.preview.projectInfo, + projectNotAvailable: state.preview.projectNotAvailable, projectStudios: state.preview.projectStudios, registrationOpen: state.navigation.registrationOpen, remixes: state.preview.remixes, @@ -980,6 +982,9 @@ const mapDispatchToProps = dispatch => ({ getParentInfo: id => { dispatch(previewActions.getParentInfo(id)); }, + getProjectInfo: (id, token) => { + dispatch(previewActions.getProjectInfo(id, token)); + }, getRemixes: id => { dispatch(previewActions.getRemixes(id)); }, @@ -1050,13 +1055,11 @@ const mapDispatchToProps = dispatch => ({ } }); -const ConnectedPreview = connect( +module.exports.View = connect( mapStateToProps, mapDispatchToProps )(Preview); -module.exports.View = ProjectViewHOC(ConnectedPreview); - // replace old Scratch 2.0-style hashtag URLs with updated format if (window.location.hash) { let pathname = window.location.pathname; From 60ad7e5125f1b7e2c431fd3c7f162c7c6ac0cbb8 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 30 Sep 2019 10:56:42 -0400 Subject: [PATCH 23/43] removed unnecessary return statement --- src/views/preview/project-view.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/preview/project-view.jsx b/src/views/preview/project-view.jsx index cc192af46..c41ec84ee 100644 --- a/src/views/preview/project-view.jsx +++ b/src/views/preview/project-view.jsx @@ -237,7 +237,7 @@ class Preview extends React.Component { } getProjectData (projectId, showAlerts) { if (projectId <= 0) return 0; - return storage + storage .load(storage.AssetType.Project, projectId, storage.DataFormat.JSON) .then(projectAsset => { // NOTE: this is turning up null, breaking the line below. let input = projectAsset.data; From bc77322526e66b64ee2d107e7bfc575ca9335bd4 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 30 Sep 2019 11:28:36 -0400 Subject: [PATCH 24/43] clear store between tests --- test/unit/components/navigation.test.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit/components/navigation.test.jsx b/test/unit/components/navigation.test.jsx index 727f48239..0072dc4cd 100644 --- a/test/unit/components/navigation.test.jsx +++ b/test/unit/components/navigation.test.jsx @@ -9,6 +9,7 @@ describe('Navigation', () => { let store; beforeEach(() => { + store = null; }); const getNavigationWrapper = props => { From 484cf46b8c75ddc85e2541f15388f5ab432a0047 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 30 Sep 2019 11:49:04 -0400 Subject: [PATCH 25/43] move join flow modal down in small window widths --- src/components/modal/join/modal.scss | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/components/modal/join/modal.scss b/src/components/modal/join/modal.scss index 616a82a32..d6bbf845c 100644 --- a/src/components/modal/join/modal.scss +++ b/src/components/modal/join/modal.scss @@ -4,3 +4,10 @@ .mod-join { width: 27.4375rem; } + +/* lower the modal slightly to accomodate Scratch logo above it */ +.modal-sizes { + @media #{$medium}, #{$medium-height} { + margin: 3.5rem auto; + } +} From 309e5a77e1833d746c2e200d9a53e0e9a73c694e Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Mon, 30 Sep 2019 23:57:11 -0400 Subject: [PATCH 26/43] try adding Page to make build succeed --- src/views/preview/embed.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/views/preview/embed.jsx b/src/views/preview/embed.jsx index cb2d9ba9b..f458948be 100644 --- a/src/views/preview/embed.jsx +++ b/src/views/preview/embed.jsx @@ -2,6 +2,9 @@ const React = require('react'); const ErrorBoundary = require('../../components/errorboundary/errorboundary.jsx'); const render = require('../../lib/render.jsx'); +// Require this even though we don't use it because, without it, webpack runs out of memory... +const Page = require('../../components/page/www/page.jsx'); // eslint-disable-line no-unused-vars + const previewActions = require('../../redux/preview.js'); const isSupportedBrowser = require('../../lib/supported-browser').default; From 06124331ccae76ba1a8e625f38fdb7a43960ce5d Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Tue, 1 Oct 2019 00:46:32 -0400 Subject: [PATCH 27/43] removing Page --- src/views/preview/embed.jsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/views/preview/embed.jsx b/src/views/preview/embed.jsx index f458948be..cb2d9ba9b 100644 --- a/src/views/preview/embed.jsx +++ b/src/views/preview/embed.jsx @@ -2,9 +2,6 @@ const React = require('react'); const ErrorBoundary = require('../../components/errorboundary/errorboundary.jsx'); const render = require('../../lib/render.jsx'); -// Require this even though we don't use it because, without it, webpack runs out of memory... -const Page = require('../../components/page/www/page.jsx'); // eslint-disable-line no-unused-vars - const previewActions = require('../../redux/preview.js'); const isSupportedBrowser = require('../../lib/supported-browser').default; From cff71249b95d2306b349dec1f95047102aa985b0 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2019 16:11:35 +0000 Subject: [PATCH 28/43] chore(package): update scratch-gui to version 0.1.0-prerelease.20191001155549 Closes #3419 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0b3089f84..87388352b 100644 --- a/package.json +++ b/package.json @@ -126,7 +126,7 @@ "redux-mock-store": "^1.2.3", "redux-thunk": "2.0.1", "sass-loader": "6.0.6", - "scratch-gui": "0.1.0-prerelease.20190926141105", + "scratch-gui": "0.1.0-prerelease.20191001155549", "scratch-l10n": "latest", "selenium-webdriver": "3.6.0", "slick-carousel": "1.6.0", From da72a44539ed5eda1bee75512dea52b1200c5ad1 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Tue, 1 Oct 2019 16:11:39 +0000 Subject: [PATCH 29/43] chore(package): update lockfile package-lock.json --- package-lock.json | 936 +++++++++++++++++++++++----------------------- 1 file changed, 468 insertions(+), 468 deletions(-) diff --git a/package-lock.json b/package-lock.json index 98cce130d..1fc68ecb7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,471 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/cli": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.6.2.tgz", + "integrity": "sha512-JDZ+T/br9pPfT2lmAMJypJDTTTHM9ePD/ED10TRjRzJVdEVy+JB3iRlhzYmTt5YkNgHvxWGlUVnLtdv6ruiDrQ==", + "dev": true, + "requires": { + "chokidar": "^2.1.8", + "commander": "^2.8.1", + "convert-source-map": "^1.1.0", + "fs-readdir-recursive": "^1.1.0", + "glob": "^7.0.0", + "lodash": "^4.17.13", + "mkdirp": "^0.5.1", + "output-file-sync": "^2.0.0", + "slash": "^2.0.0", + "source-map": "^0.5.0" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "optional": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "optional": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true, + "optional": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true, + "optional": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "optional": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "optional": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "optional": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "optional": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "optional": true + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "optional": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "optional": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "optional": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "optional": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "optional": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "optional": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "optional": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true, + "optional": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "optional": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true, + "optional": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "optional": true + }, + "output-file-sync": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-2.0.1.tgz", + "integrity": "sha512-mDho4qm7WgIXIGf4eYU1RHN2UU5tPfVYVSRwDJw0uTmj35DQUt/eNp19N7v6T3SrR0ESTEf2up2CGO73qI35zQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "is-plain-obj": "^1.1.0", + "mkdirp": "^0.5.1" + } + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, "@babel/code-frame": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", @@ -15095,9 +15560,9 @@ } }, "scratch-gui": { - "version": "0.1.0-prerelease.20190926141105", - "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20190926141105.tgz", - "integrity": "sha512-0/OLyeiU1qa+e35dsy+P/sz0V9bdgbID5Rs1VUuxpj+F+8QFUNuoZTWwKgqtAIb4M7Q6zg5ve+1iDrnzOMw3bQ==", + "version": "0.1.0-prerelease.20191001155549", + "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20191001155549.tgz", + "integrity": "sha512-Y0A1+/EQGWUAJZN8fi0oGk8MgM9X5hXA0SwgbxOuWMT/lppu44Q3BXPYkJGVY3dOKsXCSTgwgm6DGG0Xj3cvtw==", "dev": true }, "scratch-l10n": { @@ -15110,471 +15575,6 @@ "@babel/core": "^7.1.2", "babel-plugin-react-intl": "^3.0.1", "transifex": "1.6.6" - }, - "dependencies": { - "@babel/cli": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.6.2.tgz", - "integrity": "sha512-JDZ+T/br9pPfT2lmAMJypJDTTTHM9ePD/ED10TRjRzJVdEVy+JB3iRlhzYmTt5YkNgHvxWGlUVnLtdv6ruiDrQ==", - "dev": true, - "requires": { - "chokidar": "^2.1.8", - "commander": "^2.8.1", - "convert-source-map": "^1.1.0", - "fs-readdir-recursive": "^1.1.0", - "glob": "^7.0.0", - "lodash": "^4.17.13", - "mkdirp": "^0.5.1", - "output-file-sync": "^2.0.0", - "slash": "^2.0.0", - "source-map": "^0.5.0" - } - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "optional": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "optional": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true, - "optional": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true, - "optional": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "optional": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "optional": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "optional": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "optional": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "optional": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "optional": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "optional": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "optional": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "optional": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "optional": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "optional": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true, - "optional": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "optional": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true, - "optional": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "optional": true - }, - "output-file-sync": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-2.0.1.tgz", - "integrity": "sha512-mDho4qm7WgIXIGf4eYU1RHN2UU5tPfVYVSRwDJw0uTmj35DQUt/eNp19N7v6T3SrR0ESTEf2up2CGO73qI35zQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "is-plain-obj": "^1.1.0", - "mkdirp": "^0.5.1" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } } }, "scratch-parser": { From 62fc3457e7a6b07fd8ba9478201d29771c096449 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Tue, 1 Oct 2019 16:43:33 -0400 Subject: [PATCH 30/43] turn off automplete and autocorrect for join-flow inputs --- src/components/formik-forms/formik-radio-button.jsx | 4 ++++ src/components/join-flow/email-step.jsx | 4 ++++ src/components/join-flow/join-flow-step.jsx | 5 ++++- src/components/join-flow/username-step.jsx | 12 ++++++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/components/formik-forms/formik-radio-button.jsx b/src/components/formik-forms/formik-radio-button.jsx index b7ee7f488..4d131ba52 100644 --- a/src/components/formik-forms/formik-radio-button.jsx +++ b/src/components/formik-forms/formik-radio-button.jsx @@ -89,8 +89,12 @@ const FormikRadioButton = ({ > {isCustomInput && ( onSetCustom(event.target.value)} diff --git a/src/components/join-flow/email-step.jsx b/src/components/join-flow/email-step.jsx index 803bd1bf8..c5b941738 100644 --- a/src/components/join-flow/email-step.jsx +++ b/src/components/join-flow/email-step.jsx @@ -160,6 +160,9 @@ class EmailStep extends React.Component { onSubmit={handleSubmit} > ( -
+
{headerImgSrc && (
diff --git a/src/components/join-flow/username-step.jsx b/src/components/join-flow/username-step.jsx index 007c02d2b..a3907bfb7 100644 --- a/src/components/join-flow/username-step.jsx +++ b/src/components/join-flow/username-step.jsx @@ -143,6 +143,9 @@ class UsernameStep extends React.Component { {this.props.intl.formatMessage({id: 'registration.createUsername'})}
Date: Tue, 1 Oct 2019 18:04:47 -0400 Subject: [PATCH 31/43] truncate custom gender to 25 chars long --- src/components/join-flow/gender-step.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/join-flow/gender-step.jsx b/src/components/join-flow/gender-step.jsx index aeeb970e6..fa450eb44 100644 --- a/src/components/join-flow/gender-step.jsx +++ b/src/components/join-flow/gender-step.jsx @@ -150,8 +150,8 @@ class GenderStep extends React.Component { value={values.custom} /* eslint-disable react/jsx-no-bind */ onSetCustom={newCustomVal => setValues({ - gender: newCustomVal, - custom: newCustomVal + gender: newCustomVal.substring(0, 25), + custom: newCustomVal.substring(0, 25) })} onSetCustomRef={this.handleSetCustomRef} /* eslint-enable react/jsx-no-bind */ From 5db507201ad4f6498dcaed0089a6c4d35630be16 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Tue, 1 Oct 2019 18:15:38 -0400 Subject: [PATCH 32/43] truncate username, passwords, and email to their max stored lengths --- src/components/join-flow/email-step.jsx | 2 +- src/components/join-flow/username-step.jsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/join-flow/email-step.jsx b/src/components/join-flow/email-step.jsx index 803bd1bf8..878214d05 100644 --- a/src/components/join-flow/email-step.jsx +++ b/src/components/join-flow/email-step.jsx @@ -174,7 +174,7 @@ class EmailStep extends React.Component { /* eslint-disable react/jsx-no-bind */ onBlur={() => validateField('email')} onChange={e => { - setFieldValue('email', e.target.value); + setFieldValue('email', e.target.value.substring(0, 254)); setFieldTouched('email'); setFieldError('email', null); }} diff --git a/src/components/join-flow/username-step.jsx b/src/components/join-flow/username-step.jsx index 007c02d2b..f325b3a7e 100644 --- a/src/components/join-flow/username-step.jsx +++ b/src/components/join-flow/username-step.jsx @@ -157,7 +157,7 @@ class UsernameStep extends React.Component { /* eslint-disable react/jsx-no-bind */ onBlur={() => validateField('username')} onChange={e => { - setFieldValue('username', e.target.value); + setFieldValue('username', e.target.value.substring(0, 30)); setFieldTouched('username'); setFieldError('username', null); }} @@ -187,7 +187,7 @@ class UsernameStep extends React.Component { validationClassName="validation-full-width-input" onBlur={() => validateField('password')} onChange={e => { - setFieldValue('password', e.target.value); + setFieldValue('password', e.target.value.substring(0, 128)); setFieldTouched('password'); setFieldError('password', null); }} @@ -225,7 +225,7 @@ class UsernameStep extends React.Component { validationClassName="validation-full-width-input" onBlur={() => validateField('passwordConfirm')} onChange={e => { - setFieldValue('passwordConfirm', e.target.value); + setFieldValue('passwordConfirm', e.target.value.substring(0, 128)); setFieldTouched('passwordConfirm'); setFieldError('passwordConfirm', null); }} From f9b436182bb3d78f032f1cfd9cfe1c4d8c9a8755 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Tue, 1 Oct 2019 18:21:14 -0400 Subject: [PATCH 33/43] Revert "removing Page" This reverts commit 06124331ccae76ba1a8e625f38fdb7a43960ce5d. --- src/views/preview/embed.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/views/preview/embed.jsx b/src/views/preview/embed.jsx index cb2d9ba9b..f458948be 100644 --- a/src/views/preview/embed.jsx +++ b/src/views/preview/embed.jsx @@ -2,6 +2,9 @@ const React = require('react'); const ErrorBoundary = require('../../components/errorboundary/errorboundary.jsx'); const render = require('../../lib/render.jsx'); +// Require this even though we don't use it because, without it, webpack runs out of memory... +const Page = require('../../components/page/www/page.jsx'); // eslint-disable-line no-unused-vars + const previewActions = require('../../redux/preview.js'); const isSupportedBrowser = require('../../lib/supported-browser').default; From 28fd111199cd01b5e21c24766b5f72695b5e9b9d Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler Date: Tue, 1 Oct 2019 18:34:19 -0400 Subject: [PATCH 34/43] Revert "turn off automplete and autocorrect for join-flow inputs" --- src/components/formik-forms/formik-radio-button.jsx | 4 ---- src/components/join-flow/email-step.jsx | 4 ---- src/components/join-flow/join-flow-step.jsx | 5 +---- src/components/join-flow/username-step.jsx | 12 ------------ 4 files changed, 1 insertion(+), 24 deletions(-) diff --git a/src/components/formik-forms/formik-radio-button.jsx b/src/components/formik-forms/formik-radio-button.jsx index 4d131ba52..b7ee7f488 100644 --- a/src/components/formik-forms/formik-radio-button.jsx +++ b/src/components/formik-forms/formik-radio-button.jsx @@ -89,12 +89,8 @@ const FormikRadioButton = ({ > {isCustomInput && ( onSetCustom(event.target.value)} diff --git a/src/components/join-flow/email-step.jsx b/src/components/join-flow/email-step.jsx index c5b941738..803bd1bf8 100644 --- a/src/components/join-flow/email-step.jsx +++ b/src/components/join-flow/email-step.jsx @@ -160,9 +160,6 @@ class EmailStep extends React.Component { onSubmit={handleSubmit} > ( - +
{headerImgSrc && (
diff --git a/src/components/join-flow/username-step.jsx b/src/components/join-flow/username-step.jsx index a3907bfb7..007c02d2b 100644 --- a/src/components/join-flow/username-step.jsx +++ b/src/components/join-flow/username-step.jsx @@ -143,9 +143,6 @@ class UsernameStep extends React.Component { {this.props.intl.formatMessage({id: 'registration.createUsername'})}
Date: Wed, 2 Oct 2019 18:29:28 -0400 Subject: [PATCH 35/43] adjust email centering --- src/components/join-flow/join-flow-steps.scss | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/join-flow/join-flow-steps.scss b/src/components/join-flow/join-flow-steps.scss index de027bbe3..e9c93b4a9 100644 --- a/src/components/join-flow/join-flow-steps.scss +++ b/src/components/join-flow/join-flow-steps.scss @@ -136,9 +136,8 @@ } .join-flow-inner-email-step { - padding-top: 1.25rem; + padding-top: .75rem; padding-bottom: 0; - min-height: 16.625rem; } .join-flow-inner-welcome-step { @@ -217,7 +216,7 @@ .join-flow-email-checkbox-row { font-size: .75rem; - margin: 1.5rem .125rem .25rem; + margin: 1.5rem .125rem 1rem; } a.join-flow-link:link, a.join-flow-link:visited, a.join-flow-link:active { From 54c548426345e84a9862109a72dd653f4f31df8c Mon Sep 17 00:00:00 2001 From: Chris Garrity Date: Wed, 2 Oct 2019 15:57:06 -0700 Subject: [PATCH 36/43] Copy changes for Download page. --- .../extension-requirements.jsx | 2 +- .../install-scratch/install-scratch.jsx | 11 ++- src/l10n.json | 8 +- src/views/download/download.jsx | 75 ++++++++++++++----- src/views/download/download.scss | 4 +- src/views/download/l10n.json | 21 ++++-- 6 files changed, 88 insertions(+), 33 deletions(-) diff --git a/src/components/extension-landing/extension-requirements.jsx b/src/components/extension-landing/extension-requirements.jsx index 3754eb02c..8cc82888c 100644 --- a/src/components/extension-landing/extension-requirements.jsx +++ b/src/components/extension-landing/extension-requirements.jsx @@ -44,7 +44,7 @@ const ExtensionRequirements = props => ( alt="" src="svgs/extensions/android.svg" /> - Android 5.0+ + Android 6.0+ )} diff --git a/src/components/install-scratch/install-scratch.jsx b/src/components/install-scratch/install-scratch.jsx index eb1c40beb..e82e4779a 100644 --- a/src/components/install-scratch/install-scratch.jsx +++ b/src/components/install-scratch/install-scratch.jsx @@ -46,11 +46,16 @@ const InstallScratch = ({ > - {isDownloaded(currentOS) && ( + {currentOS === OS_ENUM.WINDOWS && ( + )} + {currentOS === OS_ENUM.MACOS && ( + )} {isFromGooglePlay(currentOS) && ( diff --git a/src/l10n.json b/src/l10n.json index b8f94ce88..375bccd8d 100644 --- a/src/l10n.json +++ b/src/l10n.json @@ -137,12 +137,14 @@ "installScratch.or": "or", "installScratch.directDownload": "Direct download", "installScratch.desktopHeaderTitle": "Install Scratch Desktop", - "installScratch.appHeaderTitle": "Install Scratch for {operatingsystem}", + "installScratch.appHeaderTitle": "Install the Scratch app for {operatingsystem}", "installScratch.downloadScratchDesktop": "Download Scratch Desktop", "installScratch.downloadScratchAppGeneric": "Download Scratch for {operatingsystem}", - "installScratch.getScratchAppPlay": "Get Scratch on the Google Play Store", + "installScratch.getScratchAppPlay": "Get the Scratch app on the Google Play Store", + "installScratch.getScratchAppMacOs": "Get the Scratch app on the Mac App Store", + "installScratch.getScratchAppWindows": "Get the Scratch app on the Microsoft Store", "installScratch.useScratchApp": "Open the Scratch app on your device.", - + "installScratchLink.installHeaderTitle": "Install Scratch Link", "installScratchLink.downloadAndInstall": "Download and install Scratch Link.", "installScratchLink.startScratchLink": "Start Scratch Link and make sure it is running. It should appear in your toolbar.", diff --git a/src/views/download/download.jsx b/src/views/download/download.jsx index b0f77a4a8..10ad737b1 100644 --- a/src/views/download/download.jsx +++ b/src/views/download/download.jsx @@ -95,7 +95,7 @@ class Download extends React.Component { alt="" src="svgs/extensions/android.svg" /> - Android 5.0+ + Android 6.0+ )} @@ -181,7 +181,17 @@ class Download extends React.Component { - {isDownloaded(this.state.OS) && ( + {CHROME_APP_RELEASED && ( + +

+ +

+

+ +

+
+ )} + {!CHROME_APP_RELEASED && (

@@ -191,25 +201,29 @@ class Download extends React.Component {

)} - {isFromGooglePlay(this.state.OS) && ( + {CHROME_APP_RELEASED && (

- +

-

- -

+ {isDownloaded(this.state.OS) && ( +

+ +

+ )} + {isFromGooglePlay(this.state.OS) && ( +

+ +

+ )}
)} -

- {isFromGooglePlay(this.state.OS) ? - : - - } -

-

- -

{isDownloaded(this.state.OS) && (CHROME_APP_RELEASED ? (

@@ -248,6 +262,25 @@ class Download extends React.Component {

)} +

+ {CHROME_APP_RELEASED ? + : + + } +

+

+ +

+ {CHROME_APP_RELEASED && ( + +

+ +

+

+ +

+
+ )} {!CHROME_APP_RELEASED && (

@@ -259,10 +292,16 @@ class Download extends React.Component { )}

- + {CHROME_APP_RELEASED ? + : + + }

- + {CHROME_APP_RELEASED ? + : + + }

diff --git a/src/views/download/download.scss b/src/views/download/download.scss index 3e2b4064c..bd238de64 100644 --- a/src/views/download/download.scss +++ b/src/views/download/download.scss @@ -56,7 +56,7 @@ .download-info { padding-right: $cols1; - max-width: $cols6 + ($gutter / $em); + max-width: $cols7 + ($gutter / $em); align-items: flex-start; .download-copy { @@ -110,7 +110,7 @@ .download-image { width: 100%; - max-width: $cols6; + max-width: $cols5; img { max-width: 100%; diff --git a/src/views/download/l10n.json b/src/views/download/l10n.json index c1559b070..92e204675 100644 --- a/src/views/download/l10n.json +++ b/src/views/download/l10n.json @@ -1,35 +1,44 @@ { "download.title": "Scratch Desktop", "download.intro": "You can install the Scratch Desktop editor to work on projects without an internet connection. This version will work on Windows and MacOS.", - "download.appTitle": "Download Scratch", - "download.appIntro": "You can install Scratch for free to work on projects without an internet connection.", + "download.appTitle": "Download the Scratch App", + "download.appIntro": "Would you like to create and save Scratch projects without an internet connection? Download the free Scratch app.", "download.requirements": "Requirements", "download.imgAltDownloadIllustration" : "Scratch 3.0 Desktop screenshot", "download.troubleshootingTitle": "FAQ", "download.startScratchDesktop": "Start Scratch Desktop", "download.howDoIInstall": "How do I install Scratch Desktop?", + "download.whenSupportLinuxApp": "When will you have the Scratch app available for Linux?", "download.whenSupportLinux": "When will you have Scratch Desktop for Linux?", "download.supportLinuxAnswer" : "Scratch Desktop on Linux is currently not supported. We are working with partners and the open-source community to determine if there is a way we can support Linux in the future. Stay tuned!", + "download.whenSupportLinuxAppAnswer" : "The Scratch app is currently not supported on Linux. We are working with partners and the open-source community to determine if there is a way we can support Linux in the future. Stay tuned!", "download.supportChromeOS" : "When will you have Scratch Desktop for Chromebooks?", "download.supportChromeOSAnswer": "Scratch Desktop for Chromebooks is not yet available. We are working on it and expect to release later in 2019.", "download.olderVersionsTitle" : "Older Versions", - "download.olderVersions": "Looking for earlier Scratch Offline Editors?", + "download.olderVersions": "Looking for an earlier version of Scratch?", "download.scratch1-4Desktop" : "Scratch 1.4", "download.scratch2Desktop" : "Scratch 2.0 Offline Editor", "download.cannotAccessMacStore" : "What if I can't access the Mac App Store?", "download.cannotAccessWindowsStore" : "What if I can't access the Microsoft Store?", "download.macMoveToApplications" : "Open the .dmg file. Move Scratch Desktop into Applications.", "download.winMoveToApplications" : "Run the .exe file.", + "download.doIHaveToDownload" : "Do I have to download an app to use Scratch?", + "download.doIHaveToDownloadAnswer" : "No. You can also use the Scratch project editor in any web browser on any device by going to scratch.mit.edu and clicking \"Create\".", "download.canIUseScratchLink" : "Can I use Scratch Link to connect to extensions?", "download.canIUseScratchLinkAnswer" : "Yes. However, you will need an Internet connection to use Scratch Link.", "download.canIUseExtensions" : "Can I connect to hardware extensions?", "download.canIUseExtensionsAnswer" : "Yes. With the Scratch app you can connect to extensions, and you do not need Scratch Link.", + "download.howConnectHardwareDevices" : "How do I connect the Scratch app to hardware devices?", + "download.howConnectHardwareDevicesAnswerLink" : "You will need to install and run Scratch Link in order to connect to hardware devices when using Scratch app for {operatingsystem}. You will also need an internet connection to use Scratch Link.", + "download.howConnectHardwareDevicesAnswerApp" : "With the Scratch app you can connect to hardware devices like the micro:bit or LEGO Boost. When using the Scratch app for {operatingsystem} you do not need Scratch Link.", "download.desktopAndBrowser": "Can I use Scratch Desktop and also have Scratch open in the browser?", "download.appAndBrowser": "Can I use the Scratch app and also have Scratch open in the browser?", "download.yesAnswer" : "Yes.", + "download.onPhone": "Can I install Scratch on my Android phone?", + "download.onPhoneAnswer" : "No. The current version of Scratch for Android only works on tablets.", "download.canIShare": "Can I share from Scratch Desktop?", "download.canIShareAnswer": "This isn’t supported currently. For now, you can save a project from Scratch Desktop, upload it to your Scratch account, and share it there. In a later version we will add the ability to upload to your Scratch account directly in Scratch Desktop.", - "download.canIShareApp": "Can I share from Scratch for {operatingsystem}?", - "download.canIShareAnswerPlayStore": "Yes. Click the 3-dots menu on a project in the lobby and select Share from the options. In addition to sharing by email you can sign in to your Scratch account and share the project on the Scratch Community.", - "download.canIShareAnswerDownloaded": "This isn’t supported currently. For now, you can save a project from Scratch for {operatingsystem}, upload it to your Scratch account, and share it there. In a later version we will add the ability to upload to your Scratch account directly in Scratch for {operatingsystem}." + "download.canIShareApp": "Can I share to the online community from the Scratch app for {operatingsystem}?", + "download.canIShareAnswerPlayStore": "Yes. Click the 3-dots menu on a project in the lobby and select \"Share\" from the options. In addition to sharing by email, you can sign in to your Scratch account and share a project to the Scratch online community.", + "download.canIShareAnswerDownloaded": "Sharing directly to online community from the Scratch app for {operatingsystem} is not currently supported. For now, you can export a project from the Scratch app, then log onto the Scratch website, and upload and share your project there." } From 916be715c12d84bc6386cac5ef98a154689afbab Mon Sep 17 00:00:00 2001 From: Chris Garrity Date: Wed, 2 Oct 2019 16:33:41 -0700 Subject: [PATCH 37/43] Other Scratch app changes * Footer * ideas page * FAQ * parents --- src/components/footer/www/footer.jsx | 2 +- src/l10n.json | 2 +- src/views/faq/faq.jsx | 8 ++++---- src/views/faq/l10n.json | 12 ++++++------ src/views/ideas/l10n.json | 4 ++-- src/views/parents/l10n.json | 4 ++-- src/views/parents/parents.jsx | 4 ++-- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/components/footer/www/footer.jsx b/src/components/footer/www/footer.jsx index ad5f4c4b9..ae31006c9 100644 --- a/src/components/footer/www/footer.jsx +++ b/src/components/footer/www/footer.jsx @@ -135,7 +135,7 @@ const Footer = props => (
- +
diff --git a/src/l10n.json b/src/l10n.json index 375bccd8d..36aec4783 100644 --- a/src/l10n.json +++ b/src/l10n.json @@ -66,7 +66,7 @@ "general.notRequired": "Not Required", "general.okay": "Okay", "general.other": "Other", - "general.offlineEditor": "Offline Editor", + "general.download": "Download", "general.password": "Password", "general.press": "Press", "general.privacyPolicy": "Privacy Policy", diff --git a/src/views/faq/faq.jsx b/src/views/faq/faq.jsx index 557969351..598d2ee23 100644 --- a/src/views/faq/faq.jsx +++ b/src/views/faq/faq.jsx @@ -59,7 +59,7 @@ const Faq = injectIntl(props => (
  • + )}} />
  • @@ -69,7 +69,7 @@ const Faq = injectIntl(props => (
    + )}} />
    @@ -482,7 +482,7 @@ const Faq = injectIntl(props => ( id="faq.noInternetBody" values={{downloadLink: ( - + )}} /> @@ -523,7 +523,7 @@ const Faq = injectIntl(props => ( id="faq.lawComplianceBody2" values={{downloadLink: ( - + )}} /> diff --git a/src/views/faq/l10n.json b/src/views/faq/l10n.json index 731614e75..c58f66861 100644 --- a/src/views/faq/l10n.json +++ b/src/views/faq/l10n.json @@ -30,11 +30,11 @@ "faq.requirementsTabletSafari":"Mobile Safari (11+)", "faq.requirementsNote":"Note:", "faq.requirementsNoteDesktop":"If your computer doesn’t meet these requirements, you can try the {downloadLink} editor (see next item in FAQ). ", - "faq.scratchDesktop":"Scratch Desktop", + "faq.scratchApp":"Scratch app", "faq.requirementsNoteWebGL":"If you encounter a WebGL error, try a different browser.", "faq.requirementsNoteTablets":"On tablets, there is currently not a way to use \"key pressed\" blocks or right-click context menus.", "faq.offlineTitle":"Do you have a downloadable version so I can create and view projects offline?", - "faq.offlineBody":"The Scratch Desktop editor allows you to create Scratch projects without an internet connection. You can download {downloadLink} from the website. This was previously called the Scratch Offline editor.", + "faq.offlineBody":"The Scratch app allows you to create Scratch projects without an internet connection. You can download the {downloadLink} from the Scratch website or the app store for your device. (This was previously called the \"Scratch Offline Editor\").", "faq.uploadOldTitle":"Can I still upload projects created with older versions of Scratch to the website?", "faq.uploadOldBody":"Yes: You can share or upload projects made with earlier versions of Scratch, and they will be visible and playable. (However, you can’t download projects made with or edited in later versions of Scratch and open them in earlier versions. For example, you can’t open a Scratch 3.0 project in the desktop version of {scratch2Link}, because Scratch 2.0 doesn’t know how to read the .sb3 project file format.)", "faq.scratch2":"Scratch 2.0", @@ -182,9 +182,9 @@ "faq.howBody":"Scratch is used in hundreds of thousands of schools around the world, in many different subject areas (including language arts, science, history, math, and computer science). You can learn more about strategies and resources for using Scratch in schools and other learning environments (such as museums, libraries, and community centers) on our {educatorsLink}.", "faq.educatorsLinkText":"Educators Page", "faq.noInternetTitle":"Is there a way for students to use Scratch without an internet connection?", - "faq.noInternetBody":"Yes. {downloadLink} is a version of Scratch that runs on a desktop or laptop computer. Currently, Scratch Desktop is available for Mac and Windows machines.", + "faq.noInternetBody":"Yes. The {downloadLink} is a downloadable version of Scratch that can run on tablets, laptops, and desktops. Currently, the Scratch app is available on Windows, Mac, Chromebook, and Android devices.", "faq.communityTitle":"Can I turn off the online community for my students?", - "faq.communityBody":"The Scratch online community provides a way for young people to share, collaborate, and learn with their peers within a moderated community governed by the Scratch {cgLink}. However, we understand that some educators prefer that their students not participate in an online community. These educators may wish to install Scratch Desktop, which runs offline and locally on a desktop or laptop computer.", + "faq.communityBody":"The Scratch online community provides a way for young people to share, collaborate, and learn with their peers within a moderated community governed by the Scratch {cgLink}. However, we understand that some educators prefer that their students not participate in an online community. These educators may wish to install the Scratch app, which runs offline and locally on a desktop or laptop computer.", "faq.teacherAccountTitle":"What is a Scratch Teacher Account?", "faq.teacherAccountBody":"A Scratch Teacher Account provides teachers and other educators with additional features to manage student participation on Scratch, including the ability to create student accounts, organize student projects into studios, and monitor student comments. For more information on Scratch Teacher Accounts, see the {eduFaqLink}.", "faq.eduFaqLinkText":"Scratch Teacher Account FAQ", @@ -192,7 +192,7 @@ "faq.requestBody":"You may request a Scratch Teacher Account from the {educatorsLink} on Scratch. We ask for additional information during the registration process in order to verify your role as an educator.", "faq.dataTitle":"What data does Scratch collect about students?", "faq.dataBody":"When a student first signs up on Scratch, we ask for basic demographic data including gender, age (birth month and year), country, and an email address for verification. This data is used (in aggregated form) in research studies intended to improve our understanding of how people learn with Scratch. When an educator uses a Scratch Teacher Account to create student accounts in bulk, students are not required to provide an email address for account setup.", - "faq.lawComplianceTitle":"Is Scratch (online version) compliant with United States local and federal data privacy laws?", + "faq.lawComplianceTitle":"Is the online version of Scratch compliant with United States local and federal data privacy laws?", "faq.lawComplianceBody1":"Scratch cares deeply about the privacy of students and of all individuals who use our platform. We have in place physical and electronic procedures to protect the information we collect on the Scratch website. Although we are not in a position to offer contractual guarantees with each entity that uses our free educational product, we are in compliance with all United States federal laws that are applicable to MIT and the Scratch Foundation, the organizations that have created and maintained Scratch. We encourage you to read the Scratch Privacy Policy for more information.", - "faq.lawComplianceBody2":"If you would like to build projects with Scratch without submitting any Personal Information to us, you can download {downloadLink}. Projects created in Scratch Desktop are not accessible by the Scratch Team, and using Scratch Desktop does not disclose any personally identifying information to Scratch unless you upload these projects to the Scratch online community." + "faq.lawComplianceBody2":"If you would like to build projects with Scratch without submitting any Personal Information to us, you can download the {downloadLink}. Projects created in the Scratch app are not accessible by the Scratch Team, and using the Scratch app does not disclose any personally identifying information to Scratch unless you upload these projects to the Scratch online community." } diff --git a/src/views/ideas/l10n.json b/src/views/ideas/l10n.json index ca182da98..a2975778a 100644 --- a/src/views/ideas/l10n.json +++ b/src/views/ideas/l10n.json @@ -27,8 +27,8 @@ "ideas.tryTheTutorial": "Try the tutorial", "ideas.codingCards": "Coding Cards", "ideas.educatorGuide": "Educator Guide", - "ideas.desktopEditorHeader": "Scratch Desktop Download", - "ideas.desktopEditorBody": "To create projects without an Internet connection, you can download Scratch Desktop.", + "ideas.desktopEditorHeader": "Scratch App Download", + "ideas.desktopEditorBody": "To create projects without an Internet connection, you can download the Scratch app.", "ideas.questionsHeader": "Questions", "ideas.questionsBody": "Have more questions? See the Frequently Asked Questions or visit the Help with Scripts Forum.", diff --git a/src/views/parents/l10n.json b/src/views/parents/l10n.json index 36a530273..5086e7c2c 100644 --- a/src/views/parents/l10n.json +++ b/src/views/parents/l10n.json @@ -34,8 +34,8 @@ "parents.faqPrivacyPolicyBody": "To protect children's online privacy, we limit what we collect during the signup process, and what we make public on the website. We don't sell or rent account information to anyone. You can find out more about our {privacyPolicy} page.", "parents.faqFAQLinkText": "frequently asked questions page", "parents.faqOfflineTitle": "Is there a way to use Scratch without participating online?", - "parents.faqOfflineBody" : "The Scratch Desktop editor allows you to create Scratch projects without an internet connection. You can download {scratchDesktop} from the website. This was previously called the Scratch Offline editor.", - "parents.faqScratchDesktop": "Scratch Desktop", + "parents.faqOfflineBody" : "Yes, the Scratch app allows you to create Scratch projects without an internet connection. You can download the {scratchApp} from the Scratch website or the app store on your device.", + "parents.faqScratchApp": "Scratch app", "parents.faqOffline2LinkText": "Scratch 2.0 offline editor", "parents.faqOffline14LinkText": "Scratch 1.4 offline editor" } diff --git a/src/views/parents/parents.jsx b/src/views/parents/parents.jsx index cc640e9e0..362de0a38 100644 --- a/src/views/parents/parents.jsx +++ b/src/views/parents/parents.jsx @@ -247,10 +247,10 @@ const Landing = () => ( ) From 2b23a41a45b66006d2958fc99370a70c47e75c26 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Thu, 3 Oct 2019 12:35:36 -0400 Subject: [PATCH 38/43] several fixes to join modal responsiveness --- src/components/join-flow/join-flow-step.scss | 8 ++++++- .../join-flow/next-step-button.scss | 6 ++++++ src/components/modal/join/modal.scss | 21 ++++++++++++++++++- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/components/join-flow/join-flow-step.scss b/src/components/join-flow/join-flow-step.scss index 5ae543ca9..dee487434 100644 --- a/src/components/join-flow/join-flow-step.scss +++ b/src/components/join-flow/join-flow-step.scss @@ -42,10 +42,16 @@ margin: 0; border-top-left-radius: 1rem; border-top-right-radius: 1rem; + + /* match the small window setting for modal as a whole */ + @media #{$small}, #{$small-height} { + border-top-left-radius: 0rem; + border-top-right-radius: 0rem; + } } .join-flow-header-image { - width: 27.5rem; + width: 100%; } .join-flow-footer-message { diff --git a/src/components/join-flow/next-step-button.scss b/src/components/join-flow/next-step-button.scss index 366972f11..2747dc178 100644 --- a/src/components/join-flow/next-step-button.scss +++ b/src/components/join-flow/next-step-button.scss @@ -14,6 +14,12 @@ transition: background-color .25s ease; background-color: $ui-orange-90percent; } + + /* match the small window setting for modal as a whole */ + @media #{$small}, #{$small-height} { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + } } .next-step-title { diff --git a/src/components/modal/join/modal.scss b/src/components/modal/join/modal.scss index d6bbf845c..416cc1400 100644 --- a/src/components/modal/join/modal.scss +++ b/src/components/modal/join/modal.scss @@ -1,13 +1,32 @@ @import "../../../colors"; @import "../../../frameless"; +/* unusually for a modal, the join flow modal cares about the screen around it +being clickable, because of the standalone join view. */ .mod-join { width: 27.4375rem; + + @media #{$small} { + width: auto; + } +} + +/* enable vertical scrolling when modal showing, if page is short */ +.modal-overlay { + @media #{$small-height}, #{$medium-height} { + overflow: auto; + } +} + +.modal-content { + @media #{$small}, #{$small-height} { + height: unset; + } } /* lower the modal slightly to accomodate Scratch logo above it */ .modal-sizes { - @media #{$medium}, #{$medium-height} { + @media #{$small}, #{$small-height}, #{$medium}, #{$medium-height} { margin: 3.5rem auto; } } From 2a57da32a5fcca51066c1b0694e8de5ba7336c79 Mon Sep 17 00:00:00 2001 From: Ben Wheeler Date: Thu, 3 Oct 2019 12:39:58 -0400 Subject: [PATCH 39/43] rewrite join view scss without changing styles --- src/views/join/join.scss | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/views/join/join.scss b/src/views/join/join.scss index 9aefc03fa..673258fc8 100644 --- a/src/views/join/join.scss +++ b/src/views/join/join.scss @@ -8,21 +8,11 @@ left: calc(25% - 76px); .logo { - width: 76px; - } + width: 76px; + } } -@media #{$small} { - .join { - left: calc(50% - 38px); - } -} -@media #{$medium} { - .join { - left: calc(50% - 38px); - } -} -@media #{$intermediate} { +@media #{$small}, #{$medium}, #{$intermediate} { .join { left: calc(50% - 38px); } From b55baf40932579c9d58fee73e42c7817cc51b611 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2019 14:20:34 +0000 Subject: [PATCH 40/43] chore(package): update scratch-gui to version 0.1.0-prerelease.20191009140622 Closes #3428 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 87388352b..c05e6d588 100644 --- a/package.json +++ b/package.json @@ -126,7 +126,7 @@ "redux-mock-store": "^1.2.3", "redux-thunk": "2.0.1", "sass-loader": "6.0.6", - "scratch-gui": "0.1.0-prerelease.20191001155549", + "scratch-gui": "0.1.0-prerelease.20191009140622", "scratch-l10n": "latest", "selenium-webdriver": "3.6.0", "slick-carousel": "1.6.0", From a83b4dd6fbcd1d87b5fff203919532ff1de678a4 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2019 14:20:38 +0000 Subject: [PATCH 41/43] chore(package): update lockfile package-lock.json --- package-lock.json | 128 +++++++++++++++++++++++----------------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1fc68ecb7..19b039f44 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@babel/cli": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.6.2.tgz", - "integrity": "sha512-JDZ+T/br9pPfT2lmAMJypJDTTTHM9ePD/ED10TRjRzJVdEVy+JB3iRlhzYmTt5YkNgHvxWGlUVnLtdv6ruiDrQ==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.6.3.tgz", + "integrity": "sha512-kWKOEeuylpa781yCeA5//eEx1u3WtLZqbi2VWXLKmb3QDPb5T2f7Yk311MK7bvvjR70dluAeiu4VXXsG1WwJsw==", "dev": true, "requires": { "chokidar": "^2.1.8", @@ -19,7 +19,7 @@ "mkdirp": "^0.5.1", "output-file-sync": "^2.0.0", "slash": "^2.0.0", - "source-map": "^0.5.0" + "source-map": "^0.6.1" }, "dependencies": { "anymatch": { @@ -462,9 +462,9 @@ "dev": true }, "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } @@ -479,25 +479,25 @@ } }, "@babel/core": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.6.2.tgz", - "integrity": "sha512-l8zto/fuoZIbncm+01p8zPSDZu/VuuJhAfA7d/AbzM09WR7iVhavvfNDYCNpo1VvLk6E6xgAoP9P+/EMJHuRkQ==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.6.3.tgz", + "integrity": "sha512-QfQ5jTBgXLzJuo7Mo8bZK/ePywmgNRgk/UQykiKwEtZPiFIn8ZqE6jB+AnD1hbB1S2xQyL4//it5vuAUOVAMTw==", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.2", + "@babel/generator": "^7.6.3", "@babel/helpers": "^7.6.2", - "@babel/parser": "^7.6.2", + "@babel/parser": "^7.6.3", "@babel/template": "^7.6.0", - "@babel/traverse": "^7.6.2", - "@babel/types": "^7.6.0", + "@babel/traverse": "^7.6.3", + "@babel/types": "^7.6.3", "convert-source-map": "^1.1.0", "debug": "^4.1.0", "json5": "^2.1.0", "lodash": "^4.17.13", "resolve": "^1.3.2", "semver": "^5.4.1", - "source-map": "^0.5.0" + "source-map": "^0.6.1" }, "dependencies": { "@babel/code-frame": { @@ -510,21 +510,21 @@ } }, "@babel/generator": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.2.tgz", - "integrity": "sha512-j8iHaIW4gGPnViaIHI7e9t/Hl8qLjERI6DcV9kEpAIDJsAOrcnXqRS7t+QbhL76pwbtqP+QCQLL0z1CyVmtjjQ==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.3.tgz", + "integrity": "sha512-hLhYbAb3pHwxjlijC4AQ7mqZdcoujiNaW7izCT04CIowHK8psN0IN8QjDv0iyFtycF5FowUOTwDloIheI25aMw==", "dev": true, "requires": { - "@babel/types": "^7.6.0", + "@babel/types": "^7.6.3", "jsesc": "^2.5.1", "lodash": "^4.17.13", - "source-map": "^0.5.0" + "source-map": "^0.6.1" } }, "@babel/parser": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.2.tgz", - "integrity": "sha512-mdFqWrSPCmikBoaBYMuBulzTIKuXVPtEISFbRRVNwMWpCms/hmE2kRq0bblUHaNRKrjRlmVbx1sDHmjmRgD2Xg==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.3.tgz", + "integrity": "sha512-sUZdXlva1dt2Vw2RqbMkmfoImubO0D0gaCrNngV6Hi0DA4x3o4mlrq0tbfY0dZEUIccH8I6wQ4qgEtwcpOR6Qg==", "dev": true }, "@babel/template": { @@ -539,26 +539,26 @@ } }, "@babel/traverse": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.2.tgz", - "integrity": "sha512-8fRE76xNwNttVEF2TwxJDGBLWthUkHWSldmfuBzVRmEDWOtu4XdINTgN7TDWzuLg4bbeIMLvfMFD9we5YcWkRQ==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.3.tgz", + "integrity": "sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw==", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.2", + "@babel/generator": "^7.6.3", "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.2", - "@babel/types": "^7.6.0", + "@babel/parser": "^7.6.3", + "@babel/types": "^7.6.3", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" } }, "@babel/types": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.1.tgz", - "integrity": "sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz", + "integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -588,9 +588,9 @@ "dev": true }, "json5": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", - "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", + "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", "dev": true, "requires": { "minimist": "^1.2.0" @@ -609,9 +609,9 @@ "dev": true }, "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, "to-fast-properties": { @@ -696,21 +696,21 @@ }, "dependencies": { "@babel/generator": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.2.tgz", - "integrity": "sha512-j8iHaIW4gGPnViaIHI7e9t/Hl8qLjERI6DcV9kEpAIDJsAOrcnXqRS7t+QbhL76pwbtqP+QCQLL0z1CyVmtjjQ==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.3.tgz", + "integrity": "sha512-hLhYbAb3pHwxjlijC4AQ7mqZdcoujiNaW7izCT04CIowHK8psN0IN8QjDv0iyFtycF5FowUOTwDloIheI25aMw==", "dev": true, "requires": { - "@babel/types": "^7.6.0", + "@babel/types": "^7.6.3", "jsesc": "^2.5.1", "lodash": "^4.17.13", - "source-map": "^0.5.0" + "source-map": "^0.6.1" } }, "@babel/parser": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.2.tgz", - "integrity": "sha512-mdFqWrSPCmikBoaBYMuBulzTIKuXVPtEISFbRRVNwMWpCms/hmE2kRq0bblUHaNRKrjRlmVbx1sDHmjmRgD2Xg==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.3.tgz", + "integrity": "sha512-sUZdXlva1dt2Vw2RqbMkmfoImubO0D0gaCrNngV6Hi0DA4x3o4mlrq0tbfY0dZEUIccH8I6wQ4qgEtwcpOR6Qg==", "dev": true }, "@babel/template": { @@ -725,17 +725,17 @@ } }, "@babel/traverse": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.2.tgz", - "integrity": "sha512-8fRE76xNwNttVEF2TwxJDGBLWthUkHWSldmfuBzVRmEDWOtu4XdINTgN7TDWzuLg4bbeIMLvfMFD9we5YcWkRQ==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.3.tgz", + "integrity": "sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw==", "dev": true, "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.2", + "@babel/generator": "^7.6.3", "@babel/helper-function-name": "^7.1.0", "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.2", - "@babel/types": "^7.6.0", + "@babel/parser": "^7.6.3", + "@babel/types": "^7.6.3", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" @@ -753,9 +753,9 @@ } }, "@babel/types": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.1.tgz", - "integrity": "sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz", + "integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -791,9 +791,9 @@ "dev": true }, "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, "to-fast-properties": { @@ -15560,15 +15560,15 @@ } }, "scratch-gui": { - "version": "0.1.0-prerelease.20191001155549", - "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20191001155549.tgz", - "integrity": "sha512-Y0A1+/EQGWUAJZN8fi0oGk8MgM9X5hXA0SwgbxOuWMT/lppu44Q3BXPYkJGVY3dOKsXCSTgwgm6DGG0Xj3cvtw==", + "version": "0.1.0-prerelease.20191009140622", + "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20191009140622.tgz", + "integrity": "sha512-ot8UYhjTxNu/RO1oEaPLHIEwczEDNSakuCMjR/a3ZIDdL3agqPR9rJH/I1NFlErcWy2i5SzbrwrABXybp23LFg==", "dev": true }, "scratch-l10n": { - "version": "3.5.20190924223720", - "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.5.20190924223720.tgz", - "integrity": "sha512-5bNisTEe9WJI+w3TQQ7XVutoFZuBKtHLLfijFd2vOkLv93XoxEQvpW62uz34uyj2tX9N8JPzCB+KaJalC+Z8Mg==", + "version": "3.6.20191008224547", + "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.6.20191008224547.tgz", + "integrity": "sha512-J6AgB6oB+/ibamUNeyTdtSvkPzJchiD50xoUqzzlcPOv+AWOFSTOakGPzJnGE6/WeZ0njBThvy1rh23lamqiow==", "dev": true, "requires": { "@babel/cli": "^7.1.2", From f023de809f9df7a719d6480e56f4d61781c69ba9 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2019 19:53:48 +0000 Subject: [PATCH 42/43] chore(package): update scratch-gui to version 0.1.0-prerelease.20191009193408 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c05e6d588..7f103e58a 100644 --- a/package.json +++ b/package.json @@ -126,7 +126,7 @@ "redux-mock-store": "^1.2.3", "redux-thunk": "2.0.1", "sass-loader": "6.0.6", - "scratch-gui": "0.1.0-prerelease.20191009140622", + "scratch-gui": "0.1.0-prerelease.20191009193408", "scratch-l10n": "latest", "selenium-webdriver": "3.6.0", "slick-carousel": "1.6.0", From d02510150c63e02ae8d2b57732d23128cd3f3f6a Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2019 19:53:52 +0000 Subject: [PATCH 43/43] chore(package): update lockfile package-lock.json --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 19b039f44..8d3eee170 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15560,9 +15560,9 @@ } }, "scratch-gui": { - "version": "0.1.0-prerelease.20191009140622", - "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20191009140622.tgz", - "integrity": "sha512-ot8UYhjTxNu/RO1oEaPLHIEwczEDNSakuCMjR/a3ZIDdL3agqPR9rJH/I1NFlErcWy2i5SzbrwrABXybp23LFg==", + "version": "0.1.0-prerelease.20191009193408", + "resolved": "https://registry.npmjs.org/scratch-gui/-/scratch-gui-0.1.0-prerelease.20191009193408.tgz", + "integrity": "sha512-FSYIjelP9NX7zrGah0od+UTszdbQcBHqyW2zTHE42hBwvg+nj+Q/p7IEIsAEG6hh7B++TXugzL5bKNmVZnjycQ==", "dev": true }, "scratch-l10n": {