From 6d185ccc9f6701e9af3fead784b61ff4e07c21d3 Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Tue, 26 Jun 2018 12:01:30 -0400 Subject: [PATCH] Add sprite saving to the backpack with dragging back out --- .../sprite-selector/sprite-list.jsx | 1 + src/containers/backpack.jsx | 14 +++++++---- src/containers/target-pane.jsx | 6 +++++ src/lib/backpack-api.js | 10 ++++++-- src/lib/backpack/sprite-payload.js | 24 +++++++++++++++++++ 5 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 src/lib/backpack/sprite-payload.js diff --git a/src/components/sprite-selector/sprite-list.jsx b/src/components/sprite-selector/sprite-list.jsx index e8ab7f8d0..a29912b79 100644 --- a/src/components/sprite-selector/sprite-list.jsx +++ b/src/components/sprite-selector/sprite-list.jsx @@ -71,6 +71,7 @@ const SpriteList = function (props) { [styles.raised]: isRaised, [styles.receivedBlocks]: receivedBlocks })} + dragPayload={sprite} dragType={DragConstants.SPRITE} id={sprite.id} index={index} diff --git a/src/containers/backpack.jsx b/src/containers/backpack.jsx index 35fc11bf1..aedf2f35d 100644 --- a/src/containers/backpack.jsx +++ b/src/containers/backpack.jsx @@ -7,12 +7,14 @@ import { saveBackpackObject, deleteBackpackObject, soundPayload, - costumePayload + costumePayload, + spritePayload } from '../lib/backpack-api'; import DragConstants from '../lib/drag-constants'; import {connect} from 'react-redux'; import storage from '../lib/storage'; +import VM from 'scratch-vm'; class Backpack extends React.Component { constructor (props) { @@ -45,7 +47,7 @@ class Backpack extends React.Component { } } componentWillReceiveProps (newProps) { - const dragTypes = [DragConstants.COSTUME, DragConstants.SOUND]; + const dragTypes = [DragConstants.COSTUME, DragConstants.SOUND, DragConstants.SPRITE]; // If `dragging` becomes true, record the drop area rectangle if (newProps.dragInfo.dragging && !this.props.dragInfo.dragging) { this.dropAreaRect = this.ref && this.ref.getBoundingClientRect(); @@ -83,10 +85,13 @@ class Backpack extends React.Component { case DragConstants.SOUND: payloader = soundPayload; break; + case DragConstants.SPRITE: + payloader = spritePayload; + break; } if (!payloader) return; - payloader(dragInfo.payload) + payloader(dragInfo.payload, this.props.vm) .then(payload => saveBackpackObject({ host: this.props.host, token: this.props.token, @@ -152,7 +157,8 @@ Backpack.propTypes = { }), host: PropTypes.string, token: PropTypes.string, - username: PropTypes.string + username: PropTypes.string, + vm: PropTypes.instanceOf(VM) }; const getTokenAndUsername = state => { diff --git a/src/containers/target-pane.jsx b/src/containers/target-pane.jsx index 1d637330a..55f1a23ed 100644 --- a/src/containers/target-pane.jsx +++ b/src/containers/target-pane.jsx @@ -135,6 +135,12 @@ class TargetPane extends React.Component { if (dragInfo.dragType === DragConstants.SPRITE) { // Add one to both new and target index because we are not counting/moving the stage this.props.vm.reorderTarget(dragInfo.index + 1, dragInfo.newIndex + 1); + } else if (dragInfo.dragType === DragConstants.BACKPACK_SPRITE) { + // TODO storage does not have a way of loading zips right now, and may never need it. + // So for now just grab the zip manually. + fetch(dragInfo.payload.bodyUrl) + .then(response => response.arrayBuffer()) + .then(zip => this.props.vm.addSprite(zip)); } else if (targetId) { // Something is being dragged over one of the sprite tiles or the backdrop. // Dropping assets like sounds and costumes duplicate the asset on the diff --git a/src/lib/backpack-api.js b/src/lib/backpack-api.js index 9a2b19e95..f0c51f140 100644 --- a/src/lib/backpack-api.js +++ b/src/lib/backpack-api.js @@ -1,6 +1,7 @@ import xhr from 'xhr'; import costumePayload from './backpack/costume-payload'; import soundPayload from './backpack/sound-payload'; +import spritePayload from './backpack/sprite-payload'; const getBackpackContents = ({ host, @@ -19,9 +20,13 @@ const getBackpackContents = ({ return reject(); } // Add a new property for the full thumbnail url, which includes the host. + // Also include a full body url for loading sprite zips // TODO retreiving the images through storage would allow us to remove this. return resolve(response.body.map(item => ( - Object.assign({}, item, {thumbnailUrl: `${host}/${item.thumbnail}`}) + Object.assign({}, item, { + thumbnailUrl: `${host}/${item.thumbnail}`, + bodyUrl: `${host}/${item.body}` + }) ))); }); }); @@ -72,5 +77,6 @@ export { saveBackpackObject, deleteBackpackObject, costumePayload, - soundPayload + soundPayload, + spritePayload }; diff --git a/src/lib/backpack/sprite-payload.js b/src/lib/backpack/sprite-payload.js new file mode 100644 index 000000000..c9dea7f22 --- /dev/null +++ b/src/lib/backpack/sprite-payload.js @@ -0,0 +1,24 @@ +import jpegThumbnail from './jpeg-thumbnail'; +import storage from '../storage'; + +const spritePayload = (sprite, vm) => vm.exportSprite( + sprite.id, + 'base64' +).then(zippedSprite => { + const payload = { + type: 'sprite', + name: sprite.name, + mime: 'application/zip', + body: zippedSprite, + // Filled in below + thumbnail: '' + }; + + const costumeDataUrl = storage.get(sprite.costume.assetId).encodeDataURI(); + return jpegThumbnail(costumeDataUrl).then(thumbnail => { + payload.thumbnail = thumbnail.replace('data:image/jpeg;base64,', ''); + return payload; + }); +}); + +export default spritePayload;