mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-24 14:55:37 +00:00
fix invalid/stale mic selection windows
This commit is contained in:
@@ -111,8 +111,8 @@ function DropdownItem({
|
||||
);
|
||||
}
|
||||
|
||||
function Separator() {
|
||||
return <div className={styles.sep} />;
|
||||
function Separator({ dropdown = false }: { dropdown?: boolean }) {
|
||||
return <div className={dropdown ? styles.ddSep : styles.sep} />;
|
||||
}
|
||||
|
||||
function MicDeviceRow({
|
||||
@@ -196,6 +196,7 @@ export function LaunchWindow() {
|
||||
const showWebcamControls = webcamEnabled && !recording;
|
||||
const { devices, selectedDeviceId, setSelectedDeviceId } = useMicrophoneDevices(
|
||||
microphoneEnabled || micDropdownOpen,
|
||||
microphoneDeviceId,
|
||||
);
|
||||
const {
|
||||
devices: videoDevices,
|
||||
@@ -206,9 +207,11 @@ export function LaunchWindow() {
|
||||
const supportsHudCaptureProtection = platform !== "linux";
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedDeviceId && selectedDeviceId !== "default") {
|
||||
setMicrophoneDeviceId(selectedDeviceId);
|
||||
if (!selectedDeviceId) {
|
||||
return;
|
||||
}
|
||||
|
||||
setMicrophoneDeviceId(selectedDeviceId === "default" ? undefined : selectedDeviceId);
|
||||
}, [selectedDeviceId, setMicrophoneDeviceId]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -691,7 +694,7 @@ export function LaunchWindow() {
|
||||
/>
|
||||
</button>
|
||||
|
||||
<Separator />
|
||||
<Separator />
|
||||
|
||||
<IconButton
|
||||
onClick={toggleMicrophone}
|
||||
@@ -719,7 +722,7 @@ export function LaunchWindow() {
|
||||
<Timer size={18} />
|
||||
</IconButton>
|
||||
|
||||
<Separator />
|
||||
<Separator />
|
||||
|
||||
<button
|
||||
type="button"
|
||||
@@ -731,7 +734,7 @@ export function LaunchWindow() {
|
||||
<div className={styles.recDot} />
|
||||
</button>
|
||||
|
||||
<Separator />
|
||||
<Separator />
|
||||
|
||||
<IconButton
|
||||
buttonRef={moreButtonRef}
|
||||
@@ -878,7 +881,9 @@ export function LaunchWindow() {
|
||||
onSelect={() => {
|
||||
setMicrophoneEnabled(true);
|
||||
setSelectedDeviceId(device.deviceId);
|
||||
setMicrophoneDeviceId(device.deviceId);
|
||||
setMicrophoneDeviceId(
|
||||
device.deviceId === "default" ? undefined : device.deviceId,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -1,107 +1,113 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export interface MicrophoneDevice {
|
||||
deviceId: string
|
||||
label: string
|
||||
groupId: string
|
||||
deviceId: string;
|
||||
label: string;
|
||||
groupId: string;
|
||||
}
|
||||
|
||||
let hasRequestedMicrophoneLabels = false
|
||||
let hasRequestedMicrophoneLabels = false;
|
||||
|
||||
export function useMicrophoneDevices(enabled: boolean = true) {
|
||||
const [devices, setDevices] = useState<MicrophoneDevice[]>([])
|
||||
const [selectedDeviceId, setSelectedDeviceId] = useState<string>('default')
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
export function useMicrophoneDevices(enabled: boolean = true, preferredDeviceId?: string) {
|
||||
const [devices, setDevices] = useState<MicrophoneDevice[]>([]);
|
||||
const [selectedDeviceId, setSelectedDeviceId] = useState<string>("default");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!enabled) {
|
||||
return
|
||||
}
|
||||
useEffect(() => {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mounted = true
|
||||
let mounted = true;
|
||||
|
||||
const loadDevices = async () => {
|
||||
let permissionStream: MediaStream | null = null
|
||||
const loadDevices = async () => {
|
||||
let permissionStream: MediaStream | null = null;
|
||||
|
||||
try {
|
||||
setIsLoading(true)
|
||||
setError(null)
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
let allDevices = await navigator.mediaDevices.enumerateDevices()
|
||||
let audioInputs = allDevices
|
||||
.filter((device) => device.kind === 'audioinput')
|
||||
.map((device) => ({
|
||||
deviceId: device.deviceId,
|
||||
label: device.label || `Microphone ${device.deviceId.slice(0, 8)}`,
|
||||
groupId: device.groupId,
|
||||
}))
|
||||
let allDevices = await navigator.mediaDevices.enumerateDevices();
|
||||
let audioInputs = allDevices
|
||||
.filter((device) => device.kind === "audioinput")
|
||||
.map((device) => ({
|
||||
deviceId: device.deviceId,
|
||||
label: device.label || `Microphone ${device.deviceId.slice(0, 8)}`,
|
||||
groupId: device.groupId,
|
||||
}));
|
||||
|
||||
const needsLabelPermission =
|
||||
audioInputs.length > 0 && audioInputs.every((device) => !device.label.trim())
|
||||
const needsLabelPermission =
|
||||
audioInputs.length > 0 && audioInputs.every((device) => !device.label.trim());
|
||||
|
||||
if (needsLabelPermission && !hasRequestedMicrophoneLabels) {
|
||||
hasRequestedMicrophoneLabels = true
|
||||
permissionStream = await navigator.mediaDevices.getUserMedia({ audio: true })
|
||||
allDevices = await navigator.mediaDevices.enumerateDevices()
|
||||
audioInputs = allDevices
|
||||
.filter((device) => device.kind === 'audioinput')
|
||||
.map((device) => ({
|
||||
deviceId: device.deviceId,
|
||||
label: device.label || `Microphone ${device.deviceId.slice(0, 8)}`,
|
||||
groupId: device.groupId,
|
||||
}))
|
||||
}
|
||||
if (needsLabelPermission && !hasRequestedMicrophoneLabels) {
|
||||
hasRequestedMicrophoneLabels = true;
|
||||
permissionStream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||
allDevices = await navigator.mediaDevices.enumerateDevices();
|
||||
audioInputs = allDevices
|
||||
.filter((device) => device.kind === "audioinput")
|
||||
.map((device) => ({
|
||||
deviceId: device.deviceId,
|
||||
label: device.label || `Microphone ${device.deviceId.slice(0, 8)}`,
|
||||
groupId: device.groupId,
|
||||
}));
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
setDevices(audioInputs)
|
||||
setSelectedDeviceId((currentDeviceId) => {
|
||||
if (currentDeviceId === 'default' && audioInputs.length > 0) {
|
||||
return audioInputs[0].deviceId
|
||||
}
|
||||
if (mounted) {
|
||||
setDevices(audioInputs);
|
||||
setSelectedDeviceId((currentDeviceId) => {
|
||||
const normalizedPreferredDeviceId = preferredDeviceId ?? "default";
|
||||
if (audioInputs.some((device) => device.deviceId === normalizedPreferredDeviceId)) {
|
||||
return normalizedPreferredDeviceId;
|
||||
}
|
||||
|
||||
if (
|
||||
currentDeviceId !== 'default' &&
|
||||
audioInputs.some((device) => device.deviceId === currentDeviceId)
|
||||
) {
|
||||
return currentDeviceId
|
||||
}
|
||||
if (
|
||||
currentDeviceId !== "default" &&
|
||||
audioInputs.some((device) => device.deviceId === currentDeviceId)
|
||||
) {
|
||||
return currentDeviceId;
|
||||
}
|
||||
|
||||
return audioInputs[0]?.deviceId ?? 'default'
|
||||
})
|
||||
setIsLoading(false)
|
||||
}
|
||||
} catch (error) {
|
||||
if (mounted) {
|
||||
const message = error instanceof Error ? error.message : 'Failed to enumerate audio devices'
|
||||
setError(message)
|
||||
setIsLoading(false)
|
||||
console.error('Error loading microphone devices:', error)
|
||||
}
|
||||
} finally {
|
||||
permissionStream?.getTracks().forEach((track) => track.stop())
|
||||
}
|
||||
}
|
||||
return (
|
||||
audioInputs.find((device) => device.deviceId !== "default")?.deviceId ??
|
||||
audioInputs[0]?.deviceId ??
|
||||
"default"
|
||||
);
|
||||
});
|
||||
setIsLoading(false);
|
||||
}
|
||||
} catch (error) {
|
||||
if (mounted) {
|
||||
const message =
|
||||
error instanceof Error ? error.message : "Failed to enumerate audio devices";
|
||||
setError(message);
|
||||
setIsLoading(false);
|
||||
console.error("Error loading microphone devices:", error);
|
||||
}
|
||||
} finally {
|
||||
permissionStream?.getTracks().forEach((track) => track.stop());
|
||||
}
|
||||
};
|
||||
|
||||
void loadDevices()
|
||||
void loadDevices();
|
||||
|
||||
const handleDeviceChange = () => {
|
||||
void loadDevices()
|
||||
}
|
||||
const handleDeviceChange = () => {
|
||||
void loadDevices();
|
||||
};
|
||||
|
||||
navigator.mediaDevices.addEventListener('devicechange', handleDeviceChange)
|
||||
navigator.mediaDevices.addEventListener("devicechange", handleDeviceChange);
|
||||
|
||||
return () => {
|
||||
mounted = false
|
||||
navigator.mediaDevices.removeEventListener('devicechange', handleDeviceChange)
|
||||
}
|
||||
}, [enabled])
|
||||
return () => {
|
||||
mounted = false;
|
||||
navigator.mediaDevices.removeEventListener("devicechange", handleDeviceChange);
|
||||
};
|
||||
}, [enabled, preferredDeviceId]);
|
||||
|
||||
return {
|
||||
devices,
|
||||
selectedDeviceId,
|
||||
setSelectedDeviceId,
|
||||
isLoading,
|
||||
error,
|
||||
}
|
||||
return {
|
||||
devices,
|
||||
selectedDeviceId,
|
||||
setSelectedDeviceId,
|
||||
isLoading,
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user