fix: decouple voice intent from server enforcement to prevent involuntary unmute

When a moderator lifted a server mute/deafen, the client was involuntarily
turning on the user's microphone because isMuted/isDeafened conflated user
intent with server enforcement. Now intent (isMuted/isDeafened) is never
mutated by server events. Effective state (intent || serverEnforcement) is
computed at broadcast and hardware time via centralized helpers.
This commit is contained in:
Jannis Braun
2026-03-09 22:25:56 +01:00
parent db1909d785
commit 907f8285cd
9 changed files with 164 additions and 233 deletions
@@ -7,6 +7,7 @@ import { ScreenShareSettingsPopover } from './ScreenShareSettingsPopover';
import { ConnectionInfoPopover } from './ConnectionInfoPopover';
import { startScreenShare, stopScreenShare } from '../../utils/screenShare';
import { hasPermissionBit, PermissionBits } from '../../utils/permissions';
import { broadcastVoiceStatus } from '../../utils/voice';
/**
* VoiceControls renders the voice status + button rows.
@@ -50,9 +51,7 @@ export function VoiceControls() {
const willEnable = !isCameraOn;
await room.localParticipant.setCameraEnabled(willEnable);
toggleCamera();
// Broadcast camera state via WebSocket
const { isMuted: m, isDeafened: d, isScreenSharing: ss } = useVoiceStore.getState();
wsSend({ type: 'voice_status', isMuted: m, isDeafened: d, isCameraOn: willEnable, isScreenSharing: ss }, voiceOrigin);
broadcastVoiceStatus();
} catch (err) {
console.error('[VoiceControls] Failed to toggle camera:', err);
}
@@ -64,14 +63,10 @@ export function VoiceControls() {
try {
if (!isScreenSharing) {
const started = await startScreenShare(room);
if (started) {
const { isMuted: m, isDeafened: d, isCameraOn: c } = useVoiceStore.getState();
wsSend({ type: 'voice_status', isMuted: m, isDeafened: d, isCameraOn: c, isScreenSharing: true }, voiceOrigin);
}
if (started) broadcastVoiceStatus();
} else {
await stopScreenShare(room);
const { isMuted: m, isDeafened: d, isCameraOn: c } = useVoiceStore.getState();
wsSend({ type: 'voice_status', isMuted: m, isDeafened: d, isCameraOn: c, isScreenSharing: false }, voiceOrigin);
broadcastVoiceStatus();
}
} catch (err) {
console.error('[VoiceControls] Failed to toggle screen share:', err);