akborana4 commited on
Commit
068ccc0
·
verified ·
1 Parent(s): 32f6e58

Update client/src/App.jsx

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