fix: reliable speaking indicator via direct store writes + polling safety net

Eliminate double-state architecture (useState → useEffect bridge → store)
that lost speaking events due to React 18 batching. ActiveSpeakersChanged
now writes speakingParticipantIds directly to voiceStore; 200ms poll
catches missed SDK events. Each VoiceUser subscribes to its own identity
via fine-grained selector for minimal re-renders. Also adds connection
quality indicator and ConnectionInfoPopover.
This commit is contained in:
Jannis Braun
2026-02-23 02:36:39 +01:00
parent 176f4db27e
commit 9091f08ced
10 changed files with 604 additions and 46 deletions
@@ -93,12 +93,10 @@ export function AppLayout() {
const currentVoiceChannelId = useVoiceStore((s) => s.currentVoiceChannelId);
const activeDmCall = useVoiceStore((s) => s.activeDmCall);
const setParticipants = useVoiceStore((s) => s.setParticipants);
const {
connect: connectVoice,
connectDm: connectDmVoice,
disconnect: disconnectVoice,
participants: voiceParticipants,
isConnected: isVoiceConnected,
isConnecting: isVoiceConnecting,
connectedChannelId,
@@ -107,11 +105,6 @@ export function AppLayout() {
// Initialize WebSocket
const { isConnected: isWsConnected } = useWebSocket();
// Sync participants to store
useEffect(() => {
setParticipants(voiceParticipants);
}, [voiceParticipants, setParticipants]);
// Track the last channel we attempted to connect to, to prevent effect loops
const lastAttemptedRef = React.useRef<string | null>(null);