pluto-mams
Version:
Pluto Multi App Messaging Service
179 lines (178 loc) • 6.68 kB
JavaScript
import { v4 as uuidv4 } from "uuid";
/**
* Events that Pluto Multi App sends to all running XRPKs
* Subscribe to these events with PMAEventHandler.addEventListener()
*/
export var PMAEventType;
(function (PMAEventType) {
PMAEventType["appAdded"] = "plutomams-event-app-added";
PMAEventType["appRemoved"] = "plutomams-event-app-removed";
PMAEventType["conversationUserUpdate"] = "plutomams-event-conversation-user-update";
PMAEventType["promiseResponse"] = "plutomams-event-promise-response";
})(PMAEventType || (PMAEventType = {}));
/**
* All events from an XRPK are sent using this type
* @disclaimer Not intended to be used directly. Use the provided public functions to send events to Pluto Multi App
* */
export const XRPKEventType = "plutomams-xrpk-message";
/**
* Event descriptions that can be sent to Pluto Multi App from an XRPK
* @disclaimer These are not event types, but descriptions of events. Use the provided public functions to send events to Pluto Multi App
*/
export var XRPKEventTypeDescription;
(function (XRPKEventTypeDescription) {
XRPKEventTypeDescription["launchXRPK"] = "launch-xrpk";
XRPKEventTypeDescription["removeXRPK"] = "remove-xrpk";
XRPKEventTypeDescription["getAppList"] = "retrieve-app-list";
})(XRPKEventTypeDescription || (XRPKEventTypeDescription = {}));
;
/**
* Pluto Multi App Event Handler
* Use this class to send and receive messages to and from Pluto Multi App
*/
export default class PMAEventHandler {
constructor() {
const urlParams = new URLSearchParams(location.search);
let appIdQueryParam = urlParams.get("appid");
// TODO: Handle the case where an appid is not set by Pluto Multi App
this._appId = appIdQueryParam ? appIdQueryParam : uuidv4();
this._eventHandler = new EventHandler();
Object.values(PMAEventType).forEach(type => {
this._eventHandler.registerEvent(type);
window.parent.addEventListener(type, (e) => this._eventHandler.dispatchEvent(type, e.detail));
});
this._openPromises = {};
window.parent.addEventListener(PMAEventType.promiseResponse, (e) => this.handleResponse(e.detail.responseId, e.detail.data, e.detail.error));
}
/**
* Get the app ID of this XRPK, provided by Pluto Multi App
*/
get appId() {
// TODO: Handle the case where an appid is not set by Pluto Multi App
return this._appId;
}
/**
* Add an event listenter for Pluto Multi App Events
* @param type The `PlutoMAEventType` to subscribe to
* @param callback The callback to run when the event is receieved
*/
addEventListener(type, callback) {
this._eventHandler.addEventListener(type, callback);
}
/**
* Request to launch an XRPK in Pluto Multi App
* @param linkUrl The url of the XRPK. Must link directly to the file (ie. .wbn file)
* @returns A promise once a response is receieved from Pluto Multi App
*/
launchXRPK(linkUrl) {
if (!this.isIframe()) {
console.log("App is not running as an xrpk; cannot send an event to Pluto Multi App");
}
let responseId = this.generateResponseId(this._appId);
return new Promise((resolve, reject) => {
this._openPromises[responseId] = { resolve, reject };
window.parent.dispatchEvent(new CustomEvent(XRPKEventType, {
detail: {
type: XRPKEventTypeDescription.launchXRPK,
appid: this.appId,
url: linkUrl,
responseId: responseId,
},
}));
});
}
/**
* Request to remove an XRPK in Pluto Multi App
* @param appIdToRemove The app ID of the app to be removed
* @returns A promise once a response is receieved from Pluto Multi App
*/
removeXRPK(appIdToRemove) {
if (!this.isIframe()) {
console.log("App is not running as an xrpk; cannot send an event to Pluto Multi App");
}
let responseId = this.generateResponseId(this._appId);
return new Promise((resolve, reject) => {
this._openPromises[responseId] = { resolve, reject };
window.parent.dispatchEvent(new CustomEvent(XRPKEventType, {
detail: {
type: XRPKEventTypeDescription.removeXRPK,
appid: this.appId,
appIdToRemove,
responseId,
},
}));
});
}
/**
* Get the current app list from Pluto Multi App
* @return A promise that when resolved includes the app list
* @returns A promise once a response is receieved from Pluto Multi App
*/
retrieveAppList() {
if (!this.isIframe()) {
console.log("App is not running as an xrpk; cannot send an event to Pluto Multi App");
}
let responseId = this.generateResponseId(this._appId);
return new Promise((resolve, reject) => {
this._openPromises[responseId] = { resolve, reject };
window.parent.dispatchEvent(new CustomEvent(XRPKEventType, {
detail: {
type: XRPKEventTypeDescription.getAppList,
appId: this.appId,
responseId: responseId,
},
}));
});
}
// Private functions
handleResponse(responseId, data, error) {
if (responseId in this._openPromises) {
let { resolve, reject } = this._openPromises[responseId];
if (error)
reject(error);
else if (data)
resolve(data);
else
resolve();
delete this._openPromises[responseId];
}
}
generateResponseId(messageId) {
return `${messageId}-response-${uuidv4()}`;
}
isIframe() {
try {
return window.self !== window.top;
}
catch (e) {
return true;
}
}
}
// Private helper classes
class Event {
constructor(type) {
this.type = type;
this.callbacks = [];
}
registerCallback(callback) {
this.callbacks.push(callback);
}
}
class EventHandler {
constructor() {
this.events = {};
}
registerEvent(type) {
var event = new Event(type);
this.events[type] = event;
}
dispatchEvent(type, args) {
this.events[type].callbacks.forEach((callback) => {
callback(args);
});
}
addEventListener(type, callback) {
this.events[type].registerCallback(callback);
}
}