Skip to content
Snippets Groups Projects
Unverified Commit a00a5298 authored by Karishma Chadha's avatar Karishma Chadha Committed by GitHub
Browse files

Merge pull request #3539 from kchadha/cloud-connect

Cloud Data Provider
parents b18b428a 52b3d44e
No related branches found
No related tags found
No related merge requests found
...@@ -73,6 +73,7 @@ class GUI extends React.Component { ...@@ -73,6 +73,7 @@ class GUI extends React.Component {
const { const {
/* eslint-disable no-unused-vars */ /* eslint-disable no-unused-vars */
assetHost, assetHost,
cloudHost,
error, error,
hideIntro, hideIntro,
isError, isError,
...@@ -104,6 +105,7 @@ class GUI extends React.Component { ...@@ -104,6 +105,7 @@ class GUI extends React.Component {
GUI.propTypes = { GUI.propTypes = {
assetHost: PropTypes.string, assetHost: PropTypes.string,
children: PropTypes.node, children: PropTypes.node,
cloudHost: PropTypes.string,
error: PropTypes.oneOfType([PropTypes.object, PropTypes.string]), error: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
fetchingProject: PropTypes.bool, fetchingProject: PropTypes.bool,
hideIntro: PropTypes.bool, hideIntro: PropTypes.bool,
......
import log from './log.js';
class CloudProvider {
/**
* A cloud data provider which creates and manages a web socket connection
* to the Scratch cloud data server. This provider is responsible for
* interfacing with the VM's cloud io device.
* @param {string} cloudHost The url for the cloud data server
* @param {VirtualMachine} vm The Scratch virtual machine to interface with
* @param {string} username The username to associate cloud data updates with
* @param {string} projectId The id associated with the project containing
* cloud data.
*/
constructor (cloudHost, vm, username, projectId) {
this.vm = vm;
this.username = username;
this.projectId = projectId;
// Open a websocket connection to the clouddata server
this.openConnection(cloudHost);
}
/**
* Open a new websocket connection to the clouddata server.
* @param {string} cloudHost The cloud data server to connect to.
*/
openConnection (cloudHost) {
if (window.WebSocket === null) {
log.warn('Websocket support is not available in this browser');
this.connection = null;
return;
}
this.connection = new WebSocket((location.protocol === 'http:' ? 'ws://' : 'wss://') + cloudHost);
this.connection.onerror = e => {
log.error(`Websocket connection error: ${JSON.stringify(e)}`);
// TODO Add re-connection attempt logic here
};
this.connection.onmessage = event => {
const messageString = event.data;
log.info(`Received websocket message: ${messageString}`);
const message = JSON.parse(messageString);
if (message.method === 'set') {
const varData = {
varUpdate: {
name: message.name,
value: message.value
}
};
this.vm.postIOData('cloud', varData);
}
};
this.connection.onopen = () => {
this.writeToServer('handshake');
log.info(`Successfully connected to clouddata server.`);
};
this.connection.onclose = () => {
log.info(`Closed connection to websocket`);
};
}
/**
* Format and send a message to the cloud data server.
* @param {string} methodName The message method, indicating the action to perform.
* @param {string} dataName The name of the cloud variable this message pertains to
* @param {string | number} dataValue The value to set the cloud variable to
* @param {number} dataIndex The index of the item to update (for cloud lists)
* @param {string} dataNewName The new name for the cloud variable (if renaming)
*/
writeToServer (methodName, dataName, dataValue, dataIndex, dataNewName) {
const msg = {};
msg.method = methodName;
msg.user = this.username;
msg.project_id = this.projectId;
if (dataName) msg.name = dataName;
if (dataValue) msg.value = dataValue;
if (dataIndex) msg.index = dataIndex;
if (dataNewName) msg.new_name = dataNewName;
const dataToWrite = JSON.stringify(msg);
this.sendCloudData(dataToWrite);
}
/**
* Send a formatted message to the cloud data server.
* @param {string} data The formatted message to send.
*/
sendCloudData (data) {
this.connection.send(`${data}\n`);
log.info(`Sent message to clouddata server: ${data}`);
}
/**
* Provides an API for the VM's cloud IO device to update
* a cloud variable on the server.
* @param {string} name The name of the variable to update
* @param {string | number} value The new value for the variable
*/
updateVariable (name, value) {
this.writeToServer('set', name, value);
}
/**
* Closes the connection to the web socket and clears the cloud
* provider of references related to the cloud data project.
*/
requestCloseConnection () {
this.connection.close();
this.clear();
}
/**
* Clear this provider of references related to the project
* and current state.
*/
clear () {
this.connection = null;
this.vm = null;
this.username = null;
this.projectId = null;
}
}
export default CloudProvider;
...@@ -102,7 +102,6 @@ const vmListenerHOC = function (WrappedComponent) { ...@@ -102,7 +102,6 @@ const vmListenerHOC = function (WrappedComponent) {
/* eslint-disable no-unused-vars */ /* eslint-disable no-unused-vars */
attachKeyboardEvents, attachKeyboardEvents,
shouldEmitTargetsUpdate, shouldEmitTargetsUpdate,
username,
onBlockDragUpdate, onBlockDragUpdate,
onKeyDown, onKeyDown,
onKeyUp, onKeyUp,
......
...@@ -5,6 +5,7 @@ import {connect} from 'react-redux'; ...@@ -5,6 +5,7 @@ import {connect} from 'react-redux';
import VM from 'scratch-vm'; import VM from 'scratch-vm';
import AudioEngine from 'scratch-audio'; import AudioEngine from 'scratch-audio';
import CloudProvider from '../lib/cloud-provider';
import { import {
LoadingStates, LoadingStates,
...@@ -46,6 +47,18 @@ const vmManagerHOC = function (WrappedComponent) { ...@@ -46,6 +47,18 @@ const vmManagerHOC = function (WrappedComponent) {
return this.props.vm.loadProject(this.props.projectData) return this.props.vm.loadProject(this.props.projectData)
.then(() => { .then(() => {
this.props.onLoadedProject(this.props.loadingState, this.props.canSave); this.props.onLoadedProject(this.props.loadingState, this.props.canSave);
// If the cloud host exists, open a cloud connection and
// set the vm's cloud provider.
if (this.props.cloudHost) {
// TODO check if we should actually
// connect to cloud data based on info from the loaded project and
// info about the user (e.g. scratcher status)
this.props.vm.setCloudProvider(new CloudProvider(
this.props.cloudHost,
this.props.vm,
this.props.username,
this.props.projectId));
}
}) })
.catch(e => { .catch(e => {
this.props.onError(e); this.props.onError(e);
...@@ -54,12 +67,14 @@ const vmManagerHOC = function (WrappedComponent) { ...@@ -54,12 +67,14 @@ const vmManagerHOC = function (WrappedComponent) {
render () { render () {
const { const {
/* eslint-disable no-unused-vars */ /* eslint-disable no-unused-vars */
cloudHost,
fontsLoaded, fontsLoaded,
loadingState, loadingState,
onError: onErrorProp, onError: onErrorProp,
onLoadedProject: onLoadedProjectProp, onLoadedProject: onLoadedProjectProp,
projectData, projectData,
projectId, projectId,
username,
/* eslint-enable no-unused-vars */ /* eslint-enable no-unused-vars */
isLoadingWithId: isLoadingWithIdProp, isLoadingWithId: isLoadingWithIdProp,
vm, vm,
...@@ -77,6 +92,7 @@ const vmManagerHOC = function (WrappedComponent) { ...@@ -77,6 +92,7 @@ const vmManagerHOC = function (WrappedComponent) {
VMManager.propTypes = { VMManager.propTypes = {
canSave: PropTypes.bool, canSave: PropTypes.bool,
cloudHost: PropTypes.string,
fontsLoaded: PropTypes.bool, fontsLoaded: PropTypes.bool,
isLoadingWithId: PropTypes.bool, isLoadingWithId: PropTypes.bool,
loadingState: PropTypes.oneOf(LoadingStates), loadingState: PropTypes.oneOf(LoadingStates),
...@@ -84,6 +100,7 @@ const vmManagerHOC = function (WrappedComponent) { ...@@ -84,6 +100,7 @@ const vmManagerHOC = function (WrappedComponent) {
onLoadedProject: PropTypes.func, onLoadedProject: PropTypes.func,
projectData: PropTypes.oneOfType([PropTypes.object, PropTypes.string]), projectData: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
username: PropTypes.string,
vm: PropTypes.instanceOf(VM).isRequired vm: PropTypes.instanceOf(VM).isRequired
}; };
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment