feat(voice): persist A/V device preferences in localStorage

This commit is contained in:
2026-07-09 12:36:33 -04:00
parent 1139e90fc3
commit 62e8354d03
2 changed files with 48 additions and 0 deletions
@@ -1,5 +1,6 @@
import { useEffect, useState } from 'react';
import { useVoiceStore } from '../stores/voice.ts';
import { saveDevicePref } from '../stores/voice.ts';
interface Props {
onClose: () => void;
@@ -48,6 +49,7 @@ export function DeviceSettingsModal({ onClose }: Props) {
if (!room) return;
try {
await room.switchActiveDevice(kind, deviceId);
saveDevicePref(kind, deviceId);
if (kind === 'audioinput') setActiveAudioInput(deviceId);
if (kind === 'audiooutput') setActiveAudioOutput(deviceId);
if (kind === 'videoinput') setActiveVideoInput(deviceId);
+46
View File
@@ -64,6 +64,42 @@ interface VoiceState {
dismissWhisper: (timestamp: number) => void;
}
const DEVICE_PREFS_KEY = 'dc_voice_device_prefs';
interface DevicePrefs {
audioInput?: string;
audioOutput?: string;
videoInput?: string;
}
function loadDevicePrefs(): DevicePrefs {
try {
return JSON.parse(localStorage.getItem(DEVICE_PREFS_KEY) || '{}');
} catch {
return {};
}
}
function saveDevicePrefs(prefs: DevicePrefs) {
localStorage.setItem(DEVICE_PREFS_KEY, JSON.stringify(prefs));
}
export function saveDevicePref(kind: MediaDeviceKind, deviceId: string) {
const prefs = loadDevicePrefs();
if (kind === 'audioinput') prefs.audioInput = deviceId;
else if (kind === 'audiooutput') prefs.audioOutput = deviceId;
else if (kind === 'videoinput') prefs.videoInput = deviceId;
saveDevicePrefs(prefs);
}
export function getSavedDeviceId(kind: MediaDeviceKind): string | undefined {
const prefs = loadDevicePrefs();
if (kind === 'audioinput') return prefs.audioInput;
if (kind === 'audiooutput') return prefs.audioOutput;
if (kind === 'videoinput') return prefs.videoInput;
return undefined;
}
function participantToVoice(p: Participant): VoiceParticipant {
const micPub = p.getTrackPublication(Track.Source.Microphone);
const camPub = p.getTrackPublication(Track.Source.Camera);
@@ -215,6 +251,16 @@ export const useVoiceStore = create<VoiceState>((set, get) => ({
console.warn("[voice] failed to set initial mute state:", micErr);
}
// Restore saved device preferences
const prefs = loadDevicePrefs();
try {
if (prefs.audioInput) await room.switchActiveDevice('audioinput', prefs.audioInput);
if (prefs.audioOutput) await room.switchActiveDevice('audiooutput', prefs.audioOutput);
if (prefs.videoInput) await room.switchActiveDevice('videoinput', prefs.videoInput);
} catch (devErr) {
console.warn("[voice] failed to restore saved devices:", devErr);
}
set({ isJoining: false });
// Notify WebSocket peers