/* eslint-disable react-hooks/exhaustive-deps */ import { useState, useRef, useEffect } from "react"; import SoundIcon from "../icons/SoundIcon"; import VideoFilledIcon from "../icons/VideoFilledIcon"; import ModalWrapper from "../ModalWrapper"; import Button from "../ui/Button"; import RangeInput from "../ui/RangeInput"; import Select from "../ui/Select"; import MicrophoneFilledIcon from "../icons/MicrophoneFilledIcon"; import Switch from "../ui/Switch"; import clsx from "clsx"; import useModalStore from "../../store/modalStore"; import SoundCheckModal from "./SoundCheckModal"; import VoiceCheckModal from "./VoiceCheckModal"; import LoaderIcon from "../icons/LoaderIcon"; interface MediaDevice { deviceId: string; label: string; } function SettingsModal() { const [microphoneVolume, setMicrophoneVolume] = useState(50); const [speakerVolume, setSpeakerVolume] = useState(50); // Списки устройств const [microphones, setMicrophones] = useState([]); const [speakers, setSpeakers] = useState([]); const [cameras, setCameras] = useState([]); // Выбранные устройства const [selectedMicrophone, setSelectedMicrophone] = useState(""); const [selectedSpeaker, setSelectedSpeaker] = useState(""); const [selectedCamera, setSelectedCamera] = useState(""); const [mediaType, setMediaType] = useState<"sound" | "video">("sound"); const [participantsVideosHidden, setParticipantsVideosHidden] = useState(false); const [isVideoTestingError, setIsVideoTestingError] = useState(false); const [isVideoTestingLoading, setIsVideoTestingLoading] = useState(false); const [isVideoTesting, setIsVideoTesting] = useState(false); const videoRef = useRef(null); const streamRef = useRef(null); const [isLoadingMicrophones, setIsLoadingMicrophones] = useState(false); const [isLoadingSpeakers, setIsLoadingSpeakers] = useState(false); const [isLoadingCameras, setIsLoadingCameras] = useState(false); const { setModal } = useModalStore(); // Остановка видео function stopVideoTest() { if (streamRef.current) { streamRef.current.getTracks().forEach((track) => track.stop()); streamRef.current = null; } if (videoRef.current) videoRef.current.srcObject = null; setIsVideoTesting(false); } // Загрузка аудио устройств async function loadAudioDevices() { setIsLoadingMicrophones(true); setIsLoadingSpeakers(true); try { // Запрашиваем разрешения на аудио const stream = await navigator.mediaDevices.getUserMedia({ audio: true, }); // Сразу останавливаем стрим после получения разрешений stream.getTracks().forEach((track) => track.stop()); const devices = await navigator.mediaDevices.enumerateDevices(); console.log("Найдено аудио устройств:", devices); const audioInputs: MediaDevice[] = []; const audioOutputs: MediaDevice[] = []; devices.forEach((device) => { const deviceInfo: MediaDevice = { deviceId: device.deviceId, label: device.label || `${device.kind} (${device.deviceId.slice(0, 8)})`, }; if (device.kind === "audioinput") { audioInputs.push(deviceInfo); } else if (device.kind === "audiooutput") { audioOutputs.push(deviceInfo); } }); console.log("Микрофоны:", audioInputs); console.log("Динамики:", audioOutputs); setMicrophones(audioInputs); setIsLoadingMicrophones(false); setSpeakers(audioOutputs); setIsLoadingSpeakers(false); // Устанавливаем первое устройство по умолчанию if (audioInputs.length > 0 && !selectedMicrophone) { setSelectedMicrophone(audioInputs[0].label); } if (audioOutputs.length > 0 && !selectedSpeaker) { setSelectedSpeaker(audioOutputs[0].label); } } catch (error) { console.error("Ошибка при загрузке аудио устройств:", error); // Пробуем загрузить устройства без разрешений try { const devices = await navigator.mediaDevices.enumerateDevices(); console.log("Аудио устройства без разрешений:", devices); const audioInputs: MediaDevice[] = []; const audioOutputs: MediaDevice[] = []; devices.forEach((device) => { const deviceInfo: MediaDevice = { deviceId: device.deviceId, label: device.label || `${device.kind} (требуется разрешение)`, }; if (device.kind === "audioinput") { audioInputs.push(deviceInfo); } else if (device.kind === "audiooutput") { audioOutputs.push(deviceInfo); } }); setMicrophones(audioInputs); setSpeakers(audioOutputs); } catch (err) { console.error("Не удалось загрузить аудио устройства:", err); } finally { setIsLoadingMicrophones(false); setIsLoadingSpeakers(false); } } } // Загрузка видео устройств async function loadVideoDevices() { setIsLoadingCameras(true); try { // Запрашиваем разрешения на видео const stream = await navigator.mediaDevices.getUserMedia({ video: true, }); // Сразу останавливаем стрим после получения разрешений stream.getTracks().forEach((track) => track.stop()); const devices = await navigator.mediaDevices.enumerateDevices(); console.log("Найдено видео устройств:", devices); const videoInputs: MediaDevice[] = []; devices.forEach((device) => { if (device.kind === "videoinput") { const deviceInfo: MediaDevice = { deviceId: device.deviceId, label: device.label || `${device.kind} (${device.deviceId})`, }; videoInputs.push(deviceInfo); } }); console.log("Камеры:", videoInputs); setCameras(videoInputs); setIsLoadingCameras(false); // Устанавливаем первое устройство по умолчанию if (videoInputs.length > 0 && !selectedCamera) { setSelectedCamera(videoInputs[0].label); } } catch (error) { console.error("Ошибка при загрузке видео устройств:", error); // Пробуем загрузить устройства без разрешений try { const devices = await navigator.mediaDevices.enumerateDevices(); console.log("Видео устройства без разрешений:", devices); const videoInputs: MediaDevice[] = []; devices.forEach((device) => { if (device.kind === "videoinput") { const deviceInfo: MediaDevice = { deviceId: device.deviceId, label: device.label || `${device.kind} (требуется разрешение)`, }; videoInputs.push(deviceInfo); } }); setCameras(videoInputs); } catch (err) { console.error("Не удалось загрузить видео устройства:", err); } finally { setIsLoadingCameras(false); } } } // Запуск видео async function startVideoTest() { try { setIsVideoTestingLoading(true); setIsVideoTestingError(false); // Находим deviceId выбранной камеры const selectedCameraDevice = cameras.find( (cam) => cam.label === selectedCamera ); const constraints: MediaStreamConstraints = { video: selectedCameraDevice ? { deviceId: { exact: selectedCameraDevice.deviceId } } : true, audio: false, }; const stream = await navigator.mediaDevices.getUserMedia(constraints); streamRef.current = stream; if (videoRef.current) videoRef.current.srcObject = stream; setIsVideoTesting(true); } catch (error) { console.error("Ошибка при доступе к камере:", error); setIsVideoTestingError(true); } finally { setIsVideoTestingLoading(false); } } // Загружаем только аудио устройства при монтировании useEffect(() => { loadAudioDevices(); // Слушаем изменения устройств (подключение/отключение) const handleDeviceChange = () => { // Загружаем аудио всегда loadAudioDevices(); // Загружаем видео только если находимся на вкладке "Видео" if (mediaType === "video") { loadVideoDevices(); } }; navigator.mediaDevices.addEventListener("devicechange", handleDeviceChange); return () => { navigator.mediaDevices.removeEventListener( "devicechange", handleDeviceChange ); }; }, [mediaType]); // Загружаем видео устройства и запускаем видео при переключении на вкладку "Видео" useEffect(() => { if (mediaType === "video") { // Всегда загружаем камеры при переходе на вкладку, если их еще нет if (cameras.length === 0 && !isLoadingCameras) { loadVideoDevices(); } // Запускаем видео только если камеры уже загружены if (cameras.length > 0) { startVideoTest(); } return stopVideoTest; } }, [mediaType, cameras.length]); // Перезапускаем видео при смене камеры useEffect(() => { if (isVideoTesting && selectedCamera) { stopVideoTest(); startVideoTest(); } }, [selectedCamera]); // Открыть модальное окно проверки микрофона const openMicrophoneCheck = () => { setModal( ); }; // Открыть модальное окно проверки динамика const openSpeakerCheck = () => { setModal( ); }; return (
{mediaType === "sound" && (

Микрофон

{isLoadingMicrophones ? (

Загрузка микрофонов...

) : microphones.length === 0 ? (

Микрофоны не найдены. Проверьте подключение устройства и разрешения браузера.

) : (
s.label)} defaultOption={selectedSpeaker} onSelect={setSelectedSpeaker} />
)}

{speakerVolume.toFixed(0)}%

)} {mediaType === "video" && (

Камера

{isLoadingCameras ? (

Загрузка камер...

) : cameras.length > 0 ? (