feat(voice-store): add pruneStaleDevices to clear stale persisted device ids
This commit is contained in:
@@ -89,6 +89,7 @@ interface VoiceState {
|
|||||||
setInputDevice: (deviceId: string) => void;
|
setInputDevice: (deviceId: string) => void;
|
||||||
setOutputDevice: (deviceId: string) => void;
|
setOutputDevice: (deviceId: string) => void;
|
||||||
setCameraDeviceId: (deviceId: string | null) => void;
|
setCameraDeviceId: (deviceId: string | null) => void;
|
||||||
|
pruneStaleDevices: () => Promise<void>;
|
||||||
pttActive: boolean;
|
pttActive: boolean;
|
||||||
setMuted: (muted: boolean) => void;
|
setMuted: (muted: boolean) => void;
|
||||||
setPttActive: (active: boolean) => void;
|
setPttActive: (active: boolean) => void;
|
||||||
@@ -322,6 +323,39 @@ export const useVoiceStore = create<VoiceState>()(
|
|||||||
|
|
||||||
setCameraDeviceId: (deviceId) => set({ cameraDeviceId: deviceId }),
|
setCameraDeviceId: (deviceId) => set({ cameraDeviceId: deviceId }),
|
||||||
|
|
||||||
|
pruneStaleDevices: async () => {
|
||||||
|
try {
|
||||||
|
const devs = await navigator.mediaDevices.enumerateDevices();
|
||||||
|
// Firefox/Safari hide deviceIds pre-permission. If the enumerated set for a
|
||||||
|
// kind has zero non-empty deviceIds, skip pruning that kind — we cannot
|
||||||
|
// distinguish "stale" from "obscured".
|
||||||
|
const audioInIds = new Set(
|
||||||
|
devs.filter(d => d.kind === 'audioinput' && d.deviceId).map(d => d.deviceId)
|
||||||
|
);
|
||||||
|
const audioOutIds = new Set(
|
||||||
|
devs.filter(d => d.kind === 'audiooutput' && d.deviceId).map(d => d.deviceId)
|
||||||
|
);
|
||||||
|
const videoInIds = new Set(
|
||||||
|
devs.filter(d => d.kind === 'videoinput' && d.deviceId).map(d => d.deviceId)
|
||||||
|
);
|
||||||
|
|
||||||
|
const s = get();
|
||||||
|
const next: Partial<VoiceState> = {};
|
||||||
|
if (audioInIds.size > 0 && s.inputDeviceId !== 'default' && !audioInIds.has(s.inputDeviceId)) {
|
||||||
|
next.inputDeviceId = 'default';
|
||||||
|
}
|
||||||
|
if (audioOutIds.size > 0 && s.outputDeviceId !== 'default' && !audioOutIds.has(s.outputDeviceId)) {
|
||||||
|
next.outputDeviceId = 'default';
|
||||||
|
}
|
||||||
|
if (videoInIds.size > 0 && s.cameraDeviceId !== null && !videoInIds.has(s.cameraDeviceId)) {
|
||||||
|
next.cameraDeviceId = null;
|
||||||
|
}
|
||||||
|
if (Object.keys(next).length) set(next);
|
||||||
|
} catch {
|
||||||
|
// enumerateDevices unavailable (no MediaDevices API or permission policy denial) — skip
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
setMuted: (muted: boolean) => set({ isMuted: muted }),
|
setMuted: (muted: boolean) => set({ isMuted: muted }),
|
||||||
setPttActive: (active: boolean) => set({ pttActive: active }),
|
setPttActive: (active: boolean) => set({ pttActive: active }),
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user