From dd17436eb056382772cb8b4c0a5fb540bc36467a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 25 Jul 2024 19:59:02 +0100 Subject: [PATCH 1/3] Fix Jitsi by updating device mute updates over postMessage API Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/vector/jitsi/index.ts | 50 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/vector/jitsi/index.ts b/src/vector/jitsi/index.ts index b30ee666a88..81747df5153 100644 --- a/src/vector/jitsi/index.ts +++ b/src/vector/jitsi/index.ts @@ -34,7 +34,6 @@ import type { JitsiMeetExternalAPIConstructor, ExternalAPIEventCallbacks, JitsiMeetExternalAPI as _JitsiMeetExternalAPI, - AudioMuteStatusChangedEvent, LogEvent, VideoMuteStatusChangedEvent, ExternalAPIOptions as _ExternalAPIOptions, @@ -159,7 +158,7 @@ const setupCompleted = (async (): Promise => { const handleAction = ( action: WidgetApiAction, - handler: (request: IWidgetApiRequestData) => Promise, + handler: (request: IWidgetApiRequestData) => Promise, ): void => { widgetApi!.on(`action:${action}`, async (ev: CustomEvent) => { ev.preventDefault(); @@ -167,8 +166,7 @@ const setupCompleted = (async (): Promise => { let response: IWidgetApiResponseData; try { - await handler(ev.detail.data); - response = {}; + response = (await handler(ev.detail.data)) ?? {}; } catch (e) { if (e instanceof Error) { response = { error: { message: e.message } }; @@ -194,25 +192,24 @@ const setupCompleted = (async (): Promise => { meetApi?.executeCommand("hangup"); } }); - handleAction(ElementWidgetActions.MuteAudio, async () => { - if (meetApi && !(await meetApi.isAudioMuted())) { - meetApi.executeCommand("toggleAudio"); + handleAction(ElementWidgetActions.DeviceMute, async (params) => { + if (!meetApi) return; + + if (Object.keys(params).length === 0) { + // Handle query + return { + audio_enabled: !(await meetApi.isAudioMuted()), + video_enabled: !(await meetApi.isVideoMuted()), + }; } - }); - handleAction(ElementWidgetActions.UnmuteAudio, async () => { - if (meetApi && (await meetApi.isAudioMuted())) { + + if (params.audio_enabled !== !(await meetApi.isAudioMuted())) { meetApi.executeCommand("toggleAudio"); } - }); - handleAction(ElementWidgetActions.MuteVideo, async () => { - if (meetApi && !(await meetApi.isVideoMuted())) { - meetApi.executeCommand("toggleVideo"); - } - }); - handleAction(ElementWidgetActions.UnmuteVideo, async () => { - if (meetApi && (await meetApi.isVideoMuted())) { + if (params.video_enabled !== !(await meetApi.isVideoMuted())) { meetApi.executeCommand("toggleVideo"); } + return params; }); handleAction(ElementWidgetActions.TileLayout, async () => { meetApi?.executeCommand("setTileView", true); @@ -473,7 +470,7 @@ async function joinConference(audioInput?: string | null, videoInput?: string | meetApi.on("videoConferenceLeft", onVideoConferenceLeft); meetApi.on("readyToClose", closeConference as ExternalAPIEventCallbacks["readyToClose"]); meetApi.on("errorOccurred", onErrorOccurred); - meetApi.on("audioMuteStatusChanged", onAudioMuteStatusChanged); + meetApi.on("audioMuteStatusChanged", onMuteStatusChanged); meetApi.on("videoMuteStatusChanged", onVideoMuteStatusChanged); (["videoConferenceJoined", "participantJoined", "participantLeft"] as const).forEach((event) => { @@ -523,9 +520,12 @@ const onErrorOccurred = ({ error }: Parameters { - const action = muted ? ElementWidgetActions.MuteAudio : ElementWidgetActions.UnmuteAudio; - void widgetApi?.transport.send(action, {}); +const onMuteStatusChanged = async (): Promise => { + if (!meetApi) return; + void widgetApi?.transport.send(ElementWidgetActions.DeviceMute, { + audio_enabled: !(await meetApi.isAudioMuted()), + video_enabled: !(await meetApi.isVideoMuted()), + }); }; const onVideoMuteStatusChanged = ({ muted }: VideoMuteStatusChangedEvent): void => { @@ -534,11 +534,9 @@ const onVideoMuteStatusChanged = ({ muted }: VideoMuteStatusChangedEvent): void // hanging up, which we need to ignore by padding the timeout here, // otherwise the React SDK will mistakenly think the user turned off // their video by hand - setTimeout(() => { - if (meetApi) void widgetApi?.transport.send(ElementWidgetActions.MuteVideo, {}); - }, 200); + setTimeout(() => onMuteStatusChanged, 200); } else { - void widgetApi?.transport.send(ElementWidgetActions.UnmuteVideo, {}); + void onMuteStatusChanged(); } }; From 5a1b38cd7424b2d144425f47bf62cb4dd4c01ae8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 25 Jul 2024 20:07:00 +0100 Subject: [PATCH 2/3] Tidy Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/vector/jitsi/index.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/vector/jitsi/index.ts b/src/vector/jitsi/index.ts index 81747df5153..ff962362aba 100644 --- a/src/vector/jitsi/index.ts +++ b/src/vector/jitsi/index.ts @@ -195,18 +195,22 @@ const setupCompleted = (async (): Promise => { handleAction(ElementWidgetActions.DeviceMute, async (params) => { if (!meetApi) return; + const [audioEnabled, videoEnabled] = ( + await Promise.all([meetApi.isAudioMuted(), meetApi.isVideoMuted()]) + ).map((muted) => !muted); + if (Object.keys(params).length === 0) { // Handle query return { - audio_enabled: !(await meetApi.isAudioMuted()), - video_enabled: !(await meetApi.isVideoMuted()), + audio_enabled: audioEnabled, + video_enabled: videoEnabled, }; } - if (params.audio_enabled !== !(await meetApi.isAudioMuted())) { + if (params.audio_enabled !== audioEnabled) { meetApi.executeCommand("toggleAudio"); } - if (params.video_enabled !== !(await meetApi.isVideoMuted())) { + if (params.video_enabled !== videoEnabled) { meetApi.executeCommand("toggleVideo"); } return params; From 88ddf05930985b43362e58398efabbe8d51d0a22 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 25 Jul 2024 20:10:11 +0100 Subject: [PATCH 3/3] DRY Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/vector/jitsi/index.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/vector/jitsi/index.ts b/src/vector/jitsi/index.ts index ff962362aba..a62bef19873 100644 --- a/src/vector/jitsi/index.ts +++ b/src/vector/jitsi/index.ts @@ -102,6 +102,14 @@ let widgetApi: WidgetApi | undefined; let meetApi: _JitsiMeetExternalAPI | undefined; let skipOurWelcomeScreen = false; +async function checkAudioVideoEnabled(): Promise<[audioEnabled: boolean, videoEnabled: boolean]> { + if (!meetApi) return [false, false]; + const [audioEnabled, videoEnabled] = (await Promise.all([meetApi.isAudioMuted(), meetApi.isVideoMuted()])).map( + (muted) => !muted, + ); + return [audioEnabled, videoEnabled]; +} + const setupCompleted = (async (): Promise => { try { // Queue a config.json lookup asap, so we can use it later on. We want this to be concurrent with @@ -195,9 +203,7 @@ const setupCompleted = (async (): Promise => { handleAction(ElementWidgetActions.DeviceMute, async (params) => { if (!meetApi) return; - const [audioEnabled, videoEnabled] = ( - await Promise.all([meetApi.isAudioMuted(), meetApi.isVideoMuted()]) - ).map((muted) => !muted); + const [audioEnabled, videoEnabled] = await checkAudioVideoEnabled(); if (Object.keys(params).length === 0) { // Handle query @@ -526,9 +532,10 @@ const onErrorOccurred = ({ error }: Parameters => { if (!meetApi) return; + const [audioEnabled, videoEnabled] = await checkAudioVideoEnabled(); void widgetApi?.transport.send(ElementWidgetActions.DeviceMute, { - audio_enabled: !(await meetApi.isAudioMuted()), - video_enabled: !(await meetApi.isVideoMuted()), + audio_enabled: audioEnabled, + video_enabled: videoEnabled, }); };