Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d390e67542 | ||
|
|
a8b7be6197 | ||
|
|
35da6ace36 | ||
|
|
8590ceb4ce | ||
|
|
96e637cfc0 | ||
|
|
f1470feb0a | ||
|
|
abe79b30ff | ||
|
|
5d1b1fb4c0 | ||
|
|
9265ee6323 | ||
|
|
519256c50c | ||
|
|
17324e5340 | ||
|
|
7d01844371 | ||
|
|
bc1b787974 | ||
|
|
3d1ad740dd | ||
|
|
4c76f654b7 | ||
|
|
bd835adcd4 | ||
|
|
78e4cfce68 | ||
|
|
deb5889a2c | ||
|
|
2030ad54bc | ||
|
|
a95604008d | ||
|
|
bb21288cff | ||
|
|
f31977489f | ||
|
|
f179587c2e | ||
|
|
fa3d9c9e1e | ||
|
|
459eb2b767 | ||
|
|
4e1c6afe02 | ||
|
|
dc69ccea64 | ||
|
|
03d9f8f2c6 | ||
|
|
5d006cf677 | ||
|
|
6c9657e453 | ||
|
|
e82ec50e7b | ||
|
|
09ccbb4c01 | ||
|
|
e3b2ab22a6 | ||
|
|
a2bc04e5d4 | ||
|
|
a4c81e55c0 | ||
|
|
2b571d9d83 | ||
|
|
6caaaf6f36 | ||
|
|
2b9550f3d9 | ||
|
|
94bb677fd8 | ||
|
|
5b6f33e1a4 | ||
|
|
32723a8876 | ||
|
|
a1fc068dd1 | ||
|
|
225fc772f6 | ||
|
|
2a167ba775 | ||
|
|
193717a1a9 | ||
|
|
4fc140ec2c | ||
|
|
2ffac96579 | ||
|
|
bc57adc12c | ||
|
|
dc64aa92fd | ||
|
|
8b5ff14ac9 | ||
|
|
6641c4696b | ||
|
|
a680d30930 | ||
|
|
1dce71e787 | ||
|
|
1451416e6b | ||
|
|
f4f20203f4 | ||
|
|
2df0c80b30 | ||
|
|
6414985461 | ||
|
|
230debb911 | ||
|
|
b42291d10b | ||
|
|
f9ea1088e8 | ||
|
|
bb837f57e2 | ||
|
|
059d3f709b | ||
|
|
26ca5da725 | ||
|
|
b74f19e6e6 | ||
|
|
7b9aed73f3 | ||
|
|
280f139ba2 | ||
|
|
def5cdc450 | ||
|
|
79fe377f19 | ||
|
|
b2dd3ed421 |
No files matched your search
+5
-2
@@ -1,16 +1,18 @@
|
||||
language: node_js
|
||||
sudo: required
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
addons:
|
||||
chrome: stable
|
||||
node_js:
|
||||
- 8
|
||||
env:
|
||||
global:
|
||||
- CHROMEDRIVER_VERSION=LATEST
|
||||
- NODE_ENV=production
|
||||
- SMOKE_URL=https://llk.github.io/scratch-gui/$TRAVIS_PULL_REQUEST_BRANCH
|
||||
- NODE_OPTIONS=--max-old-space-size=7250
|
||||
- NPM_TAG=latest
|
||||
- RELEASE_VERSION="0.1.0-prerelease.$(date +'%Y%m%d%H%M%S')"
|
||||
- SMOKE_URL=https://llk.github.io/scratch-gui/$TRAVIS_PULL_REQUEST_BRANCH
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
@@ -74,6 +76,7 @@ deploy:
|
||||
on:
|
||||
all_branches: true
|
||||
condition: $TRAVIS_EVENT_TYPE != cron && ! $TRAVIS_BRANCH =~ ^greenkeeper/
|
||||
tags: false # Don't push tags to gh-pages
|
||||
skip_cleanup: true
|
||||
script: npm run deploy -- -x -e $TRAVIS_BRANCH -r https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git
|
||||
- provider: script
|
||||
|
||||
Generated
+24
-24
@@ -928,9 +928,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/babel__core": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.2.tgz",
|
||||
"integrity": "sha512-cfCCrFmiGY/yq0NuKNxIQvZFy9kY/1immpSpTngOnyIbD4+eJOG5mxphhHDv3CHL9GltO4GcKr54kGBg3RNdbg==",
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.3.tgz",
|
||||
"integrity": "sha512-8fBo0UR2CcwWxeX7WIIgJ7lXjasFxoYgRnFHUj+hRvKkpiBJbxhdAPTCY6/ZKM0uxANFVzt4yObSLuTiTnazDA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/parser": "^7.1.0",
|
||||
@@ -941,9 +941,9 @@
|
||||
}
|
||||
},
|
||||
"@types/babel__generator": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.0.2.tgz",
|
||||
"integrity": "sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ==",
|
||||
"version": "7.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.0.tgz",
|
||||
"integrity": "sha512-c1mZUu4up5cp9KROs/QAw0gTeHrw/x7m52LcnvMxxOZ03DmLwPV0MlGmlgzV3cnSdjhJOZsj7E7FHeioai+egw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.0.0"
|
||||
@@ -2664,9 +2664,9 @@
|
||||
}
|
||||
},
|
||||
"chromedriver": {
|
||||
"version": "76.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-76.0.0.tgz",
|
||||
"integrity": "sha512-jGyqs0N+lMo9iaNQxGKNPiLJWb2L9s2rwbRr1jJeQ37n6JQ1+5YMGviv/Fx5Z08vBWYbAvrKEzFsuYf8ppl+lw==",
|
||||
"version": "77.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-77.0.0.tgz",
|
||||
"integrity": "sha512-mZa1IVx4HD8rDaItWbnS470mmypgiWsDiu98r0NkiT4uLm3qrANl4vOU6no6vtWtLQiW5kt1POcIbjeNpsLbXA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"del": "^4.1.1",
|
||||
@@ -11339,9 +11339,9 @@
|
||||
}
|
||||
},
|
||||
"scratch-blocks": {
|
||||
"version": "0.1.0-prerelease.1568071968",
|
||||
"resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.1568071968.tgz",
|
||||
"integrity": "sha512-oXDeEOBXadE8aEfmWa6kgwhVMN9AZMqIy54sCuNArktyfrZdZgVQr+muhEuPwiDF1vssGbVjX5Yo4uYrMJf9Mg==",
|
||||
"version": "0.1.0-prerelease.1569422127",
|
||||
"resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.1569422127.tgz",
|
||||
"integrity": "sha512-flOmjUPWVNQSPTCd4LE8xbPd1X/MePBgWNrWllevn0WilJNWMVbX6Bs3gtRIZM8rvEuKaUzcLutwGdtws9XFQQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"exports-loader": "0.6.3",
|
||||
@@ -11349,9 +11349,9 @@
|
||||
}
|
||||
},
|
||||
"scratch-l10n": {
|
||||
"version": "3.5.20190827223456",
|
||||
"resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.5.20190827223456.tgz",
|
||||
"integrity": "sha512-Zsl9lrNW69omsxGRgwMN0i54SxPI/yJqWi1GubQw8qu2plkFHCC0ngoyaRkTxoYpHEJYSB6s8T8v6VrcEI1Y0A==",
|
||||
"version": "3.5.20190924223720",
|
||||
"resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.5.20190924223720.tgz",
|
||||
"integrity": "sha512-5bNisTEe9WJI+w3TQQ7XVutoFZuBKtHLLfijFd2vOkLv93XoxEQvpW62uz34uyj2tX9N8JPzCB+KaJalC+Z8Mg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/cli": "^7.1.2",
|
||||
@@ -11361,9 +11361,9 @@
|
||||
}
|
||||
},
|
||||
"scratch-paint": {
|
||||
"version": "0.2.0-prerelease.20190822205009",
|
||||
"resolved": "https://registry.npmjs.org/scratch-paint/-/scratch-paint-0.2.0-prerelease.20190822205009.tgz",
|
||||
"integrity": "sha512-kkeEegtHCP9tH1YjQyY116sYWhUNlRULsDoGkGkesNgJGO2CU2rQblMnHk+F5nV2actdMdqjTXihjpZtySBaxg==",
|
||||
"version": "0.2.0-prerelease.20190923154429",
|
||||
"resolved": "https://registry.npmjs.org/scratch-paint/-/scratch-paint-0.2.0-prerelease.20190923154429.tgz",
|
||||
"integrity": "sha512-vuHulFufVR9MZLTC015Z9/ybcgS/73Wrj4V7rt7zMquFg1YKDY5MyGm45cQtDx93+I6CRc12J4mtU8cZ0uTGaw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@scratch/paper": "0.11.20190729152410",
|
||||
@@ -11476,9 +11476,9 @@
|
||||
}
|
||||
},
|
||||
"scratch-render": {
|
||||
"version": "0.1.0-prerelease.20190904212449",
|
||||
"resolved": "https://registry.npmjs.org/scratch-render/-/scratch-render-0.1.0-prerelease.20190904212449.tgz",
|
||||
"integrity": "sha512-AfSBYbEvG3E44pSiBniU6Lzc7YhVemc1uJLUkBrU8KvWPvCx5dRfAiASSnjHNaDZV8Vv4QRpV3i/fepjI6Eofw==",
|
||||
"version": "0.1.0-prerelease.20190912174749",
|
||||
"resolved": "https://registry.npmjs.org/scratch-render/-/scratch-render-0.1.0-prerelease.20190912174749.tgz",
|
||||
"integrity": "sha512-GHXtprwGBFZld5wnGqw2sfySbxFZfKuQfkcEyqOZlYPgf7uQ9FEJ1MEoFhX9T6le0C9XA9f/fvh50MEWc1R3TA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"grapheme-breaker": "0.3.2",
|
||||
@@ -11563,9 +11563,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"scratch-vm": {
|
||||
"version": "0.2.0-prerelease.20190822194548",
|
||||
"resolved": "https://registry.npmjs.org/scratch-vm/-/scratch-vm-0.2.0-prerelease.20190822194548.tgz",
|
||||
"integrity": "sha512-FYzGttFAoU4WnZybidzElRPY5WMLkHdzc0ANd5ALq0IhcevAy2eKJh9SCuyN9v7wsPgDAr4Q71sCgOWV1Jwy0w==",
|
||||
"version": "0.2.0-prerelease.20190918022946",
|
||||
"resolved": "https://registry.npmjs.org/scratch-vm/-/scratch-vm-0.2.0-prerelease.20190918022946.tgz",
|
||||
"integrity": "sha512-wkKrnhIMpv9xfUH3SSruEDE7PD+Vd8cX9VKRp34POshoYGYKCK5/sGTuRi35zMEWAwxTKtxWs4B6HzH7ySBf/Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@vernier/godirect": "1.5.0",
|
||||
|
||||
+6
-6
@@ -44,7 +44,7 @@
|
||||
"babel-loader": "^8.0.4",
|
||||
"base64-loader": "1.0.0",
|
||||
"bowser": "1.9.4",
|
||||
"chromedriver": "76.0.0",
|
||||
"chromedriver": "77.0.0",
|
||||
"classnames": "2.2.6",
|
||||
"computed-style-to-inline-style": "3.0.0",
|
||||
"copy-webpack-plugin": "^4.5.1",
|
||||
@@ -107,13 +107,13 @@
|
||||
"redux-throttle": "0.1.1",
|
||||
"rimraf": "^2.6.1",
|
||||
"scratch-audio": "0.1.0-prerelease.20190114210212",
|
||||
"scratch-l10n": "3.5.20190827223456",
|
||||
"scratch-blocks": "0.1.0-prerelease.1568071968",
|
||||
"scratch-paint": "0.2.0-prerelease.20190822205009",
|
||||
"scratch-render": "0.1.0-prerelease.20190904212449",
|
||||
"scratch-l10n": "3.5.20190924223720",
|
||||
"scratch-blocks": "0.1.0-prerelease.1569422127",
|
||||
"scratch-paint": "0.2.0-prerelease.20190923154429",
|
||||
"scratch-render": "0.1.0-prerelease.20190912174749",
|
||||
"scratch-storage": "1.3.2",
|
||||
"scratch-svg-renderer": "0.2.0-prerelease.20190822202608",
|
||||
"scratch-vm": "0.2.0-prerelease.20190822194548",
|
||||
"scratch-vm": "0.2.0-prerelease.20190918022946",
|
||||
"selenium-webdriver": "3.6.0",
|
||||
"startaudiocontext": "1.2.1",
|
||||
"style-loader": "^0.23.0",
|
||||
|
||||
@@ -50,8 +50,16 @@ $fade-out-distance: 100px;
|
||||
overflow-y: scroll;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.list-area:after {
|
||||
/* Make sure there is room to scroll beyond the last tile */
|
||||
padding-bottom: 70px;
|
||||
content: '';
|
||||
display: block;
|
||||
height: 70px;
|
||||
width: 100%;
|
||||
flex-shrink: 0;
|
||||
order: 99999999;
|
||||
}
|
||||
|
||||
.list-item {
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
border: 1px solid $ui-black-transparent;
|
||||
border-radius: 5px;
|
||||
padding: 3px;
|
||||
|
||||
/* Force these to be on their own render layer because they update often */
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
.meter-container {
|
||||
margin-right: 5px;
|
||||
height: 180px;
|
||||
|
||||
@@ -265,6 +265,7 @@ const SoundEditor = props => (
|
||||
onClick={props.onSlower}
|
||||
/>
|
||||
<IconButton
|
||||
disabled={props.tooLoud}
|
||||
className={classNames(styles.effectButton, styles.flipInRtl)}
|
||||
img={louderIcon}
|
||||
title={<FormattedMessage {...messages.louder} />}
|
||||
@@ -340,6 +341,7 @@ SoundEditor.propTypes = {
|
||||
onUndo: PropTypes.func.isRequired,
|
||||
playhead: PropTypes.number,
|
||||
setRef: PropTypes.func,
|
||||
tooLoud: PropTypes.bool.isRequired,
|
||||
trimEnd: PropTypes.number,
|
||||
trimStart: PropTypes.number
|
||||
};
|
||||
|
||||
@@ -340,9 +340,9 @@ class Blocks extends React.Component {
|
||||
const targetSounds = target.getSounds();
|
||||
const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML();
|
||||
return makeToolboxXML(target.isStage, target.id, dynamicBlocksXML,
|
||||
targetCostumes[0].name,
|
||||
stageCostumes[0].name,
|
||||
targetSounds.length > 0 ? targetSounds[0].name : ''
|
||||
targetCostumes[targetCostumes.length - 1].name,
|
||||
stageCostumes[stageCostumes.length - 1].name,
|
||||
targetSounds.length > 0 ? targetSounds[targetSounds.length - 1].name : ''
|
||||
);
|
||||
} catch {
|
||||
return null;
|
||||
|
||||
@@ -44,10 +44,10 @@ class RecordingStep extends React.Component {
|
||||
alert(this.props.intl.formatMessage(messages.alertMsg)); // eslint-disable-line no-alert
|
||||
}
|
||||
handleLevelUpdate (level) {
|
||||
this.setState({level});
|
||||
if (this.props.recording) {
|
||||
this.setState({levels: (this.state.levels || []).concat([level])});
|
||||
}
|
||||
this.setState({
|
||||
level: level,
|
||||
levels: this.props.recording ? (this.state.levels || []).concat([level]) : this.state.levels
|
||||
});
|
||||
}
|
||||
handleRecord () {
|
||||
this.audioRecorder.startRecording();
|
||||
|
||||
@@ -14,6 +14,8 @@ import log from '../lib/log.js';
|
||||
|
||||
const UNDO_STACK_SIZE = 99;
|
||||
|
||||
const MAX_RMS = 1.2;
|
||||
|
||||
class SoundEditor extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
@@ -265,6 +267,15 @@ class SoundEditor extends React.Component {
|
||||
}
|
||||
});
|
||||
}
|
||||
tooLoud () {
|
||||
const numChunks = this.state.chunkLevels.length;
|
||||
const startIndex = this.state.trimStart === null ?
|
||||
0 : Math.floor(this.state.trimStart * numChunks);
|
||||
const endIndex = this.state.trimEnd === null ?
|
||||
numChunks - 1 : Math.ceil(this.state.trimEnd * numChunks);
|
||||
const trimChunks = this.state.chunkLevels.slice(startIndex, endIndex);
|
||||
return Math.max(...trimChunks) > MAX_RMS;
|
||||
}
|
||||
getUndoItem () {
|
||||
return {
|
||||
...this.copyCurrentBuffer(),
|
||||
@@ -399,6 +410,7 @@ class SoundEditor extends React.Component {
|
||||
name={this.props.name}
|
||||
playhead={this.state.playhead}
|
||||
setRef={this.setRef}
|
||||
tooLoud={this.tooLoud()}
|
||||
trimEnd={this.state.trimEnd}
|
||||
trimStart={this.state.trimStart}
|
||||
onChangeName={this.handleChangeName}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import 'get-float-time-domain-data';
|
||||
import getUserMedia from 'get-user-media-promise';
|
||||
import SharedAudioContext from './shared-audio-context.js';
|
||||
import {computeRMS} from './audio-util.js';
|
||||
import {computeRMS, computeChunkedRMS} from './audio-util.js';
|
||||
|
||||
class AudioRecorder {
|
||||
constructor () {
|
||||
this.audioContext = new SharedAudioContext();
|
||||
this.bufferLength = 1024;
|
||||
this.bufferLength = 8192;
|
||||
|
||||
this.userMediaStream = null;
|
||||
this.mediaStreamSource = null;
|
||||
@@ -51,7 +51,7 @@ class AudioRecorder {
|
||||
this.userMediaStream = userMediaStream;
|
||||
this.mediaStreamSource = this.audioContext.createMediaStreamSource(userMediaStream);
|
||||
this.sourceNode = this.audioContext.createGain();
|
||||
this.scriptProcessorNode = this.audioContext.createScriptProcessor(this.bufferLength, 2, 2);
|
||||
this.scriptProcessorNode = this.audioContext.createScriptProcessor(this.bufferLength, 1, 1);
|
||||
|
||||
this.scriptProcessorNode.onaudioprocess = processEvent => {
|
||||
if (this.recording && !this.disposed) {
|
||||
@@ -68,9 +68,9 @@ class AudioRecorder {
|
||||
|
||||
const update = () => {
|
||||
if (this.disposed) return;
|
||||
requestAnimationFrame(update);
|
||||
this.analyserNode.getFloatTimeDomainData(dataArray);
|
||||
onUpdate(computeRMS(dataArray));
|
||||
requestAnimationFrame(update);
|
||||
};
|
||||
|
||||
requestAnimationFrame(update);
|
||||
@@ -83,7 +83,16 @@ class AudioRecorder {
|
||||
}
|
||||
|
||||
stop () {
|
||||
const chunkLevels = this.buffers.map(buffer => computeRMS(buffer));
|
||||
const buffer = new Float32Array(this.buffers.length * this.bufferLength);
|
||||
|
||||
let offset = 0;
|
||||
for (let i = 0; i < this.buffers.length; i++) {
|
||||
const bufferChunk = this.buffers[i];
|
||||
buffer.set(bufferChunk, offset);
|
||||
offset += bufferChunk.length;
|
||||
}
|
||||
|
||||
const chunkLevels = computeChunkedRMS(buffer);
|
||||
const maxRMS = Math.max.apply(null, chunkLevels);
|
||||
const threshold = maxRMS / 8;
|
||||
|
||||
@@ -105,15 +114,6 @@ class AudioRecorder {
|
||||
trimEnd = 1;
|
||||
}
|
||||
|
||||
const buffer = new Float32Array(this.buffers.length * this.bufferLength);
|
||||
|
||||
let offset = 0;
|
||||
for (let i = 0; i < this.buffers.length; i++) {
|
||||
const bufferChunk = this.buffers[i];
|
||||
buffer.set(bufferChunk, offset);
|
||||
offset += bufferChunk.length;
|
||||
}
|
||||
|
||||
return {
|
||||
levels: chunkLevels,
|
||||
samples: buffer,
|
||||
|
||||
@@ -14,8 +14,16 @@ class VolumeEffect {
|
||||
this.gain.gain.setValueAtTime(volume, endSeconds);
|
||||
this.gain.gain.exponentialRampToValueAtTime(1.0, endSeconds + this.rampLength);
|
||||
|
||||
// Use a waveshaper node to prevent sample values from exceeding -1 or 1.
|
||||
// Without this, gain can cause samples to exceed this range, then they
|
||||
// are clipped on save, and the sound is distorted on load.
|
||||
this.waveShaper = this.audioContext.createWaveShaper();
|
||||
this.waveShaper.curve = new Float32Array([-1, 1]);
|
||||
this.waveShaper.oversample = 'none';
|
||||
|
||||
this.input.connect(this.gain);
|
||||
this.gain.connect(this.output);
|
||||
this.gain.connect(this.waveShaper);
|
||||
this.waveShaper.connect(this.output);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Basketball 1",
|
||||
"md5": "e494c4f44897d94e0541f7036a302449.svg",
|
||||
"md5": "ae21eac3d1814aee1d37ae82ea287816.svg",
|
||||
"type": "backdrop",
|
||||
"tags": [
|
||||
"sports",
|
||||
|
||||
@@ -2563,7 +2563,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Dinosaur1-c",
|
||||
"md5": "a2bb28d5ba55ba997bec63772437cecf.svg",
|
||||
"md5": "78f7e20f0e187fdca81195e4cc134175.svg",
|
||||
"type": "costume",
|
||||
"tags": [
|
||||
"animals",
|
||||
@@ -10493,7 +10493,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Watermelon-a",
|
||||
"md5": "26fecef75cf3b6e0d98bff5c06475036.svg",
|
||||
"md5": "8736ecc2524895733534c888cd91fa1f.svg",
|
||||
"type": "costume",
|
||||
"tags": [
|
||||
"food",
|
||||
@@ -10511,7 +10511,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Watermelon-b",
|
||||
"md5": "fbdaf4d1d349edd3ddf3a1c4528aa9ec.svg",
|
||||
"md5": "1ed1c8b78eae2ee7422074d7f883031d.svg",
|
||||
"type": "costume",
|
||||
"tags": [
|
||||
"food",
|
||||
@@ -10528,7 +10528,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Watermelon-c",
|
||||
"md5": "5976c10412306fc093c1d1930fa05119.svg",
|
||||
"md5": "677738282686d2dcce35d731c3ddc043.svg",
|
||||
"type": "costume",
|
||||
"tags": [
|
||||
"food",
|
||||
|
||||
@@ -3845,7 +3845,7 @@
|
||||
{
|
||||
"costumeName": "dinosaur1-c",
|
||||
"baseLayerID": -1,
|
||||
"baseLayerMD5": "a2bb28d5ba55ba997bec63772437cecf.svg",
|
||||
"baseLayerMD5": "78f7e20f0e187fdca81195e4cc134175.svg",
|
||||
"bitmapResolution": 1,
|
||||
"rotationCenterX": 81,
|
||||
"rotationCenterY": 91
|
||||
@@ -15612,7 +15612,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Watermelon",
|
||||
"md5": "26fecef75cf3b6e0d98bff5c06475036.svg",
|
||||
"md5": "8736ecc2524895733534c888cd91fa1f.svg",
|
||||
"type": "sprite",
|
||||
"tags": [
|
||||
"food",
|
||||
@@ -15643,7 +15643,7 @@
|
||||
{
|
||||
"costumeName": "watermelon-a",
|
||||
"baseLayerID": -1,
|
||||
"baseLayerMD5": "26fecef75cf3b6e0d98bff5c06475036.svg",
|
||||
"baseLayerMD5": "8736ecc2524895733534c888cd91fa1f.svg",
|
||||
"bitmapResolution": 1,
|
||||
"rotationCenterX": 40,
|
||||
"rotationCenterY": 27
|
||||
@@ -15651,7 +15651,7 @@
|
||||
{
|
||||
"costumeName": "watermelon-b",
|
||||
"baseLayerID": -1,
|
||||
"baseLayerMD5": "fbdaf4d1d349edd3ddf3a1c4528aa9ec.svg",
|
||||
"baseLayerMD5": "1ed1c8b78eae2ee7422074d7f883031d.svg",
|
||||
"bitmapResolution": 1,
|
||||
"rotationCenterX": 22,
|
||||
"rotationCenterY": 27
|
||||
@@ -15659,7 +15659,7 @@
|
||||
{
|
||||
"costumeName": "watermelon-c",
|
||||
"baseLayerID": -1,
|
||||
"baseLayerMD5": "5976c10412306fc093c1d1930fa05119.svg",
|
||||
"baseLayerMD5": "677738282686d2dcce35d731c3ddc043.svg",
|
||||
"bitmapResolution": 1,
|
||||
"rotationCenterX": 21,
|
||||
"rotationCenterY": 15
|
||||
|
||||
@@ -87,9 +87,9 @@ const vmListenerHOC = function (WrappedComponent) {
|
||||
// Don't capture keys intended for Blockly inputs.
|
||||
if (e.target !== document && e.target !== document.body) return;
|
||||
|
||||
const key = (!e.key || e.key === 'Dead') ? e.keyCode : e.key;
|
||||
this.props.vm.postIOData('keyboard', {
|
||||
keyCode: e.keyCode,
|
||||
key: e.key,
|
||||
key: key,
|
||||
isDown: true
|
||||
});
|
||||
|
||||
@@ -102,9 +102,9 @@ const vmListenerHOC = function (WrappedComponent) {
|
||||
handleKeyUp (e) {
|
||||
// Always capture up events,
|
||||
// even those that have switched to other targets.
|
||||
const key = (!e.key || e.key === 'Dead') ? e.keyCode : e.key;
|
||||
this.props.vm.postIOData('keyboard', {
|
||||
keyCode: e.keyCode,
|
||||
key: e.key,
|
||||
key: key,
|
||||
isDown: false
|
||||
});
|
||||
|
||||
|
||||
@@ -182,7 +182,8 @@ describe('Working with the blocks', () => {
|
||||
|
||||
// Rename the costume
|
||||
await clickText('Costumes');
|
||||
const el = await findByXpath("//input[@value='costume1']");
|
||||
await clickText('costume2', scope.costumesTab);
|
||||
const el = await findByXpath("//input[@value='costume2']");
|
||||
await el.sendKeys('newname');
|
||||
|
||||
// Make sure it is updated in the block menu
|
||||
@@ -197,7 +198,8 @@ describe('Working with the blocks', () => {
|
||||
|
||||
// Rename the costume
|
||||
await clickText('Costumes');
|
||||
const el = await findByXpath("//input[@value='costume1']");
|
||||
await clickText('costume2', scope.costumesTab);
|
||||
const el = await findByXpath("//input[@value='costume2']");
|
||||
await el.sendKeys('<NewCostume>');
|
||||
|
||||
// Make sure it is updated in the block menu
|
||||
@@ -209,17 +211,15 @@ describe('Working with the blocks', () => {
|
||||
await clickText('Sound', scope.blocksTab);
|
||||
});
|
||||
|
||||
// NOTE: This test describes the current behavior so that changes are not
|
||||
// introduced inadvertly, but I know this is not the desired behavior
|
||||
test('Adding costumes DOES NOT update the default costume name in the toolbox', async () => {
|
||||
test('Adding costumes DOES update the default costume name in the toolbox', async () => {
|
||||
await loadUri(uri);
|
||||
|
||||
// By default, costume1 is in the costume tab
|
||||
// By default, costume2 is in the costume tab
|
||||
await clickText('Looks', scope.blocksTab);
|
||||
await driver.sleep(500); // Wait for scroll to finish
|
||||
await clickText('costume1', scope.blocksTab);
|
||||
await clickText('costume2', scope.blocksTab);
|
||||
|
||||
// Also check that adding a new costume does not update the list
|
||||
// Also check that adding a new costume does update the list
|
||||
await clickText('Costumes');
|
||||
const el = await findByXpath('//button[@aria-label="Choose a Costume"]');
|
||||
await driver.actions().mouseMove(el)
|
||||
@@ -227,14 +227,13 @@ describe('Working with the blocks', () => {
|
||||
await driver.sleep(500); // Wait for thermometer menu to come up
|
||||
await clickXpath('//button[@aria-label="Paint"]');
|
||||
await clickText('costume3', scope.costumesTab);
|
||||
// Check that the menu has not been updated
|
||||
// Check that the menu has been updated
|
||||
await clickText('Code');
|
||||
await clickText('costume1', scope.blocksTab);
|
||||
await clickText('costume3', scope.blocksTab);
|
||||
});
|
||||
|
||||
// NOTE: This test describes the current behavior so that changes are not
|
||||
// introduced inadvertly, but I know this is not the desired behavior
|
||||
test('Adding a sound DOES NOT update the default sound name in the toolbox', async () => {
|
||||
// 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 () => {
|
||||
await loadUri(uri);
|
||||
await clickText('Sounds');
|
||||
await clickXpath('//button[@aria-label="Choose a Sound"]');
|
||||
@@ -242,7 +241,7 @@ describe('Working with the blocks', () => {
|
||||
await clickText('Code');
|
||||
await clickText('Sound', scope.blocksTab);
|
||||
await driver.sleep(500); // Wait for scroll to finish
|
||||
await clickText('Meow', scope.blocksTab); // Meow, not A Bass
|
||||
await clickText('A\u00A0Bass', scope.blocksTab); // Need for block text
|
||||
});
|
||||
|
||||
// Regression test for switching between editor/player causing toolbox to stop updating
|
||||
|
||||
@@ -133,4 +133,49 @@ describe('VMListenerHOC', () => {
|
||||
const actions = store.getActions();
|
||||
expect(actions.length).toEqual(0);
|
||||
});
|
||||
|
||||
test('keypresses go to the vm', () => {
|
||||
const Component = () => (<div />);
|
||||
const WrappedComponent = vmListenerHOC(Component);
|
||||
|
||||
// Mock document.addEventListener so we can trigger keypresses manually
|
||||
// Cannot use the enzyme simulate method because that only works on synthetic events
|
||||
const eventTriggers = {};
|
||||
document.addEventListener = jest.fn((event, cb) => {
|
||||
eventTriggers[event] = cb;
|
||||
});
|
||||
|
||||
vm.postIOData = jest.fn();
|
||||
|
||||
store = mockStore({
|
||||
scratchGui: {
|
||||
mode: {isFullScreen: true},
|
||||
modals: {soundRecorder: true},
|
||||
vm: vm
|
||||
}
|
||||
});
|
||||
mount(
|
||||
<WrappedComponent
|
||||
attachKeyboardEvents
|
||||
store={store}
|
||||
vm={vm}
|
||||
/>
|
||||
);
|
||||
|
||||
// keyboard events that do not target the document or body are ignored
|
||||
eventTriggers.keydown({key: 'A', target: null});
|
||||
expect(vm.postIOData).not.toHaveBeenLastCalledWith('keyboard', {key: 'A', isDown: true});
|
||||
|
||||
// keydown/up with target as the document are sent to the vm via postIOData
|
||||
eventTriggers.keydown({key: 'A', target: document});
|
||||
expect(vm.postIOData).toHaveBeenLastCalledWith('keyboard', {key: 'A', isDown: true});
|
||||
|
||||
eventTriggers.keyup({key: 'A', target: document});
|
||||
expect(vm.postIOData).toHaveBeenLastCalledWith('keyboard', {key: 'A', isDown: false});
|
||||
|
||||
// When key is 'Dead' e.g. bluetooth keyboards on iOS, it sends keyCode instead
|
||||
// because the VM can process both named keys or keyCodes as the `key` property
|
||||
eventTriggers.keyup({key: 'Dead', keyCode: 10, target: document});
|
||||
expect(vm.postIOData).toHaveBeenLastCalledWith('keyboard', {key: 10, isDown: false});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user