import React, { useEffect, useRef, useState } from 'react' import styles from './PreJoin.module.css' import { ControlBar, ControlButton, MicrophoneMeter, modifierKeyLabel, isMacPlatform } from 'avanza-ui' import { FiMic, FiVideo, FiSettings } from 'react-icons/fi' type Props = { roomName?: string onProceed: () => void onCancel?: () => void serverUrl?: string token?: string } export default function PreJoin({ roomName, onProceed, onCancel }: Props) { const videoRef = useRef(null) const [name, setName] = useState(() => { try { return localStorage.getItem('avanzacast_user') || '' } catch { return '' } }) const [micEnabled, setMicEnabled] = useState(true) const [camEnabled, setCamEnabled] = useState(true) const [error, setError] = useState(null) const [isChecking, setIsChecking] = useState(false) // checkbox state is local only; do NOT persist skip preference so PreJoin always appears const [skipNextTime, setSkipNextTime] = useState(false) // keep preview stream active for meter and preview const [previewStream, setPreviewStream] = useState(null) // Use shared platform utils const isMac = isMacPlatform() const modLabel = modifierKeyLabel() const micHint = `${modLabel.display} + D` const camHint = `${modLabel.display} + E` useEffect(() => { // ensure any old skip flag does not affect behavior: remove legacy key try { localStorage.removeItem('broadcast:skipPrejoin') } catch (e) {} // request preview stream whenever toggles change let mounted = true let localStream: MediaStream | null = null ;(async () => { try { if (!navigator?.mediaDevices?.getUserMedia) return localStream = await navigator.mediaDevices.getUserMedia({ audio: micEnabled, video: camEnabled }) if (!mounted) { try { localStream.getTracks().forEach(t => t.stop()) } catch (e) {} return } setPreviewStream(localStream) if (videoRef.current) { videoRef.current.srcObject = localStream videoRef.current.play().catch(() => {}) } } catch (e: any) { // ignore permission errors } })() // Keyboard shortcuts: toggle mic/camera. Support Ctrl on Windows/Linux and Meta (⌘) on macOS. const onKeyDown = (e: KeyboardEvent) => { // ignore when focused on input/textarea or when modifier keys conflict with browser shortcuts const active = document.activeElement; const tag = active && (active as HTMLElement).tagName; if (tag === 'INPUT' || tag === 'TEXTAREA' || (active as HTMLElement)?.isContentEditable) return; const mod = isMac ? e.metaKey : e.ctrlKey // Mod + D -> toggle mic if (mod && !e.shiftKey && (e.key === 'd' || e.key === 'D')) { e.preventDefault() setMicEnabled(v => !v) } // Mod + E -> toggle camera (requested) if (mod && !e.shiftKey && (e.key === 'e' || e.key === 'E')) { e.preventDefault() setCamEnabled(v => !v) } // Mod + Shift + C -> alternate camera shortcut (also supported) if ((e.ctrlKey || e.metaKey) && e.shiftKey && (e.key === 'c' || e.key === 'C')) { e.preventDefault() setCamEnabled(v => !v) } } window.addEventListener('keydown', onKeyDown) return () => { mounted = false if (localStream) { try { localStream.getTracks().forEach(t => t.stop()) } catch (e) {} } // clear previewStream state setPreviewStream(null) window.removeEventListener('keydown', onKeyDown) } }, [micEnabled, camEnabled]) const handleProceed = async () => { setError(null) setIsChecking(true) try { // request permissions explicitly await navigator.mediaDevices.getUserMedia({ audio: micEnabled, video: camEnabled }) // save name try { if (name) localStorage.setItem('avanzacast_user', name) } catch (e) {} // proceed to connect onProceed() } catch (e: any) { setError(e?.message || 'No se pudo acceder a la cámara/micrófono') } finally { setIsChecking(false) } } const toggleMic = async () => { setMicEnabled(v => !v) } const toggleCam = async () => { setCamEnabled(v => !v) } return (
Configura tu estudio
Entrar al estudio no iniciará automáticamente la transmisión.
} label={micEnabled ? 'Desactivar audio' : 'Activar audio'} active={micEnabled} danger={!micEnabled} layout="column" variant="studio" onClick={toggleMic} hint={micHint} size="md" /> } label={camEnabled ? 'Detener cámara' : 'Iniciar cámara'} active={camEnabled} danger={!camEnabled} layout="column" variant="studio" onClick={toggleCam} hint={camHint} size="md" /> } label={'Configuración'} active={true} layout="column" variant="studio" onClick={() => { /* abrir modal de settings si aplica */ }} size="md" />
{/* Leyenda de atajos: muestra las combinaciones detectadas (ej: ⌘ + D) */}
{error &&
{error}
}
Nombre para mostrar
setName(e.target.value)} placeholder="Tu nombre" />
Título (opcional)
setSkipNextTime(e.target.checked)} />
) }