From cf4137d4b23e5af5baf3c3a1f056da789e51d8b7 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen Date: Wed, 22 Apr 2020 19:20:28 +0300 Subject: [PATCH] Make WidgetAPI an EventEmitter + use for terminate + cleanups Use EventEmitter for emitting events, rename terminate event code, plus misc cleanups from review. Signed-off-by: Pauli Virtanen --- src/WidgetMessaging.js | 2 -- src/components/views/elements/AppTile.js | 22 +++++++++--------- src/widgets/WidgetApi.ts | 29 ++++++++++++++---------- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/WidgetMessaging.js b/src/WidgetMessaging.js index 6a20c340a5..375e7dd8a3 100644 --- a/src/WidgetMessaging.js +++ b/src/WidgetMessaging.js @@ -89,8 +89,6 @@ 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() { diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 18be0eeb67..057643c725 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -346,7 +346,7 @@ export default class AppTile extends React.Component { _endWidgetActions() { let promise; - if (this._hasCapability('m.receive_terminate')) { + if (this._hasCapability('im.vector.receive_terminate')) { // Wait for widget to terminate within a timeout const timeout = 2000; const messaging = ActiveWidgetStore.getWidgetMessaging(this.props.app.id); @@ -396,20 +396,20 @@ export default class AppTile extends React.Component { this.setState({deleting: true}); this._endWidgetActions().then(() => { - WidgetUtils.setRoomWidget( + return WidgetUtils.setRoomWidget( this.props.room.roomId, this.props.app.id, - ).catch((e) => { - console.error('Failed to delete widget', e); - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + ); + }).catch((e) => { + console.error('Failed to delete widget', e); + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - Modal.createTrackedDialog('Failed to remove widget', '', ErrorDialog, { - title: _t('Failed to remove widget'), - description: _t('An error ocurred whilst trying to remove the widget from the room'), - }); - }).finally(() => { - this.setState({deleting: false}); + Modal.createTrackedDialog('Failed to remove widget', '', ErrorDialog, { + title: _t('Failed to remove widget'), + description: _t('An error ocurred whilst trying to remove the widget from the room'), }); + }).finally(() => { + this.setState({deleting: false}); }); }, }); diff --git a/src/widgets/WidgetApi.ts b/src/widgets/WidgetApi.ts index 6a29955713..c5420dca38 100644 --- a/src/widgets/WidgetApi.ts +++ b/src/widgets/WidgetApi.ts @@ -18,12 +18,13 @@ limitations under the License. // https://github.com/turt2live/matrix-dimension/blob/4f92d560266635e5a3c824606215b84e8c0b19f5/web/app/shared/services/scalar/scalar-widget.api.ts import { randomString } from "matrix-js-sdk/src/randomstring"; +import { EventEmitter } from "events"; export enum Capability { Screenshot = "m.capability.screenshot", Sticker = "m.sticker", AlwaysOnScreen = "m.always_on_screen", - ReceiveTerminate = "m.receive_terminate", + ReceiveTerminate = "im.vector.receive_terminate", } export enum KnownWidgetActions { @@ -64,14 +65,17 @@ export interface FromWidgetRequest extends WidgetRequest { /** * Handles Riot <--> Widget interactions for embedded/standalone widgets. + * + * Emitted events: + * - terminate(wait): client requested the widget to terminate. + * Call the argument 'wait(promise)' to postpone the finalization until + * the given promise resolves. */ -export class WidgetApi { +export class WidgetApi extends EventEmitter { private origin: string; 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). @@ -79,10 +83,11 @@ export class WidgetApi { public expectingExplicitReady = false; constructor(currentUrl: string, private widgetId: string, private requestedCapabilities: string[]) { + super(); + 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 @@ -104,11 +109,15 @@ 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(() => { + // Finalization needs to be async, so postpone with a promise + let finalizePromise = Promise.resolve(); + const wait = promise => { + finalizePromise = finalizePromise.then(value => promise); + } + this.emit('terminate', wait); + Promise.resolve(finalizePromise).then(() => { this.replyToRequest(payload, {}); }); - this.terminatePromiseResolve(); } else { console.warn(`[WidgetAPI] Got unexpected action: ${payload.action}`); } @@ -127,10 +136,6 @@ 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;