diff --git a/src/room/GroupCallView.test.tsx b/src/room/GroupCallView.test.tsx index 2aef571a..97da083e 100644 --- a/src/room/GroupCallView.test.tsx +++ b/src/room/GroupCallView.test.tsx @@ -248,7 +248,7 @@ test.skip("GroupCallView plays a leave sound synchronously in widget mode", asyn expect(leaveRTCSession).toHaveBeenCalledOnce(); }); -test.skip("Should close widget when all other left and have time to play a sound", async () => { +test("Should close widget when all other left and have time to play a sound", async () => { const user = userEvent.setup(); const widgetClosedCalled = Promise.withResolvers(); const widgetSendMock = vi.fn().mockImplementation((action: string) => { @@ -284,8 +284,9 @@ test.skip("Should close widget when all other left and have time to play a sound resolvePlaySound.resolve(); await flushPromises(); - expect(playSound).toHaveBeenCalledWith("left"); - + // Expect the leave sound to be played but silent (volumeOverwrite = 0) + // The allOthersLeft effect should already play a leave sound for the last user in the call. + expect(playSound).toHaveBeenCalledWith("left", 0); await widgetClosedCalled.promise; await flushPromises(); expect(widgetStopMock).toHaveBeenCalledOnce(); diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 009ac3a6..73065d36 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -57,10 +57,7 @@ import { } from "../UrlParams"; import { E2eeType } from "../e2ee/e2eeType"; import { useAudioContext } from "../useAudioContext"; -import { - callEventAudioSounds, - type CallEventSounds, -} from "./CallEventAudioRenderer"; +import { callEventAudioSounds } from "./CallEventAudioRenderer"; import { useLatest } from "../useLatest"; import { usePageTitle } from "../usePageTitle"; import { @@ -323,7 +320,9 @@ export const GroupCallView: FC = ({ case "allOthersLeft": // When "allOthersLeft", the leaveSoundEffect$ in CallEventAudioRenderer // already plays the "left" sound when the remote participant's media - // disappears. Playing it here too would cause the sound to play twice. + // disappears. We play it here silenced (volumeOverwrite = 0) so we have the right duration in the audioPromise. + // (used to destory the widget) + audioPromise = leaveSoundContext.current?.playSound("left", 0); break; case "timeout": case "decline": diff --git a/src/useAudioContext.tsx b/src/useAudioContext.tsx index 4d08dde8..4a7c031c 100644 --- a/src/useAudioContext.tsx +++ b/src/useAudioContext.tsx @@ -114,7 +114,7 @@ interface Props { } interface UseAudioContext { - playSound(soundName: S): Promise; + playSound(soundName: S, volumeOverwrite?: number): Promise; playSoundLooping(soundName: S, delayS?: number): () => Promise; /** * Map of sound name to duration in seconds. @@ -195,7 +195,7 @@ export function useAudioContext( } return { - playSound: async (name): Promise => { + playSound: async (name, volumeOverwrite?: number): Promise => { if (!audioBuffers[name]) { logger.debug(`Tried to play a sound that wasn't buffered (${name})`); return; @@ -203,7 +203,7 @@ export function useAudioContext( return playSound( audioContext, audioBuffers[name], - soundEffectVolume * earpieceVolume, + volumeOverwrite ?? soundEffectVolume * earpieceVolume, earpiecePan, ); },