From bbf0ba4e25aedb5fd7afc5f8463c34492a236de3 Mon Sep 17 00:00:00 2001
From: Paul Kaplan <pkaplan@media.mit.edu>
Date: Tue, 19 Jun 2018 09:30:05 -0400
Subject: [PATCH] Add drag payload for backpack contents and drop handlers for
 editor tabs

---
 src/components/backpack/backpack.jsx    | 10 ++++++++++
 src/containers/backpack.jsx             |  9 +++++++++
 src/containers/costume-tab.jsx          | 19 +++++++++++++++++--
 src/containers/sound-tab.jsx            | 19 +++++++++++++++++--
 src/containers/sprite-selector-item.jsx |  8 +++++++-
 src/lib/drag-constants.js               |  7 ++++++-
 6 files changed, 66 insertions(+), 6 deletions(-)

diff --git a/src/components/backpack/backpack.jsx b/src/components/backpack/backpack.jsx
index 3711cafd4..563ec84a3 100644
--- a/src/components/backpack/backpack.jsx
+++ b/src/components/backpack/backpack.jsx
@@ -1,6 +1,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import {FormattedMessage} from 'react-intl';
+import DragConstants from '../../lib/drag-constants';
 import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx';
 import SpriteSelectorItem from '../../containers/sprite-selector-item.jsx';
 import styles from './backpack.css';
@@ -8,6 +9,13 @@ import styles from './backpack.css';
 // TODO make sprite selector item not require onClick
 const noop = () => {};
 
+const dragTypeMap = {
+    costume: DragConstants.BACKPACK_COSTUME,
+    sound: DragConstants.BACKPACK_SOUND,
+    code: DragConstants.BACKPACK_CODE,
+    sprite: DragConstants.BACKPACK_SPRITE
+};
+
 const Backpack = ({contents, error, expanded, loading, onToggle}) => (
     <div className={styles.backpackContainer}>
         <div
@@ -60,6 +68,8 @@ const Backpack = ({contents, error, expanded, loading, onToggle}) => (
                                         className={styles.backpackItem}
                                         costumeURL={item.thumbnailUrl}
                                         details={item.name}
+                                        dragPayload={item}
+                                        dragType={dragTypeMap[item.type]}
                                         key={item.id}
                                         name={item.type}
                                         selected={false}
diff --git a/src/containers/backpack.jsx b/src/containers/backpack.jsx
index 878a6cd5f..a489279b2 100644
--- a/src/containers/backpack.jsx
+++ b/src/containers/backpack.jsx
@@ -4,6 +4,7 @@ import bindAll from 'lodash.bindall';
 import BackpackComponent from '../components/backpack/backpack.jsx';
 import {getBackpackContents} from '../lib/backpack-api';
 import {connect} from 'react-redux';
+import storage from '../lib/storage';
 
 class Backpack extends React.Component {
     constructor (props) {
@@ -20,6 +21,14 @@ class Backpack extends React.Component {
             expanded: false,
             contents: []
         };
+
+        // If a host is given, add it as a web source to the storage module
+        if (props.host) {
+            storage.addWebSource(
+                [storage.AssetType.ImageVector, storage.AssetType.ImageBitmap, storage.AssetType.Sound],
+                asset => `${props.host}/${asset.assetId}.${asset.dataFormat}`
+            );
+        }
     }
     handleToggle () {
         const newState = !this.state.expanded;
diff --git a/src/containers/costume-tab.jsx b/src/containers/costume-tab.jsx
index 965f81abe..9cf6051a2 100644
--- a/src/containers/costume-tab.jsx
+++ b/src/containers/costume-tab.jsx
@@ -19,6 +19,11 @@ import {
     openBackdropLibrary
 } from '../reducers/modals';
 
+import {
+    activateTab,
+    SOUNDS_TAB_INDEX
+} from '../reducers/editor-tab';
+
 import addLibraryBackdropIcon from '../components/asset-panel/icon--add-backdrop-lib.svg';
 import addLibraryCostumeIcon from '../components/asset-panel/icon--add-costume-lib.svg';
 import fileUploadIcon from '../components/action-menu/icon--file-upload.svg';
@@ -191,14 +196,22 @@ class CostumeTab extends React.Component {
         this.fileInput.click();
     }
     handleDrop (dropInfo) {
-        // Eventually will handle other kinds of drop events, right now just
-        // the reordering events.
         if (dropInfo.dragType === DragConstants.COSTUME) {
             const sprite = this.props.vm.editingTarget.sprite;
             const activeCostume = sprite.costumes[this.state.selectedCostumeIndex];
             this.props.vm.reorderCostume(this.props.vm.editingTarget.id,
                 dropInfo.index, dropInfo.newIndex);
             this.setState({selectedCostumeIndex: sprite.costumes.indexOf(activeCostume)});
+        } else if (dropInfo.dragType === DragConstants.BACKPACK_COSTUME) {
+            this.props.vm.addCostume(dropInfo.payload.body, {
+                name: dropInfo.payload.name
+            });
+        } else if (dropInfo.dragType === DragConstants.BACKPACK_SOUND) {
+            this.props.onActivateSoundsTab();
+            this.props.vm.addSound({
+                md5: dropInfo.payload.body,
+                name: dropInfo.payload.name
+            });
         }
     }
     setFileInput (input) {
@@ -303,6 +316,7 @@ CostumeTab.propTypes = {
     cameraModalVisible: PropTypes.bool,
     editingTarget: PropTypes.string,
     intl: intlShape,
+    onActivateSoundsTab: PropTypes.func.isRequired,
     onNewCostumeFromCameraClick: PropTypes.func.isRequired,
     onNewLibraryBackdropClick: PropTypes.func.isRequired,
     onNewLibraryCostumeClick: PropTypes.func.isRequired,
@@ -333,6 +347,7 @@ const mapStateToProps = state => ({
 });
 
 const mapDispatchToProps = dispatch => ({
+    onActivateSoundsTab: () => dispatch(activateTab(SOUNDS_TAB_INDEX)),
     onNewLibraryBackdropClick: e => {
         e.preventDefault();
         dispatch(openBackdropLibrary());
diff --git a/src/containers/sound-tab.jsx b/src/containers/sound-tab.jsx
index 097457d8a..74b1eb5d4 100644
--- a/src/containers/sound-tab.jsx
+++ b/src/containers/sound-tab.jsx
@@ -28,6 +28,11 @@ import {
     openSoundRecorder
 } from '../reducers/modals';
 
+import {
+    activateTab,
+    COSTUMES_TAB_INDEX
+} from '../reducers/editor-tab';
+
 class SoundTab extends React.Component {
     constructor (props) {
         super(props);
@@ -120,8 +125,6 @@ class SoundTab extends React.Component {
     }
 
     handleDrop (dropInfo) {
-        // Eventually will handle other kinds of drop events, right now just
-        // the reordering events.
         if (dropInfo.dragType === DragConstants.SOUND) {
             const sprite = this.props.vm.editingTarget.sprite;
             const activeSound = sprite.sounds[this.state.selectedSoundIndex];
@@ -130,6 +133,16 @@ class SoundTab extends React.Component {
                 dropInfo.index, dropInfo.newIndex);
 
             this.setState({selectedSoundIndex: sprite.sounds.indexOf(activeSound)});
+        } else if (dropInfo.dragType === DragConstants.BACKPACK_COSTUME) {
+            this.props.onActivateCostumesTab();
+            this.props.vm.addCostume(dropInfo.payload.body, {
+                name: dropInfo.payload.name
+            });
+        } else if (dropInfo.dragType === DragConstants.BACKPACK_SOUND) {
+            this.props.vm.addSound({
+                md5: dropInfo.payload.body,
+                name: dropInfo.payload.name
+            }).then(this.handleNewSound);
         }
     }
 
@@ -235,6 +248,7 @@ class SoundTab extends React.Component {
 SoundTab.propTypes = {
     editingTarget: PropTypes.string,
     intl: intlShape,
+    onActivateCostumesTab: PropTypes.func.isRequired,
     onNewSoundFromLibraryClick: PropTypes.func.isRequired,
     onNewSoundFromRecordingClick: PropTypes.func.isRequired,
     onRequestCloseSoundLibrary: PropTypes.func.isRequired,
@@ -264,6 +278,7 @@ const mapStateToProps = state => ({
 });
 
 const mapDispatchToProps = dispatch => ({
+    onActivateCostumesTab: () => dispatch(activateTab(COSTUMES_TAB_INDEX)),
     onNewSoundFromLibraryClick: e => {
         e.preventDefault();
         dispatch(openSoundLibrary());
diff --git a/src/containers/sprite-selector-item.jsx b/src/containers/sprite-selector-item.jsx
index 5efe49428..4c968f827 100644
--- a/src/containers/sprite-selector-item.jsx
+++ b/src/containers/sprite-selector-item.jsx
@@ -52,7 +52,8 @@ class SpriteSelectorItem extends React.Component {
                 currentOffset: currentOffset,
                 dragging: true,
                 dragType: this.props.dragType,
-                index: this.props.index
+                index: this.props.index,
+                payload: this.props.dragPayload
             });
             this.noClick = true;
         }
@@ -98,6 +99,7 @@ class SpriteSelectorItem extends React.Component {
             onClick,
             onDeleteButtonClick,
             onDuplicateButtonClick,
+            dragPayload,
             receivedBlocks,
             /* eslint-enable no-unused-vars */
             ...props
@@ -120,6 +122,10 @@ SpriteSelectorItem.propTypes = {
     assetId: PropTypes.string,
     costumeURL: PropTypes.string,
     dispatchSetHoveredSprite: PropTypes.func.isRequired,
+    dragPayload: PropTypes.shape({
+        name: PropTypes.string,
+        body: PropTypes.string
+    }),
     dragType: PropTypes.string,
     id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
     index: PropTypes.number,
diff --git a/src/lib/drag-constants.js b/src/lib/drag-constants.js
index 86f064da9..ce4da5af2 100644
--- a/src/lib/drag-constants.js
+++ b/src/lib/drag-constants.js
@@ -1,5 +1,10 @@
 export default {
     SOUND: 'SOUND',
     COSTUME: 'COSTUME',
-    SPRITE: 'SPRITE'
+    SPRITE: 'SPRITE',
+
+    BACKPACK_SOUND: 'BACKPACK_SOUND',
+    BACKPACK_COSTUME: 'BACKPACK_COSTUME',
+    BACKPACK_SPRITE: 'BACKPACK_SPRITE',
+    BACKPACK_CODE: 'BACKPACK_CODE'
 };
-- 
GitLab