feat(voice): persist A/V device preferences in localStorage
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user