Spaces:
Sleeping
Sleeping
File size: 4,857 Bytes
068ccc0 cb63061 068ccc0 cb63061 068ccc0 cb63061 068ccc0 cb63061 068ccc0 cb63061 068ccc0 cb63061 068ccc0 cb63061 068ccc0 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
import React, { useEffect, useMemo, useState } from 'react';
import Room from './Room.jsx';
import { ToastProvider, useToasts } from './Toasts.jsx';
import { log } from './logger.js';
import { prettyError } from './utils.js';
function Lobby({ onEnter }) {
const { push } = useToasts();
const [name, setName] = useState('');
const [rooms, setRooms] = useState([]);
const [loading, setLoading] = useState(false);
const [creating, setCreating] = useState(false);
const [newRoomName, setNewRoomName] = useState('');
const [newRoomId, setNewRoomId] = useState('');
const fetchRooms = async () => {
try {
setLoading(true);
const res = await fetch('/api/rooms');
const json = await res.json();
setRooms(json.rooms || []);
setLoading(false);
} catch (e) {
setLoading(false);
log.error(e);
push(`Failed to fetch rooms: ${prettyError(e)}`, 'bad', 4000);
}
};
useEffect(() => {
fetchRooms();
const t = setInterval(fetchRooms, 5000);
return () => clearInterval(t);
}, []);
const canEnter = useMemo(() => name.trim().length >= 2, [name]);
return (
<div className="container">
<div className="panel" style={{ marginBottom: 16 }}>
<div className="row">
<div className="col">
<div className="section-title">Your name</div>
<input className="input" value={name} onChange={e => setName(e.target.value)} placeholder="DJ Neo" />
<div style={{ marginTop: 8, color: 'var(--muted)', fontSize: 13 }}>
Your name appears in the party member list.
</div>
</div>
<div className="col">
<div className="section-title">Create a new party</div>
<div className="row" style={{ gap: 8 }}>
<div className="col">
<input className="input" placeholder="Party name" value={newRoomName} onChange={e => setNewRoomName(e.target.value)} />
</div>
<div className="col">
<input className="input" placeholder="Party ID (letters/numbers)" value={newRoomId} onChange={e => setNewRoomId(e.target.value.replace(/\s+/g, '-'))} />
</div>
</div>
<div style={{ marginTop: 10, display: 'flex', gap: 10 }}>
<button
className="btn primary"
onClick={() => {
if (!canEnter) return push('Please enter your name', 'warn');
if (!newRoomId.trim()) return push('Please provide a Party ID', 'warn');
onEnter({ roomId: newRoomId.trim(), name: name.trim(), asHost: true, roomName: newRoomName.trim() || newRoomId.trim() });
}}
>Create & enter as host</button>
<button className="btn" onClick={fetchRooms} disabled={loading}>{loading ? 'Refreshing...' : 'Refresh rooms'}</button>
</div>
</div>
</div>
</div>
<div className="panel">
<div className="section-title">Active parties</div>
{rooms.length === 0 ? (
<div style={{ color: 'var(--muted)' }}>No active parties yet. Create one above.</div>
) : (
<div className="room-grid">
{rooms.map(r => (
<div key={r.id} className="room-card">
<div style={{ fontWeight: 700 }}>{r.name}</div>
<div className="meta" style={{ marginTop: 4 }}>
ID: <span className="kbd">{r.id}</span> · Members: {r.members} · {r.isPlaying ? 'Playing' : 'Paused'}
</div>
<div style={{ marginTop: 10, display: 'flex', gap: 8 }}>
<button
className="btn good"
onClick={() => {
if (!canEnter) return push('Please enter your name', 'warn');
onEnter({ roomId: r.id, name: name.trim(), asHost: false });
}}
>Join</button>
</div>
</div>
))}
</div>
)}
</div>
</div>
);
}
function Shell({ children }) {
return (
<div className="app-shell">
<div className="top-glow"></div>
<div className="header">
<div className="container">
<div className="brand">
<div className="logo"></div>
<div>Neon Party</div>
</div>
</div>
</div>
{children}
</div>
);
}
export default function App() {
const [session, setSession] = useState(null);
if (session) {
return (
<ToastProvider>
<Shell>
<Room {...session} />
</Shell>
</ToastProvider>
);
}
return (
<ToastProvider>
<Shell>
<Lobby onEnter={setSession} />
</Shell>
</ToastProvider>
);
}
|