mirror of
https://github.com/vector-im/element-call.git
synced 2026-06-06 11:45:53 +00:00
Merge pull request #3995 from element-hq/toger5/fix-double-leave-sound
Fix play of second leave sound
This commit is contained in:
@@ -251,12 +251,15 @@ test.skip("GroupCallView plays a leave sound synchronously in widget mode", asyn
|
|||||||
expect(leaveRTCSession).toHaveBeenCalledOnce();
|
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 user = userEvent.setup();
|
||||||
const widgetClosedCalled = Promise.withResolvers<void>();
|
let widgetClosedCalled = false;
|
||||||
|
const { promise: widgetClosedPromise, resolve: widgetClosedResolver } =
|
||||||
|
Promise.withResolvers<void>();
|
||||||
const widgetSendMock = vi.fn().mockImplementation((action: string) => {
|
const widgetSendMock = vi.fn().mockImplementation((action: string) => {
|
||||||
if (action === ElementWidgetActions.Close) {
|
if (action === ElementWidgetActions.Close) {
|
||||||
widgetClosedCalled.resolve();
|
widgetClosedCalled = true;
|
||||||
|
widgetClosedResolver();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const widgetStopMock = vi.fn().mockResolvedValue(undefined);
|
const widgetStopMock = vi.fn().mockResolvedValue(undefined);
|
||||||
@@ -272,7 +275,7 @@ test.skip("Should close widget when all other left and have time to play a sound
|
|||||||
lazyActions: new LazyEventEmitter(),
|
lazyActions: new LazyEventEmitter(),
|
||||||
};
|
};
|
||||||
const resolvePlaySound = Promise.withResolvers<void>();
|
const resolvePlaySound = Promise.withResolvers<void>();
|
||||||
playSound = vi.fn().mockReturnValue(resolvePlaySound);
|
playSound = vi.fn().mockReturnValue(resolvePlaySound.promise);
|
||||||
(useAudioContext as MockedFunction<typeof useAudioContext>).mockReturnValue({
|
(useAudioContext as MockedFunction<typeof useAudioContext>).mockReturnValue({
|
||||||
playSound,
|
playSound,
|
||||||
playSoundLooping: vitest.fn(),
|
playSoundLooping: vitest.fn(),
|
||||||
@@ -283,16 +286,17 @@ test.skip("Should close widget when all other left and have time to play a sound
|
|||||||
const leaveButton = getByText("SimulateOtherLeft");
|
const leaveButton = getByText("SimulateOtherLeft");
|
||||||
await user.click(leaveButton);
|
await user.click(leaveButton);
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
expect(widgetSendMock).not.toHaveBeenCalled();
|
expect(widgetClosedCalled).toBeFalsy();
|
||||||
resolvePlaySound.resolve();
|
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.
|
||||||
await widgetClosedCalled.promise;
|
expect(playSound).toHaveBeenCalledWith("left", 0);
|
||||||
|
await widgetClosedPromise;
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
expect(widgetClosedCalled).toBeTruthy();
|
||||||
expect(widgetStopMock).toHaveBeenCalledOnce();
|
expect(widgetStopMock).toHaveBeenCalledOnce();
|
||||||
});
|
}, 80000);
|
||||||
|
|
||||||
test("Should close widget when all other left", async () => {
|
test("Should close widget when all other left", async () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ export const GroupCallView: FC<Props> = ({
|
|||||||
|
|
||||||
const muteAllAudio = useBehavior(muteAllAudio$);
|
const muteAllAudio = useBehavior(muteAllAudio$);
|
||||||
const leaveSoundContext = useLatest(
|
const leaveSoundContext = useLatest(
|
||||||
useAudioContext({
|
useAudioContext<CallEventSounds>({
|
||||||
sounds: callEventAudioSounds,
|
sounds: callEventAudioSounds,
|
||||||
latencyHint: "interactive",
|
latencyHint: "interactive",
|
||||||
muted: muteAllAudio,
|
muted: muteAllAudio,
|
||||||
@@ -318,12 +318,26 @@ export const GroupCallView: FC<Props> = ({
|
|||||||
(
|
(
|
||||||
reason: "timeout" | "user" | "allOthersLeft" | "decline" | "error",
|
reason: "timeout" | "user" | "allOthersLeft" | "decline" | "error",
|
||||||
): void => {
|
): void => {
|
||||||
let playSound: CallEventSounds = "left";
|
let audioPromise: Promise<void> | undefined = undefined;
|
||||||
if (reason === "timeout" || reason === "decline") playSound = reason;
|
switch (reason) {
|
||||||
|
case "allOthersLeft":
|
||||||
|
// When "allOthersLeft", the leaveSoundEffect$ in CallEventAudioRenderer
|
||||||
|
// already plays the "left" sound when the remote participant's media
|
||||||
|
// 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":
|
||||||
|
audioPromise = leaveSoundContext.current?.playSound(reason);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
audioPromise = leaveSoundContext.current?.playSound("left");
|
||||||
|
}
|
||||||
|
|
||||||
setJoined(false);
|
setJoined(false);
|
||||||
setLeft(true);
|
setLeft(true);
|
||||||
const audioPromise = leaveSoundContext.current?.playSound(playSound);
|
|
||||||
// We need to wait until the callEnded event is tracked on PostHog,
|
// We need to wait until the callEnded event is tracked on PostHog,
|
||||||
// otherwise the iframe may get killed first.
|
// otherwise the iframe may get killed first.
|
||||||
const posthogRequest = new Promise((resolve) => {
|
const posthogRequest = new Promise((resolve) => {
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ interface Props<S extends string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface UseAudioContext<S extends string> {
|
interface UseAudioContext<S extends string> {
|
||||||
playSound(soundName: S): Promise<void>;
|
playSound(soundName: S, volumeOverwrite?: number): Promise<void>;
|
||||||
playSoundLooping(soundName: S, delayS?: number): () => Promise<void>;
|
playSoundLooping(soundName: S, delayS?: number): () => Promise<void>;
|
||||||
/**
|
/**
|
||||||
* Map of sound name to duration in seconds.
|
* Map of sound name to duration in seconds.
|
||||||
@@ -195,7 +195,7 @@ export function useAudioContext<S extends string>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
playSound: async (name): Promise<void> => {
|
playSound: async (name, volumeOverwrite?: number): Promise<void> => {
|
||||||
if (!audioBuffers[name]) {
|
if (!audioBuffers[name]) {
|
||||||
logger.debug(`Tried to play a sound that wasn't buffered (${name})`);
|
logger.debug(`Tried to play a sound that wasn't buffered (${name})`);
|
||||||
return;
|
return;
|
||||||
@@ -203,7 +203,7 @@ export function useAudioContext<S extends string>(
|
|||||||
return playSound(
|
return playSound(
|
||||||
audioContext,
|
audioContext,
|
||||||
audioBuffers[name],
|
audioBuffers[name],
|
||||||
soundEffectVolume * earpieceVolume,
|
volumeOverwrite ?? soundEffectVolume * earpieceVolume,
|
||||||
earpiecePan,
|
earpiecePan,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user