diff --git a/src/components/views/elements/AppTile.tsx b/src/components/views/elements/AppTile.tsx index 5f7c0543c4..d8a29e81fc 100644 --- a/src/components/views/elements/AppTile.tsx +++ b/src/components/views/elements/AppTile.tsx @@ -84,6 +84,7 @@ interface IState { error: Error; menuDisplayed: boolean; widgetPageTitle: string; + requiresClient: boolean; } import { logger } from "matrix-js-sdk/src/logger"; @@ -114,8 +115,10 @@ export default class AppTile extends React.Component { this.persistKey = getPersistKey(this.props.app.id); try { this.sgWidget = new StopGapWidget(this.props); - this.sgWidget.on("preparing", this.onWidgetPrepared); + this.sgWidget.on("preparing", this.onWidgetPreparing); this.sgWidget.on("ready", this.onWidgetReady); + // emits when the capabilites have been setup or changed + this.sgWidget.on("capabilitiesNotified", this.onWidgetCapabilitiesNotified); } catch (e) { logger.log("Failed to construct widget", e); this.sgWidget = null; @@ -155,6 +158,10 @@ export default class AppTile extends React.Component { error: null, menuDisplayed: false, widgetPageTitle: this.props.widgetPageTitle, + // requiresClient is initially set to true. This avoids the broken state of the popout + // button being visible (for an instance) and then disappearing when the widget is loaded. + // requiresClient <-> hide the popout button + requiresClient: true, }; } @@ -216,7 +223,7 @@ export default class AppTile extends React.Component { } try { this.sgWidget = new StopGapWidget(newProps); - this.sgWidget.on("preparing", this.onWidgetPrepared); + this.sgWidget.on("preparing", this.onWidgetPreparing); this.sgWidget.on("ready", this.onWidgetReady); this.startWidget(); } catch (e) { @@ -287,7 +294,7 @@ export default class AppTile extends React.Component { if (this.sgWidget) this.sgWidget.stop({ forceDestroy: true }); } - private onWidgetPrepared = (): void => { + private onWidgetPreparing = (): void => { this.setState({ loading: false }); }; @@ -297,6 +304,12 @@ export default class AppTile extends React.Component { } }; + private onWidgetCapabilitiesNotified = (): void => { + this.setState({ + requiresClient: this.sgWidget.widgetApi.hasCapability(MatrixCapabilities.RequiresClient), + }); + }; + private onAction = (payload): void => { if (payload.widgetId === this.props.app.id) { switch (payload.action) { @@ -512,7 +525,7 @@ export default class AppTile extends React.Component { { this.props.showTitle && this.getTileTitle() } - { this.props.showPopout && this.emit("preparing")); this.messaging.on("ready", () => this.emit("ready")); + this.messaging.on("capabilitiesNotified", () => this.emit("capabilitiesNotified")); this.messaging.on(`action:${WidgetApiFromWidgetAction.OpenModalWidget}`, this.onOpenModal); WidgetMessagingStore.instance.storeMessaging(this.mockWidget, this.messaging); diff --git a/src/stores/widgets/StopGapWidgetDriver.ts b/src/stores/widgets/StopGapWidgetDriver.ts index e8f550b92c..da844b1923 100644 --- a/src/stores/widgets/StopGapWidgetDriver.ts +++ b/src/stores/widgets/StopGapWidgetDriver.ts @@ -73,7 +73,9 @@ export class StopGapWidgetDriver extends WidgetDriver { // Always allow screenshots to be taken because it's a client-induced flow. The widget can't // spew screenshots at us and can't request screenshots of us, so it's up to us to provide the // button if the widget says it supports screenshots. - this.allowedCapabilities = new Set([...allowedCapabilities, MatrixCapabilities.Screenshots]); + this.allowedCapabilities = new Set([...allowedCapabilities, + MatrixCapabilities.Screenshots, + MatrixCapabilities.RequiresClient]); // Grant the permissions that are specific to given widget types if (WidgetType.JITSI.matches(this.forWidget.type) && forWidgetKind === WidgetKind.Room) {