PostMessageProvider with on('connected' | 'disconnected') (#279)

* PostMessageProvider with on('connected' | 'disconnected')

* Update packages/extension/src/background/types.ts
This commit is contained in:
Amaury Martiny 2020-03-24 11:35:37 +01:00 committed by GitHub
parent 1dee8f1d0f
commit 956a2c37ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 56 additions and 12 deletions

View File

@ -247,7 +247,7 @@ export default class State {
public rpcSend (request: RequestRpcSend, port: chrome.runtime.Port): Promise<JsonRpcResponse> {
const provider = this.#injectedProviders.get(port);
assert(provider, 'Cannot call pub(rpc.subscribe) before provider has been set');
assert(provider, 'Cannot call pub(rpc.subscribe) before provider is set');
return provider.send(request.method, request.params);
}
@ -280,15 +280,25 @@ export default class State {
public rpcSubscribe ({ method, params, type }: RequestRpcSubscribe, cb: ProviderInterfaceCallback, port: chrome.runtime.Port): Promise<number> {
const provider = this.#injectedProviders.get(port);
assert(provider, 'Cannot call pub(rpc.subscribe) before provider has been set');
assert(provider, 'Cannot call pub(rpc.subscribe) before provider is set');
return provider.subscribe(type, method, params, cb);
}
public rpcSubscribeConnected (_request: null, cb: ProviderInterfaceCallback, port: chrome.runtime.Port): void {
const provider = this.#injectedProviders.get(port);
assert(provider, 'Cannot call pub(rpc.subscribeConnected) before provider is set');
cb(null, provider.isConnected()); // Immediately send back current isConnected
provider.on('connected', () => cb(null, true));
provider.on('disconnected', () => cb(null, false));
}
public rpcUnsubscribe (request: RequestRpcUnsubscribe, port: chrome.runtime.Port): Promise<boolean> {
const provider = this.#injectedProviders.get(port);
assert(provider, 'Cannot call pub(rpc.unsubscribe) before provider has been set');
assert(provider, 'Cannot call pub(rpc.unsubscribe) before provider is set');
return provider.unsubscribe(request.type, request.method, request.subscriptionId);
}

View File

@ -98,6 +98,18 @@ export default class Tabs {
return true;
}
private rpcSubscribeConnected (request: null, id: string, port: chrome.runtime.Port): Promise<boolean> {
const innerCb = createSubscription<'pub(rpc.subscribeConnected)'>(id, port);
const cb = (_error: Error | null, data: SubscriptionMessageTypes['pub(rpc.subscribeConnected)']): void => innerCb(data);
this.#state.rpcSubscribeConnected(request, cb, port);
port.onDisconnect.addListener((): void => {
unsubscribe(id);
});
return Promise.resolve(true);
}
private async rpcUnsubscribe (request: RequestRpcUnsubscribe, port: chrome.runtime.Port): Promise<boolean> {
return this.#state.rpcUnsubscribe(request, port);
}
@ -135,6 +147,9 @@ export default class Tabs {
case 'pub(rpc.subscribe)':
return this.rpcSubscribe(request as RequestRpcSubscribe, id, port);
case 'pub(rpc.subscribeConnected)':
return this.rpcSubscribeConnected(request as null, id, port);
case 'pub(rpc.unsubscribe)':
return this.rpcUnsubscribe(request as RequestRpcUnsubscribe, port);

View File

@ -72,6 +72,7 @@ export interface RequestSignatures {
'pub(rpc.send)': [RequestRpcSend, JsonRpcResponse];
'pub(rpc.startProvider)': [string, ProviderMeta];
'pub(rpc.subscribe)': [RequestRpcSubscribe, number, JsonRpcResponse];
'pub(rpc.subscribeConnected)': [null, boolean, boolean];
'pub(rpc.unsubscribe)': [RequestRpcUnsubscribe, boolean];
}

View File

@ -29,6 +29,9 @@ interface SubscriptionHandler {
export default class PostMessageProvider implements InjectedProvider {
readonly #eventemitter: EventEmitter;
// Whether or not the actual extension background provider is connected
#isConnected = false
readonly #sendRequest: SendRequest;
// Subscription IDs are (historically) not guaranteed to be globally unique;
@ -43,11 +46,6 @@ export default class PostMessageProvider implements InjectedProvider {
public constructor (sendRequest: SendRequest) {
this.#eventemitter = new EventEmitter();
this.#sendRequest = sendRequest;
// Give subscribers time to subscribe
setTimeout((): void => {
this.#eventemitter.emit('connected');
});
}
/**
@ -78,8 +76,7 @@ export default class PostMessageProvider implements InjectedProvider {
* @return {boolean} true if connected
*/
public isConnected (): boolean {
// FIXME This should see if the extension's state's provider is connected
return true;
return this.#isConnected;
}
public listProviders (): Promise<ProviderList> {
@ -121,8 +118,29 @@ export default class PostMessageProvider implements InjectedProvider {
}
}
public startProvider (key: string): Promise<ProviderMeta> {
return this.#sendRequest('pub(rpc.startProvider)', key);
/**
* @summary Spawn a provider on the extension background.
*/
public async startProvider (key: string): Promise<ProviderMeta> {
// Disconnect from the previous provider
this.#isConnected = false;
this.#eventemitter.emit('disconnected');
const meta = await this.#sendRequest('pub(rpc.startProvider)', key);
this.#sendRequest('pub(rpc.subscribeConnected)', null, (connected) => {
this.#isConnected = connected;
if (connected) {
this.#eventemitter.emit('connected');
} else {
this.#eventemitter.emit('disconnected');
}
return true;
});
return meta;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any