@thebigcrunch/sdk
Version:
The big crunch SDK library
228 lines (202 loc) • 6.75 kB
JavaScript
import EventEmitter from "events";
import { getConfig } from "./config";
import {
createCrunchSocket,
getSpaceListener,
addSpaceListener,
getCrunchSocket
} from "./globalVars";
const getSocket = function getSocket() {
let crunchSocket = getCrunchSocket();
if (!crunchSocket) {
const url = getConfig().spacesSocketUrl;
crunchSocket = createCrunchSocket(url);
crunchSocket.on("update", (value, space) => {
// Bug here - If you're listening to the same cell on tbc_x_y and uuid this will only send it to one.
const spaceListener =
getSpaceListener(atob(space.location)) ||
getSpaceListener(space.encodedUuid);
if (spaceListener) {
spaceListener.lastValue = value;
spaceListener.lastSpace = space;
spaceListener.emitter.emit("spaceUpdate", value, space);
} else {
// This happens if a connection has been closed and there is no longer a callback for it.
//We should leave the room in this case perhaps.
console.error(
"Received a space value we were not listening to: ",
space.value
);
}
});
crunchSocket.on("spaceNotFound", spaceId => {
const spaceListener =
getSpaceListener(spaceId) || getSpaceListener(atob(spaceId));
if (spaceListener) {
spaceListener.emitter.emit("spaceNotFound", "invalid space");
}
});
crunchSocket.on("user", (user, spaceId) => {
const spaceListener =
getSpaceListener(spaceId) || getSpaceListener(atob(spaceId));
if (spaceListener) {
spaceListener.emitter.emit("user", user);
}
});
} else {
// reusing existing crunch sockets
}
return crunchSocket;
};
// Listens to changes in the whole space (for SDK users)
const listenToSpace = (
spaceId,
sectorId,
callback,
errorCallback,
userCallback
) => {
const crunchSocket = getSocket();
let spaceListener = getSpaceListener(spaceId);
if (!spaceListener) {
spaceListener = addSpaceListener(spaceId, new EventEmitter(), "", {});
crunchSocket.emit("join", spaceId, sectorId);
} else {
callback(spaceListener.lastValue, spaceListener.lastSpace);
}
spaceListener.emitter.on("spaceUpdate", callback);
spaceListener.emitter.on("spaceNotFound", errorCallback);
if (userCallback !== undefined) {
crunchSocket.emit("get_user", spaceId, sectorId, userCallback);
}
return {
close: () => {
spaceListener.emitter.removeListener("spaceUpdate", callback);
spaceListener.emitter.removeListener(
"spaceNotFound",
errorCallback
);
}
};
};
function readSpaceId(encodedSpaceId) {
if (!encodedSpaceId) {
return;
}
if (encodedSpaceId.slice(0, 3) === "tbc") {
// Old school format tbc_x_y
return encodedSpaceId;
}
try {
const decodedString = atob(encodedSpaceId);
// Middle school format ascii encoded tbc_x_y
if (decodedString.slice(0, 3) === "tbc") {
return atob(encodedSpaceId);
} else {
// Current model nothing to do here.
return encodedSpaceId;
}
} catch (e) {
// Not a valid base64 string
// Current model nothing to do here.
return encodedSpaceId;
}
}
function createCallBacks(callback, errorCallback, userCallback) {
const changeCall = typeof callback === "function" ? callback : undefined;
const errorChangeCallback =
typeof errorCallback === "function" ? errorCallback : () => {};
const userArrivesCallback =
typeof userCallback === "function" ? userCallback : undefined;
return {
changeCall,
errorChangeCallback,
userArrivesCallback
};
}
// Register for updates to the specified space ID.
// - callback: function will be invoked with native values and the end-user friendly space object
export function crunch(encodedSpaceId, callback, errorCallback, userCallback) {
const spaceId = readSpaceId(encodedSpaceId);
const callbacks = createCallBacks(callback, errorCallback, userCallback);
if (!callbacks.changeCall) {
console.error(
"The Big Crunch: You must supply a callback function to TBC.crunch()"
);
return;
}
if (!spaceId) {
console.error("Cell ID not provided to TBC.crunch");
return;
}
return listenToSpace(
spaceId,
undefined,
callbacks.changeCall,
callbacks.errorChangeCallback,
callbacks.userArrivesCallback
);
}
// This is more performant
export function crunchWithSectorId(
encodedSpaceId,
sectorId,
callback,
errorCallback,
userCallback
) {
const spaceId = readSpaceId(encodedSpaceId);
const callbacks = createCallBacks(callback, errorCallback, userCallback);
if (!callbacks.changeCall) {
console.error(
"The Big Crunch: You must supply a callback function to TBC.crunch()"
);
return;
}
if (!spaceId) {
console.error("Cell ID not provided to TBC.crunch");
return;
}
if (!sectorId) {
console.error("Sector ID not provided to TBC.crunch");
return;
}
return listenToSpace(
spaceId,
sectorId,
callbacks.changeCall,
callbacks.errorChangeCallback,
callbacks.userArrivesCallback
);
}
export function crunchBySpaceName(spaceId, name, callback, errorCallback) {
if (!spaceId || !name) {
console.error(
"The Big Crunch: You must supply a spaceId and a name to TBC.crunchBySpaceName"
);
}
crunchSocket = getSocket();
let spaceListenerId;
crunchSocket.emit(
"get_space_uuid_from_name_space",
{ spaceId, name },
cellUuid => {
spaceListenerId = cellUuid;
// Hacky, we need to return this close function.
return crunch(cellUuid, callback, errorCallback).close;
}
);
const listener = {
close: () => {
let spaceListener = getSpaceListener(spaceListenerId);
spaceListener.emitter.removeListener("spaceUpdate", callback);
if (errorCallback) {
spaceListener.emitter.removeListener(
"spaceNotFound",
errorCallback
);
}
}
};
return listener;
}