From 7d1bd02e31cefaa7c3e7f456f20be45891e5d2d8 Mon Sep 17 00:00:00 2001 From: hobokenchicken Date: Thu, 9 Jul 2026 13:08:02 -0400 Subject: [PATCH] fix(voice): AudioRenderers listens to room events directly, not zustand --- web/src/components/AudioRenderers.tsx | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/web/src/components/AudioRenderers.tsx b/web/src/components/AudioRenderers.tsx index 5bf372d..b38023b 100644 --- a/web/src/components/AudioRenderers.tsx +++ b/web/src/components/AudioRenderers.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useVoiceStore } from '../stores/voice.ts'; import { Track, RoomEvent } from 'livekit-client'; import type { Participant, TrackPublication, Room } from 'livekit-client'; @@ -13,7 +13,6 @@ function flushPendingAudio() { } } -// ponytail: one-time document listener to flush on any user interaction let listenerAttached = false; function ensureInteractionListener() { if (listenerAttached) return; @@ -68,9 +67,25 @@ function RemoteAudioTrack({ participant, room }: { participant: Participant; roo export function AudioRenderers() { const room = useVoiceStore((s) => s._room); - // @ts-ignore ponytail: subscribe to trigger re-render when remote users join/leave - useVoiceStore((s) => s.participants); // @ts-ignore - + // ponytail: use a counter that increments on every participant change + // zustand's Object.is check on the participants array wasn't triggering re-renders + const [, setTick] = useState(0); + + useEffect(() => { + if (!room) return; + const bump = () => setTick((t) => t + 1); + room.on(RoomEvent.ParticipantConnected, bump); + room.on(RoomEvent.ParticipantDisconnected, bump); + room.on(RoomEvent.TrackSubscribed, bump); + room.on(RoomEvent.TrackUnsubscribed, bump); + return () => { + room.off(RoomEvent.ParticipantConnected, bump); + room.off(RoomEvent.ParticipantDisconnected, bump); + room.off(RoomEvent.TrackSubscribed, bump); + room.off(RoomEvent.TrackUnsubscribed, bump); + }; + }, [room]); + if (!room) return null; const remoteParticipants = Array.from(room.remoteParticipants.values());