import { useState, useEffect, useRef, useCallback } from "react"; import { useNetworks } from "@/hooks/use-networks"; import { cn } from "@/lib/utils"; import { Check } from "lucide-react"; interface ConfigureStreamStepProps { networkId: string | null; onCancel: () => void; } export function ConfigureStreamStep({ networkId, onCancel, }: ConfigureStreamStepProps) { const { data: networks } = useNetworks(); const network = networks?.find((n) => n.id === networkId); const members = network?.humans ?? []; const [name, setName] = useState(""); const [selectedEmails, setSelectedEmails] = useState>(new Set()); // -1 = name input is focused, 0+ = member list index const [focusedIndex, setFocusedIndex] = useState(-1); const nameRef = useRef(null); const containerRef = useRef(null); useEffect(() => { nameRef.current?.focus(); }, []); // Return focus to the name input when navigating back up useEffect(() => { if (focusedIndex === -1) { nameRef.current?.focus(); } else { // Blur the input so arrow keys don't move the cursor nameRef.current?.blur(); containerRef.current?.focus(); } }, [focusedIndex]); const toggleMember = useCallback((email: string) => { setSelectedEmails((prev) => { const next = new Set(prev); if (next.has(email)) next.delete(email); else next.add(email); return next; }); }, []); const handleSubmit = useCallback(async () => { if (!name.trim() || !networkId) return; // TODO: create stream particle, then attach recorded/text content onCancel(); }, [name, networkId, onCancel]); const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { switch (e.key) { case "Escape": e.preventDefault(); onCancel(); return; case "Enter": if (e.metaKey || e.ctrlKey) { e.preventDefault(); handleSubmit(); } else if (focusedIndex === -1 && name.trim() && members.length > 0) { // Enter in name input → move to member list e.preventDefault(); setFocusedIndex(0); } return; case "ArrowDown": e.preventDefault(); setFocusedIndex((i) => Math.min(i + 1, members.length - 1)); return; case "ArrowUp": e.preventDefault(); setFocusedIndex((i) => Math.max(i - 1, -1)); return; case " ": if (focusedIndex >= 0) { e.preventDefault(); toggleMember(members[focusedIndex].email); } return; } }, [onCancel, handleSubmit, focusedIndex, members, name, toggleMember], ); return (
{/* Stream name */}
{ setName(e.target.value); setFocusedIndex(-1); }} onFocus={() => setFocusedIndex(-1)} placeholder="Give it a name..." className="w-full rounded-md border border-white/10 bg-white/5 px-3 py-2 text-sm text-white placeholder-white/30 outline-none focus:border-white/30" />
{/* Member selection */} {members.length > 0 && (
{members.map((member, index) => { const isSelected = selectedEmails.has(member.email); const isFocused = focusedIndex === index; const initials = member.email_prefix .slice(0, 2) .toUpperCase(); return ( ); })}
)}
{/* Keyboard hints */}
Esc {" "} cancel ↑↓ {" "} navigate Space {" "} toggle ⌘+Enter {" "} create
); }