Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

POC: add a sprite #4

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"prune": "./prune-gh-pages.sh",
"i18n:push": "tx-push-src scratch-editor interface translations/en.json",
"i18n:src": "rimraf ./translations/messages/src && babel src > tmp.js && rimraf tmp.js && build-i18n-src ./translations/messages/src ./translations/ && npm run i18n:push",
"start": "webpack-dev-server",
"start": "NODE_OPTIONS=--openssl-legacy-provider webpack-dev-server",
"test": "npm run test:lint && npm run test:unit && npm run build && npm run test:integration",
"test:integration": "jest --runInBand test[\\\\/]integration",
"test:lint": "eslint . --ext .js,.jsx",
Expand Down
16 changes: 12 additions & 4 deletions src/containers/costume-library.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import PropTypes from 'prop-types';
import React from 'react';
import {defineMessages, injectIntl, intlShape} from 'react-intl';
import VM from 'scratch-vm';
import {connect} from 'react-redux';

import costumeLibraryContent from '../lib/libraries/costumes.json';
import spriteTags from '../lib/libraries/sprite-tags';
import LibraryComponent from '../components/library/library.jsx';

Expand Down Expand Up @@ -36,14 +36,16 @@ class CostumeLibrary extends React.PureComponent {
}
render () {
return (
<LibraryComponent
data={costumeLibraryContent}
this.props.costumes
? <LibraryComponent
data={this.props.costumes}
id="costumeLibrary"
tags={spriteTags}
title={this.props.intl.formatMessage(messages.libraryTitle)}
onItemSelected={this.handleItemSelected}
onRequestClose={this.props.onRequestClose}
/>
: null
);
}
}
Expand All @@ -54,4 +56,10 @@ CostumeLibrary.propTypes = {
vm: PropTypes.instanceOf(VM).isRequired
};

export default injectIntl(CostumeLibrary);
const mapStateToProps = state => {
return {
costumes: state.assets.costumes,
};
};

export default injectIntl(connect(mapStateToProps,null)(CostumeLibrary));
29 changes: 16 additions & 13 deletions src/containers/costume-tab.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import paintIcon from '../components/action-menu/icon--paint.svg';
import surpriseIcon from '../components/action-menu/icon--surprise.svg';
import searchIcon from '../components/action-menu/icon--search.svg';

import costumeLibraryContent from '../lib/libraries/costumes.json';
import backdropLibraryContent from '../lib/libraries/backdrops.json';

let messages = defineMessages({
Expand Down Expand Up @@ -168,16 +167,18 @@ class CostumeTab extends React.Component {
this.handleNewCostume(emptyCostume(name));
}
handleSurpriseCostume () {
const item = costumeLibraryContent[Math.floor(Math.random() * costumeLibraryContent.length)];
const vmCostume = {
name: item.name,
md5: item.md5ext,
rotationCenterX: item.rotationCenterX,
rotationCenterY: item.rotationCenterY,
bitmapResolution: item.bitmapResolution,
skinId: null
};
this.handleNewCostume(vmCostume, true /* fromCostumeLibrary */);
if(this.props.costumes){
const item = this.props.costumes[Math.floor(Math.random() * this.props.costumes.length)];
const vmCostume = {
name: item.name,
md5: item.md5ext,
rotationCenterX: item.rotationCenterX,
rotationCenterY: item.rotationCenterY,
bitmapResolution: item.bitmapResolution,
skinId: null
};
this.handleNewCostume(vmCostume, true /* fromCostumeLibrary */);
}
}
handleSurpriseBackdrop () {
const item = backdropLibraryContent[Math.floor(Math.random() * backdropLibraryContent.length)];
Expand Down Expand Up @@ -349,15 +350,17 @@ CostumeTab.propTypes = {
name: PropTypes.string.isRequired
}))
}),
vm: PropTypes.instanceOf(VM)
vm: PropTypes.instanceOf(VM),
costumes: PropTypes.arrayOf(PropTypes.object)
};

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,
costumes: state.assets.costumes
});

const mapDispatchToProps = dispatch => ({
Expand Down
51 changes: 50 additions & 1 deletion src/containers/gui.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
closeTelemetryModal,
openExtensionLibrary
} from '../reducers/modals';
import { setCostumes, setSprites } from '../reducers/assets.js';

import FontLoaderHOC from '../lib/font-loader-hoc.jsx';
import LocalizationHOC from '../lib/localization-hoc.jsx';
Expand All @@ -45,6 +46,8 @@ class GUI extends React.Component {
setIsScratchDesktop(this.props.isScratchDesktop);
this.props.onStorageInit(storage);
this.props.onVmInit(this.props.vm);
this.getSpritesFromApi();
this.getCostumesFromApi();
}
componentDidUpdate (prevProps) {
if (this.props.projectId !== prevProps.projectId && this.props.projectId !== null) {
Expand All @@ -56,6 +59,46 @@ class GUI extends React.Component {
this.props.onProjectLoaded();
}
}
getSpritesFromApi () {
return fetch(
'http://localhost:3000/ccm/scratch-api/sprites'
)
.then((response) => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then((data) => {
this.props.onSetSprites(data);
})
.catch((error) => {
console.error(
'There was a problem fetching the sprites:',
error
);
});
}
getCostumesFromApi () {
return fetch(
'http://localhost:3000/ccm/scratch-api/costumes'
)
.then((response) => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then((data) => {
this.props.onSetCostumes(data);
})
.catch((error) => {
console.error(
'There was a problem fetching the costumes:',
error
);
});
}
render () {
if (this.props.isError) {
throw new Error(
Expand All @@ -73,6 +116,8 @@ class GUI extends React.Component {
onStorageInit,
onUpdateProjectId,
onVmInit,
onSetSprites,
onSetCostumes,
projectHost,
projectId,
/* eslint-enable no-unused-vars */
Expand Down Expand Up @@ -110,6 +155,8 @@ GUI.propTypes = {
onStorageInit: PropTypes.func,
onUpdateProjectId: PropTypes.func,
onVmInit: PropTypes.func,
onSetSprites: PropTypes.func,
onSetCostumes: PropTypes.func,
projectHost: PropTypes.string,
projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
telemetryModalVisible: PropTypes.bool,
Expand Down Expand Up @@ -161,7 +208,9 @@ const mapDispatchToProps = dispatch => ({
onActivateSoundsTab: () => dispatch(activateTab(SOUNDS_TAB_INDEX)),
onRequestCloseBackdropLibrary: () => dispatch(closeBackdropLibrary()),
onRequestCloseCostumeLibrary: () => dispatch(closeCostumeLibrary()),
onRequestCloseTelemetryModal: () => dispatch(closeTelemetryModal())
onRequestCloseTelemetryModal: () => dispatch(closeTelemetryModal()),
onSetSprites: (sprites) => dispatch(setSprites(sprites)),
onSetCostumes: (costumes) => dispatch(setCostumes(costumes)),
});

const ConnectedGUI = injectIntl(connect(
Expand Down
2 changes: 1 addition & 1 deletion src/containers/library-item.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class LibraryItem extends React.PureComponent {
render () {
const iconMd5 = this.curIconMd5();
const iconURL = iconMd5 ?
`https://cdn.assets.scratch.mit.edu/internalapi/asset/${iconMd5}/get/` :
`http://localhost:3000/image/${iconMd5}` :
this.props.iconRawURL;
return (
<LibraryItemComponent
Expand Down
19 changes: 14 additions & 5 deletions src/containers/sprite-library.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import PropTypes from 'prop-types';
import React from 'react';
import {injectIntl, intlShape, defineMessages} from 'react-intl';
import VM from 'scratch-vm';
import {connect} from 'react-redux';

import spriteLibraryContent from '../lib/libraries/sprites.json';
import randomizeSpritePosition from '../lib/randomize-sprite-position';
import spriteTags from '../lib/libraries/sprite-tags';

Expand Down Expand Up @@ -34,14 +34,16 @@ class SpriteLibrary extends React.PureComponent {
}
render () {
return (
<LibraryComponent
data={spriteLibraryContent}
this.props.sprites
? <LibraryComponent
data={this.props.sprites}
id="spriteLibrary"
tags={spriteTags}
title={this.props.intl.formatMessage(messages.libraryTitle)}
onItemSelected={this.handleItemSelect}
onRequestClose={this.props.onRequestClose}
/>
: null
);
}
}
Expand All @@ -50,7 +52,14 @@ SpriteLibrary.propTypes = {
intl: intlShape.isRequired,
onActivateBlocksTab: PropTypes.func.isRequired,
onRequestClose: PropTypes.func,
vm: PropTypes.instanceOf(VM).isRequired
vm: PropTypes.instanceOf(VM).isRequired,
sprites: PropTypes.arrayOf(PropTypes.object)
};

export default injectIntl(SpriteLibrary);
const mapStateToProps = state => {
return {
sprites: state.assets.sprites,
};
};

export default injectIntl(connect(mapStateToProps,null)(SpriteLibrary));
41 changes: 23 additions & 18 deletions src/containers/target-pane.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {setRestore} from '../reducers/restore-deletion';
import DragConstants from '../lib/drag-constants';
import TargetPaneComponent from '../components/target-pane/target-pane.jsx';
import {BLOCKS_DEFAULT_SCALE} from '../lib/layout-constants';
import spriteLibraryContent from '../lib/libraries/sprites.json';
import {handleFileUpload, spriteUpload} from '../lib/file-uploader.js';
import sharedMessages from '../lib/shared-messages';
import {emptySprite} from '../lib/empty-assets';
Expand Down Expand Up @@ -106,13 +105,15 @@ class TargetPane extends React.Component {
}
}
handleSurpriseSpriteClick () {
const surpriseSprites = spriteLibraryContent.filter(sprite =>
(sprite.tags.indexOf('letters') === -1) && (sprite.tags.indexOf('numbers') === -1)
);
const item = surpriseSprites[Math.floor(Math.random() * surpriseSprites.length)];
randomizeSpritePosition(item);
this.props.vm.addSprite(JSON.stringify(item))
.then(this.handleActivateBlocksTab);
if(this.props.spritesAsset){
const surpriseSprites = this.props.spritesAsset.filter(sprite =>
(sprite.tags.indexOf('letters') === -1) && (sprite.tags.indexOf('numbers') === -1)
);
const item = surpriseSprites[Math.floor(Math.random() * surpriseSprites.length)];
randomizeSpritePosition(item);
this.props.vm.addSprite(JSON.stringify(item))
.then(this.handleActivateBlocksTab);
}
}
handlePaintSpriteClick () {
const formatMessage = this.props.intl.formatMessage;
Expand Down Expand Up @@ -244,6 +245,7 @@ class TargetPane extends React.Component {
onReceivedBlocks,
onShowImporting,
workspaceMetrics,
spritesAsset,
...componentProps
} = this.props;
/* eslint-enable no-unused-vars */
Expand Down Expand Up @@ -286,16 +288,19 @@ TargetPane.propTypes = {
...targetPaneProps
};

const mapStateToProps = state => ({
editingTarget: state.scratchGui.targets.editingTarget,
hoveredTarget: state.scratchGui.hoveredTarget,
isRtl: state.locales.isRtl,
spriteLibraryVisible: state.scratchGui.modals.spriteLibrary,
sprites: state.scratchGui.targets.sprites,
stage: state.scratchGui.targets.stage,
raiseSprites: state.scratchGui.blockDrag,
workspaceMetrics: state.scratchGui.workspaceMetrics
});
const mapStateToProps = state => {
return {
editingTarget: state.scratchGui.targets.editingTarget,
hoveredTarget: state.scratchGui.hoveredTarget,
isRtl: state.locales.isRtl,
spriteLibraryVisible: state.scratchGui.modals.spriteLibrary,
sprites: state.scratchGui.targets.sprites,
stage: state.scratchGui.targets.stage,
raiseSprites: state.scratchGui.blockDrag,
workspaceMetrics: state.scratchGui.workspaceMetrics,
spritesAsset: state.assets.sprites,
}
}

const mapDispatchToProps = dispatch => ({
onNewSpriteClick: e => {
Expand Down
5 changes: 4 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import GUI from './containers/gui.jsx';
import AppStateHOC from './lib/app-state-hoc.jsx';
import GuiReducer, {guiInitialState, guiMiddleware, initEmbedded, initFullScreen, initPlayer} from './reducers/gui';
import LocalesReducer, {localesInitialState, initLocale} from './reducers/locales';
import AssetsReducer, { assetsInitialState } from "./reducers/assets.js";
import {ScratchPaintReducer} from 'scratch-paint';
import {setFullScreen, setPlayer} from './reducers/mode';
import {remixProject} from './reducers/project-state';
Expand All @@ -10,7 +11,8 @@ import {setAppElement} from 'react-modal';
const guiReducers = {
locales: LocalesReducer,
scratchGui: GuiReducer,
scratchPaint: ScratchPaintReducer
scratchPaint: ScratchPaintReducer,
assets: AssetsReducer
};

export {
Expand All @@ -25,6 +27,7 @@ export {
initFullScreen,
initLocale,
localesInitialState,
assetsInitialState,
remixProject,
setFullScreen,
setPlayer
Expand Down
7 changes: 5 additions & 2 deletions src/lib/app-state-hoc.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ConnectedIntlProvider from './connected-intl-provider.jsx';
import localesReducer, {initLocale, localesInitialState} from '../reducers/locales';

import {setPlayer, setFullScreen} from '../reducers/mode.js';
import assetsReducer, { assetsInitialState } from "../reducers/assets.js";

import locales from 'scratch-l10n';
import {detectLocale} from './detect-locale';
Expand Down Expand Up @@ -69,11 +70,13 @@ const AppStateHOC = function (WrappedComponent, localesOnly) {
reducers = {
locales: localesReducer,
scratchGui: guiReducer,
scratchPaint: ScratchPaintReducer
scratchPaint: ScratchPaintReducer,
assets: assetsReducer
};
initialState = {
locales: initializedLocales,
scratchGui: initializedGui
scratchGui: initializedGui,
assets: assetsInitialState
};
enhancer = composeEnhancers(guiMiddleware);
}
Expand Down
Loading