From 4b4f114dcd147587c3286f150f8a7e2c602ef3dd Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 15 Sep 2025 13:58:03 +0100 Subject: [PATCH] Refactor onLeave to take a sound so we don't need to repeat the sound --- src/room/CallEventAudioRenderer.tsx | 14 +++++++++++ src/room/GroupCallView.test.tsx | 2 +- src/room/GroupCallView.tsx | 12 +++++++--- src/room/InCallView.tsx | 36 ++++++++--------------------- src/state/CallViewModel.ts | 1 + 5 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/room/CallEventAudioRenderer.tsx b/src/room/CallEventAudioRenderer.tsx index 797501b6..23997c37 100644 --- a/src/room/CallEventAudioRenderer.tsx +++ b/src/room/CallEventAudioRenderer.tsx @@ -16,6 +16,10 @@ import handSoundOgg from "../sound/raise_hand.ogg"; import handSoundMp3 from "../sound/raise_hand.mp3"; import screenShareStartedOgg from "../sound/screen_share_started.ogg"; import screenShareStartedMp3 from "../sound/screen_share_started.mp3"; +import declineMp3 from "../sound/call_declined.mp3?url"; +import declineOgg from "../sound/call_declined.ogg?url"; +import timeoutMp3 from "../sound/call_timeout.mp3?url"; +import timeoutOgg from "../sound/call_timeout.ogg?url"; import { useAudioContext } from "../useAudioContext"; import { prefetchSounds } from "../soundUtils"; import { useLatest } from "../useLatest"; @@ -37,8 +41,18 @@ export const callEventAudioSounds = prefetchSounds({ mp3: screenShareStartedMp3, ogg: screenShareStartedOgg, }, + decline: { + mp3: declineMp3, + ogg: declineOgg, + }, + timeout: { + mp3: timeoutMp3, + ogg: timeoutOgg, + }, }); +export type CallEventSounds = keyof Awaited; + export function CallEventAudioRenderer({ vm, muted, diff --git a/src/room/GroupCallView.test.tsx b/src/room/GroupCallView.test.tsx index 15ac010e..084c06ec 100644 --- a/src/room/GroupCallView.test.tsx +++ b/src/room/GroupCallView.test.tsx @@ -106,7 +106,7 @@ beforeEach(() => { ({ onLeave }) => { return (
- +
); }, diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 18ec1a6a..dbc3ea18 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -53,7 +53,10 @@ import { InviteModal } from "./InviteModal"; import { HeaderStyle, type UrlParams, useUrlParams } from "../UrlParams"; import { E2eeType } from "../e2ee/e2eeType"; import { useAudioContext } from "../useAudioContext"; -import { callEventAudioSounds } from "./CallEventAudioRenderer"; +import { + callEventAudioSounds, + type CallEventSounds, +} from "./CallEventAudioRenderer"; import { useLatest } from "../useLatest"; import { usePageTitle } from "../usePageTitle"; import { @@ -317,8 +320,11 @@ export const GroupCallView: FC = ({ const navigate = useNavigate(); const onLeave = useCallback( - (cause: "user" | "error" = "user"): void => { - const audioPromise = leaveSoundContext.current?.playSound("left"); + ( + cause: "user" | "error" = "user", + playSound: CallEventSounds = "left", + ): void => { + const audioPromise = leaveSoundContext.current?.playSound(playSound); // In embedded/widget mode the iFrame will be killed right after the call ended prohibiting the posthog event from getting sent, // therefore we want the event to be sent instantly without getting queued/batched. const sendInstantly = !!widget; diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 5cb89061..43e3ea0c 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -95,7 +95,10 @@ import { } from "../reactions/useReactionsSender"; import { ReactionsAudioRenderer } from "./ReactionAudioRenderer"; import { ReactionsOverlay } from "./ReactionsOverlay"; -import { CallEventAudioRenderer } from "./CallEventAudioRenderer"; +import { + CallEventAudioRenderer, + CallEventSounds, +} from "./CallEventAudioRenderer"; import { debugTileLayout as debugTileLayoutSetting, useExperimentalToDeviceTransport as useExperimentalToDeviceTransportSetting, @@ -119,10 +122,6 @@ import { prefetchSounds } from "../soundUtils"; import { useAudioContext } from "../useAudioContext"; import ringtoneMp3 from "../sound/ringtone.mp3?url"; import ringtoneOgg from "../sound/ringtone.ogg?url"; -import declineMp3 from "../sound/call_declined.mp3?url"; -import declineOgg from "../sound/call_declined.ogg?url"; -import timeoutMp3 from "../sound/call_timeout.mp3?url"; -import timeoutOgg from "../sound/call_timeout.ogg?url"; const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {}); @@ -233,7 +232,7 @@ export interface InCallViewProps { livekitRoom: LivekitRoom; muteStates: MuteStates; /** Function to call when the user explicitly ends the call */ - onLeave: () => void; + onLeave: (cause: "user", soundFile?: CallEventSounds) => void; header: HeaderStyle; otelGroupCallMembership?: OTelGroupCallMembership; connState: ECConnectionState; @@ -283,8 +282,6 @@ export const InCallView: FC = ({ const pickupPhaseSoundCache = useInitial(async () => { return prefetchSounds({ waiting: { mp3: ringtoneMp3, ogg: ringtoneOgg }, - decline: { mp3: declineMp3, ogg: declineOgg }, - timeout: { mp3: timeoutMp3, ogg: timeoutOgg }, }); }); @@ -354,7 +351,7 @@ export const InCallView: FC = ({ const showFooter = useBehavior(vm.showFooter$); const earpieceMode = useBehavior(vm.earpieceMode$); const audioOutputSwitcher = useBehavior(vm.audioOutputSwitcher$); - useSubscription(vm.autoLeave$, onLeave); + useSubscription(vm.autoLeave$, () => onLeave("user")); // We need to set the proper timings on the animation based upon the sound length. const ringDuration = pickupPhaseAudio?.soundDuration["waiting"] ?? 1; @@ -378,25 +375,10 @@ export const InCallView: FC = ({ // When we enter timeout or decline we will leave the call. useEffect((): void | (() => void) => { if (callPickupState === "timeout") { - void pickupPhaseAudio - ?.playSound("timeout") - .catch((e) => { - logger.error("Failed to play timeout sound", e); - }) - .finally(() => { - onLeave(); - }); + onLeave("user", "timeout"); } if (callPickupState === "decline") { - // Wait for the sound to finish before leaving - void pickupPhaseAudio - ?.playSound("decline") - .catch((e) => { - logger.error("Failed to play decline sound", e); - }) - .finally(() => { - onLeave(); - }); + onLeave("user", "decline"); } }, [callPickupState, onLeave, pickupPhaseAudio]); @@ -849,7 +831,7 @@ export const InCallView: FC = ({ c !== null && c !== "success"), map(([, userMedia]) => userMedia), pairwise(),