diff --git a/src/app/plugins/call/CallEmbed.ts b/src/app/plugins/call/CallEmbed.ts index 7d87b4109..f029bfaec 100644 --- a/src/app/plugins/call/CallEmbed.ts +++ b/src/app/plugins/call/CallEmbed.ts @@ -133,6 +133,15 @@ export class CallEmbed { private themeKind: ElementCallThemeKind = 'dark'; + // The theme the frame was loaded with (its URL `theme` param), and whether + // the widget API is ready. Until it is, the iframe may still be on its + // initial about:blank (our own origin), so a send to the call's origin is + // refused ("target origin … does not match the recipient window's origin") + // and the request hangs to a timeout. Theme changes wait for ready. + private loadedThemeKind: ElementCallThemeKind = 'dark'; + + private widgetReady = false; + // Watchdog: detects an iframe that never reaches a usable state. private loadWatchdog?: ReturnType; @@ -360,6 +369,7 @@ export class CallEmbed { this.iframe = iframe; this.container = container; this.themeKind = themeKind; + this.loadedThemeKind = themeKind; const controlState = initialControlState ?? new CallControlState(true, false, true); this.control = new CallControl(controlState, call, iframe); @@ -375,6 +385,13 @@ export class CallEmbed { // happy path (onCallJoined) clears it too; these cover earlier signals so a // user sitting in the lobby/prescreen isn't flagged as an error. this.disposables.push(this.onReady(() => this.settleLoad())); + this.disposables.push( + this.onReady(() => { + this.widgetReady = true; + // A theme change made while the frame was loading. + if (this.themeKind !== this.loadedThemeKind) this.sendTheme(); + }), + ); this.disposables.push(this.onCapabilitiesNotified(() => this.settleLoad())); this.disposables.push(this.onPreparingError(() => this.settleLoad('iframe'))); @@ -399,12 +416,18 @@ export class CallEmbed { return this.room.roomId; } - public setTheme(theme: ElementCallThemeKind) { + public setTheme(theme: ElementCallThemeKind): Promise { this.themeKind = theme; + // Before ready, the URL theme applies and the change is sent on ready. + if (!this.widgetReady) return Promise.resolve(); + return this.sendTheme(); + } + + private sendTheme(): Promise { return this.call.transport - .send(WidgetApiToWidgetAction.ThemeChange, { name: theme }) + .send(WidgetApiToWidgetAction.ThemeChange, { name: this.themeKind }) .catch(() => { - // Widget transport not ready yet; theme URL param is the fallback + // The frame went away mid-send; nothing to do. }); }