feat(phase6): video grid, noise suppression, whisper system
- New VideoGrid component: live camera feeds (local + remote) in VoicePanel - Camera toggle in VoiceControls: enable/disable video per participant - Noise suppression toggle in VoiceControls + voice store - VOICE_WHISPER WS event: backend routes whisper to target user only - Whisper UI: per-participant whisper button, notification toasts with dismiss - Fix circular import between voice.ts and ws.ts (use lazy accessor)
This commit is contained in:
@@ -227,6 +227,28 @@ func (h *Hub) ClientCount() int {
|
||||
return len(h.clients)
|
||||
}
|
||||
|
||||
// SendToUser sends an event only to the specified user's connected client(s).
|
||||
func (h *Hub) SendToUser(userID string, event Event) {
|
||||
data, err := json.Marshal(event)
|
||||
if err != nil {
|
||||
h.logger.Error("failed to marshal event", "type", event.Type, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
|
||||
for client := range h.clients {
|
||||
if client.UserID == userID {
|
||||
select {
|
||||
case client.send <- data:
|
||||
default:
|
||||
go func(c *Client) { h.unregister <- c }(client)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RefreshUserServers loads the server membership for a user from the database
|
||||
// and caches it. Call when a client connects or when server membership changes.
|
||||
func (h *Hub) RefreshUserServers(userID string) {
|
||||
|
||||
Reference in New Issue
Block a user