diff --git a/src/components/connection-modal/connection-modal.jsx b/src/components/connection-modal/connection-modal.jsx index 99c9a6a53..8631ab2ac 100644 --- a/src/components/connection-modal/connection-modal.jsx +++ b/src/components/connection-modal/connection-modal.jsx @@ -28,6 +28,7 @@ const ConnectionModalComponent = props => ( contentLabel={props.name} headerClassName={styles.header} headerImage={props.smallPeripheralImage} + id="connectionModal" onHelp={props.onHelp} onRequestClose={props.onCancel} > diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx index 389f63d63..176dea6e1 100644 --- a/src/components/gui/gui.jsx +++ b/src/components/gui/gui.jsx @@ -30,6 +30,7 @@ import TipsLibrary from '../../containers/tips-library.jsx'; import Cards from '../../containers/cards.jsx'; import Alerts from '../../containers/alerts.jsx'; import DragLayer from '../../containers/drag-layer.jsx'; +import ConnectionModal from '../../containers/connection-modal.jsx'; import layout, {STAGE_SIZE_MODES} from '../../lib/layout-constants'; import {resolveStageSize} from '../../lib/screen-utils'; @@ -68,6 +69,7 @@ const GUIComponent = props => { canSaveAsCopy, canShare, children, + connectionModalVisible, costumeLibraryVisible, costumesTabVisible, enableCommunity, @@ -157,6 +159,11 @@ const GUIComponent = props => { {alertsVisible ? ( ) : null} + {connectionModalVisible ? ( + + ) : null} {costumeLibraryVisible ? ( ext.extensionId === extensionId); - if (extension) { - this.setState({connectionModal: { - extensionId: extensionId, - useAutoScan: extension.useAutoScan, - peripheralImage: extension.peripheralImage, - smallPeripheralImage: extension.smallPeripheralImage, - peripheralButtonImage: extension.peripheralButtonImage, - name: extension.name, - connectingMessage: extension.connectingMessage, - helpLink: extension.helpLink - }}); - } - } - handleConnectionModalClose () { - this.setState({connectionModal: null}); + this.props.onOpenConnectionModal(extensionId); } handleStatusButtonUpdate () { this.ScratchBlocks.refreshStatusButtons(this.workspace); @@ -434,6 +416,7 @@ class Blocks extends React.Component { isRtl, isVisible, onActivateColorPicker, + onOpenConnectionModal, onOpenSoundRecorder, updateToolboxState, onActivateCustomProcedures, @@ -460,14 +443,6 @@ class Blocks extends React.Component { onOk={this.handlePromptCallback} /> ) : null} - {this.state.connectionModal ? ( - - ) : null} {extensionLibraryVisible ? ( ({ const mapDispatchToProps = dispatch => ({ onActivateColorPicker: callback => dispatch(activateColorPicker(callback)), onActivateCustomProcedures: (data, callback) => dispatch(activateCustomProcedures(data, callback)), + onOpenConnectionModal: id => { + dispatch(setConnectionModalExtensionId(id)); + dispatch(openConnectionModal()); + }, onOpenSoundRecorder: () => { dispatch(activateTab(SOUNDS_TAB_INDEX)); dispatch(openSoundRecorder()); diff --git a/src/containers/connection-modal.jsx b/src/containers/connection-modal.jsx index ecf6ab052..c0eb38b77 100644 --- a/src/containers/connection-modal.jsx +++ b/src/containers/connection-modal.jsx @@ -4,12 +4,16 @@ import bindAll from 'lodash.bindall'; import ConnectionModalComponent, {PHASES} from '../components/connection-modal/connection-modal.jsx'; import VM from 'scratch-vm'; import analytics from '../lib/analytics'; +import extensionData from '../lib/libraries/extensions/index.jsx'; +import {connect} from 'react-redux'; +import {closeConnectionModal} from '../reducers/modals'; class ConnectionModal extends React.Component { constructor (props) { super(props); bindAll(this, [ 'handleScanning', + 'handleCancel', 'handleConnected', 'handleConnecting', 'handleDisconnect', @@ -17,6 +21,7 @@ class ConnectionModal extends React.Component { 'handleHelp' ]); this.state = { + extension: extensionData.find(ext => ext.extensionId === props.extensionId), phase: props.vm.getPeripheralIsConnected(props.extensionId) ? PHASES.connected : PHASES.scanning }; @@ -46,19 +51,24 @@ class ConnectionModal extends React.Component { }); } handleDisconnect () { - this.props.onStatusButtonUpdate(this.props.extensionId, 'not ready'); - this.props.vm.disconnectPeripheral(this.props.extensionId); - this.props.onCancel(); + try { + this.props.vm.disconnectPeripheral(this.props.extensionId); + } finally { + this.props.onCancel(); + } } handleCancel () { - // If we're not connected to a peripheral, close the websocket so we stop scanning. - if (!this.props.vm.getPeripheralIsConnected(this.props.extensionId)) { - this.props.vm.disconnectPeripheral(this.props.extensionId); + try { + // If we're not connected to a peripheral, close the websocket so we stop scanning. + if (!this.props.vm.getPeripheralIsConnected(this.props.extensionId)) { + this.props.vm.disconnectPeripheral(this.props.extensionId); + } + } finally { + // Close the modal. + this.props.onCancel(); } - this.props.onCancel(); } handleError () { - this.props.onStatusButtonUpdate(); // Assume errors that come in during scanning phase are the result of not // having scratch-link installed. if (this.state.phase === PHASES.scanning || this.state.phase === PHASES.unavailable) { @@ -77,7 +87,6 @@ class ConnectionModal extends React.Component { } } handleConnected () { - this.props.onStatusButtonUpdate(); this.setState({ phase: PHASES.connected }); @@ -88,7 +97,7 @@ class ConnectionModal extends React.Component { }); } handleHelp () { - window.open(this.props.helpLink, '_blank'); + window.open(this.state.extension.helpLink, '_blank'); analytics.event({ category: 'extensions', action: 'help', @@ -98,17 +107,17 @@ class ConnectionModal extends React.Component { render () { return ( ({ + extensionId: state.scratchGui.connectionModal.extensionId +}); + +const mapDispatchToProps = dispatch => ({ + onCancel: () => { + dispatch(closeConnectionModal()); + } +}); + +export default connect( + mapStateToProps, + mapDispatchToProps +)(ConnectionModal); diff --git a/src/containers/gui.jsx b/src/containers/gui.jsx index eeb3f83ce..ed5dc9272 100644 --- a/src/containers/gui.jsx +++ b/src/containers/gui.jsx @@ -132,6 +132,7 @@ const mapStateToProps = (state, ownProps) => { backdropLibraryVisible: state.scratchGui.modals.backdropLibrary, blocksTabVisible: state.scratchGui.editorTab.activeTabIndex === BLOCKS_TAB_INDEX, cardsVisible: state.scratchGui.cards.visible, + connectionModalVisible: state.scratchGui.modals.connectionModal, costumeLibraryVisible: state.scratchGui.modals.costumeLibrary, costumesTabVisible: state.scratchGui.editorTab.activeTabIndex === COSTUMES_TAB_INDEX, importInfoVisible: state.scratchGui.modals.importInfo, diff --git a/src/reducers/connection-modal.js b/src/reducers/connection-modal.js new file mode 100644 index 000000000..abac45e20 --- /dev/null +++ b/src/reducers/connection-modal.js @@ -0,0 +1,30 @@ +const SET_ID = 'scratch-gui/connection-modal/setId'; + +const initialState = { + extensionId: null +}; + +const reducer = function (state, action) { + if (typeof state === 'undefined') state = initialState; + switch (action.type) { + case SET_ID: + return Object.assign({}, state, { + extensionId: action.extensionId + }); + default: + return state; + } +}; + +const setConnectionModalExtensionId = function (extensionId) { + return { + type: SET_ID, + extensionId: extensionId + }; +}; + +export { + reducer as default, + initialState as connectionModalInitialState, + setConnectionModalExtensionId +}; diff --git a/src/reducers/gui.js b/src/reducers/gui.js index 1eb254b47..682913f62 100644 --- a/src/reducers/gui.js +++ b/src/reducers/gui.js @@ -3,6 +3,7 @@ import alertsReducer, {alertsInitialState} from './alerts'; import assetDragReducer, {assetDragInitialState} from './asset-drag'; import cardsReducer, {cardsInitialState} from './cards'; import colorPickerReducer, {colorPickerInitialState} from './color-picker'; +import connectionModalReducer, {connectionModalInitialState} from './connection-modal'; import customProceduresReducer, {customProceduresInitialState} from './custom-procedures'; import blockDragReducer, {blockDragInitialState} from './block-drag'; import editorTabReducer, {editorTabInitialState} from './editor-tab'; @@ -33,6 +34,7 @@ const guiInitialState = { blockDrag: blockDragInitialState, cards: cardsInitialState, colorPicker: colorPickerInitialState, + connectionModal: connectionModalInitialState, customProcedures: customProceduresInitialState, editorTab: editorTabInitialState, mode: modeInitialState, @@ -100,6 +102,7 @@ const guiReducer = combineReducers({ blockDrag: blockDragReducer, cards: cardsReducer, colorPicker: colorPickerReducer, + connectionModal: connectionModalReducer, customProcedures: customProceduresReducer, editorTab: editorTabReducer, mode: modeReducer, diff --git a/src/reducers/modals.js b/src/reducers/modals.js index ddcb50806..d385f2613 100644 --- a/src/reducers/modals.js +++ b/src/reducers/modals.js @@ -13,10 +13,9 @@ const MODAL_PREVIEW_INFO = 'previewInfo'; const MODAL_SOUND_LIBRARY = 'soundLibrary'; const MODAL_SPRITE_LIBRARY = 'spriteLibrary'; const MODAL_SOUND_RECORDER = 'soundRecorder'; - +const MODAL_CONNECTION = 'connectionModal'; const MODAL_TIPS_LIBRARY = 'tipsLibrary'; - const initialState = { [MODAL_BACKDROP_LIBRARY]: false, [MODAL_CAMERA_CAPTURE]: false, @@ -28,6 +27,7 @@ const initialState = { [MODAL_SOUND_LIBRARY]: false, [MODAL_SPRITE_LIBRARY]: false, [MODAL_SOUND_RECORDER]: false, + [MODAL_CONNECTION]: false, [MODAL_TIPS_LIBRARY]: false }; @@ -98,6 +98,10 @@ const openSoundRecorder = function () { analytics.pageview('/modals/microphone'); return openModal(MODAL_SOUND_RECORDER); }; +const openConnectionModal = function () { + analytics.pageview('/modals/connection'); + return openModal(MODAL_CONNECTION); +}; const openTipsLibrary = function () { analytics.pageview('/modals/tips'); return openModal(MODAL_TIPS_LIBRARY); @@ -135,6 +139,9 @@ const closeSoundRecorder = function () { const closeTipsLibrary = function () { return closeModal(MODAL_TIPS_LIBRARY); }; +const closeConnectionModal = function () { + return closeModal(MODAL_CONNECTION); +}; export { reducer as default, initialState as modalsInitialState, @@ -149,6 +156,7 @@ export { openSpriteLibrary, openSoundRecorder, openTipsLibrary, + openConnectionModal, closeBackdropLibrary, closeCameraCapture, closeCostumeLibrary, @@ -159,5 +167,6 @@ export { closeSpriteLibrary, closeSoundLibrary, closeSoundRecorder, - closeTipsLibrary + closeTipsLibrary, + closeConnectionModal };