import { useCallback, useEffect, useRef, useState } from "react"; import { Room, RoomEvent } from "livekit-client"; import { useAuth } from "../../auth/AuthContext"; import "./Live.css"; const LIVEKIT_URL = "wss://live.us1.sociowire.com"; const LIVEKIT_API = "https://live.us1.sociowire.com"; function roomNameFromPost(post) { const raw = (post?.embed_url || post?.video_url || "").toString().trim(); if (raw.startsWith("livekit://")) { return raw.slice("livekit://".length); } return raw; } export default function LiveKitViewerModal({ post, onClose }) { const { username, token: authToken } = useAuth(); const userID = username || `viewer_${Date.now()}`; const roomName = roomNameFromPost(post); const [isConnecting, setIsConnecting] = useState(false); const [error, setError] = useState(null); const [viewerCount, setViewerCount] = useState(0); const [videoTrack, setVideoTrack] = useState(null); const roomRef = useRef(null); const videoRef = useRef(null); const getToken = useCallback(async () => { const res = await fetch(`${LIVEKIT_API}/api/livekit/token`, { method: "POST", headers: { "Content-Type": "application/json", ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}), }, body: JSON.stringify({ room: roomName, identity: userID, name: username || userID, canPublish: false, canSubscribe: true, }), }); if (!res.ok) { throw new Error("Failed to get LiveKit token"); } const data = await res.json(); return data.token; }, [authToken, roomName, userID, username]); useEffect(() => { if (!roomName) { setError("Missing room name"); return; } let mounted = true; const connect = async () => { setIsConnecting(true); setError(null); try { const token = await getToken(); if (!mounted) return; const room = new Room({ adaptiveStream: true, dynacast: true }); roomRef.current = room; room.on(RoomEvent.TrackSubscribed, (track) => { if (track.kind === "video") { setVideoTrack(track); } }); room.on(RoomEvent.ParticipantConnected, () => { setViewerCount((prev) => prev + 1); }); room.on(RoomEvent.ParticipantDisconnected, () => { setViewerCount((prev) => Math.max(0, prev - 1)); }); room.on(RoomEvent.Disconnected, () => { setViewerCount(0); }); await room.connect(LIVEKIT_URL, token); setViewerCount(room.participants?.size || 0); } catch (err) { if (mounted) { setError(err.message || "Failed to connect"); } } finally { if (mounted) setIsConnecting(false); } }; connect(); return () => { mounted = false; if (roomRef.current) { roomRef.current.disconnect(); roomRef.current = null; } }; }, [roomName, getToken]); useEffect(() => { if (!videoRef.current || !videoTrack) return; videoTrack.attach(videoRef.current); return () => { try { videoTrack.detach(videoRef.current); } catch {} }; }, [videoTrack]); const handleClose = useCallback(() => { if (roomRef.current) { roomRef.current.disconnect(); roomRef.current = null; } onClose?.(); }, [onClose]); if (!post) return null; return (
{ if (e.target === e.currentTarget) handleClose(); }}>
LIVE
{post?.title || "Live Stream"}
{viewerCount}
{isConnecting ? (
Connecting...
) : error ? (
{error}
) : (
)}
); }