signal-desktop/ts/CI.ts

103 lines
2.6 KiB
TypeScript
Raw Normal View History

2021-08-11 19:29:07 +00:00
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { ipcRenderer } from 'electron';
import { explodePromise } from './util/explodePromise';
2021-11-08 21:43:37 +00:00
import { SECOND } from './util/durations';
import * as log from './logging/log';
import type { IPCResponse as ChallengeResponseType } from './challenge';
2021-08-11 19:29:07 +00:00
type ResolveType = (data: unknown) => void;
2023-01-13 00:24:59 +00:00
export type CIType = {
deviceName: string;
handleEvent: (event: string, data: unknown) => unknown;
setProvisioningURL: (url: string) => unknown;
solveChallenge: (response: ChallengeResponseType) => unknown;
waitForEvent: (event: string, timeout?: number) => unknown;
};
export function getCI(deviceName: string): CIType {
const eventListeners = new Map<string, Array<ResolveType>>();
const completedEvents = new Map<string, Array<unknown>>();
ipcRenderer.on('ci:event', (_, event, data) => {
handleEvent(event, data);
});
function waitForEvent(event: string, timeout = 60 * SECOND) {
const pendingCompleted = completedEvents.get(event) || [];
2021-08-11 19:29:07 +00:00
const pending = pendingCompleted.shift();
if (pending) {
log.info(`CI: resolving pending result for ${event}`, pending);
2021-08-11 19:29:07 +00:00
if (pendingCompleted.length === 0) {
2023-01-13 00:24:59 +00:00
completedEvents.delete(event);
2021-08-11 19:29:07 +00:00
}
return pending;
}
log.info(`CI: waiting for event ${event}`);
2021-11-08 21:43:37 +00:00
const { resolve, reject, promise } = explodePromise();
const timer = setTimeout(() => {
reject(new Error('Timed out'));
}, timeout);
2021-08-11 19:29:07 +00:00
2023-01-13 00:24:59 +00:00
let list = eventListeners.get(event);
2021-08-11 19:29:07 +00:00
if (!list) {
list = [];
2023-01-13 00:24:59 +00:00
eventListeners.set(event, list);
2021-08-11 19:29:07 +00:00
}
2021-11-08 21:43:37 +00:00
list.push((value: unknown) => {
clearTimeout(timer);
resolve(value);
});
2021-08-11 19:29:07 +00:00
return promise;
}
2023-01-13 00:24:59 +00:00
function setProvisioningURL(url: string): void {
handleEvent('provisioning-url', url);
2021-08-11 19:29:07 +00:00
}
2023-01-13 00:24:59 +00:00
function handleEvent(event: string, data: unknown): void {
const list = eventListeners.get(event) || [];
2021-08-11 19:29:07 +00:00
const resolve = list.shift();
if (resolve) {
if (list.length === 0) {
2023-01-13 00:24:59 +00:00
eventListeners.delete(event);
2021-08-11 19:29:07 +00:00
}
log.info(`CI: got event ${event} with data`, data);
2021-08-11 19:29:07 +00:00
resolve(data);
return;
}
log.info(`CI: postponing event ${event}`);
2021-08-11 19:29:07 +00:00
2023-01-13 00:24:59 +00:00
let resultList = completedEvents.get(event);
2021-08-11 19:29:07 +00:00
if (!resultList) {
resultList = [];
2023-01-13 00:24:59 +00:00
completedEvents.set(event, resultList);
2021-08-11 19:29:07 +00:00
}
resultList.push(data);
}
2023-01-13 00:24:59 +00:00
function solveChallenge(response: ChallengeResponseType): void {
window.Signal.challengeHandler?.onResponse(response);
}
2023-01-13 00:24:59 +00:00
return {
deviceName,
handleEvent,
setProvisioningURL,
solveChallenge,
waitForEvent,
};
2021-08-11 19:29:07 +00:00
}