feat: voice status visibility + sidebar persistence fixes

- Add WebSocket voice_status/voice_status_update events so mute/deafen
  icons are visible in the sidebar without joining the voice channel
- Server tracks voiceUserStates and includes them in the ready payload
- Re-register voice channel on WebSocket reconnect to prevent sidebar
  users from disappearing after idle timeout
- Re-broadcast deafen state to late joiners via LiveKit data channel
- Fix black grid tile when video stops (enabled-flag guards)
- Remove duplicate mute/deafen from VoiceControls (replaced with
  Video Quality + Noise Suppression)
- Fix missing users in sidebar voice list (identity matching + fallback)
This commit is contained in:
Jannis Braun
2026-02-19 07:58:50 +01:00
parent 503e483b82
commit 0da4a530d6
10 changed files with 307 additions and 102 deletions
@@ -19,9 +19,9 @@ export function VoiceUser({ participant, large }: VoiceUserProps) {
const perUserVolume = participantVolumes.get(participant.userId) ?? 100;
const isLocal = participant.isLocal;
// Determine active video track — prioritize screen share, check readyState
const liveScreen = participant.screenTrack?.readyState === 'live' ? participant.screenTrack : null;
const liveCamera = participant.videoTrack?.readyState === 'live' ? participant.videoTrack : null;
// Determine active video track — prioritize screen share, check both enabled flag and readyState
const liveScreen = participant.isScreenSharing && participant.screenTrack?.readyState === 'live' ? participant.screenTrack : null;
const liveCamera = participant.isCameraOn && participant.videoTrack?.readyState === 'live' ? participant.videoTrack : null;
const activeVideoTrack = liveScreen ?? liveCamera;
const hasVideo = activeVideoTrack !== null;
const isScreenShare = liveScreen !== null;
@@ -154,6 +154,14 @@ export function VoiceUser({ participant, large }: VoiceUserProps) {
</svg>
</div>
)}
{(isLocal ? isDeafened : participant.isDeafened) && (
<div className="w-5 h-5 bg-discord-red/90 rounded-full flex items-center justify-center">
<svg width="12" height="12" viewBox="0 0 24 24" fill="white">
<path d="M12 3c-4.97 0-9 4.03-9 9v7c0 1.1.9 2 2 2h2v-7H5v-2c0-3.87 3.13-7 7-7s7 3.13 7 7v2h-2v7h2c1.1 0 2-.9 2-2v-7c0-4.97-4.03-9-9-9z" />
<line x1="3" y1="3" x2="21" y2="21" stroke="white" strokeWidth="2" />
</svg>
</div>
)}
{participant.isScreenSharing && !isScreenShare && (
<div className="w-5 h-5 bg-discord-blurple/90 rounded-full flex items-center justify-center">
<svg width="12" height="12" viewBox="0 0 24 24" fill="white">