cloutcontracts
Version:
CCS Agoric DAPP Implementation
146 lines (134 loc) • 3.6 kB
JavaScript
// @ts-check
/* globals window document */
import { registerSocket, getActiveSocket, closeSocket } from './socket';
// Wallet bridge
/**
* @typedef {Object} SocketHandler
* @property {() => void} [onConnect]
* @property {(msg: Record<string, unknown>) => void} [onMessage]
* @property {() => void} [onDisconnect]
*/
const walletBridgeId = 'walletBridgeIFrame';
let walletLoaded = false;
const connectSubscriptions = new Set();
const messageSubscriptions = new Set();
let initializedIframe = false;
/**
* Make a new wallet bridge "socket", via postMessage to an iframe.
*
* @param {SocketHandler} handler
* @param {string} endpoint
*/
function createSocket(
{ onConnect, onDisconnect, onMessage },
endpoint = '/private/wallet-bridge',
) {
let ifr = /** @type {HTMLIFrameElement} */ (document.getElementById(
walletBridgeId,
));
if (!ifr) {
ifr = document.querySelector(`#${walletBridgeId}`);
}
if (!initializedIframe) {
initializedIframe = true;
window.addEventListener('message', (ev) => {
// console.log('dapp ui got', ev);
if (ev.data && ev.data.type === 'walletBridgeLoaded') {
walletLoaded = true;
for (const sub of connectSubscriptions.keys()) {
sub();
}
connectSubscriptions.clear();
} else {
const obj = ev.data;
for (const sub of messageSubscriptions.keys()) {
sub(obj);
}
}
});
}
let ifrQueue = [];
const flushQueue = () => {
const q = ifrQueue;
ifrQueue = undefined;
ifr.removeEventListener('load', flushQueue);
while (q.length) {
const obj = q.shift();
ifr.contentWindow.postMessage(obj, window.origin);
}
};
ifr.addEventListener('load', flushQueue);
// FIXME: Don't assume our location.
const queryPos = endpoint.indexOf('?');
if (queryPos >= 0) {
ifr.src = `lib/agoric-wallet.html${endpoint.substr(queryPos)}`;
} else {
ifr.src = 'lib/agoric-wallet.html';
}
if (onMessage) {
messageSubscriptions.add(onMessage);
}
const messageListeners = new Set();
registerSocket(endpoint, {
send(obj) {
if (ifrQueue) {
ifrQueue.push(obj);
} else {
ifr.contentWindow.postMessage(obj, window.origin);
}
},
addHandler(handler) {
messageListeners.add(handler);
messageSubscriptions.add(handler);
},
removeHandler(handler) {
messageSubscriptions.delete(handler);
messageListeners.delete(handler);
},
close() {
walletLoaded = false;
if (onConnect) {
connectSubscriptions.delete(onConnect);
}
if (onMessage) {
messageSubscriptions.delete(onMessage);
}
for (const sub of messageListeners.keys()) {
messageSubscriptions.delete(sub);
}
ifr = /** @type {HTMLIFrameElement} */ (document.getElementById(
walletBridgeId,
));
if (ifr) {
ifr.src = '';
}
if (onDisconnect) {
onDisconnect();
}
},
});
if (onConnect) {
if (walletLoaded) {
onConnect();
} else {
connectSubscriptions.add(onConnect);
}
}
}
/**
* Start a given socket connection.
*
* @param {SocketHandler} socketListeners
* @param {*} endpoint
*/
export function activateSocket(
socketListeners = {},
endpoint = '/private/wallet-bridge',
) {
if (getActiveSocket(endpoint)) return;
createSocket(socketListeners, endpoint);
}
export function deactivateSocket(endpoint = '/private/wallet-bridge') {
if (!getActiveSocket(endpoint)) return;
closeSocket(endpoint);
}