From 3274e2aed5c43f86d04d80c79a29800ad657caf5 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 13 Oct 2020 14:39:27 -0600 Subject: [PATCH 1/9] Run templating on the popout URL too Fixes https://github.com/vector-im/element-web/issues/15443 --- src/stores/widgets/StopGapWidget.ts | 42 +++++++++++++++++------------ 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/stores/widgets/StopGapWidget.ts b/src/stores/widgets/StopGapWidget.ts index 1eb4f9cd27..f7d6d1853e 100644 --- a/src/stores/widgets/StopGapWidget.ts +++ b/src/stores/widgets/StopGapWidget.ts @@ -74,6 +74,16 @@ class ElementWidget extends Widget { return super.templateUrl; } + public get popoutTemplateUrl(): string { + if (WidgetType.JITSI.matches(this.type)) { + return WidgetUtils.getLocalJitsiWrapperUrl({ + forLocalRender: false, // The only important difference between this and templateUrl() + auth: super.rawData?.auth, + }); + } + return this.templateUrl; // use this instead of super to ensure we get appropriate templating + } + public get rawData(): IWidgetData { let conferenceId = super.rawData['conferenceId']; if (conferenceId === undefined) { @@ -94,8 +104,8 @@ class ElementWidget extends Widget { }; } - public getCompleteUrl(params: ITemplateParams): string { - return runTemplate(this.templateUrl, { + public getCompleteUrl(params: ITemplateParams, asPopout=false): string { + return runTemplate(asPopout ? this.popoutTemplateUrl : this.templateUrl, { // we need to supply a whole widget to the template, but don't have // easy access to the definition the superclass is using, so be sad // and gutwrench it. @@ -109,7 +119,7 @@ class ElementWidget extends Widget { export class StopGapWidget extends EventEmitter { private messaging: ClientWidgetApi; - private mockWidget: Widget; + private mockWidget: ElementWidget; private scalarToken: string; constructor(private appTileProps: IAppTileProps) { @@ -133,12 +143,23 @@ export class StopGapWidget extends EventEmitter { * The URL to use in the iframe */ public get embedUrl(): string { + return this.runUrlTemplate({asPopout: false}); + } + + /** + * The URL to use in the popout + */ + public get popoutUrl(): string { + return this.runUrlTemplate({asPopout: true}); + } + + private runUrlTemplate(opts: {asPopout: boolean} = {asPopout: false}): string { const templated = this.mockWidget.getCompleteUrl({ currentRoomId: RoomViewStore.getRoomId(), currentUserId: MatrixClientPeg.get().getUserId(), userDisplayName: OwnProfileStore.instance.displayName, userHttpAvatarUrl: OwnProfileStore.instance.getHttpAvatarUrl(), - }); + }, opts?.asPopout); // Add in some legacy support sprinkles // TODO: Replace these with proper widget params @@ -158,19 +179,6 @@ export class StopGapWidget extends EventEmitter { return parsed.toString().replace(/%24/g, '$'); } - /** - * The URL to use in the popout - */ - public get popoutUrl(): string { - if (WidgetType.JITSI.matches(this.mockWidget.type)) { - return WidgetUtils.getLocalJitsiWrapperUrl({ - forLocalRender: false, - auth: this.mockWidget.rawData?.auth, - }); - } - return this.embedUrl; - } - public get isManagedByManager(): boolean { return !!this.scalarToken; } From f2c874ccb606609c56f86ae730a8741f0959f06c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 13 Oct 2020 14:55:44 -0600 Subject: [PATCH 2/9] Ensure widgets are destroyed cleanly when minimized Fixes https://github.com/vector-im/element-web/issues/15444 (an artifact of joining a call then minimizing the widget) Also fixes other issues relating to widgets not loading when being minimized/maximized. --- src/components/views/elements/AppTile.js | 9 ++++++++- src/components/views/elements/PersistentApp.js | 5 +++++ src/stores/widgets/StopGapWidget.ts | 4 ++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index fda2652d12..c732d8ec95 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -240,10 +240,14 @@ export default class AppTile extends React.Component { this.iframe.src = 'about:blank'; } + if (WidgetType.JITSI.matches(this.props.app.type)) { + dis.dispatch({action: 'hangup_conference'}); + } + // Delete the widget from the persisted store for good measure. PersistedElement.destroyElement(this._persistKey); - this._sgWidget.stop(); + this._sgWidget.stop({forceDestroy: true}); } /* If user has permission to modify widgets, delete the widget, @@ -387,6 +391,9 @@ export default class AppTile extends React.Component { if (this.props.show) { // if we were being shown, end the widget as we're about to be minimized. this._endWidgetActions(); + } else { + // restart the widget actions + this._resetWidget(this.props); } dis.dispatch({ action: 'appsDrawer', diff --git a/src/components/views/elements/PersistentApp.js b/src/components/views/elements/PersistentApp.js index a3e413151a..8b2aa5c87c 100644 --- a/src/components/views/elements/PersistentApp.js +++ b/src/components/views/elements/PersistentApp.js @@ -58,6 +58,11 @@ export default class PersistentApp extends React.Component { const persistentWidgetInRoomId = ActiveWidgetStore.getRoomId(this.state.persistentWidgetId); if (this.state.roomId !== persistentWidgetInRoomId) { const persistentWidgetInRoom = MatrixClientPeg.get().getRoom(persistentWidgetInRoomId); + + // Sanity check the room - the widget may have been destroyed between render cycles, and + // thus no room is associated anymore. + if (!persistentWidgetInRoom) return null; + // get the widget data const appEvent = WidgetUtils.getRoomWidgets(persistentWidgetInRoom).find((ev) => { return ev.getStateKey() === ActiveWidgetStore.getPersistentWidgetId(); diff --git a/src/stores/widgets/StopGapWidget.ts b/src/stores/widgets/StopGapWidget.ts index f7d6d1853e..998dd2afb3 100644 --- a/src/stores/widgets/StopGapWidget.ts +++ b/src/stores/widgets/StopGapWidget.ts @@ -283,8 +283,8 @@ export class StopGapWidget extends EventEmitter { } } - public stop() { - if (ActiveWidgetStore.getPersistentWidgetId() === this.mockWidget.id) { + public stop(opts = {forceDestroy: false}) { + if (!opts?.forceDestroy && ActiveWidgetStore.getPersistentWidgetId() === this.mockWidget.id) { console.log("Skipping destroy - persistent widget"); return; } From 7ef4de31159052d470c9079df5ba7fc936837967 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 13 Oct 2020 14:56:27 -0600 Subject: [PATCH 3/9] Appease the linter --- src/stores/widgets/StopGapWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/widgets/StopGapWidget.ts b/src/stores/widgets/StopGapWidget.ts index 998dd2afb3..41b040b8c6 100644 --- a/src/stores/widgets/StopGapWidget.ts +++ b/src/stores/widgets/StopGapWidget.ts @@ -153,7 +153,7 @@ export class StopGapWidget extends EventEmitter { return this.runUrlTemplate({asPopout: true}); } - private runUrlTemplate(opts: {asPopout: boolean} = {asPopout: false}): string { + private runUrlTemplate(opts = {asPopout: false}): string { const templated = this.mockWidget.getCompleteUrl({ currentRoomId: RoomViewStore.getRoomId(), currentUserId: MatrixClientPeg.get().getUserId(), From aa28459b5a0452a1c084e9c222335ddb19dc7587 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 14 Oct 2020 13:54:27 -0600 Subject: [PATCH 4/9] Don't supply popout widgets with widget parameters Fixes https://github.com/vector-im/element-web/issues/15443 --- src/stores/widgets/StopGapWidget.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/stores/widgets/StopGapWidget.ts b/src/stores/widgets/StopGapWidget.ts index 41b040b8c6..7f437dfce0 100644 --- a/src/stores/widgets/StopGapWidget.ts +++ b/src/stores/widgets/StopGapWidget.ts @@ -161,17 +161,20 @@ export class StopGapWidget extends EventEmitter { userHttpAvatarUrl: OwnProfileStore.instance.getHttpAvatarUrl(), }, opts?.asPopout); - // Add in some legacy support sprinkles + const parsed = new URL(templated); + + // Add in some legacy support sprinkles (for non-popout widgets) // TODO: Replace these with proper widget params // See https://github.com/matrix-org/matrix-doc/pull/1958/files#r405714833 - const parsed = new URL(templated); - parsed.searchParams.set('widgetId', this.mockWidget.id); - parsed.searchParams.set('parentUrl', window.location.href.split('#', 2)[0]); + if (!opts?.asPopout) { + parsed.searchParams.set('widgetId', this.mockWidget.id); + parsed.searchParams.set('parentUrl', window.location.href.split('#', 2)[0]); - // Give the widget a scalar token if we're supposed to (more legacy) - // TODO: Stop doing this - if (this.scalarToken) { - parsed.searchParams.set('scalar_token', this.scalarToken); + // Give the widget a scalar token if we're supposed to (more legacy) + // TODO: Stop doing this + if (this.scalarToken) { + parsed.searchParams.set('scalar_token', this.scalarToken); + } } // Replace the encoded dollar signs back to dollar signs. They have no special meaning From 596c723dea03b8499a85c54b959be48ef8c96cd1 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 19 Oct 2020 10:47:17 +0100 Subject: [PATCH 5/9] Fix Jitsi OpenIDC auth Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/stores/widgets/StopGapWidget.ts | 60 +++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/stores/widgets/StopGapWidget.ts b/src/stores/widgets/StopGapWidget.ts index 7f437dfce0..d0a19285dd 100644 --- a/src/stores/widgets/StopGapWidget.ts +++ b/src/stores/widgets/StopGapWidget.ts @@ -17,6 +17,8 @@ import { Room } from "matrix-js-sdk/src/models/room"; import { ClientWidgetApi, + IGetOpenIDActionRequest, + IGetOpenIDActionResponseData, IStickerActionRequest, IStickyActionRequest, ITemplateParams, @@ -25,8 +27,10 @@ import { IWidgetApiRequestEmptyData, IWidgetData, MatrixCapabilities, + OpenIDRequestState, runTemplate, Widget, + WidgetApiToWidgetAction, WidgetApiFromWidgetAction, } from "matrix-widget-api"; import { StopGapWidgetDriver } from "./StopGapWidgetDriver"; @@ -43,6 +47,8 @@ import ActiveWidgetStore from "../ActiveWidgetStore"; import { objectShallowClone } from "../../utils/objects"; import defaultDispatcher from "../../dispatcher/dispatcher"; import { ElementWidgetActions } from "./ElementWidgetActions"; +import Modal from "../../Modal"; +import WidgetOpenIDPermissionsDialog from "../../components/views/dialogs/WidgetOpenIDPermissionsDialog"; // TODO: Destroy all of this code @@ -190,12 +196,66 @@ export class StopGapWidget extends EventEmitter { return !!this.messaging; } + private get widgetId() { + return this.messaging.widget.id; + } + + private onOpenIdReq = async (ev: CustomEvent) => { + if (ev?.detail?.widgetId !== this.widgetId) return; + + const rawUrl = this.appTileProps.app.url; + const widgetSecurityKey = WidgetUtils.getWidgetSecurityKey(this.widgetId, rawUrl, this.appTileProps.userWidget); + + const settings = SettingsStore.getValue("widgetOpenIDPermissions"); + if (settings.deny && settings.deny.includes(widgetSecurityKey)) { + this.messaging.transport.reply(ev.detail, { + state: OpenIDRequestState.Blocked, + }); + return; + } + if (settings.allow && settings.allow.includes(widgetSecurityKey)) { + const credentials = await MatrixClientPeg.get().getOpenIdToken(); + this.messaging.transport.reply(ev.detail, { + state: OpenIDRequestState.Allowed, + ...credentials, + }); + return; + } + + // Confirm that we received the request + this.messaging.transport.reply(ev.detail, { + state: OpenIDRequestState.PendingUserConfirmation, + }); + + // Actually ask for permission to send the user's data + Modal.createTrackedDialog("OpenID widget permissions", '', WidgetOpenIDPermissionsDialog, { + widgetUrl: rawUrl.substr(0, rawUrl.lastIndexOf("?")), + widgetId: this.widgetId, + isUserWidget: this.appTileProps.userWidget, + + onFinished: async (confirm) => { + const responseBody: IGetOpenIDActionResponseData = { + state: confirm ? OpenIDRequestState.Allowed : OpenIDRequestState.Blocked, + original_request_id: ev.detail.requestId, // eslint-disable-line camelcase + }; + if (confirm) { + const credentials = await MatrixClientPeg.get().getOpenIdToken(); + Object.assign(responseBody, credentials); + } + this.messaging.transport.send(WidgetApiToWidgetAction.OpenIDCredentials, responseBody).catch(error => { + console.error("Failed to send OpenID credentials: ", error); + }); + }, + }); + }; + public start(iframe: HTMLIFrameElement) { if (this.started) return; const driver = new StopGapWidgetDriver( this.appTileProps.whitelistCapabilities || []); this.messaging = new ClientWidgetApi(this.mockWidget, iframe, driver); this.messaging.addEventListener("preparing", () => this.emit("preparing")); this.messaging.addEventListener("ready", () => this.emit("ready")); + this.messaging.addEventListener(`action:${WidgetApiFromWidgetAction.GetOpenIDCredentials}`, this.onOpenIdReq); WidgetMessagingStore.instance.storeMessaging(this.mockWidget, this.messaging); if (!this.appTileProps.userWidget && this.appTileProps.room) { From 11199f89f4e399464aae964c06016b22d5d438ae Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 19 Oct 2020 16:42:20 +0100 Subject: [PATCH 6/9] Adjust for new widget messaging APIs As part of changing to the `events` package, the API surface changed slightly. Related to https://github.com/vector-im/element-web/issues/15493 --- src/stores/widgets/StopGapWidget.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/stores/widgets/StopGapWidget.ts b/src/stores/widgets/StopGapWidget.ts index d0a19285dd..17302d0ab9 100644 --- a/src/stores/widgets/StopGapWidget.ts +++ b/src/stores/widgets/StopGapWidget.ts @@ -253,9 +253,9 @@ export class StopGapWidget extends EventEmitter { if (this.started) return; const driver = new StopGapWidgetDriver( this.appTileProps.whitelistCapabilities || []); this.messaging = new ClientWidgetApi(this.mockWidget, iframe, driver); - this.messaging.addEventListener("preparing", () => this.emit("preparing")); - this.messaging.addEventListener("ready", () => this.emit("ready")); - this.messaging.addEventListener(`action:${WidgetApiFromWidgetAction.GetOpenIDCredentials}`, this.onOpenIdReq); + this.messaging.on("preparing", () => this.emit("preparing")); + this.messaging.on("ready", () => this.emit("ready")); + this.messaging.on(`action:${WidgetApiFromWidgetAction.GetOpenIDCredentials}`, this.onOpenIdReq); WidgetMessagingStore.instance.storeMessaging(this.mockWidget, this.messaging); if (!this.appTileProps.userWidget && this.appTileProps.room) { @@ -263,7 +263,7 @@ export class StopGapWidget extends EventEmitter { } if (WidgetType.JITSI.matches(this.mockWidget.type)) { - this.messaging.addEventListener("action:set_always_on_screen", + this.messaging.on("action:set_always_on_screen", (ev: CustomEvent) => { if (this.messaging.hasCapability(MatrixCapabilities.AlwaysOnScreen)) { ActiveWidgetStore.setWidgetPersistence(this.mockWidget.id, ev.detail.data.value); @@ -273,7 +273,7 @@ export class StopGapWidget extends EventEmitter { }, ); } else if (WidgetType.STICKERPICKER.matches(this.mockWidget.type)) { - this.messaging.addEventListener(`action:${ElementWidgetActions.OpenIntegrationManager}`, + this.messaging.on(`action:${ElementWidgetActions.OpenIntegrationManager}`, (ev: CustomEvent) => { // Acknowledge first ev.preventDefault(); @@ -307,7 +307,7 @@ export class StopGapWidget extends EventEmitter { // TODO: Replace this event listener with appropriate driver functionality once the API // establishes a sane way to send events back and forth. - this.messaging.addEventListener(`action:${WidgetApiFromWidgetAction.SendSticker}`, + this.messaging.on(`action:${WidgetApiFromWidgetAction.SendSticker}`, (ev: CustomEvent) => { // Acknowledge first ev.preventDefault(); From 2219419f520d6d4910fc362878d9cf385cfb61c6 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 19 Oct 2020 16:56:33 +0100 Subject: [PATCH 7/9] Upgrade widget API --- package.json | 2 +- yarn.lock | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 71c8074a93..c3336a2ad5 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "linkifyjs": "^2.1.9", "lodash": "^4.17.19", "matrix-js-sdk": "8.5.0", - "matrix-widget-api": "^0.1.0-beta.3", + "matrix-widget-api": "^0.1.0-beta.5", "minimist": "^1.2.5", "pako": "^1.0.11", "parse5": "^5.1.1", diff --git a/yarn.lock b/yarn.lock index 5630ee2b64..481b3ebc38 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3791,6 +3791,11 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +events@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" + integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg== + except@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/except/-/except-0.1.3.tgz#98261c91958551536b44482238e9783fb73d292a" @@ -5954,10 +5959,12 @@ matrix-react-test-utils@^0.2.2: resolved "https://registry.yarnpkg.com/matrix-react-test-utils/-/matrix-react-test-utils-0.2.2.tgz#c87144d3b910c7edc544a6699d13c7c2bf02f853" integrity sha512-49+7gfV6smvBIVbeloql+37IeWMTD+fiywalwCqk8Dnz53zAFjKSltB3rmWHso1uecLtQEcPtCijfhzcLXAxTQ== -matrix-widget-api@^0.1.0-beta.3: - version "0.1.0-beta.3" - resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-0.1.0-beta.3.tgz#356965ca357172ee056e3fd86fd96879b059a114" - integrity sha512-j7nxdhLQfdU6snsdBA29KQR0DmT8/vl6otOvGqPCV0OCHpq1312cP79Eg4JzJKIFI3A76Qha3nYx6G9/aapwXg== +matrix-widget-api@^0.1.0-beta.5: + version "0.1.0-beta.5" + resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-0.1.0-beta.5.tgz#dd7f24a177aa590d812bd4e92e2c3ac225c5557e" + integrity sha512-J3GBJtVMFuEM/EWFylc0IlkPjdgmWxrkGYPaZ0LSmxp+OlNJxYfnWPR6F6HveW+Z8C1i0vq+BTueofSqKv2zDg== + dependencies: + events "^3.2.0" mdast-util-compact@^1.0.0: version "1.0.4" From 10fbdef6d62245c19f40b4d1e5a64c92d4ad77a4 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 20 Oct 2020 11:23:48 +0100 Subject: [PATCH 8/9] Prepare changelog for v3.6.1 --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a22954c3f..34a3498f9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +Changes in [3.6.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.6.1) (2020-10-20) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.6.0...v3.6.1) + + * [Release] Adjust for new widget messaging APIs + [\#5342](https://github.com/matrix-org/matrix-react-sdk/pull/5342) + * [Release] Fix Jitsi OpenIDC auth + [\#5335](https://github.com/matrix-org/matrix-react-sdk/pull/5335) + Changes in [3.6.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.6.0) (2020-10-12) =================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.6.0-rc.1...v3.6.0) From 3b90ed0664c3bdae558f75ed612f4a9fb965591b Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 20 Oct 2020 11:23:48 +0100 Subject: [PATCH 9/9] v3.6.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c3336a2ad5..2bda5c8b6e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.6.0", + "version": "3.6.1", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": {