From 2c5fff20f5af05d2e59a0ceeb190c6419b6446cb Mon Sep 17 00:00:00 2001 From: martbost Date: Wed, 16 Sep 2026 17:28:26 -0500 Subject: [PATCH] Members can change their username and email: in-place profile card replaces the separate alert-email opt-in for signed-in owners The first-time gate cannot be dismissed; editing later can. The 'Email me my alerts' card becomes 'Your member profile' for a signed-in owner, showing the username and confirmed email with Change buttons, so the verified address is the one source of truth for both messaging and payout alerts. Visitors and non-owners keep the original opt-in form unchanged. Co-Authored-By: Claude Opus 5 (1M context) --- public/my.html | 4 ++-- public/my.js | 22 ++++++++++++++++++++++ public/profile-gate.js | 31 ++++++++++++++++++++++++++++--- 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/public/my.html b/public/my.html index 909236e..cf2bf64 100644 --- a/public/my.html +++ b/public/my.html @@ -21,7 +21,7 @@

Your pipeline

Money forming below you. Each generation in your leg pays your position at exactly one level β€” when a member's level catches up to their depth, their next upgrade comes to you.

Messages

Wallet-verified messaging along your matrix lines β€” your upline and your team can reach you here, and you can reach them. No email needed; your wallet is your identity. Messages are member-to-member; the team admin can review them for abuse.

Loading…
-

Email me my alerts

Get an email the moment this position is paid, and when it needs an upgrade to catch incoming pay β€” so you never miss one. Opt in with your email; unsubscribe anytime.

+

Email me my alerts

Get an email the moment this position is paid, and when it needs an upgrade to catch incoming pay β€” so you never miss one. Opt in with your email; unsubscribe anytime.

Just joined under this position?

Welcome to the team! Enter the new member ID the RM Circle dApp gave you β€” we'll verify it on the blockchain and let the team know you're in.

Payments received

Every payment your position has received, straight from the smart contract.

@@ -31,5 +31,5 @@
All figures are read live from the RM Circle smart contract on Polygon and are historical facts, not a promise of future results. Participation involves cryptocurrency and smart-contract risk. Never use funds you cannot afford to lose.
- + diff --git a/public/my.js b/public/my.js index 0e8d981..0bdff4f 100644 --- a/public/my.js +++ b/public/my.js @@ -622,6 +622,28 @@ async function renderAlerts(d){ const el=document.getElementById('dAlerts'); if(!el)return; + // A signed-in owner manages ONE identity here: the verified profile email is + // the alert address, and both it and the username are editable in place. + // Everyone else keeps the original opt-in form, so nothing existing breaks. + try{ + const pr=window.RMCProfile?await window.RMCProfile.status():null; + if(pr&&pr.signedIn!==false&&Number(pr.id)===Number(d.id)&&pr.profile&&pr.profile.complete){ + const hd=document.getElementById('dAlertsHead'),intro=document.getElementById('dAlertsIntro'); + if(hd)hd.textContent='Your member profile'; + if(intro)intro.innerHTML='How your team leader reaches you, and where your payout alerts go. Your email is never shown to other members and never sold.'; + el.innerHTML=`
`+ + `
Username
@${esc(pr.profile.username)}
`+ + `
Email βœ“ confirmed
${esc(pr.profile.email)}
`+ + `
`+ + ``+ + `

Payout alerts go to this address. Changing it here changes both.

`; + const again=()=>renderAlerts(d); + const bu=document.getElementById('pgEditUser'),bm=document.getElementById('pgEditMail'); + if(bu)bu.addEventListener('click',async()=>{await window.RMCProfile.edit('username');again();}); + if(bm)bm.addEventListener('click',async()=>{await window.RMCProfile.edit('email');again();}); + return; + } + }catch(e){} let st={subscribed:false}; try{st=await (await fetch('/api/public/alert-status?id='+d.id)).json();}catch(e){} function subscribedView(email){ diff --git a/public/profile-gate.js b/public/profile-gate.js index 9f24fa0..3fd69ec 100644 --- a/public/profile-gate.js +++ b/public/profile-gate.js @@ -9,7 +9,7 @@ // Usage: await window.RMCProfile.require(); // resolves once the profile is complete (function () { 'use strict'; - var back = null, resolveDone = null, state = null; + var back = null, resolveDone = null, state = null, editable = false; var GOLD = '#d4af37', TEAL = '#4ed6cb'; function el(tag, css, html) { @@ -35,13 +35,24 @@ 'background:#0a1119;border:1px solid rgba(212,175,55,.55);box-shadow:0 24px 70px rgba(0,0,0,.7);' + 'font-family:system-ui,Segoe UI,Arial,sans-serif;color:#e8eef6;padding:22px 22px 20px;'); card.id = 'pgCard'; + if (editable) { // editing an existing profile can be abandoned; the first-time gate can not + var x = el('button', 'position:absolute;top:10px;right:12px;z-index:2;width:34px;height:34px;border-radius:50%;border:none;' + + 'cursor:pointer;background:rgba(255,255,255,.08);color:#fff;font-size:20px;line-height:1;font-weight:700;', '×'); + x.setAttribute('aria-label', 'Close'); + x.addEventListener('click', close); + card.appendChild(x); + back.addEventListener('click', function (e) { if (e.target === back) close(); }); + } back.appendChild(card); document.body.appendChild(back); return card; } function head(card, step) { + var keep = card.querySelector('button[aria-label="Close"]'); card.innerHTML = ''; + if (keep) card.appendChild(keep); + if (editable) return card; card.appendChild(el('div', 'text-align:center;letter-spacing:2px;text-transform:uppercase;font-size:11px;font-weight:700;color:' + GOLD + ';margin-bottom:8px;', 'Position #' + esc(state.id) + ' Β· step ' + step + ' of 2')); return card; @@ -79,6 +90,7 @@ try { var r = await api('/api/public/profile/username', { username: i.value }); state.profile = r.profile; + if (editable) { close(); return; } next(); } catch (e) { err.textContent = e.message; err.style.display = 'block'; b.disabled = false; b.textContent = 'Save and continue'; @@ -130,6 +142,7 @@ try { var r = await api('/api/public/profile/email-verify', { code: ci.value }); state.profile = r.profile; + if (editable) { close(); return; } done(); } catch (e) { err.textContent = e.message; err.style.display = 'block'; cb.disabled = false; cb.textContent = 'Confirm and finish'; @@ -154,7 +167,7 @@ function close() { if (back && back.parentNode) back.parentNode.removeChild(back); back = null; - if (resolveDone) { var r = resolveDone; resolveDone = null; r(state && state.profile); } + if (resolveDone) { var r = resolveDone; resolveDone = null; r((state && state.profile) || null); } } function next() { if (!state.profile || !state.profile.username) return stepUsername(); @@ -179,6 +192,18 @@ return new Promise(function (res) { resolveDone = res; next(); }); } async function status() { try { return await api('/api/public/profile'); } catch (e) { return null; } } + // Change an existing username or email. Dismissable, unlike the first-time gate. + // Resolves with the profile when saved, or null if the member closed it. + async function edit(kind) { + if (back) return null; + try { state = await api('/api/public/profile'); } catch (e) { return null; } + if (!state || state.signedIn === false) return null; + editable = true; + shell(); + var p = new Promise(function (res) { resolveDone = res; }); + if (kind === 'email') stepEmail(); else stepUsername(); + var out = await p; editable = false; return out; + } - window.RMCProfile = { require: require_, status: status }; + window.RMCProfile = { require: require_, status: status, edit: edit }; })();