diff --git a/src/components/menu-bar/menu-bar.jsx b/src/components/menu-bar/menu-bar.jsx
index a5a6d46db1d..eb2797d69f2 100644
--- a/src/components/menu-bar/menu-bar.jsx
+++ b/src/components/menu-bar/menu-bar.jsx
@@ -348,7 +348,7 @@ class MenuBar extends React.Component {
}
}
handleConnectionMouseUp () {
- if (this.props.deviceId) {
+ if (Object.keys(this.props.devices).length > 0) {
this.props.onOpenConnectionModal();
} else {
this.props.onDeviceIsEmpty();
@@ -384,8 +384,8 @@ class MenuBar extends React.Component {
}
}
handleUploadFirmware () {
- if (this.props.deviceId) {
- this.props.vm.uploadFirmwareToPeripheral(this.props.deviceId);
+ if (Object.keys(this.props.devices).length > 0) {
+ this.props.vm.uploadFirmwareToPeripheral(this.props.devices.id);
this.props.onSetRealtimeConnection(false);
this.props.onOpenUploadProgress();
} else {
@@ -670,6 +670,21 @@ class MenuBar extends React.Component {
+ {Object.values(this.props.devices).map(device => (
+
+
+
+ {device.name}
+
+
+ ))}
- {
- this.props.deviceName ? (
-
- {this.props.deviceName}
-
- ) : (
-
- )}
+
{
stageSizeMode: state.scratchGui.stageSize.stageSize,
vm: state.scratchGui.vm,
peripheralName: state.scratchGui.connectionModal.peripheralName,
- deviceId: state.scratchGui.device.deviceId,
- deviceName: state.scratchGui.device.deviceName,
- deviceNames: state.scratchGui.device.deviceName
+ devices: state.scratchGui.devices
};
};
diff --git a/src/containers/blocks.jsx b/src/containers/blocks.jsx
index 634b892f162..f656db48960 100644
--- a/src/containers/blocks.jsx
+++ b/src/containers/blocks.jsx
@@ -30,6 +30,7 @@ import {activateCustomProcedures, deactivateCustomProcedures} from '../reducers/
import {updateMetrics} from '../reducers/workspace-metrics';
import {setCodeEditorValue} from '../reducers/code';
import {setDeviceId, setDeviceName, setDeviceType} from '../reducers/device';
+import {addDevice} from '../reducers/devices';
import {setSupportSwitchMode} from '../reducers/program-mode';
import {setBaudrate} from '../reducers/hardware-console';
@@ -412,9 +413,7 @@ class Blocks extends React.Component {
const targetSounds = target.getSounds();
const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(target);
- const device = this.props.deviceData.find(item => item.deviceId === this.props.deviceId);
-
- return makeToolboxXML(false, device, target.isStage, target.id, dynamicBlocksXML,
+ return makeToolboxXML(false, this.props.devices, target.isStage, target.id, dynamicBlocksXML,
this.props.isRealtimeMode,
targetCostumes[targetCostumes.length - 1].name,
stageCostumes[stageCostumes.length - 1].name,
@@ -476,7 +475,7 @@ class Blocks extends React.Component {
if (deviceId) {
const dev = this.props.deviceData.find(ext => ext.deviceId === deviceId);
- this.props.onDeviceSelected(dev.deviceId, dev.name, dev.type);
+ this.props.onDeviceSelected(dev);
this.ScratchBlocks.Device.setDevice(dev.deviceId, dev.type);
if (dev.defaultBaudRate) {
this.props.onSetBaudrate(dev.defaultBaudRate);
@@ -560,7 +559,7 @@ class Blocks extends React.Component {
}
handleScratchExtensionRemoved (extensionInfo) {
if (extensionInfo && extensionInfo.deviceId) {
- this.props.onDeviceSelected(null, null, null);
+ this.props.onDeviceSelected(null);
this.props.vm.runtime.setRealtimeMode(true);
this.props.onSetSupportSwitchMode(false);
}
@@ -570,6 +569,7 @@ class Blocks extends React.Component {
}
}
handleDeviceExtensionAdded (deviceExtensionsRegister) {
+ console.log('[handleDeviceExtensionAdded]');
if (deviceExtensionsRegister.defineMessages) {
this.ScratchBlocks = deviceExtensionsRegister.defineMessages(this.ScratchBlocks);
}
@@ -588,6 +588,7 @@ class Blocks extends React.Component {
}
}
handleDeviceExtensionRemoved () {
+ console.log('[handleDeviceExtensionRemoved]');
const toolboxXML = this.getToolboxXML();
if (toolboxXML) {
this.props.updateToolboxState(toolboxXML);
@@ -607,15 +608,13 @@ class Blocks extends React.Component {
this.workspace.toolbox_.setSelectedCategoryById(categoryId);
});
}
- handleDeviceSelected (categoryId) {
- const device = this.props.deviceData.find(ext => ext.deviceId === categoryId);
-
+ handleDeviceSelected (device) {
+ console.log('[handleDeviceSelected] device:', device);
if (device && device.launchPeripheralConnectionFlow) {
this.handleConnectionModalStart();
}
-
this.withToolboxUpdates(() => {
- this.workspace.toolbox_.setSelectedCategoryById(categoryId);
+ this.workspace.toolbox_.setSelectedCategoryById(device.deviceId);
});
}
setBlocks (blocks) {
@@ -703,9 +702,8 @@ class Blocks extends React.Component {
canUseCloud,
customProceduresVisible,
deviceData,
- deviceId,
+ devices,
deviceLibraryVisible,
- deviceType,
peripheralName,
extensionLibraryVisible,
options,
@@ -789,8 +787,7 @@ Blocks.propTypes = {
canUseCloud: PropTypes.bool,
customProceduresVisible: PropTypes.bool,
deviceData: PropTypes.instanceOf(Array).isRequired,
- deviceId: PropTypes.string,
- deviceType: PropTypes.string,
+ devices: PropTypes.objectOf(PropTypes.object),
peripheralName: PropTypes.string,
deviceLibraryVisible: PropTypes.bool,
extensionLibraryVisible: PropTypes.bool,
@@ -891,8 +888,7 @@ const mapStateToProps = state => ({
state.scratchGui.mode.isFullScreen
),
deviceData: state.scratchGui.deviceData.deviceData,
- deviceId: state.scratchGui.device.deviceId,
- deviceType: state.scratchGui.device.deviceType,
+ devices: state.scratchGui.devices,
peripheralName: state.scratchGui.connectionModal.peripheralName,
deviceLibraryVisible: state.scratchGui.modals.deviceLibrary,
extensionLibraryVisible: state.scratchGui.modals.extensionLibrary,
@@ -909,10 +905,11 @@ const mapStateToProps = state => ({
const mapDispatchToProps = dispatch => ({
onActivateColorPicker: callback => dispatch(activateColorPicker(callback)),
onActivateCustomProcedures: (data, callback) => dispatch(activateCustomProcedures(data, callback)),
- onDeviceSelected: (id, name, type) => {
- dispatch(setDeviceId(id));
- dispatch(setDeviceName(name));
- dispatch(setDeviceType(type));
+ onDeviceSelected: device => {
+ dispatch(addDevice(device));
+ dispatch(setDeviceId(device.deviceId));
+ dispatch(setDeviceName(device.name));
+ dispatch(setDeviceType(device.type));
},
onOpenConnectionModal: () => {
dispatch(openConnectionModal());
diff --git a/src/containers/device-library.jsx b/src/containers/device-library.jsx
index b353b29af7f..859f9494f8f 100644
--- a/src/containers/device-library.jsx
+++ b/src/containers/device-library.jsx
@@ -66,20 +66,22 @@ class DeviceLibrary extends React.PureComponent {
requestLoadDevice (device) {
const id = device.deviceId;
+ const deviceType = device.type;
+ const pnpidList = device.pnpidList;
const deviceExtensions = device.deviceExtensions;
if (id && !device.disabled) {
if (this.props.vm.extensionManager.isDeviceLoaded(id)) {
- this.props.onDeviceSelected(id);
+ this.props.onDeviceSelected(device);
} else {
- this.props.vm.extensionManager.loadDeviceURL(device).then(() => {
+ this.props.vm.extensionManager.loadDeviceURL(id, deviceType, pnpidList).then(() => {
this.props.vm.extensionManager.getDeviceExtensionsList().then(() => {
// TODO: Add a event for install device extension
// the large extensions will take many times to load
// A loading interface should be launched.
this.props.vm.installDeviceExtensions(Object.assign([], deviceExtensions));
});
- this.props.onDeviceSelected(id);
+ this.props.onDeviceSelected(device);
analytics.event({
category: 'devices',
action: 'select device',
diff --git a/src/lib/make-toolbox-xml.js b/src/lib/make-toolbox-xml.js
index 867373de74a..c3aeaf65807 100644
--- a/src/lib/make-toolbox-xml.js
+++ b/src/lib/make-toolbox-xml.js
@@ -723,7 +723,7 @@ const xmlClose = '';
/**
* @param {!boolean} isInitialSetup - Whether the toolbox is for initial setup. If the mode is "initial setup",
* blocks with localized default parameters (e.g. ask and wait) should not be loaded. (LLK/scratch-gui#5445)
- * @param {?object} device - Full data of current selected deivce.
+ * @param {?object} devices - Full data of current selected deivces.
* @param {?boolean} isStage - Whether the toolbox is for a stage-type target. This is always set to true
* when isInitialSetup is true.
* @param {?string} targetId - The current editing target
@@ -737,7 +737,7 @@ const xmlClose = '';
* @param {?string} soundName - The name of the default selected sound dropdown.
* @returns {string} - a ScratchBlocks-style XML document for the contents of the toolbox.
*/
-const makeToolboxXML = function (isInitialSetup, device = null, isStage = true, targetId, categoriesXML = [],
+const makeToolboxXML = function (isInitialSetup, devices = null, isStage = true, targetId, categoriesXML = [],
isRealtimeMode = true,
costumeName = '', backdropName = '', soundName = '') {
isStage = isInitialSetup || isStage;
@@ -759,10 +759,11 @@ const makeToolboxXML = function (isInitialSetup, device = null, isStage = true,
let everything = [xmlOpen];
- if (device) {
+ console.log('[makeToolboxXML] devices:', devices);
+ if (devices && Object.keys(devices).length > 0) {
+ const device = devices[Object.keys(devices)[0]];
const baseToolboxXml = device.baseToolBoxXml(isInitialSetup, isStage, targetId, isRealtimeMode,
costumeName, backdropName, soundName);
-
everything = everything.concat(baseToolboxXml);
} else {
const motionXML = moveCategory('motion') || motion(isInitialSetup, isStage, targetId);
diff --git a/src/reducers/devices.js b/src/reducers/devices.js
new file mode 100644
index 00000000000..e1aba567a70
--- /dev/null
+++ b/src/reducers/devices.js
@@ -0,0 +1,44 @@
+const ADD_DEVICE = 'scratch-gui/devices/add';
+const REMOVE_DEVICE = 'scratch-gui/devices/remove';
+
+const initialState = {
+};
+
+const reducer = function (state, action) {
+ if (typeof state === 'undefined') state = initialState;
+ const newState = Object.assign({}, state);
+ switch (action.type) {
+ case ADD_DEVICE: {
+ newState[action.deviceId] = action.device;
+ return newState;
+ }
+ case REMOVE_DEVICE: {
+ delete newState[action.deviceId];
+ return newState;
+ }
+ default:
+ return state;
+ }
+};
+
+const addDevice = function (device) {
+ return {
+ type: ADD_DEVICE,
+ deviceId: device.deviceId,
+ device: device
+ };
+};
+
+const removeDevice = function (deviceId) {
+ return {
+ type: REMOVE_DEVICE,
+ deviceId: deviceId
+ };
+};
+
+export {
+ reducer as default,
+ initialState as devicesInitialState,
+ addDevice,
+ removeDevice
+};
diff --git a/src/reducers/gui.js b/src/reducers/gui.js
index 37e94186451..8bc083a9e70 100644
--- a/src/reducers/gui.js
+++ b/src/reducers/gui.js
@@ -31,6 +31,7 @@ import programModeReducer, {programModeInitialState} from './program-mode';
import codeReducer, {codeInitialState} from './code';
import deviceReducer, {deviceInitialState} from './device';
import deviceDataReducer, {deviceDataInitialState} from './device-data';
+import devicesReducer, {devicesInitialState} from './devices';
import hardwareConsoleReducer, {hardwareConsoleInitialState} from './hardware-console';
import updateReducer, {updateInitialState} from './update';
@@ -49,6 +50,7 @@ const guiInitialState = {
customProcedures: customProceduresInitialState,
device: deviceInitialState,
deviceData: deviceDataInitialState,
+ devices: devicesInitialState,
hardwareConsole: hardwareConsoleInitialState,
editorTab: editorTabInitialState,
mode: modeInitialState,
@@ -154,6 +156,7 @@ const guiReducer = combineReducers({
customProcedures: customProceduresReducer,
device: deviceReducer,
deviceData: deviceDataReducer,
+ devices: devicesReducer,
editorTab: editorTabReducer,
mode: modeReducer,
hardwareConsole: hardwareConsoleReducer,