"use client"; import { useCallback, useEffect, useRef, useState } from "react"; import { createLocalTracks, Track, type LocalTrack } from "livekit-client"; export function PreJoinScreen({ onContinue }: { onContinue: (tracks: LocalTrack[]) => void }) { const [tracks, setTracks] = useState([]); const [error, setError] = useState(null); const [micOn, setMicOn] = useState(true); const [cameraOn, setCameraOn] = useState(true); const [speakerTested, setSpeakerTested] = useState(false); const [requestingDevices, setRequestingDevices] = useState(false); const videoRef = useRef(null); const handedOffRef = useRef(false); const initialDeviceRequestRef = useRef(false); const videoTrack = tracks.find((track) => track.kind === Track.Kind.Video); const audioTrack = tracks.find((track) => track.kind === Track.Kind.Audio); useEffect(() => { const videoElement = videoRef.current; if (!videoElement || !videoTrack) return; videoTrack.attach(videoElement); return () => { videoTrack.detach(videoElement); }; }, [videoTrack]); useEffect(() => { return () => { if (!handedOffRef.current) tracks.forEach((track) => track.stop()); }; }, [tracks]); const requestDevices = useCallback(async () => { if (requestingDevices) return; tracks.forEach((track) => track.stop()); setError(null); setRequestingDevices(true); try { const nextTracks = await createLocalTracks({ audio: true, video: true }); setTracks(nextTracks); setMicOn(true); setCameraOn(true); } catch { setError("Kamera oder Mikrofon konnten nicht freigegeben werden. Du kannst trotzdem ohne sie beitreten."); } finally { setRequestingDevices(false); } }, [requestingDevices, tracks]); useEffect(() => { if (initialDeviceRequestRef.current) return; initialDeviceRequestRef.current = true; void requestDevices(); }, [requestDevices]); async function toggleMic() { if (!audioTrack) return; if (micOn) await audioTrack.mute(); else await audioTrack.unmute(); setMicOn(!micOn); } async function toggleCamera() { if (!videoTrack) return; if (cameraOn) await videoTrack.mute(); else await videoTrack.unmute(); setCameraOn(!cameraOn); } function testSpeaker() { const AudioContextClass = window.AudioContext || (window as Window & { webkitAudioContext?: typeof AudioContext }).webkitAudioContext; if (!AudioContextClass) return; const context = new AudioContextClass(); const oscillator = context.createOscillator(); const gain = context.createGain(); oscillator.frequency.value = 660; gain.gain.value = 0.08; oscillator.connect(gain).connect(context.destination); oscillator.start(); oscillator.stop(context.currentTime + 0.25); oscillator.addEventListener("ended", () => void context.close()); setSpeakerTested(true); } function continueToCall() { handedOffRef.current = true; onContinue(tracks); } return (

Vorbereitung

Prüfe deine Geräte

Erlaube Kamera und Mikrofon, teste kurz den Ton und tritt dann dem geschützten Meeting bei.

{videoTrack && cameraOn ? (
{error &&

{error}

}
); }