From 2abdbf168fc76d59fd645d71f9f70a64ed3ee223 Mon Sep 17 00:00:00 2001 From: PxlLoewe <72106766+PxlLoewe@users.noreply.github.com> Date: Fri, 25 Jul 2025 21:30:03 -0700 Subject: [PATCH] fixes #117 --- apps/dispatch/app/_components/Audio/Audio.tsx | 36 +++++----- apps/dispatch/app/_store/audioStore.ts | 66 ++++++++++++------- 2 files changed, 62 insertions(+), 40 deletions(-) diff --git a/apps/dispatch/app/_components/Audio/Audio.tsx b/apps/dispatch/app/_components/Audio/Audio.tsx index abe069a6..8b908570 100644 --- a/apps/dispatch/app/_components/Audio/Audio.tsx +++ b/apps/dispatch/app/_components/Audio/Audio.tsx @@ -25,6 +25,7 @@ import { useSounds } from "_components/Audio/useSounds"; export const Audio = () => { const { speakingParticipants, + resetSpeakingParticipants, isTalking, toggleTalking, transmitBlocked, @@ -104,7 +105,7 @@ export const Audio = () => { data-tip="Nachricht entfernen" > {state === "connected" && (
- {connectionQuality === ConnectionQuality.Excellent && } - {connectionQuality === ConnectionQuality.Good && } - {connectionQuality === ConnectionQuality.Poor && } - {connectionQuality === ConnectionQuality.Lost && } + {connectionQuality === ConnectionQuality.Excellent && } + {connectionQuality === ConnectionQuality.Good && } + {connectionQuality === ConnectionQuality.Poor && } + {connectionQuality === ConnectionQuality.Lost && } {connectionQuality === ConnectionQuality.Unknown && ( - + )}
{remoteParticipants}
@@ -184,7 +186,7 @@ export const Audio = () => { {ROOMS.map((r) => (
  • @@ -201,12 +203,12 @@ export const Audio = () => { ))}
  • diff --git a/apps/dispatch/app/_store/audioStore.ts b/apps/dispatch/app/_store/audioStore.ts index b1b789c0..c0aaefe1 100644 --- a/apps/dispatch/app/_store/audioStore.ts +++ b/apps/dispatch/app/_store/audioStore.ts @@ -25,28 +25,29 @@ import { usePilotConnectionStore } from "_store/pilot/connectionStore"; let interval: NodeJS.Timeout; type TalkState = { + addSpeakingParticipant: (participant: Participant) => void; + connect: (roomName: string, role: string) => void; + connectionQuality: ConnectionQuality; + disconnect: () => void; + isTalking: boolean; + localRadioTrack: LocalTrackPublication | undefined; + message: string | null; + removeMessage: () => void; + removeSpeakingParticipant: (speakingParticipants: Participant) => void; + remoteParticipants: number; + resetSpeakingParticipants: (source: string) => void; + room: Room | null; + setSettings: (settings: Partial) => void; settings: { micDeviceId: string | null; micVolume: number; radioVolume: number; dmeVolume: number; }; - isTalking: boolean; - transmitBlocked: boolean; - removeMessage: () => void; - state: "connecting" | "connected" | "disconnected" | "error"; - message: string | null; - connectionQuality: ConnectionQuality; - remoteParticipants: number; - toggleTalking: () => void; - setSettings: (settings: Partial) => void; - connect: (roomName: string, role: string) => void; - disconnect: () => void; speakingParticipants: Participant[]; - addSpeakingParticipant: (participant: Participant) => void; - removeSpeakingParticipant: (speakingParticipants: Participant) => void; - room: Room | null; - localRadioTrack: LocalTrackPublication | undefined; + state: "connecting" | "connected" | "disconnected" | "error"; + toggleTalking: () => void; + transmitBlocked: boolean; }; const getToken = async (roomName: string) => { const response = await axios.get(`/api/livekit-token?roomName=${roomName}`); @@ -71,6 +72,15 @@ export const useAudioStore = create((set, get) => ({ remoteParticipants: 0, connectionQuality: ConnectionQuality.Unknown, room: null, + resetSpeakingParticipants: (source: string) => { + set({ + speakingParticipants: [], + isTalking: false, + transmitBlocked: false, + message: `Ruf beendet durch ${source || "eine unsichtbare Macht"}`, + }); + get().room?.localParticipant.setMicrophoneEnabled(false); + }, addSpeakingParticipant: (participant) => { set((state) => { if (!state.speakingParticipants.some((p) => p.identity === participant.identity)) { @@ -201,10 +211,15 @@ export const useAudioStore = create((set, get) => ({ set({ localRadioTrack: publishedTrack }); - set({ state: "connected", room, message: null }); + set({ state: "connected", room, isTalking: false, message: null }); }) .on(RoomEvent.Disconnected, () => { - set({ state: "disconnected", speakingParticipants: [], transmitBlocked: false }); + set({ + state: "disconnected", + speakingParticipants: [], + transmitBlocked: false, + isTalking: false, + }); handleDisconnect(); }) @@ -223,17 +238,22 @@ export const useAudioStore = create((set, get) => ({ room.registerRpcMethod("force-mute", async (data: RpcInvocationData) => { const { by } = JSON.parse(data.payload); - room.localParticipant.setMicrophoneEnabled(false); - useAudioStore.setState({ - isTalking: false, - message: `Ruf beendet durch ${by || "eine unsichtbare Macht"}`, - }); - return `Hello, ${data.callerIdentity}!`; + get().resetSpeakingParticipants(by); + return "OK"; }); interval = setInterval(() => { + // Filter forgotten participants + const oldSpeakingParticipants = get().speakingParticipants; + const speakingParticipants = oldSpeakingParticipants.filter((oP) => { + return Array.from(room.remoteParticipants.values()).find( + (p) => p.identity === oP.identity, + ); + }); + set({ remoteParticipants: room.numParticipants === 0 ? 0 : room.numParticipants - 1, // Unreliable and delayed + speakingParticipants, }); }, 500); } catch (error: Error | unknown) {