'use client'; import { decodePassphrase } from '@/lib/client-utils'; import { DebugMode } from '@/lib/Debug'; import { RecordingIndicator } from '@/lib/RecordingIndicator'; import { SettingsMenu } from '@/lib/SettingsMenu'; import { ConnectionDetails } from '@/lib/types'; import { formatChatMessageLinks, LiveKitRoom, LocalUserChoices, PreJoin, VideoConference, } from '@livekit/components-react'; import { ExternalE2EEKeyProvider, RoomOptions, VideoCodec, VideoPresets, Room, DeviceUnsupportedError, RoomConnectOptions, } from 'livekit-client'; import { useRouter } from 'next/navigation'; import React from 'react'; const CONN_DETAILS_ENDPOINT = process.env.NEXT_PUBLIC_CONN_DETAILS_ENDPOINT ?? '/api/connection-details'; const SHOW_SETTINGS_MENU = process.env.NEXT_PUBLIC_SHOW_SETTINGS_MENU == 'true'; export function PageClientImpl(props: { roomName: string; region?: string; hq: boolean; codec: VideoCodec; }) { const [preJoinChoices, setPreJoinChoices] = React.useState( undefined, ); const preJoinDefaults = React.useMemo(() => { return { username: '', videoEnabled: true, audioEnabled: true, }; }, []); const [connectionDetails, setConnectionDetails] = React.useState( undefined, ); const handlePreJoinSubmit = React.useCallback(async (values: LocalUserChoices) => { setPreJoinChoices(values); const url = new URL(CONN_DETAILS_ENDPOINT, window.location.origin); url.searchParams.append('roomName', props.roomName); url.searchParams.append('participantName', values.username); if (props.region) { url.searchParams.append('region', props.region); } const connectionDetailsResp = await fetch(url.toString()); const connectionDetailsData = await connectionDetailsResp.json(); setConnectionDetails(connectionDetailsData); }, []); const handlePreJoinError = React.useCallback((e: any) => console.error(e), []); return (
{connectionDetails === undefined || preJoinChoices === undefined ? (
) : ( )}
); } function VideoConferenceComponent(props: { userChoices: LocalUserChoices; connectionDetails: ConnectionDetails; options: { hq: boolean; codec: VideoCodec; }; }) { const e2eePassphrase = typeof window !== 'undefined' && decodePassphrase(location.hash.substring(1)); const worker = typeof window !== 'undefined' && e2eePassphrase && new Worker(new URL('livekit-client/e2ee-worker', import.meta.url)); const e2eeEnabled = !!(e2eePassphrase && worker); const keyProvider = new ExternalE2EEKeyProvider(); const [e2eeSetupComplete, setE2eeSetupComplete] = React.useState(false); const roomOptions = React.useMemo((): RoomOptions => { let videoCodec: VideoCodec | undefined = props.options.codec ? props.options.codec : 'vp9'; if (e2eeEnabled && (videoCodec === 'av1' || videoCodec === 'vp9')) { videoCodec = undefined; } return { videoCaptureDefaults: { deviceId: props.userChoices.videoDeviceId ?? undefined, resolution: props.options.hq ? VideoPresets.h2160 : VideoPresets.h720, }, publishDefaults: { dtx: false, videoSimulcastLayers: props.options.hq ? [VideoPresets.h1080, VideoPresets.h720] : [VideoPresets.h540, VideoPresets.h216], red: !e2eeEnabled, videoCodec, }, audioCaptureDefaults: { deviceId: props.userChoices.audioDeviceId ?? undefined, }, adaptiveStream: { pixelDensity: 'screen' }, dynacast: true, e2ee: e2eeEnabled ? { keyProvider, worker, } : undefined, }; }, [props.userChoices, props.options.hq, props.options.codec]); const room = React.useMemo(() => new Room(roomOptions), []); React.useEffect(() => { if (e2eeEnabled) { keyProvider .setKey(decodePassphrase(e2eePassphrase)) .then(() => { room.setE2EEEnabled(true).catch((e) => { if (e instanceof DeviceUnsupportedError) { alert( `You're trying to join an encrypted meeting, but your browser does not support it. Please update it to the latest version and try again.`, ); console.error(e); } else { throw e; } }); }) .then(() => setE2eeSetupComplete(true)); } else { setE2eeSetupComplete(true); } }, [e2eeEnabled, room, e2eePassphrase]); const connectOptions = React.useMemo((): RoomConnectOptions => { return { autoSubscribe: true, }; }, []); const router = useRouter(); const handleOnLeave = React.useCallback(() => router.push('/'), [router]); const handleError = React.useCallback((error: Error) => { console.error(error); alert(`Encountered an unexpected error, check the console logs for details: ${error.message}`); }, []); const handleEncryptionError = React.useCallback((error: Error) => { console.error(error); alert( `Encountered an unexpected encryption error, check the console logs for details: ${error.message}`, ); }, []); return ( <> ); }