Merge pull request #1967 from kchadha/sprite-upload

Sprite upload
This commit is contained in:
kchadha authored and GitHub committed 2018-05-07 10:40:51 -04:00
commit 728e4afe17
6 files changed
+98 -11

No files matched your search

+1 -1
View File
@@ -95,7 +95,7 @@
"scratch-render": "0.1.0-prerelease.20180502115145",
"scratch-svg-renderer": "0.1.0-prerelease.20180423193917",
"scratch-storage": "0.4.1",
"scratch-vm": "0.1.0-prerelease.1525459625",
"scratch-vm": "0.1.0-prerelease.1525460669",
"selenium-webdriver": "3.6.0",
"startaudiocontext": "1.2.1",
"style-loader": "^0.20.0",
@@ -34,7 +34,7 @@ const messages = defineMessages({
addSpriteFromFile: {
id: 'gui.spriteSelector.addSpriteFromFile',
description: 'Button to add a sprite in the target pane from file',
defaultMessage: 'Coming Soon'
defaultMessage: 'Upload'
}
});
@@ -51,12 +51,15 @@ const SpriteSelectorComponent = function (props) {
onChangeSpriteY,
onDeleteSprite,
onDuplicateSprite,
onFileUploadClick,
onNewSpriteClick,
onSurpriseSpriteClick,
onPaintSpriteClick,
onSelectSprite,
onSpriteUpload,
onSurpriseSpriteClick,
raised,
selectedId,
spriteFileInput,
sprites,
...componentProps
} = props;
@@ -121,7 +124,11 @@ const SpriteSelectorComponent = function (props) {
moreButtons={[
{
title: intl.formatMessage(messages.addSpriteFromFile),
img: fileUploadIcon
img: fileUploadIcon,
onClick: onFileUploadClick,
fileAccept: '.svg, .png, .jpg, .jpeg, .sprite2', // TODO add sprite 3
fileChange: onSpriteUpload,
fileInput: spriteFileInput
}, {
title: intl.formatMessage(messages.addSpriteFromSurprise),
img: surpriseIcon,
@@ -154,12 +161,15 @@ SpriteSelectorComponent.propTypes = {
onChangeSpriteY: PropTypes.func,
onDeleteSprite: PropTypes.func,
onDuplicateSprite: PropTypes.func,
onFileUploadClick: PropTypes.func,
onNewSpriteClick: PropTypes.func,
onPaintSpriteClick: PropTypes.func,
onSelectSprite: PropTypes.func,
onSpriteUpload: PropTypes.func,
onSurpriseSpriteClick: PropTypes.func,
raised: PropTypes.bool,
selectedId: PropTypes.string,
spriteFileInput: PropTypes.func,
sprites: PropTypes.shape({
id: PropTypes.shape({
costume: PropTypes.shape({
+10 -1
View File
@@ -17,6 +17,7 @@ import styles from './target-pane.css';
*/
const TargetPane = ({
editingTarget,
fileInputRef,
hoveredTarget,
spriteLibraryVisible,
onChangeSpriteDirection,
@@ -27,11 +28,13 @@ const TargetPane = ({
onChangeSpriteY,
onDeleteSprite,
onDuplicateSprite,
onFileUploadClick,
onNewSpriteClick,
onSurpriseSpriteClick,
onPaintSpriteClick,
onRequestCloseSpriteLibrary,
onSelectSprite,
onSpriteUpload,
onSurpriseSpriteClick,
raiseSprites,
stage,
sprites,
@@ -48,6 +51,7 @@ const TargetPane = ({
hoveredTarget={hoveredTarget}
raised={raiseSprites}
selectedId={editingTarget}
spriteFileInput={fileInputRef}
sprites={sprites}
onChangeSpriteDirection={onChangeSpriteDirection}
onChangeSpriteName={onChangeSpriteName}
@@ -57,9 +61,11 @@ const TargetPane = ({
onChangeSpriteY={onChangeSpriteY}
onDeleteSprite={onDeleteSprite}
onDuplicateSprite={onDuplicateSprite}
onFileUploadClick={onFileUploadClick}
onNewSpriteClick={onNewSpriteClick}
onPaintSpriteClick={onPaintSpriteClick}
onSelectSprite={onSelectSprite}
onSpriteUpload={onSpriteUpload}
onSurpriseSpriteClick={onSurpriseSpriteClick}
/>
<div className={styles.stageSelectorWrapper}>
@@ -108,6 +114,7 @@ const spriteShape = PropTypes.shape({
TargetPane.propTypes = {
editingTarget: PropTypes.string,
extensionLibraryVisible: PropTypes.bool,
fileInputRef: PropTypes.func,
hoveredTarget: PropTypes.shape({
hoveredSprite: PropTypes.string,
receivedBlocks: PropTypes.bool
@@ -120,11 +127,13 @@ TargetPane.propTypes = {
onChangeSpriteY: PropTypes.func,
onDeleteSprite: PropTypes.func,
onDuplicateSprite: PropTypes.func,
onFileUploadClick: PropTypes.func,
onNewSpriteClick: PropTypes.func,
onPaintSpriteClick: PropTypes.func,
onRequestCloseExtensionLibrary: PropTypes.func,
onRequestCloseSpriteLibrary: PropTypes.func,
onSelectSprite: PropTypes.func,
onSpriteUpload: PropTypes.func,
onSurpriseSpriteClick: PropTypes.func,
raiseSprites: PropTypes.bool,
spriteLibraryVisible: PropTypes.bool,
+1 -1
View File
@@ -39,7 +39,7 @@ class SpriteLibrary extends React.PureComponent {
clearInterval(this.intervalId);
}
handleItemSelect (item) {
this.props.vm.addSprite2(JSON.stringify(item.json));
this.props.vm.addSprite(JSON.stringify(item.json));
analytics.event({
category: 'library',
action: 'Select Sprite',
+26 -3
View File
@@ -13,6 +13,7 @@ import {setReceivedBlocks} from '../reducers/hovered-target';
import TargetPaneComponent from '../components/target-pane/target-pane.jsx';
import spriteLibraryContent from '../lib/libraries/sprites.json';
import {handleFileUpload, spriteUpload} from '../lib/file-uploader.js';
class TargetPane extends React.Component {
constructor (props) {
@@ -27,9 +28,13 @@ class TargetPane extends React.Component {
'handleChangeSpriteY',
'handleDeleteSprite',
'handleDuplicateSprite',
'handleNewSprite',
'handleSelectSprite',
'handleSurpriseSpriteClick',
'handlePaintSpriteClick'
'handlePaintSpriteClick',
'handleFileUploadClick',
'handleSpriteUpload',
'setFileInput'
]);
}
componentDidMount () {
@@ -67,19 +72,34 @@ class TargetPane extends React.Component {
}
handleSurpriseSpriteClick () {
const item = spriteLibraryContent[Math.floor(Math.random() * spriteLibraryContent.length)];
this.props.vm.addSprite2(JSON.stringify(item.json));
this.props.vm.addSprite(JSON.stringify(item.json));
}
handlePaintSpriteClick () {
// @todo this is brittle, will need to be refactored for localized libraries
const emptyItem = spriteLibraryContent.find(item => item.name === 'Empty');
if (emptyItem) {
this.props.vm.addSprite2(JSON.stringify(emptyItem.json)).then(() => {
this.props.vm.addSprite(JSON.stringify(emptyItem.json)).then(() => {
setTimeout(() => { // Wait for targets update to propagate before tab switching
this.props.onActivateTab(COSTUMES_TAB_INDEX);
});
});
}
}
handleNewSprite (spriteJSONString) {
this.props.vm.addSprite(spriteJSONString);
}
handleFileUploadClick () {
this.fileInput.click();
}
handleSpriteUpload (e) {
const storage = this.props.vm.runtime.storage;
handleFileUpload(e.target, (buffer, fileType, fileName) => {
spriteUpload(buffer, fileType, fileName, storage, this.handleNewSprite);
});
}
setFileInput (input) {
this.fileInput = input;
}
handleBlockDragEnd (blocks) {
if (this.props.hoveredTarget.sprite && this.props.hoveredTarget.sprite !== this.props.editingTarget) {
this.props.vm.shareBlocksToTarget(blocks, this.props.hoveredTarget.sprite);
@@ -95,6 +115,7 @@ class TargetPane extends React.Component {
return (
<TargetPaneComponent
{...componentProps}
fileInputRef={this.setFileInput}
onChangeSpriteDirection={this.handleChangeSpriteDirection}
onChangeSpriteName={this.handleChangeSpriteName}
onChangeSpriteSize={this.handleChangeSpriteSize}
@@ -103,8 +124,10 @@ class TargetPane extends React.Component {
onChangeSpriteY={this.handleChangeSpriteY}
onDeleteSprite={this.handleDeleteSprite}
onDuplicateSprite={this.handleDuplicateSprite}
onFileUploadClick={this.handleFileUploadClick}
onPaintSpriteClick={this.handlePaintSpriteClick}
onSelectSprite={this.handleSelectSprite}
onSpriteUpload={this.handleSpriteUpload}
onSurpriseSpriteClick={this.handleSurpriseSpriteClick}
/>
);
+47 -2
View File
@@ -47,6 +47,7 @@ const handleFileUpload = function (fileInput, onload) {
* @property {string} dataFormat The data format of this asset, typically
* the extension to be used for that particular asset, e.g. 'svg' for vector images
* @property {string} md5 The md5 hash of the asset data, followed by '.'' and dataFormat
* @property {string} The md5 hash of the asset data // TODO remove duplication....
*/
/**
@@ -71,7 +72,8 @@ const cacheAsset = function (storage, fileName, assetType, dataFormat, data) {
return {
name: fileName,
dataFormat: dataFormat,
md5: `${md5}.${dataFormat}`
md5: `${md5}.${dataFormat}`,
assetId: md5
};
};
@@ -106,6 +108,7 @@ const costumeUpload = function (fileData, fileType, costumeName, storage, handle
break;
}
default:
log.warn(`Encountered unexpected file type: ${fileType}`);
return;
}
@@ -158,6 +161,7 @@ const soundUpload = function (fileData, fileType, soundName, storage, handleSoun
break;
}
default:
log.warn(`Encountered unexpected file type: ${fileType}`);
return;
}
@@ -171,8 +175,49 @@ const soundUpload = function (fileData, fileType, soundName, storage, handleSoun
handleSound(vmSound);
};
const spriteUpload = function (fileData, fileType, spriteName, storage, handleSprite) {
switch (fileType) {
case '':
case 'application/zip': { // We think this is a .sprite2 or .sprite3 file
handleSprite(new Uint8Array(fileData));
return;
}
case 'image/svg+xml':
case 'image/png':
case 'image/jpeg': {
// Make a sprite from an image by making it a costume first
costumeUpload(fileData, fileType, `${spriteName}-costume1`, storage, (vmCostume => {
const newSprite = {
name: spriteName,
isStage: false,
x: 0,
y: 0,
visible: true,
size: 100,
rotationStyle: 'all around',
direction: 90,
draggable: true,
currentCostume: 0,
blocks: {},
variables: {},
costumes: [vmCostume],
sounds: [] // TODO are all of these necessary?
};
// TODO probably just want sprite upload to handle this object directly
handleSprite(JSON.stringify(newSprite));
}));
return;
}
default: {
log.warn(`Encountered unexpected file type: ${fileType}`);
return;
}
}
};
export {
handleFileUpload,
costumeUpload,
soundUpload
soundUpload,
spriteUpload
};