diff --git a/src/vector/jitsi/index.ts b/src/vector/jitsi/index.ts index b30ee666a88..a62bef19873 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, @@ -103,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 @@ -159,7 +166,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 +174,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 +200,26 @@ 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; + + const [audioEnabled, videoEnabled] = await checkAudioVideoEnabled(); + + if (Object.keys(params).length === 0) { + // Handle query + return { + audio_enabled: audioEnabled, + video_enabled: videoEnabled, + }; } - }); - handleAction(ElementWidgetActions.UnmuteAudio, async () => { - if (meetApi && (await meetApi.isAudioMuted())) { + + if (params.audio_enabled !== audioEnabled) { 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 !== videoEnabled) { meetApi.executeCommand("toggleVideo"); } + return params; }); handleAction(ElementWidgetActions.TileLayout, async () => { meetApi?.executeCommand("setTileView", true); @@ -473,7 +480,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 +530,13 @@ const onErrorOccurred = ({ error }: Parameters { - const action = muted ? ElementWidgetActions.MuteAudio : ElementWidgetActions.UnmuteAudio; - void widgetApi?.transport.send(action, {}); +const onMuteStatusChanged = async (): Promise => { + if (!meetApi) return; + const [audioEnabled, videoEnabled] = await checkAudioVideoEnabled(); + void widgetApi?.transport.send(ElementWidgetActions.DeviceMute, { + audio_enabled: audioEnabled, + video_enabled: videoEnabled, + }); }; const onVideoMuteStatusChanged = ({ muted }: VideoMuteStatusChangedEvent): void => { @@ -534,11 +545,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(); } };