diff --git a/src/room/CallEventAudioRenderer.tsx b/src/room/CallEventAudioRenderer.tsx new file mode 100644 index 00000000..48267e26 --- /dev/null +++ b/src/room/CallEventAudioRenderer.tsx @@ -0,0 +1,71 @@ +/* +Copyright 2024 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only +Please see LICENSE in the repository root for full details. +*/ + +import { ReactNode, useDeferredValue, useEffect, useRef } from "react"; + +import { + playReactionsSound, + soundEffectVolumeSetting as effectSoundVolumeSetting, + useSetting, +} from "../settings/settings"; +import { CallViewModel } from "../state/CallViewModel"; +import { useObservableEagerState } from "observable-hooks"; + +// TODO: These need replacing with something more pleasant. +import enterCallSoundMp3 from "../sound/start_talk_local.mp3"; +import enterCallSoundOgg from "../sound/start_talk_local.ogg"; +import leftCallSoundMp3 from "../sound/start_talk_remote.mp3"; +import leftCallSoundOgg from "../sound/start_talk_remote.ogg"; + +export function CallEventAudioRenderer({ + vm, +}: { + vm: CallViewModel; +}): ReactNode { + const [shouldPlay] = useSetting(playReactionsSound); + const [effectSoundVolume] = useSetting(effectSoundVolumeSetting); + const memberIds = useObservableEagerState(vm.userMediaIds); + const previousMembers = useDeferredValue(memberIds); + const callEntered = useRef(null); + const callLeft = useRef(null); + + useEffect(() => { + const memberLeft = !!previousMembers.filter((m) => !memberIds.includes(m)) + .length; + const memberJoined = !!memberIds.filter((m) => !previousMembers.includes(m)) + .length; + + if (callEntered.current && callEntered.current?.paused && memberJoined) { + callEntered.current.volume = effectSoundVolume; + void callEntered.current.play(); + } + + if (callLeft.current && callLeft.current?.paused && memberLeft) { + callLeft.current.volume = effectSoundVolume; + void callLeft.current.play(); + } + }, [callEntered, callLeft, memberIds, previousMembers]); + + // Do not render any audio elements if playback is disabled. Will save + // audio file fetches. + if (!shouldPlay) { + return null; + } + + return ( + <> + + + + ); +} diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 18afa95e..e313b478 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -87,6 +87,7 @@ import { ReactionsAudioRenderer } from "./ReactionAudioRenderer"; import { useSwitchCamera } from "./useSwitchCamera"; import { soundEffectVolumeSetting, useSetting } from "../settings/settings"; import { ReactionsOverlay } from "./ReactionsOverlay"; +import { CallEventAudioRenderer } from "./CallEventAudioRenderer"; const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {}); @@ -667,6 +668,7 @@ export const InCallView: FC = ({ ))} + {renderContent()}