feat: one-click join + live pod presence
Join UX: click a member's name to join directly, or type a name and "Join" (adds to roster + joins) — removes the redundant dropdown step. "Add" still adds to the roster without joining. Presence: new GET /api/presence (LiveKit RoomServiceClient) reports who's connected per pod. The pod list polls it and shows a "N in room" badge plus a green dot on members currently connected, so people can see active pods and jump in. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ export default function App() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [pending, setPending] = useState<Set<string>>(new Set());
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [presence, setPresence] = useState<Record<string, string[]>>({});
|
||||
|
||||
// join state — keep the id and derive the pod so it never goes stale
|
||||
const [joinedPodId, setJoinedPodId] = useState<string | null>(null);
|
||||
@@ -59,6 +60,27 @@ export default function App() {
|
||||
void refresh();
|
||||
}, []);
|
||||
|
||||
// Poll live presence while browsing the pod list (not while joined — PodView
|
||||
// tracks the room live itself).
|
||||
useEffect(() => {
|
||||
if (joinedPodId) return;
|
||||
let alive = true;
|
||||
const tick = async () => {
|
||||
try {
|
||||
const p = await api.getPresence();
|
||||
if (alive) setPresence(p);
|
||||
} catch {
|
||||
/* presence is best-effort */
|
||||
}
|
||||
};
|
||||
void tick();
|
||||
const id = window.setInterval(() => void tick(), 5000);
|
||||
return () => {
|
||||
alive = false;
|
||||
window.clearInterval(id);
|
||||
};
|
||||
}, [joinedPodId]);
|
||||
|
||||
// Resume a joined session across a page refresh (auto-reconnect, no re-prompt).
|
||||
useEffect(() => {
|
||||
const raw = sessionStorage.getItem(SESSION_KEY);
|
||||
@@ -134,6 +156,20 @@ export default function App() {
|
||||
}
|
||||
}
|
||||
|
||||
// Add your name to the roster (if new) and join in one step.
|
||||
async function handleAddAndJoin(pod: Pod, name: string) {
|
||||
startPending(pod.id);
|
||||
setError(null);
|
||||
try {
|
||||
upsert(await api.addMember(pod.id, name));
|
||||
await connectToPod(pod.id, name);
|
||||
} catch (e) {
|
||||
setError((e as Error).message);
|
||||
} finally {
|
||||
endPending(pod.id);
|
||||
}
|
||||
}
|
||||
|
||||
function handleLeave() {
|
||||
room?.disconnect();
|
||||
setRoom(null);
|
||||
@@ -203,7 +239,9 @@ export default function App() {
|
||||
key={pod.id}
|
||||
pod={pod}
|
||||
busy={pending.has(pod.id)}
|
||||
presence={presence[pod.id] ?? []}
|
||||
onJoin={handleJoin}
|
||||
onAddAndJoin={handleAddAndJoin}
|
||||
onAddMember={handleAddMember}
|
||||
onRemoveMember={handleRemoveMember}
|
||||
onUpdate={handleUpdate}
|
||||
|
||||
Reference in New Issue
Block a user