Masthead is now the Fenja wordmark on its own, centred. The logout handler and the .m-logout CSS are gone; mobile.js header comment updated to reflect "two behaviours" (session check + join CTA). Users who need to log out can do so from a desktop session or by clearing cookies. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
70 lines
2.7 KiB
JavaScript
70 lines
2.7 KiB
JavaScript
// ─────────────────────────────────────────────────────────────
|
|
// protected/mobile/mobile.js — minimal client for the mobile view.
|
|
//
|
|
// Two 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.
|
|
//
|
|
// There is no logout button on the mobile view; the masthead is
|
|
// logo-only by design. Users who want to log out can do so from a
|
|
// desktop session, or by clearing cookies.
|
|
//
|
|
// 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';
|
|
}
|
|
});
|
|
}
|
|
|