Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 52 additions & 5 deletions packages/scratch-gui/src/containers/costume-tab.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import searchIcon from '../components/action-menu/icon--search.svg';
import costumeLibraryContent from '../lib/libraries/costumes.json';
import backdropLibraryContent from '../lib/libraries/backdrops.json';
import {ModalFocusContext} from '../contexts/modal-focus-context.jsx';
import {costumeShape} from '../lib/assets-prop-types.js';
import mergeDynamicAssets from '../lib/merge-dynamic-assets.js';

let messages = defineMessages({
addLibraryBackdropMsg: {
Expand Down Expand Up @@ -91,7 +93,9 @@ class CostumeTab extends React.Component {
'handleFileUploadClick',
'handleCostumeUpload',
'handleDrop',
'setFileInput'
'setFileInput',
'mergeDynamicCostumes',
'mergeDynamicBackdrops'
]);
const {
editingTarget,
Expand All @@ -104,7 +108,18 @@ class CostumeTab extends React.Component {
} else {
this.state = {selectedCostumeIndex: 0};
}
this.processedCostumes = {};
this.processedBackdrops = {};
}
componentDidMount () {
this.handleDocumentClick = () => {
if (document.activeElement !== document.body) {
document.activeElement.blur();
}
};
document.addEventListener('mousedown', this.handleDocumentClick);
}

componentWillReceiveProps (nextProps) {
const {
editingTarget,
Expand Down Expand Up @@ -133,8 +148,32 @@ class CostumeTab extends React.Component {
this.setState({selectedCostumeIndex: target.currentCostume});
}
}

componentWillUnmount () {
document.removeEventListener('mousedown', this.handleDocumentClick);
}
static contextType = ModalFocusContext;

mergeDynamicCostumes () {
if (this.processedCostumes.source === this.props.dynamicCostumes) {
return this.processedCostumes.data;
}
this.processedCostumes = mergeDynamicAssets(
costumeLibraryContent,
this.props.dynamicCostumes
);
return this.processedCostumes.data;
}
mergeDynamicBackdrops () {
if (this.processedBackdrops.source === this.props.dynamicBackdrops) {
return this.processedBackdrops.data;
}
this.processedBackdrops = mergeDynamicAssets(
backdropLibraryContent,
this.props.dynamicBackdrops
);
return this.processedBackdrops.data;
}
handleSelectCostume (costumeIndex) {
this.props.vm.editingTarget.setCostume(costumeIndex);
this.setState({selectedCostumeIndex: costumeIndex});
Expand Down Expand Up @@ -190,7 +229,9 @@ class CostumeTab extends React.Component {
this.handleNewCostume(emptyCostume(name));
}
handleSurpriseCostume () {
const item = costumeLibraryContent[Math.floor(Math.random() * costumeLibraryContent.length)];
const costumes = this.mergeDynamicCostumes();

const item = costumes[Math.floor(Math.random() * costumes.length)];
const vmCostume = {
name: item.name,
md5: item.md5ext,
Expand All @@ -202,7 +243,9 @@ class CostumeTab extends React.Component {
this.handleNewCostume(vmCostume, true /* fromCostumeLibrary */);
}
handleSurpriseBackdrop () {
const item = backdropLibraryContent[Math.floor(Math.random() * backdropLibraryContent.length)];
const backdrops = this.mergeDynamicBackdrops();

const item = backdrops[Math.floor(Math.random() * backdrops.length)];
const vmCostume = {
name: item.name,
md5: item.md5ext,
Expand Down Expand Up @@ -375,15 +418,19 @@ CostumeTab.propTypes = {
name: PropTypes.string.isRequired
}))
}),
vm: PropTypes.instanceOf(VM)
vm: PropTypes.instanceOf(VM),
dynamicCostumes: PropTypes.arrayOf(costumeShape),
dynamicBackdrops: PropTypes.arrayOf(costumeShape)
};

const mapStateToProps = state => ({
editingTarget: state.scratchGui.targets.editingTarget,
isRtl: state.locales.isRtl,
sprites: state.scratchGui.targets.sprites,
stage: state.scratchGui.targets.stage,
dragging: state.scratchGui.assetDrag.dragging
dragging: state.scratchGui.assetDrag.dragging,
dynamicCostumes: state.scratchGui.dynamicAssets.costumes,
dynamicBackdrops: state.scratchGui.dynamicAssets.backdrops
});

const mapDispatchToProps = dispatch => ({
Expand Down
28 changes: 24 additions & 4 deletions packages/scratch-gui/src/containers/sound-tab.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ import {setRestore} from '../reducers/restore-deletion';
import {showStandardAlert, closeAlertWithId} from '../reducers/alerts';
import {ModalFocusContext} from '../contexts/modal-focus-context.jsx';

import {soundShape} from '../lib/assets-prop-types.js';
import mergeDynamicAssets from '../lib/merge-dynamic-assets.js';

class SoundTab extends React.Component {
constructor (props) {
super(props);
Expand All @@ -55,9 +58,11 @@ class SoundTab extends React.Component {
'handleSoundUpload',
'handleNewSoundFromLibraryClick',
'handleDrop',
'setFileInput'
'setFileInput',
'mergeDynamicAssets'
]);
this.state = {selectedSoundIndex: 0};
this.processedSounds = {};
}

componentWillReceiveProps (nextProps) {
Expand All @@ -82,6 +87,17 @@ class SoundTab extends React.Component {

static contextType = ModalFocusContext;

mergeDynamicAssets () {
if (this.processedSounds.source === this.props.dynamicSounds) {
return this.processedSounds.data;
}
this.processedSounds = mergeDynamicAssets(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is some potential now in passing down the processedSounds to the LibraryComponents, which are children of those containers and use the same logic for mergeDynamicAssets. However, this is more of a nitpick, and I am fine with it being done in another PR.

soundLibraryContent,
this.props.dynamicSounds
);
return this.processedSounds.data;
}

handleSelectSound (soundIndex) {
this.setState({selectedSoundIndex: soundIndex});
}
Expand Down Expand Up @@ -116,7 +132,9 @@ class SoundTab extends React.Component {
}

handleSurpriseSound () {
const soundItem = soundLibraryContent[Math.floor(Math.random() * soundLibraryContent.length)];
const sounds = this.mergeDynamicAssets();

const soundItem = sounds[Math.floor(Math.random() * sounds.length)];
const vmSound = {
format: soundItem.dataFormat,
md5: soundItem.md5ext,
Expand Down Expand Up @@ -317,7 +335,8 @@ SoundTab.propTypes = {
name: PropTypes.string.isRequired
}))
}),
vm: PropTypes.instanceOf(VM).isRequired
vm: PropTypes.instanceOf(VM).isRequired,
dynamicSounds: PropTypes.arrayOf(soundShape)
};

const mapStateToProps = state => ({
Expand All @@ -326,7 +345,8 @@ const mapStateToProps = state => ({
sprites: state.scratchGui.targets.sprites,
stage: state.scratchGui.targets.stage,
soundLibraryVisible: state.scratchGui.modals.soundLibrary,
soundRecorderVisible: state.scratchGui.modals.soundRecorder
soundRecorderVisible: state.scratchGui.modals.soundRecorder,
dynamicSounds: state.scratchGui.dynamicAssets.sounds
});

const mapDispatchToProps = dispatch => ({
Expand Down
28 changes: 24 additions & 4 deletions packages/scratch-gui/src/containers/stage-selector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import backdropLibraryContent from '../lib/libraries/backdrops.json';
import {handleFileUpload, costumeUpload} from '../lib/file-uploader.js';
import {ModalFocusContext} from '../contexts/modal-focus-context.jsx';

import {costumeShape as backdropShape} from '../lib/assets-prop-types.js';
import mergeDynamicAssets from '../lib/merge-dynamic-assets.js';

const dragTypes = [
DragConstants.COSTUME,
DragConstants.SOUND,
Expand Down Expand Up @@ -54,8 +57,11 @@ class StageSelector extends React.Component {
'handleTouchEnd',
'handleDrop',
'setFileInput',
'setRef'
'setRef',
'mergeDynamicAssets'
]);

this.processedBackdrops = {};
}
componentDidMount () {
document.addEventListener('touchend', this.handleTouchEnd);
Expand All @@ -66,6 +72,16 @@ class StageSelector extends React.Component {

static contextType = ModalFocusContext;

mergeDynamicAssets () {
if (this.processedBackdrops.source === this.props.dynamicBackdrops) {
return this.processedBackdrops.data;
}
this.processedBackdrops = mergeDynamicAssets(
backdropLibraryContent,
this.props.dynamicBackdrops
);
return this.processedBackdrops.data;
}
handleTouchEnd (e) {
const {x, y} = getEventXY(e);
const {top, left, bottom, right} = this.ref.getBoundingClientRect();
Expand Down Expand Up @@ -107,7 +123,9 @@ class StageSelector extends React.Component {
handleSurpriseBackdrop (e) {
e.stopPropagation(); // Prevent click from falling through to selecting stage.
// @todo should this not add a backdrop you already have?
const item = backdropLibraryContent[Math.floor(Math.random() * backdropLibraryContent.length)];
const backdrops = this.mergeDynamicAssets();

const item = backdrops[Math.floor(Math.random() * backdrops.length)];
this.addBackdropFromLibraryItem(item, false);
}
handleEmptyBackdrop (e) {
Expand Down Expand Up @@ -198,15 +216,17 @@ StageSelector.propTypes = {
intl: intlShape.isRequired,
onCloseImporting: PropTypes.func,
onSelect: PropTypes.func,
onShowImporting: PropTypes.func
onShowImporting: PropTypes.func,
dynamicBackdrops: PropTypes.arrayOf(backdropShape)
};

const mapStateToProps = (state, {asset, id}) => ({
url: asset && asset.encodeDataURI(),
vm: state.scratchGui.vm,
receivedBlocks: state.scratchGui.hoveredTarget.receivedBlocks &&
state.scratchGui.hoveredTarget.sprite === id,
raised: state.scratchGui.blockDrag
raised: state.scratchGui.blockDrag,
dynamicBackdrops: state.scratchGui.dynamicAssets.backdrops
});

const mapDispatchToProps = dispatch => ({
Expand Down
25 changes: 22 additions & 3 deletions packages/scratch-gui/src/containers/target-pane.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import {fetchSprite, fetchCode} from '../lib/backpack-api';
import randomizeSpritePosition from '../lib/randomize-sprite-position';
import downloadBlob from '../lib/download-blob';
import {ModalFocusContext} from '../contexts/modal-focus-context.jsx';
import {spriteShape} from '../lib/assets-prop-types.js';
import mergeDynamicAssets from '../lib/merge-dynamic-assets.js';

class TargetPane extends React.Component {
constructor (props) {
Expand All @@ -50,8 +52,11 @@ class TargetPane extends React.Component {
'handlePaintSpriteClick',
'handleFileUploadClick',
'handleSpriteUpload',
'setFileInput'
'setFileInput',
'mergeDynamicAssets'
]);

this.processedSprites = {};
}
componentDidMount () {
this.props.vm.addListener('BLOCK_DRAG_END', this.handleBlockDragEnd);
Expand All @@ -62,6 +67,16 @@ class TargetPane extends React.Component {

static contextType = ModalFocusContext;

mergeDynamicAssets () {
if (this.processedSprites.source === this.props.dynamicSprites) {
return this.processedSprites.data;
}
this.processedSprites = mergeDynamicAssets(
spriteLibraryContent,
this.props.dynamicSprites
);
return this.processedSprites.data;
}
handleChangeSpriteDirection (direction) {
this.props.vm.postSpriteInfo({direction});
}
Expand Down Expand Up @@ -112,7 +127,9 @@ class TargetPane extends React.Component {
}
}
handleSurpriseSpriteClick () {
const surpriseSprites = spriteLibraryContent.filter(sprite =>
const sprites = this.mergeDynamicAssets();

const surpriseSprites = sprites.filter(sprite =>
(sprite.tags.indexOf('letters') === -1) && (sprite.tags.indexOf('numbers') === -1)
);
const item = surpriseSprites[Math.floor(Math.random() * surpriseSprites.length)];
Expand Down Expand Up @@ -296,6 +313,7 @@ TargetPane.propTypes = {
intl: intlShape.isRequired,
onCloseImporting: PropTypes.func,
onShowImporting: PropTypes.func,
dynamicSprites: PropTypes.arrayOf(spriteShape),
...targetPaneProps
};

Expand All @@ -307,7 +325,8 @@ const mapStateToProps = state => ({
sprites: state.scratchGui.targets.sprites,
stage: state.scratchGui.targets.stage,
raiseSprites: state.scratchGui.blockDrag,
workspaceMetrics: state.scratchGui.workspaceMetrics
workspaceMetrics: state.scratchGui.workspaceMetrics,
dynamicSprites: state.scratchGui.dynamicAssets.sprites
});

const mapDispatchToProps = dispatch => ({
Expand Down
Loading