mirror of
https://github.com/vector-im/element-call.git
synced 2026-07-18 18:59:23 +00:00
Integrate raise hand sound into call event renderer.
This commit is contained in:
@@ -51,7 +51,14 @@ afterEach(() => {
|
|||||||
window.HTMLMediaElement.prototype.play = originalPlayFn;
|
window.HTMLMediaElement.prototype.play = originalPlayFn;
|
||||||
});
|
});
|
||||||
|
|
||||||
test("plays a sound when entering a call", () => {
|
/**
|
||||||
|
* We don't want to play a sound when loading the call state
|
||||||
|
* because typically this occurs in two stages. We first join
|
||||||
|
* the call as a local participant and *then* the remote
|
||||||
|
* participants join from our perspective. We don't want to make
|
||||||
|
* a noise every time.
|
||||||
|
*/
|
||||||
|
test("does NOT play a sound when entering a call", () => {
|
||||||
const audioIsPlaying: string[] = mockMediaPlay();
|
const audioIsPlaying: string[] = mockMediaPlay();
|
||||||
const members = new Map([alice, bob].map((p) => [p.userId, p]));
|
const members = new Map([alice, bob].map((p) => [p.userId, p]));
|
||||||
const remoteParticipants = of([aliceParticipant]);
|
const remoteParticipants = of([aliceParticipant]);
|
||||||
@@ -75,10 +82,7 @@ test("plays a sound when entering a call", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
render(<CallEventAudioRenderer vm={vm} />);
|
render(<CallEventAudioRenderer vm={vm} />);
|
||||||
expect(audioIsPlaying).toEqual([
|
expect(audioIsPlaying).toHaveLength(0);
|
||||||
// Joining the call
|
|
||||||
enterSound,
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("plays no sound when muted", () => {
|
test("plays no sound when muted", () => {
|
||||||
@@ -141,8 +145,6 @@ test("plays a sound when a user joins", () => {
|
|||||||
});
|
});
|
||||||
// Play a sound when joining a call.
|
// Play a sound when joining a call.
|
||||||
expect(audioIsPlaying).toEqual([
|
expect(audioIsPlaying).toEqual([
|
||||||
// Joining the call
|
|
||||||
enterSound,
|
|
||||||
// Bob leaves
|
// Bob leaves
|
||||||
enterSound,
|
enterSound,
|
||||||
]);
|
]);
|
||||||
@@ -178,14 +180,12 @@ test("plays a sound when a user leaves", () => {
|
|||||||
liveKitRoom.removeParticipant(aliceParticipant);
|
liveKitRoom.removeParticipant(aliceParticipant);
|
||||||
});
|
});
|
||||||
expect(audioIsPlaying).toEqual([
|
expect(audioIsPlaying).toEqual([
|
||||||
// Joining the call
|
|
||||||
enterSound,
|
|
||||||
// Alice leaves
|
// Alice leaves
|
||||||
leaveSound,
|
leaveSound,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("plays no sound when the participant list", () => {
|
test("plays no sound when the participant list is more than the maximum size", () => {
|
||||||
const audioIsPlaying: string[] = mockMediaPlay();
|
const audioIsPlaying: string[] = mockMediaPlay();
|
||||||
const members = new Map([alice].map((p) => [p.userId, p]));
|
const members = new Map([alice].map((p) => [p.userId, p]));
|
||||||
const remoteParticipants = new Map<string, RemoteParticipant>([
|
const remoteParticipants = new Map<string, RemoteParticipant>([
|
||||||
|
|||||||
@@ -5,14 +5,17 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
Please see LICENSE in the repository root for full details.
|
Please see LICENSE in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ReactNode, useEffect } from "react";
|
import { ReactNode, useDeferredValue, useEffect, useMemo } from "react";
|
||||||
import { debounce, filter, interval, throttle } from "rxjs";
|
import { debounce, filter, interval, throttle } from "rxjs";
|
||||||
import { CallViewModel } from "../state/CallViewModel";
|
import { CallViewModel } from "../state/CallViewModel";
|
||||||
import joinCallSoundMp3 from "../sound/join_call.mp3";
|
import joinCallSoundMp3 from "../sound/join_call.mp3";
|
||||||
import joinCallSoundOgg from "../sound/join_call.ogg";
|
import joinCallSoundOgg from "../sound/join_call.ogg";
|
||||||
import leftCallSoundMp3 from "../sound/left_call.mp3";
|
import leftCallSoundMp3 from "../sound/left_call.mp3";
|
||||||
import leftCallSoundOgg from "../sound/left_call.ogg";
|
import leftCallSoundOgg from "../sound/left_call.ogg";
|
||||||
|
import handSoundOgg from "../sound/raise_hand.ogg?url";
|
||||||
|
import handSoundMp3 from "../sound/raise_hand.mp3?url";
|
||||||
import { prefetchSounds, useAudioContext } from "../useAudioContext";
|
import { prefetchSounds, useAudioContext } from "../useAudioContext";
|
||||||
|
import { useReactions } from "../useReactions";
|
||||||
|
|
||||||
// Do not play any sounds if the participant count has exceeded this
|
// Do not play any sounds if the participant count has exceeded this
|
||||||
// number.
|
// number.
|
||||||
@@ -29,6 +32,10 @@ const Sounds = prefetchSounds({
|
|||||||
mp3: leftCallSoundMp3,
|
mp3: leftCallSoundMp3,
|
||||||
ogg: leftCallSoundOgg,
|
ogg: leftCallSoundOgg,
|
||||||
},
|
},
|
||||||
|
raiseHand: {
|
||||||
|
mp3: handSoundMp3,
|
||||||
|
ogg: handSoundOgg,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export function CallEventAudioRenderer({
|
export function CallEventAudioRenderer({
|
||||||
@@ -41,6 +48,19 @@ export function CallEventAudioRenderer({
|
|||||||
latencyHint: "interactive",
|
latencyHint: "interactive",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { raisedHands } = useReactions();
|
||||||
|
const raisedHandCount = useMemo(
|
||||||
|
() => Object.keys(raisedHands).length,
|
||||||
|
[raisedHands],
|
||||||
|
);
|
||||||
|
const previousRaisedHandCount = useDeferredValue(raisedHandCount);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (audioEngineCtx && previousRaisedHandCount < raisedHandCount) {
|
||||||
|
audioEngineCtx.playSound("raiseHand");
|
||||||
|
}
|
||||||
|
}, [audioEngineCtx, previousRaisedHandCount, raisedHandCount]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!audioEngineCtx) {
|
if (!audioEngineCtx) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import {
|
|||||||
TouchEvent,
|
TouchEvent,
|
||||||
forwardRef,
|
forwardRef,
|
||||||
useCallback,
|
useCallback,
|
||||||
useDeferredValue,
|
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
@@ -81,11 +80,8 @@ import { makeSpotlightLandscapeLayout } from "../grid/SpotlightLandscapeLayout";
|
|||||||
import { makeSpotlightPortraitLayout } from "../grid/SpotlightPortraitLayout";
|
import { makeSpotlightPortraitLayout } from "../grid/SpotlightPortraitLayout";
|
||||||
import { GridTileViewModel, TileViewModel } from "../state/TileViewModel";
|
import { GridTileViewModel, TileViewModel } from "../state/TileViewModel";
|
||||||
import { ReactionsProvider, useReactions } from "../useReactions";
|
import { ReactionsProvider, useReactions } from "../useReactions";
|
||||||
import handSoundOgg from "../sound/raise_hand.ogg?url";
|
|
||||||
import handSoundMp3 from "../sound/raise_hand.mp3?url";
|
|
||||||
import { ReactionsAudioRenderer } from "./ReactionAudioRenderer";
|
import { ReactionsAudioRenderer } from "./ReactionAudioRenderer";
|
||||||
import { useSwitchCamera } from "./useSwitchCamera";
|
import { useSwitchCamera } from "./useSwitchCamera";
|
||||||
import { soundEffectVolumeSetting, useSetting } from "../settings/settings";
|
|
||||||
import { ReactionsOverlay } from "./ReactionsOverlay";
|
import { ReactionsOverlay } from "./ReactionsOverlay";
|
||||||
import { CallEventAudioRenderer } from "./CallEventAudioRenderer";
|
import { CallEventAudioRenderer } from "./CallEventAudioRenderer";
|
||||||
|
|
||||||
@@ -183,14 +179,7 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
connState,
|
connState,
|
||||||
onShareClick,
|
onShareClick,
|
||||||
}) => {
|
}) => {
|
||||||
const [soundEffectVolume] = useSetting(soundEffectVolumeSetting);
|
const { supportsReactions, sendReaction, toggleRaisedHand } = useReactions();
|
||||||
const { supportsReactions, raisedHands, sendReaction, toggleRaisedHand } =
|
|
||||||
useReactions();
|
|
||||||
const raisedHandCount = useMemo(
|
|
||||||
() => Object.keys(raisedHands).length,
|
|
||||||
[raisedHands],
|
|
||||||
);
|
|
||||||
const previousRaisedHandCount = useDeferredValue(raisedHandCount);
|
|
||||||
|
|
||||||
useWakeLock();
|
useWakeLock();
|
||||||
|
|
||||||
@@ -340,25 +329,6 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
[vm],
|
[vm],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Play a sound when the raised hand count increases.
|
|
||||||
const handRaisePlayer = useRef<HTMLAudioElement>(null);
|
|
||||||
useEffect(() => {
|
|
||||||
if (!handRaisePlayer.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (previousRaisedHandCount < raisedHandCount) {
|
|
||||||
handRaisePlayer.current.volume = soundEffectVolume;
|
|
||||||
handRaisePlayer.current.play().catch((ex) => {
|
|
||||||
logger.warn("Failed to play raise hand sound", ex);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
raisedHandCount,
|
|
||||||
handRaisePlayer,
|
|
||||||
previousRaisedHandCount,
|
|
||||||
soundEffectVolume,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
widget?.api.transport
|
widget?.api.transport
|
||||||
.send(
|
.send(
|
||||||
@@ -672,10 +642,6 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
<RoomAudioRenderer />
|
<RoomAudioRenderer />
|
||||||
{renderContent()}
|
{renderContent()}
|
||||||
<CallEventAudioRenderer vm={vm} />
|
<CallEventAudioRenderer vm={vm} />
|
||||||
<audio ref={handRaisePlayer} preload="auto" hidden>
|
|
||||||
<source src={handSoundOgg} type="audio/ogg; codecs=vorbis" />
|
|
||||||
<source src={handSoundMp3} type="audio/mpeg" />
|
|
||||||
</audio>
|
|
||||||
<ReactionsAudioRenderer />
|
<ReactionsAudioRenderer />
|
||||||
<ReactionsOverlay />
|
<ReactionsOverlay />
|
||||||
{footer}
|
{footer}
|
||||||
|
|||||||
Reference in New Issue
Block a user