Merge branch 'develop' of https://github.com/LLK/scratch-gui into develop

This commit is contained in:
BrightLin committed 2020-03-12 14:46:29 +08:00
commit aceeeba9ec
15 files changed
+15757 -11865

No files matched your search

+58 -1
View File
@@ -18,6 +18,8 @@ cd scratch-gui
npm install
```
**You may want to add `--depth=1` to the `git clone` command because there are some [large files in the git repository history](https://github.com/LLK/scratch-gui/issues/5140).**
## Getting started
Running the project requires Node.js to be installed.
@@ -74,7 +76,7 @@ See [jest cli docs](https://facebook.github.io/jest/docs/en/cli.html#content) fo
*NOTE: If you're a windows user, please run these scripts in Windows `cmd.exe` instead of Git Bash/MINGW64.*
Before running any test, make sure you have run `npm install` from this (scratch-gui) repository's top level.
Before running any tests, make sure you have run `npm install` from this (scratch-gui) repository's top level.
#### Main testing command
@@ -194,5 +196,60 @@ Further reading: [Stack Overflow](https://stackoverflow.com/questions/46602286/n
You can publish the GUI to github.io so that others on the Internet can view it.
[Read the wiki for a step-by-step guide.](https://github.com/LLK/scratch-gui/wiki/Publishing-to-GitHub-Pages)
## Understanding the project state machine
Since so much code throughout scratch-gui depends on the state of the project, which goes through many different phases of loading, displaying and saving, we created a "finite state machine" to make it clear which state it is in at any moment. This is contained in the file src/reducers/project-state.js .
It can be hard to understand the code in src/reducers/project-state.js . There are several types of data and functions used, which relate to each other:
### Loading states
These include state constant strings like:
* `NOT_LOADED` (the default state),
* `ERROR`,
* `FETCHING_WITH_ID`,
* `LOADING_VM_WITH_ID`,
* `REMIXING`,
* `SHOWING_WITH_ID`,
* `SHOWING_WITHOUT_ID`,
* etc.
### Transitions
These are names for the action which causes a state change. Some examples are:
* `START_FETCHING_NEW`,
* `DONE_FETCHING_WITH_ID`,
* `DONE_LOADING_VM_WITH_ID`,
* `SET_PROJECT_ID`,
* `START_AUTO_UPDATING`,
### How transitions relate to loading states
As this diagram of the project state machine shows, various transition actions can move us from one loading state to another:
![Project state diagram](docs/project_state_diagram.svg)
_Note: for clarity, the diagram above excludes states and transitions relating to error handling._
#### Example
Here's an example of how states transition.
Suppose a user clicks on a project, and the page starts to load with url https://scratch.mit.edu/projects/123456 .
Here's what will happen in the project state machine:
![Project state example](docs/project_state_example.png)
1. When the app first mounts, the project state is `NOT_LOADED`.
2. The `SET_PROJECT_ID` redux action is dispatched (from src/lib/project-fetcher-hoc.jsx), with `projectId` set to `123456`. This transitions the state from `NOT_LOADED` to `FETCHING_WITH_ID`.
3. The `FETCHING_WITH_ID` state. In src/lib/project-fetcher-hoc.jsx, the `projectId` value `123456` is used to request the data for that project from the server.
4. When the server responds with the data, src/lib/project-fetcher-hoc.jsx dispatches the `DONE_FETCHING_WITH_ID` action, with `projectData` set. This transitions the state from `FETCHING_WITH_ID` to `LOADING_VM_WITH_ID`.
5. The `LOADING_VM_WITH_ID` state. In src/lib/vm-manager-hoc.jsx, we load the `projectData` into Scratch's virtual machine ("the vm").
6. When loading is done, src/lib/vm-manager-hoc.jsx dispatches the `DONE_LOADING_VM_WITH_ID` action. This transitions the state from `LOADING_VM_WITH_ID` to `SHOWING_WITH_ID`
7. The `SHOWING_WITH_ID` state. Now the project appears normally and is playable and editable.
## Donate
We provide [Scratch](https://scratch.mit.edu) free of charge, and want to keep it that way! Please consider making a [donation](https://secure.donationpay.org/scratchfoundation/) to support our continued engineering, design, community, and resource development efforts. Donations of any size are appreciated. Thank you!
+3
View File
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1381px" height="1012px" viewBox="-0.5 -0.5 1381 1012" content="&lt;mxfile host=&quot;www.draw.io&quot; modified=&quot;2020-02-04T16:31:44.889Z&quot; agent=&quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36&quot; etag=&quot;8a2X5Jj65jOGG04xBBh1&quot; version=&quot;12.6.4&quot; type=&quot;device&quot;&gt;&lt;diagram name=&quot;Page-1&quot; id=&quot;58cdce13-f638-feb5-8d6f-7d28b1aa9fa0&quot;&gt;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 truncated

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

+333 -370
View File
File diff suppressed because it is too large. Load diff
+9 -9
View File
@@ -6,7 +6,7 @@
"scripts": {
"build": "npm run clean && webpack --progress --colors --bail",
"clean": "rimraf ./build && mkdirp build && rimraf ./dist && mkdirp dist",
"deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1)\"",
"deploy": "touch build/.nojekyll && gh-pages -t -d build -m \"Build for $(git log --pretty=format:%H -n1) [skip ci]\"",
"prune": "./prune-gh-pages.sh",
"i18n:push": "tx-push-src scratch-editor interface translations/en.json",
"i18n:src": "rimraf ./translations/messages/src && babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/src ./translations/ && npm run i18n:push",
@@ -44,7 +44,7 @@
"babel-loader": "^8.0.4",
"base64-loader": "1.0.0",
"bowser": "1.9.4",
"chromedriver": "78.0.1",
"chromedriver": "80.0.0",
"classnames": "2.2.6",
"computed-style-to-inline-style": "3.0.0",
"copy-webpack-plugin": "^4.5.1",
@@ -78,7 +78,7 @@
"lodash.pick": "4.4.0",
"lodash.throttle": "4.0.1",
"minilog": "3.1.0",
"mkdirp": "^0.5.1",
"mkdirp": "^1.0.3",
"omggif": "1.0.9",
"papaparse": "5.1.1",
"postcss-import": "^12.0.0",
@@ -108,13 +108,13 @@
"redux-throttle": "0.1.1",
"rimraf": "^2.6.1",
"scratch-audio": "0.1.0-prerelease.20190925183642",
"scratch-l10n": "3.7.20200108141814",
"scratch-blocks": "0.1.0-prerelease.1578322100",
"scratch-paint": "0.2.0-prerelease.20200109073728",
"scratch-render": "0.1.0-prerelease.20200113193920",
"scratch-blocks": "0.1.0-prerelease.1583868812",
"scratch-l10n": "3.7.20200310213125",
"scratch-paint": "0.2.0-prerelease.20200213174123",
"scratch-render": "0.1.0-prerelease.20200228152431",
"scratch-storage": "1.3.2",
"scratch-svg-renderer": "0.2.0-prerelease.20200109070519",
"scratch-vm": "0.2.0-prerelease.20191227164934",
"scratch-svg-renderer": "0.2.0-prerelease.20200205003400",
"scratch-vm": "0.2.0-prerelease.20200310202129",
"selenium-webdriver": "3.6.0",
"startaudiocontext": "1.2.1",
"style-loader": "^0.23.0",
+16 -12
View File
@@ -54,13 +54,10 @@ const cloudManagerHOC = function (WrappedComponent) {
canUseCloud (props) {
return !!(props.cloudHost && props.username && props.vm && props.projectId && props.hasCloudPermission);
}
shouldNotModifyCloudData (props) {
return (props.hasEverEnteredEditor && !props.canSave);
}
shouldConnect (props) {
return !this.isConnected() && this.canUseCloud(props) &&
props.isShowingWithId && props.vm.runtime.hasCloudData() &&
!this.shouldNotModifyCloudData(props);
props.canModifyCloudData;
}
shouldDisconnect (props, prevProps) {
return this.isConnected() &&
@@ -70,7 +67,7 @@ const cloudManagerHOC = function (WrappedComponent) {
(props.projectId !== prevProps.projectId) ||
(props.username !== prevProps.username) ||
// Editing someone else's project
this.shouldNotModifyCloudData(props)
!props.canModifyCloudData
);
}
isConnected () {
@@ -102,11 +99,11 @@ const cloudManagerHOC = function (WrappedComponent) {
render () {
const {
/* eslint-disable no-unused-vars */
canModifyCloudData,
cloudHost,
projectId,
username,
hasCloudPermission,
hasEverEnteredEditor,
isShowingWithId,
onShowCloudInfo,
/* eslint-enable no-unused-vars */
@@ -124,23 +121,30 @@ const cloudManagerHOC = function (WrappedComponent) {
}
CloudManager.propTypes = {
canSave: PropTypes.bool.isRequired,
canModifyCloudData: PropTypes.bool.isRequired,
cloudHost: PropTypes.string,
hasCloudPermission: PropTypes.bool,
hasEverEnteredEditor: PropTypes.bool,
isShowingWithId: PropTypes.bool,
isShowingWithId: PropTypes.bool.isRequired,
onShowCloudInfo: PropTypes.func,
projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
username: PropTypes.string,
vm: PropTypes.instanceOf(VM).isRequired
};
const mapStateToProps = state => {
CloudManager.defaultProps = {
cloudHost: null,
hasCloudPermission: false,
onShowCloudInfo: () => {},
username: null
};
const mapStateToProps = (state, ownProps) => {
const loadingState = state.scratchGui.projectState.loadingState;
return {
hasEverEnteredEditor: state.scratchGui.mode.hasEverEnteredEditor,
isShowingWithId: getIsShowingWithId(loadingState),
projectId: state.scratchGui.projectState.projectId
projectId: state.scratchGui.projectState.projectId,
// if you're editing someone else's project, you can't modify cloud data
canModifyCloudData: (!state.scratchGui.mode.hasEverEnteredEditor || ownProps.canSave)
};
};
+6 -6
View File
@@ -178,7 +178,7 @@
},
{
"name": "Blue Sky 2 ",
"md5": "5a906c2f272b77f59f0ef207857a8b3a.svg",
"md5": "8eb8790be5507fdccf73e7c1570bbbab.svg",
"type": "backdrop",
"tags": [
"outdoors",
@@ -286,7 +286,7 @@
},
{
"name": "Circles",
"md5": "4e29033ec2b891a8f1ca21242811d403.svg",
"md5": "c9847be305920807c5597d81576dd0c4.svg",
"type": "backdrop",
"tags": [
"patterns"
@@ -491,7 +491,7 @@
},
{
"name": "Hearts",
"md5": "26d3418b2fbc1af2c5fea82e1c3df1db.svg",
"md5": "f98526ccb0eec3ac7d6c8f8ab502825e.svg",
"type": "backdrop",
"tags": [
"patterns"
@@ -1050,7 +1050,7 @@
},
{
"name": "Underwater 1",
"md5": "fb907f72b310acc8b95cbf2d2cccabc9.svg",
"md5": "d3344650f594bcecdf46aa4a9441badd.svg",
"type": "backdrop",
"tags": [
"ocean",
@@ -1175,7 +1175,7 @@
},
{
"name": "Witch House",
"md5": "597b9a9813fe5d8d387283138a0b8f2b.svg",
"md5": "30085b2d27beb5acdbe895d8b3e64b04.svg",
"type": "backdrop",
"tags": [
"fantasy",
@@ -1190,7 +1190,7 @@
},
{
"name": "Woods",
"md5": "d26cfd278999c9e37d6af75c296a58df.svg",
"md5": "f3eb165d6f3fd23370f97079f2e631bf.svg",
"type": "backdrop",
"tags": [
"fantasy",
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+13973 -10065
View File
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -4,8 +4,8 @@ const randomizeSpritePosition = spriteObject => {
const randomY = Math.floor((100 * Math.random()) - 50);
if (spriteObject.hasOwnProperty('json')) {
// Library sprite object
spriteObject.json.scratchX = randomX;
spriteObject.json.scratchY = randomY;
spriteObject.json.x = randomX;
spriteObject.json.y = randomY;
} else if (spriteObject.hasOwnProperty('x') && spriteObject.hasOwnProperty('y')) {
// Scratch 3 sprite object
spriteObject.x = randomX;
+3 -1
View File
@@ -26,7 +26,9 @@ const supportedBrowser = () => {
* always returns false
*/
const recommendedBrowser = () => !bowser.isUnsupportedBrowser(minVersions, true);
const recommendedBrowser = () =>
!bowser.isUnsupportedBrowser(minVersions, true) ||
window.navigator.userAgent.toLowerCase().indexOf('googlebot') !== -1;
export {
supportedBrowser as default,
+16 -1
View File
@@ -9,6 +9,7 @@ const {
findByXpath,
getDriver,
getLogs,
Key,
loadUri,
rightClickText,
scope
@@ -185,6 +186,10 @@ describe('Working with the blocks', () => {
await clickText('costume2', scope.costumesTab);
const el = await findByXpath("//input[@value='costume2']");
await el.sendKeys('newname');
await el.sendKeys(Key.ENTER);
// wait until the updated costume appears in costume item list panel
await findByXpath("//div[contains(@class,'sprite-selector-item_is-selected_')]" +
"//div[contains(text(), 'newname')]");
// Make sure it is updated in the block menu
await clickText('Code');
@@ -201,6 +206,10 @@ describe('Working with the blocks', () => {
await clickText('costume2', scope.costumesTab);
const el = await findByXpath("//input[@value='costume2']");
await el.sendKeys('<NewCostume>');
await el.sendKeys(Key.ENTER);
// wait until the updated costume appears in costume item list panel
await findByXpath("//div[contains(@class,'sprite-selector-item_is-selected_')]" +
"//div[contains(text(), '<NewCostume>')]");
// Make sure it is updated in the block menu
await clickText('Code');
@@ -226,6 +235,9 @@ describe('Working with the blocks', () => {
.perform();
await driver.sleep(500); // Wait for thermometer menu to come up
await clickXpath('//button[@aria-label="Paint"]');
// wait until the new costume appears in costume item list panel
await findByXpath("//div[contains(@class,'sprite-selector-item_is-selected_')]" +
"//div[contains(text(), 'costume3')]");
await clickText('costume3', scope.costumesTab);
// Check that the menu has been updated
await clickText('Code');
@@ -233,11 +245,14 @@ describe('Working with the blocks', () => {
});
// Skipped because it was flakey on travis, but seems to run locally ok
test.skip('Adding a sound DOES update the default sound name in the toolbox', async () => {
test('Adding a sound DOES update the default sound name in the toolbox', async () => {
await loadUri(uri);
await clickText('Sounds');
await clickXpath('//button[@aria-label="Choose a Sound"]');
await clickText('A Bass', scope.modal); // Should close the modal
// wait until the selected sound appears in sounds item list panel
await findByXpath("//div[contains(@class,'sprite-selector-item_is-selected_')]" +
"//div[contains(text(), 'A Bass')]");
await clickText('Code');
await clickText('Sound', scope.blocksTab);
await driver.sleep(500); // Wait for scroll to finish
+1 -1
View File
@@ -193,7 +193,7 @@ describe('Working with costumes', () => {
.mouseMove(abbyElement)
.perform();
// wait for one of Abby's alternate costumes to appear
await findByXpath('//img[@src="https://cdn.assets.scratch.mit.edu/internalapi/asset/b6e23922f23b49ddc6f62f675e77417c.svg/get/"]');
await findByXpath('//img[@src="https://cdn.assets.scratch.mit.edu/internalapi/asset/45de34b47a2ce22f6f5d28bb35a44ff5.svg/get/"]');
const logs = await getLogs();
await expect(logs).toEqual([]);
});
+2 -43
View File
@@ -60,7 +60,6 @@ describe('CloudManagerHOC', () => {
mount(
<WrappedComponent
canSave
hasCloudPermission
cloudHost="nonEmpty"
store={store}
@@ -80,7 +79,6 @@ describe('CloudManagerHOC', () => {
const WrappedComponent = cloudManagerHOC(Component);
mount(
<WrappedComponent
canSave
hasCloudPermission
store={store}
username="user"
@@ -98,7 +96,6 @@ describe('CloudManagerHOC', () => {
const WrappedComponent = cloudManagerHOC(Component);
mount(
<WrappedComponent
canSave
hasCloudPermission
cloudHost="nonEmpty"
store={store}
@@ -115,7 +112,6 @@ describe('CloudManagerHOC', () => {
const WrappedComponent = cloudManagerHOC(Component);
mount(
<WrappedComponent
canSave
hasCloudPermission
cloudHost="nonEmpty"
store={stillLoadingStore}
@@ -132,7 +128,6 @@ describe('CloudManagerHOC', () => {
const WrappedComponent = cloudManagerHOC(Component);
mount(
<WrappedComponent
canSave
cloudHost="nonEmpty"
hasCloudPermission={false}
store={store}
@@ -153,7 +148,6 @@ describe('CloudManagerHOC', () => {
const mounted = mount(
<WrappedComponent
canSave
hasCloudPermission
cloudHost="nonEmpty"
store={stillLoadingStore}
@@ -182,7 +176,6 @@ describe('CloudManagerHOC', () => {
const WrappedComponent = cloudManagerHOC(Component);
const mounted = mount(
<WrappedComponent
canSave
hasCloudPermission
cloudHost="nonEmpty"
store={stillLoadingStore}
@@ -209,7 +202,6 @@ describe('CloudManagerHOC', () => {
const WrappedComponent = cloudManagerHOC(Component);
const mounted = mount(
<WrappedComponent
canSave
hasCloudPermission
cloudHost="nonEmpty"
store={store}
@@ -235,7 +227,6 @@ describe('CloudManagerHOC', () => {
const WrappedComponent = cloudManagerHOC(Component);
const mounted = mount(
<WrappedComponent
canSave
hasCloudPermission
cloudHost="nonEmpty"
store={store}
@@ -262,7 +253,6 @@ describe('CloudManagerHOC', () => {
const WrappedComponent = cloudManagerHOC(Component);
const mounted = mount(
<WrappedComponent
canSave
hasCloudPermission
cloudHost="nonEmpty"
store={store}
@@ -293,7 +283,6 @@ describe('CloudManagerHOC', () => {
const WrappedComponent = cloudManagerHOC(Component);
mount(
<WrappedComponent
canSave
hasCloudPermission
cloudHost="nonEmpty"
store={store}
@@ -315,7 +304,6 @@ describe('CloudManagerHOC', () => {
const WrappedComponent = cloudManagerHOC(Component);
mount(
<WrappedComponent
canSave
hasCloudPermission
cloudHost="nonEmpty"
store={store}
@@ -343,7 +331,6 @@ describe('CloudManagerHOC', () => {
const WrappedComponent = cloudManagerHOC(Component);
mount(
<WrappedComponent
canSave
hasCloudPermission
cloudHost="nonEmpty"
store={store}
@@ -364,12 +351,11 @@ describe('CloudManagerHOC', () => {
});
// Editor Mode Connection/Disconnection Tests
test('Entering editor mode and can\'t save project should disconnect cloud provider # 1', () => {
test('Entering editor mode and can\'t save project should disconnect cloud provider', () => {
const Component = () => <div />;
const WrappedComponent = cloudManagerHOC(Component);
const mounted = mount(
<WrappedComponent
canSave
hasCloudPermission
cloudHost="nonEmpty"
store={store}
@@ -382,34 +368,7 @@ describe('CloudManagerHOC', () => {
const requestCloseConnection = mockCloudProviderInstance.requestCloseConnection;
mounted.setProps({
canSave: false,
hasEverEnteredEditor: true
});
expect(vm.setCloudProvider.mock.calls.length).toBe(2);
expect(vm.setCloudProvider).toHaveBeenCalledWith(null);
expect(requestCloseConnection).toHaveBeenCalledTimes(1);
});
test('Entering editor mode and can\'t save project should disconnect cloud provider # 2', () => {
const Component = () => <div />;
const WrappedComponent = cloudManagerHOC(Component);
const mounted = mount(
<WrappedComponent
hasCloudPermission
canSave={false}
cloudHost="nonEmpty"
store={store}
username="user"
vm={vm}
/>
);
expect(CloudProvider).toHaveBeenCalled();
const requestCloseConnection = mockCloudProviderInstance.requestCloseConnection;
mounted.setProps({
hasEverEnteredEditor: true
canModifyCloudData: false
});
expect(vm.setCloudProvider.mock.calls.length).toBe(2);