// ───────────────────────────────────────────────────────────── // protected/mobile/mobile.js — minimal client for the mobile view. // // Three behaviours, nothing else: // 1. Confirm the session is still valid on page load. If the // session expired since the server rendered the HTML, bounce // to "/" so the user doesn't read gated content without a // session cookie (defensive — requireAuth already gates the // page request itself). // 2. POST /api/bifrost-join on CTA click; swap CTA panel → // confirmation panel on success. // 3. POST /auth/logout on log-out button; navigate to "/". // // No GSAP, no Lenis, no d3. No sharing of globals with the desktop // timeline/bifrost scripts — this file is only loaded by // protected/mobile/index.html and never by the desktop view. // ───────────────────────────────────────────────────────────── (async function checkSession() { try { const res = await fetch('/auth/me', { credentials: 'same-origin' }); if (!res.ok) { window.location.href = '/'; } } catch { // Network error — do not boot the user out; desktop behaviour is // the same. If the next action actually needs the server, we'll // surface the error there. } })(); const joinBtn = document.getElementById('m-join-btn'); const joinCta = document.getElementById('m-join-cta'); const joinConfirm = document.getElementById('m-join-confirm'); if (joinBtn && joinCta && joinConfirm) { joinBtn.addEventListener('click', async () => { joinBtn.disabled = true; try { const res = await fetch('/api/bifrost-join', { method: 'POST', credentials: 'same-origin', headers: { 'content-type': 'application/json' }, // Server reads email + sessionId from the session cookie, body // just needs to be parseable JSON for express.json() to keep // its rhythm. body: '{}', }); if (res.status === 401) { window.location.href = '/'; return; } if (!res.ok) { joinBtn.disabled = false; joinBtn.textContent = 'Try again'; return; } joinCta.hidden = true; joinConfirm.hidden = false; joinConfirm.scrollIntoView({ behavior: 'smooth', block: 'start' }); } catch { joinBtn.disabled = false; joinBtn.textContent = 'Try again'; } }); } const logoutBtn = document.getElementById('m-logout'); if (logoutBtn) { logoutBtn.addEventListener('click', async () => { try { await fetch('/auth/logout', { method: 'POST', credentials: 'same-origin', }); } catch { // If logout POST fails, still navigate home — the user's // intent is "leave". The server-side session will still be // valid until it expires, but the cookie on this device // will be cleared by the navigation away. } window.location.href = '/'; }); }