// client/src/Room.jsx
import React, { useEffect, useMemo, useState } from 'react';
import { io } from 'socket.io-client';
import Player from './Player.jsx';
import Chat from './Chat.jsx';
import MemberList from './MemberList.jsx';
import { detectMediaTypeFromUrl, getThumb } from './utils.js';
import { useToasts } from './Toasts.jsx';
// initialize socket once
const socket = io('', { transports: ['websocket'] });
// Modal for arbitrary direct URLs (MP4/MP3/etc or YouTube ID/URL)
function DirectLinkModal({ open, onClose, onPick }) {
const [url, setUrl] = useState('');
const [type, setType] = useState('auto');
useEffect(() => {
if (!open) {
setUrl('');
setType('auto');
}
}, [open]);
if (!open) return null;
return (
e.target === e.currentTarget && onClose()}>
Play a direct link
Paste a direct media URL (MP4, WEBM, MP3, M4A) or a YouTube URL/ID.
setUrl(e.target.value)}
/>
);
}
// Modal for YouTube (via Vidfly) only
function YouTubeModal({ open, onClose, onPick, ytError }) {
const [idOrUrl, setIdOrUrl] = useState('');
useEffect(() => {
if (!open) setIdOrUrl('');
}, [open]);
if (!open) return null;
return (
e.target === e.currentTarget && onClose()}>
Play YouTube (Vidfly)
Enter a YouTube URL or 11-character ID. We resolve via Vidfly and proxy if needed.
setIdOrUrl(e.target.value)}
/>
{ytError && (
YouTube failed
{ytError.message}
)}
);
}
// Panel for host to accept/queue/reject song requests
function RequestsPanel({ isHost, requests, onAct }) {
if (!isHost || !requests.length) return null;
return (