fix(voice): broadcast VOICE_JOIN/LEAVE via ws, show participants in sidebar for all users

This commit is contained in:
2026-07-09 12:47:42 -04:00
parent d20c0da498
commit 57615f5117
9 changed files with 92 additions and 12 deletions
+42
View File
@@ -0,0 +1,42 @@
import { create } from 'zustand';
export interface VoicePresenceEntry {
userId: string;
username: string;
channelId: string;
}
interface VoicePresenceState {
// channelId -> userId -> entry
presence: Record<string, Record<string, VoicePresenceEntry>>;
_handleJoin: (entry: VoicePresenceEntry) => void;
_handleLeave: (userId: string, channelId: string) => void;
getParticipants: (channelId: string) => VoicePresenceEntry[];
}
export const useVoicePresenceStore = create<VoicePresenceState>((set, get) => ({
presence: {},
_handleJoin: (entry) => {
set((state) => {
const room = { ...(state.presence[entry.channelId] || {}) };
room[entry.userId] = entry;
return { presence: { ...state.presence, [entry.channelId]: room } };
});
},
_handleLeave: (userId, channelId) => {
set((state) => {
const room = { ...(state.presence[channelId] || {}) };
delete room[userId];
const next = { ...state.presence, [channelId]: room };
if (Object.keys(room).length === 0) delete next[channelId];
return { presence: next };
});
},
getParticipants: (channelId) => {
const room = get().presence[channelId];
return room ? Object.values(room) : [];
},
}));
+19
View File
@@ -5,6 +5,7 @@ import { useServerStore } from './server.ts';
import { usePresenceStore } from './presence.ts';
import { useTypingStore } from './typing.ts';
import { useVoiceStore } from './voice.ts';
import { useVoicePresenceStore } from './voicePresence.ts';
import { useAuthStore } from './auth.ts';
import { useConversationStore } from './conversation.ts';
import { useReadStatesStore } from './readStates.ts';
@@ -274,6 +275,24 @@ export const useWebSocketStore = create<WebSocketState>((set, get) => ({
});
break;
}
case 'VOICE_JOIN': {
const { room_name, user_id, username } = payload as Record<string, string>;
if (room_name && user_id && username) {
useVoicePresenceStore.getState()._handleJoin({
userId: user_id,
username,
channelId: room_name,
});
}
break;
}
case 'VOICE_LEAVE': {
const { room_name, user_id } = payload as Record<string, string>;
if (room_name && user_id) {
useVoicePresenceStore.getState()._handleLeave(user_id, room_name);
}
break;
}
default:
break;
}