From d0c5730b9bacc669c5eb28430d6de49ec8ed3c09 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Mon, 11 Apr 2022 13:10:27 -0400 Subject: [PATCH 1/6] No longer skip the prejoin screen in video rooms --- src/vector/jitsi/index.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vector/jitsi/index.ts b/src/vector/jitsi/index.ts index c801d2ad031..3ac4d1d3fb8 100644 --- a/src/vector/jitsi/index.ts +++ b/src/vector/jitsi/index.ts @@ -127,7 +127,7 @@ const ack = (ev: CustomEvent) => widgetApi.transport.reply(ev const instanceConfig = new SnakedObject((await configPromise) ?? {}); const jitsiConfig = instanceConfig.get("jitsi_widget") ?? {}; skipOurWelcomeScreen = (new SnakedObject(jitsiConfig)) - .get("skip_built_in_welcome_screen") || isVideoChannel; + .get("skip_built_in_welcome_screen") ?? false; // Either reveal the prejoin screen, or skip straight to Jitsi depending on the config. // We don't set up the call yet though as this might lead to failure without the widget API. @@ -332,9 +332,6 @@ function joinConference() { // event handler bound in HTML // Video channel widgets need some more tailored config options if (isVideoChannel) { - // Ensure that we start on Jitsi Meet's native prejoin screen, for - // deployments that skip straight to the conference by default - options.configOverwrite.prejoinConfig = { enabled: true }; // Use a simplified set of toolbar buttons options.configOverwrite.toolbarButtons = [ "microphone", "camera", "desktop", "tileview", "hangup", From 6d117202b37f6d999c1d3208fc72a92a36e8677d Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Mon, 11 Apr 2022 13:10:55 -0400 Subject: [PATCH 2/6] Enable joining with specific devices --- src/vector/jitsi/index.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/vector/jitsi/index.ts b/src/vector/jitsi/index.ts index 3ac4d1d3fb8..7a704219867 100644 --- a/src/vector/jitsi/index.ts +++ b/src/vector/jitsi/index.ts @@ -145,7 +145,8 @@ const ack = (ev: CustomEvent) => widgetApi.transport.reply(ev widgetApi.on(`action:${ElementWidgetActions.JoinCall}`, (ev: CustomEvent) => { - joinConference(); + const { audioDevice, videoDevice } = ev.detail.data; + joinConference(audioDevice as string, videoDevice as string); ack(ev); }, ); @@ -293,7 +294,8 @@ async function notifyHangup() { } } -function joinConference() { // event handler bound in HTML +// event handler bound in HTML +function joinConference(audioDevice?: string, videoDevice?: string) { let jwt; if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) { if (!openIdToken?.access_token) { // eslint-disable-line camelcase @@ -318,6 +320,10 @@ function joinConference() { // event handler bound in HTML height: "100%", parentNode: document.querySelector("#jitsiContainer"), roomName: conferenceId, + devices: { + audioInput: audioDevice, + videoInput: videoDevice, + }, interfaceConfigOverwrite: { SHOW_JITSI_WATERMARK: false, SHOW_WATERMARK_FOR_GUESTS: false, @@ -326,6 +332,8 @@ function joinConference() { // event handler bound in HTML }, configOverwrite: { startAudioOnly, + startWithAudioMuted: !audioDevice, + startWithVideoMuted: !videoDevice, } as any, jwt: jwt, }; From 2c1bb08832a77fe9f981a24f8349fd707214eb0a Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Mon, 11 Apr 2022 13:12:18 -0400 Subject: [PATCH 3/6] Start widget API listeners earlier so that they're instantly ready for use, rather than being stuck behind the config loading --- src/vector/jitsi/index.ts | 74 ++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/src/vector/jitsi/index.ts b/src/vector/jitsi/index.ts index 7a704219867..a038505dd77 100644 --- a/src/vector/jitsi/index.ts +++ b/src/vector/jitsi/index.ts @@ -94,6 +94,7 @@ const ack = (ev: CustomEvent) => widgetApi.transport.reply(ev const parentOrigin = new URL(qsParam('parentUrl')).origin; widgetApi = new WidgetApi(qsParam("widgetId"), parentOrigin); widgetApi.requestCapabilities(VideoConferenceCapabilities); + readyPromise = Promise.all([ new Promise(resolve => { widgetApi.once(`action:${ElementWidgetActions.ClientReady}`, ev => { @@ -106,42 +107,6 @@ const ack = (ev: CustomEvent) => widgetApi.transport.reply(ev widgetApi.once("ready", () => resolve()); }), ]); - widgetApi.start(); - } else { - logger.warn("No parent URL or no widget ID - assuming no widget API is available"); - } - - // Populate the Jitsi params now - jitsiDomain = qsParam('conferenceDomain'); - conferenceId = qsParam('conferenceId'); - displayName = qsParam('displayName', true); - avatarUrl = qsParam('avatarUrl', true); // http not mxc - userId = qsParam('userId'); - jitsiAuth = qsParam('auth', true); - roomId = qsParam('roomId', true); - roomName = qsParam('roomName', true); - startAudioOnly = qsParam('isAudioOnly', true) === "true"; - isVideoChannel = qsParam('isVideoChannel', true) === "true"; - - // We've reached the point where we have to wait for the config, so do that then parse it. - const instanceConfig = new SnakedObject((await configPromise) ?? {}); - const jitsiConfig = instanceConfig.get("jitsi_widget") ?? {}; - skipOurWelcomeScreen = (new SnakedObject(jitsiConfig)) - .get("skip_built_in_welcome_screen") ?? false; - - // Either reveal the prejoin screen, or skip straight to Jitsi depending on the config. - // We don't set up the call yet though as this might lead to failure without the widget API. - toggleConferenceVisibility(skipOurWelcomeScreen); - - if (widgetApi) { - await readyPromise; - - // See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification - if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) { - // Request credentials, give callback to continue when received - openIdToken = await widgetApi.requestOpenIDConnectToken(); - logger.log("Got OpenID Connect token"); - } widgetApi.on(`action:${ElementWidgetActions.JoinCall}`, (ev: CustomEvent) => { @@ -204,6 +169,43 @@ const ack = (ev: CustomEvent) => widgetApi.transport.reply(ev } }, ); + + widgetApi.start(); + } else { + logger.warn("No parent URL or no widget ID - assuming no widget API is available"); + } + + // Populate the Jitsi params now + jitsiDomain = qsParam('conferenceDomain'); + conferenceId = qsParam('conferenceId'); + displayName = qsParam('displayName', true); + avatarUrl = qsParam('avatarUrl', true); // http not mxc + userId = qsParam('userId'); + jitsiAuth = qsParam('auth', true); + roomId = qsParam('roomId', true); + roomName = qsParam('roomName', true); + startAudioOnly = qsParam('isAudioOnly', true) === "true"; + isVideoChannel = qsParam('isVideoChannel', true) === "true"; + + // We've reached the point where we have to wait for the config, so do that then parse it. + const instanceConfig = new SnakedObject((await configPromise) ?? {}); + const jitsiConfig = instanceConfig.get("jitsi_widget") ?? {}; + skipOurWelcomeScreen = (new SnakedObject(jitsiConfig)) + .get("skip_built_in_welcome_screen") ?? false; + + // Either reveal the prejoin screen, or skip straight to Jitsi depending on the config. + // We don't set up the call yet though as this might lead to failure without the widget API. + toggleConferenceVisibility(skipOurWelcomeScreen); + + if (widgetApi) { + await readyPromise; + + // See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification + if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) { + // Request credentials, give callback to continue when received + openIdToken = await widgetApi.requestOpenIDConnectToken(); + logger.log("Got OpenID Connect token"); + } } // Now that everything should be set up, skip to the Jitsi splash screen if needed From dd74db72a5e04b95da6e656bfa3c1f17264483de Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Tue, 12 Apr 2022 18:42:40 -0400 Subject: [PATCH 4/6] Revert "Start widget API listeners earlier" This reverts commit 2c1bb08832a77fe9f981a24f8349fd707214eb0a. --- src/vector/jitsi/index.ts | 74 +++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/src/vector/jitsi/index.ts b/src/vector/jitsi/index.ts index a038505dd77..7a704219867 100644 --- a/src/vector/jitsi/index.ts +++ b/src/vector/jitsi/index.ts @@ -94,7 +94,6 @@ const ack = (ev: CustomEvent) => widgetApi.transport.reply(ev const parentOrigin = new URL(qsParam('parentUrl')).origin; widgetApi = new WidgetApi(qsParam("widgetId"), parentOrigin); widgetApi.requestCapabilities(VideoConferenceCapabilities); - readyPromise = Promise.all([ new Promise(resolve => { widgetApi.once(`action:${ElementWidgetActions.ClientReady}`, ev => { @@ -107,6 +106,42 @@ const ack = (ev: CustomEvent) => widgetApi.transport.reply(ev widgetApi.once("ready", () => resolve()); }), ]); + widgetApi.start(); + } else { + logger.warn("No parent URL or no widget ID - assuming no widget API is available"); + } + + // Populate the Jitsi params now + jitsiDomain = qsParam('conferenceDomain'); + conferenceId = qsParam('conferenceId'); + displayName = qsParam('displayName', true); + avatarUrl = qsParam('avatarUrl', true); // http not mxc + userId = qsParam('userId'); + jitsiAuth = qsParam('auth', true); + roomId = qsParam('roomId', true); + roomName = qsParam('roomName', true); + startAudioOnly = qsParam('isAudioOnly', true) === "true"; + isVideoChannel = qsParam('isVideoChannel', true) === "true"; + + // We've reached the point where we have to wait for the config, so do that then parse it. + const instanceConfig = new SnakedObject((await configPromise) ?? {}); + const jitsiConfig = instanceConfig.get("jitsi_widget") ?? {}; + skipOurWelcomeScreen = (new SnakedObject(jitsiConfig)) + .get("skip_built_in_welcome_screen") ?? false; + + // Either reveal the prejoin screen, or skip straight to Jitsi depending on the config. + // We don't set up the call yet though as this might lead to failure without the widget API. + toggleConferenceVisibility(skipOurWelcomeScreen); + + if (widgetApi) { + await readyPromise; + + // See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification + if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) { + // Request credentials, give callback to continue when received + openIdToken = await widgetApi.requestOpenIDConnectToken(); + logger.log("Got OpenID Connect token"); + } widgetApi.on(`action:${ElementWidgetActions.JoinCall}`, (ev: CustomEvent) => { @@ -169,43 +204,6 @@ const ack = (ev: CustomEvent) => widgetApi.transport.reply(ev } }, ); - - widgetApi.start(); - } else { - logger.warn("No parent URL or no widget ID - assuming no widget API is available"); - } - - // Populate the Jitsi params now - jitsiDomain = qsParam('conferenceDomain'); - conferenceId = qsParam('conferenceId'); - displayName = qsParam('displayName', true); - avatarUrl = qsParam('avatarUrl', true); // http not mxc - userId = qsParam('userId'); - jitsiAuth = qsParam('auth', true); - roomId = qsParam('roomId', true); - roomName = qsParam('roomName', true); - startAudioOnly = qsParam('isAudioOnly', true) === "true"; - isVideoChannel = qsParam('isVideoChannel', true) === "true"; - - // We've reached the point where we have to wait for the config, so do that then parse it. - const instanceConfig = new SnakedObject((await configPromise) ?? {}); - const jitsiConfig = instanceConfig.get("jitsi_widget") ?? {}; - skipOurWelcomeScreen = (new SnakedObject(jitsiConfig)) - .get("skip_built_in_welcome_screen") ?? false; - - // Either reveal the prejoin screen, or skip straight to Jitsi depending on the config. - // We don't set up the call yet though as this might lead to failure without the widget API. - toggleConferenceVisibility(skipOurWelcomeScreen); - - if (widgetApi) { - await readyPromise; - - // See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification - if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) { - // Request credentials, give callback to continue when received - openIdToken = await widgetApi.requestOpenIDConnectToken(); - logger.log("Got OpenID Connect token"); - } } // Now that everything should be set up, skip to the Jitsi splash screen if needed From ffdfdc295e7e0fb710f765da0e0a7c39fe94c4a6 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Tue, 12 Apr 2022 18:48:51 -0400 Subject: [PATCH 5/6] Tell the client when the Jitsi wrapper is ready to receive events --- src/vector/jitsi/index.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/vector/jitsi/index.ts b/src/vector/jitsi/index.ts index 7a704219867..ee77e1294a6 100644 --- a/src/vector/jitsi/index.ts +++ b/src/vector/jitsi/index.ts @@ -212,6 +212,13 @@ const ack = (ev: CustomEvent) => widgetApi.transport.reply(ev } enableJoinButton(); // always enable the button + + // Inform the client that we're ready to receive events + try { + await widgetApi?.transport.send(ElementWidgetActions.WidgetReady, {}); + } catch (e) { + logger.error(e); + } } catch (e) { logger.error("Error setting up Jitsi widget", e); document.getElementById("widgetActionContainer").innerText = "Failed to load Jitsi widget"; From caefb5f21ccf01f9c68c9e0f763ab1897219c7c6 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Thu, 14 Apr 2022 19:58:44 -0400 Subject: [PATCH 6/6] Fix video rooms getting stuck on Jitsi Meet's prejoin screen --- src/vector/jitsi/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vector/jitsi/index.ts b/src/vector/jitsi/index.ts index ee77e1294a6..8a89e5799ca 100644 --- a/src/vector/jitsi/index.ts +++ b/src/vector/jitsi/index.ts @@ -347,6 +347,9 @@ function joinConference(audioDevice?: string, videoDevice?: string) { // Video channel widgets need some more tailored config options if (isVideoChannel) { + // Ensure that we skip Jitsi Meet's native prejoin screen, for + // deployments that have it enabled + options.configOverwrite.prejoinConfig = { enabled: false }; // Use a simplified set of toolbar buttons options.configOverwrite.toolbarButtons = [ "microphone", "camera", "desktop", "tileview", "hangup",