From a4119d652d702f8b6c19f66d3d76ac3b89c7ca35 Mon Sep 17 00:00:00 2001 From: martbost Date: Mon, 7 Sep 2026 08:29:12 -0500 Subject: [PATCH] Sponsor shown on sign-in + new-member onboarding (username, optional profile) - Sign-in page shows "You're joining the line of @X" when arriving via a sponsor link (/api/sponsor now returns the sponsor name + avatar). - After a new account verifies (or signs up) with no username yet, an onboarding modal prompts for a username and an optional bio (both skippable), before the welcome tour. Co-Authored-By: Claude Opus 4.8 --- public/assets/my.js | 32 ++++++++++++++++++++++++++++++-- public/my.html | 24 +++++++++++++++++++++++- server.js | 11 +++++++++-- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/public/assets/my.js b/public/assets/my.js index 6ab9aac..9697f29 100644 --- a/public/assets/my.js +++ b/public/assets/my.js @@ -11,6 +11,13 @@ } if (c.rehearsal && $('faucetCard')) $('faucetCard').hidden = false; } catch (e) {} + // if they arrived through a sponsor's link, show who they're joining under + (async () => { + try { + const sp = await (await fetch('/api/sponsor')).json(); + if (sp && sp.invited && sp.name && $('sponsorNote')) { $('sponsorNoteName').textContent = sp.name; $('sponsorNote').hidden = false; } + } catch (e) {} + })(); async function api(path, body) { const r = await (await fetch(path, { method: 'POST', @@ -1255,16 +1262,37 @@ IAP.status('Fresh code sent.', 'ok'); })); $('mcVerifyBtn').addEventListener('click', busy($('mcVerifyBtn'), async () => { - await api('/api/auth/email/verify', { email: $('mcEmail').value, code: $('mcCode').value }); + const r = await api('/api/auth/email/verify', { email: $('mcEmail').value, code: $('mcCode').value }); IAP.status('You are in.', 'ok'); + if (r.created && !(r.account && r.account.username)) await showOnboard(); // pick a username first if (!(await showGauntlet())) await showLoginAd(); // welcome tour outranks the login ad await render(); })); })(); + // new-member onboarding: choose a username, optionally a bio (both skippable) + function showOnboard() { + return new Promise(resolve => { + const m = $('onboardModal'); if (!m) return resolve(); + m.hidden = false; $('obErr').hidden = true; + const done = () => { m.hidden = true; resolve(); }; + $('obSkip').onclick = done; + $('obSave').onclick = async () => { + const u = $('obUsername').value.trim(); + try { + if (u) await api('/api/my/profile', { username: u }); + const bio = $('obBio').value.trim(); + if (bio) await api('/api/my/profile-details', { bio }); + done(); + } catch (e) { $('obErr').hidden = false; $('obErr').textContent = e.message || 'Could not save that. Try a different username.'; } + }; + }); + } + $('signupBtn').addEventListener('click', busy($('signupBtn'), async () => { - await api('/api/signup', { email: $('suEmail').value, password: $('suPass').value }); + const r = await api('/api/signup', { email: $('suEmail').value, password: $('suPass').value }); IAP.status('Welcome aboard. You are in.', 'ok'); + if (!(r.account && r.account.username)) await showOnboard(); await render(); })); $('loginBtn').addEventListener('click', busy($('loginBtn'), async () => { diff --git a/public/my.html b/public/my.html index 29ae23b..848403a 100644 --- a/public/my.html +++ b/public/my.html @@ -17,6 +17,10 @@

Join free with your email. Your wallet only comes out when you buy a package or switch on payouts, and it stays yours the whole time.

+ + + +