'use client'; import React, { useEffect, useRef, useState, useCallback } from 'react'; import Script from 'next/script'; import { useRouter } from 'next/navigation'; import { DEFAULT_JITSI_CONFIG_OVERRIDES, DEFAULT_JITSI_INTERFACE_OVERRIDES, } from '@/lib/jitsi'; declare global { interface Window { JitsiMeetExternalAPI: any; } } export interface JitsiClassroomProps { roomName: string; tenantSlug: string; jwtToken?: string; domain?: string; displayName?: string; email?: string; avatarUrl?: string; isHost?: boolean; onJoined?: (event: any) => void; onLeft?: () => void; } export default function JitsiClassroom({ roomName, tenantSlug, jwtToken, domain = 'jitsi.my127001.ca', displayName = 'Participant', email, avatarUrl, isHost = false, onJoined, onLeft, }: JitsiClassroomProps) { const router = useRouter(); const containerRef = useRef(null); const jitsiApiRef = useRef(null); const [scriptLoaded, setScriptLoaded] = useState(false); const [scriptError, setScriptError] = useState(false); const [isConnected, setIsConnected] = useState(false); const [participantsCount, setParticipantsCount] = useState(1); const handleExit = useCallback(() => { if (jitsiApiRef.current) { try { jitsiApiRef.current.dispose(); } catch (e) { console.error('Error disposing Jitsi API:', e); } jitsiApiRef.current = null; } onLeft?.(); router.push(`/${tenantSlug}/classes`); }, [router, tenantSlug, onLeft]); const initJitsi = useCallback(() => { if (!window.JitsiMeetExternalAPI || !containerRef.current || jitsiApiRef.current) { return; } try { // Clear container before mounting containerRef.current.innerHTML = ''; const options = { roomName, parentNode: containerRef.current, width: '100%', height: '100%', jwt: jwtToken, userInfo: { displayName, email, }, configOverwrite: { ...DEFAULT_JITSI_CONFIG_OVERRIDES, // Host vs Participant controls startWithAudioMuted: !isHost, startWithVideoMuted: false, }, interfaceConfigOverwrite: { ...DEFAULT_JITSI_INTERFACE_OVERRIDES, }, }; const api = new window.JitsiMeetExternalAPI(domain, options); jitsiApiRef.current = api; // Event Listeners api.addEventListener('videoConferenceJoined', (event: any) => { setIsConnected(true); onJoined?.(event); }); api.addEventListener('videoConferenceLeft', () => { setIsConnected(false); handleExit(); }); api.addEventListener('participantJoined', (event: any) => { setParticipantsCount((prev) => prev + 1); }); api.addEventListener('participantLeft', () => { setParticipantsCount((prev) => Math.max(1, prev - 1)); }); api.addEventListener('readyToClose', () => { handleExit(); }); // Set avatar if provided if (avatarUrl) { api.executeCommand('avatarUrl', avatarUrl); } } catch (err) { console.error('Failed to initialize JitsiMeetExternalAPI:', err); setScriptError(true); } }, [ roomName, jwtToken, domain, displayName, email, avatarUrl, isHost, onJoined, handleExit, ]); useEffect(() => { if (scriptLoaded) { initJitsi(); } return () => { if (jitsiApiRef.current) { try { jitsiApiRef.current.dispose(); } catch { // ignore cleanup errors } jitsiApiRef.current = null; } }; }, [scriptLoaded, initJitsi]); return (
{/* External Jitsi Meet API Script */}