// ============================================================ // app.jsx — assembly + real Strava connect modal // ============================================================ function JoinModal({ open, onClose }) { const [going, setGoing] = useState(false); useEffect(() => { if (open) setGoing(false); }, [open]); useEffect(() => { if (!open) return; const onKey = (e) => { if (e.key === "Escape") onClose(); }; document.addEventListener("keydown", onKey); document.body.style.overflow = "hidden"; return () => { document.removeEventListener("keydown", onKey); document.body.style.overflow = ""; }; }, [open]); if (!open) return null; const authorize = () => { setGoing(true); window.location.href = "/auth/login"; }; return (
e.stopPropagation()}>
{!going ? (<>

Join the crew

Authorize read-only access to your Strava activities. Public stats only — your privacy settings are always respected.

SCOPE: activity:read_all · PUBLIC ONLY
) : (<>

Establishing handshake…

$ oauth --provider strava --scope activity:read_all
→ redirecting to strava…
→ authorize, then you're on the board _
)}
); } function App() { const [selected, setSelected] = useState(null); const [joinOpen, setJoinOpen] = useState(false); const [toast, setToast] = useState(null); const onJoin = useCallback(() => setJoinOpen(true), []); // Show a success toast after the Strava OAuth redirect, then clean the URL. useEffect(() => { const p = new URLSearchParams(window.location.search); if (!p.has("connected")) return; const synced = p.get("synced"); setToast(synced && synced !== "0" ? `You're on the board — ${synced} runs synced. Go run!` : "You're on the board! Your next runs will appear after the next sync."); window.history.replaceState({}, "", window.location.pathname); const t = setTimeout(() => setToast(null), 7000); return () => clearTimeout(t); }, []); return (