UNPKG

scratch-gui

Version:

GraphicaL User Interface for creating and running Scratch 3.0 projects

270 lines (241 loc) • 9.11 kB
import PropTypes from 'prop-types'; import React from 'react'; import bindAll from 'lodash.bindall'; import {defineMessages, intlShape, injectIntl} from 'react-intl'; import VM from 'scratch-vm'; import AssetPanel from '../components/asset-panel/asset-panel.jsx'; import soundIcon from '../components/asset-panel/icon--sound.svg'; import addSoundFromLibraryIcon from '../components/asset-panel/icon--add-sound-lib.svg'; import addSoundFromRecordingIcon from '../components/asset-panel/icon--add-sound-record.svg'; import fileUploadIcon from '../components/action-menu/icon--file-upload.svg'; import surpriseIcon from '../components/action-menu/icon--surprise.svg'; import RecordModal from './record-modal.jsx'; import SoundEditor from './sound-editor.jsx'; import SoundLibrary from './sound-library.jsx'; import soundLibraryContent from '../lib/libraries/sounds.json'; import {handleFileUpload, soundUpload} from '../lib/file-uploader.js'; import errorBoundaryHOC from '../lib/error-boundary-hoc.jsx'; import {connect} from 'react-redux'; import { closeSoundLibrary, openSoundLibrary, openSoundRecorder } from '../reducers/modals'; class SoundTab extends React.Component { constructor (props) { super(props); bindAll(this, [ 'handleSelectSound', 'handleDeleteSound', 'handleDuplicateSound', 'handleNewSound', 'handleSurpriseSound', 'handleFileUploadClick', 'handleSoundUpload', 'setFileInput' ]); this.state = {selectedSoundIndex: 0}; } componentWillReceiveProps (nextProps) { const { editingTarget, sprites, stage } = nextProps; const target = editingTarget && sprites[editingTarget] ? sprites[editingTarget] : stage; if (!target || !target.sounds) { return; } // If switching editing targets, reset the sound index if (this.props.editingTarget !== editingTarget) { this.setState({selectedSoundIndex: 0}); } else if (this.state.selectedSoundIndex > target.sounds.length - 1) { this.setState({selectedSoundIndex: Math.max(target.sounds.length - 1, 0)}); } } handleSelectSound (soundIndex) { this.setState({selectedSoundIndex: soundIndex}); } handleDeleteSound (soundIndex) { this.props.vm.deleteSound(soundIndex); if (soundIndex >= this.state.selectedSoundIndex) { this.setState({selectedSoundIndex: Math.max(0, soundIndex - 1)}); } } handleDuplicateSound (soundIndex) { this.props.vm.duplicateSound(soundIndex).then(() => { this.setState({selectedSoundIndex: soundIndex + 1}); }); } handleNewSound () { if (!this.props.vm.editingTarget) { return null; } const sprite = this.props.vm.editingTarget.sprite; const sounds = sprite.sounds ? sprite.sounds : []; this.setState({selectedSoundIndex: Math.max(sounds.length - 1, 0)}); } handleSurpriseSound () { const soundItem = soundLibraryContent[Math.floor(Math.random() * soundLibraryContent.length)]; const vmSound = { format: soundItem.format, md5: soundItem.md5, rate: soundItem.rate, sampleCount: soundItem.sampleCount, name: soundItem.name }; this.props.vm.addSound(vmSound).then(() => { this.handleNewSound(); }); } handleFileUploadClick () { this.fileInput.click(); } handleSoundUpload (e) { const storage = this.props.vm.runtime.storage; const handleSound = newSound => this.props.vm.addSound(newSound) .then(() => this.handleNewSound()); handleFileUpload(e.target, (buffer, fileType, fileName) => { soundUpload(buffer, fileType, fileName, storage, handleSound); }); } setFileInput (input) { this.fileInput = input; } render () { const { intl, vm, onNewSoundFromLibraryClick, onNewSoundFromRecordingClick } = this.props; if (!vm.editingTarget) { return null; } const sprite = vm.editingTarget.sprite; const sounds = sprite.sounds ? sprite.sounds.map(sound => ( { url: soundIcon, name: sound.name, details: (sound.sampleCount / sound.rate).toFixed(2) } )) : []; const messages = defineMessages({ fileUploadSound: { defaultMessage: 'Upload Sound', description: 'Button to upload sound from file in the editor tab', id: 'gui.soundTab.fileUploadSound' }, surpriseSound: { defaultMessage: 'Surprise', description: 'Button to get a random sound in the editor tab', id: 'gui.soundTab.surpriseSound' }, recordSound: { defaultMessage: 'Record', description: 'Button to record a sound in the editor tab', id: 'gui.soundTab.recordSound' }, addSound: { defaultMessage: 'Choose a Sound', description: 'Button to add a sound in the editor tab', id: 'gui.soundTab.addSoundFromLibrary' } }); return ( <AssetPanel buttons={[{ title: intl.formatMessage(messages.addSound), img: addSoundFromLibraryIcon, onClick: onNewSoundFromLibraryClick }, { title: intl.formatMessage(messages.fileUploadSound), img: fileUploadIcon, onClick: this.handleFileUploadClick, fileAccept: '.wav, .mp3', fileChange: this.handleSoundUpload, fileInput: this.setFileInput }, { title: intl.formatMessage(messages.surpriseSound), img: surpriseIcon, onClick: this.handleSurpriseSound }, { title: intl.formatMessage(messages.recordSound), img: addSoundFromRecordingIcon, onClick: onNewSoundFromRecordingClick }]} items={sounds.map(sound => ({ url: soundIcon, ...sound }))} selectedItemIndex={this.state.selectedSoundIndex} onDeleteClick={this.handleDeleteSound} onDuplicateClick={this.handleDuplicateSound} onItemClick={this.handleSelectSound} > {sprite.sounds && sprite.sounds[this.state.selectedSoundIndex] ? ( <SoundEditor soundIndex={this.state.selectedSoundIndex} /> ) : null} {this.props.soundRecorderVisible ? ( <RecordModal onNewSound={this.handleNewSound} /> ) : null} {this.props.soundLibraryVisible ? ( <SoundLibrary vm={this.props.vm} onNewSound={this.handleNewSound} onRequestClose={this.props.onRequestCloseSoundLibrary} /> ) : null} </AssetPanel> ); } } SoundTab.propTypes = { editingTarget: PropTypes.string, intl: intlShape, onNewSoundFromLibraryClick: PropTypes.func.isRequired, onNewSoundFromRecordingClick: PropTypes.func.isRequired, onRequestCloseSoundLibrary: PropTypes.func.isRequired, soundLibraryVisible: PropTypes.bool, soundRecorderVisible: PropTypes.bool, sprites: PropTypes.shape({ id: PropTypes.shape({ sounds: PropTypes.arrayOf(PropTypes.shape({ name: PropTypes.string.isRequired })) }) }), stage: PropTypes.shape({ sounds: PropTypes.arrayOf(PropTypes.shape({ name: PropTypes.string.isRequired })) }), vm: PropTypes.instanceOf(VM).isRequired }; const mapStateToProps = state => ({ editingTarget: state.targets.editingTarget, sprites: state.targets.sprites, stage: state.targets.stage, soundLibraryVisible: state.modals.soundLibrary, soundRecorderVisible: state.modals.soundRecorder }); const mapDispatchToProps = dispatch => ({ onNewSoundFromLibraryClick: e => { e.preventDefault(); dispatch(openSoundLibrary()); }, onNewSoundFromRecordingClick: () => { dispatch(openSoundRecorder()); }, onRequestCloseSoundLibrary: () => { dispatch(closeSoundLibrary()); } }); export default errorBoundaryHOC('Sound Tab')( injectIntl(connect( mapStateToProps, mapDispatchToProps )(SoundTab)) );