diff --git a/src/WidgetMessaging.js b/src/WidgetMessaging.js index 5f877bd48a..6a20c340a5 100644 --- a/src/WidgetMessaging.js +++ b/src/WidgetMessaging.js @@ -87,6 +87,19 @@ export default class WidgetMessaging { }); } + /** + * Tells the widget that it should terminate now. + * It is not necessarily called in all instances before the widget is removed, + * and the client may force termination with a timeout. + * @returns {Promise<*>} Resolves when widget has acknowledged the message. + */ + terminate() { + return this.messageToWidget({ + api: OUTBOUND_API_NAME, + action: KnownWidgetActions.Terminate, + }); + } + /** * Request a screenshot from a widget * @return {Promise} To be resolved with screenshot data when it has been generated diff --git a/src/widgets/WidgetApi.ts b/src/widgets/WidgetApi.ts index 05237d258f..e08476edb5 100644 --- a/src/widgets/WidgetApi.ts +++ b/src/widgets/WidgetApi.ts @@ -34,6 +34,7 @@ export enum KnownWidgetActions { ReceiveOpenIDCredentials = "openid_credentials", SetAlwaysOnScreen = "set_always_on_screen", ClientReady = "im.vector.ready", + Terminate = "im.vector.terminate", } export type WidgetAction = KnownWidgetActions | string; @@ -68,6 +69,8 @@ export class WidgetApi { private inFlightRequests: { [requestId: string]: (reply: FromWidgetRequest) => void } = {}; private readyPromise: Promise; private readyPromiseResolve: () => void; + private terminatePromise: Promise; + private terminatePromiseResolve: () => void; /** * Set this to true if your widget is expecting a ready message from the client. False otherwise (default). @@ -78,6 +81,7 @@ export class WidgetApi { this.origin = new URL(currentUrl).origin; this.readyPromise = new Promise(resolve => this.readyPromiseResolve = resolve); + this.terminatePromise = new Promise(resolve => this.terminatePromiseResolve = resolve); window.addEventListener("message", event => { if (event.origin !== this.origin) return; // ignore: invalid origin @@ -98,6 +102,12 @@ export class WidgetApi { // Automatically acknowledge so we can move on this.replyToRequest(payload, {}); + } else if (payload.action === KnownWidgetActions.Terminate) { + // Reply after resolving + this.terminatePromise.then(() => { + this.replyToRequest(payload, {}); + }); + this.terminatePromiseResolve(); } else { console.warn(`[WidgetAPI] Got unexpected action: ${payload.action}`); } @@ -116,6 +126,10 @@ export class WidgetApi { return this.readyPromise; } + public addTerminateCallback(action) { + this.terminatePromise = this.terminatePromise.then(action); + } + private replyToRequest(payload: ToWidgetRequest, reply: any) { if (!window.parent) return;