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) <noreply@anthropic.com>
This commit is contained in:
+2
-2
@@ -21,7 +21,7 @@
|
|||||||
<div class="table-card" style="margin-bottom:18px;border-color:rgba(123,224,161,.4)"><h2 style="margin:0 0 4px">Your pipeline</h2><p style="color:var(--muted);font-size:13px;margin:0 0 12px">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 <em>next</em> upgrade comes to you.</p><div id="dPipeline"></div></div>
|
<div class="table-card" style="margin-bottom:18px;border-color:rgba(123,224,161,.4)"><h2 style="margin:0 0 4px">Your pipeline</h2><p style="color:var(--muted);font-size:13px;margin:0 0 12px">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 <em>next</em> upgrade comes to you.</p><div id="dPipeline"></div></div>
|
||||||
<div id="dCoachCard" class="table-card" style="margin-bottom:18px;display:none;border-color:rgba(240,197,109,.35)"><h2 style="margin:0 0 4px">Coach your team</h2><p style="color:var(--muted);font-size:13px;margin:0 0 12px">The fastest way to grow your own income is helping the people below you take their next step. Here's who in <em>your</em> team could use a nudge today — updated live from the blockchain.</p><div id="dCoach"></div></div>
|
<div id="dCoachCard" class="table-card" style="margin-bottom:18px;display:none;border-color:rgba(240,197,109,.35)"><h2 style="margin:0 0 4px">Coach your team</h2><p style="color:var(--muted);font-size:13px;margin:0 0 12px">The fastest way to grow your own income is helping the people below you take their next step. Here's who in <em>your</em> team could use a nudge today — updated live from the blockchain.</p><div id="dCoach"></div></div>
|
||||||
<div class="table-card" style="margin-bottom:18px;border-color:rgba(120,160,255,.35)"><h2 style="margin:0 0 4px">Messages <span id="msgBell" class="hidden" style="font-size:14px;background:var(--gold);color:#132;border-radius:20px;padding:2px 10px;vertical-align:middle;font-weight:800">🔔 0</span></h2><p style="color:var(--muted);font-size:13px;margin:0 0 12px">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. <span class="micro">Messages are member-to-member; the team admin can review them for abuse.</span></p><div id="dMsg"><div class="empty">Loading…</div></div></div>
|
<div class="table-card" style="margin-bottom:18px;border-color:rgba(120,160,255,.35)"><h2 style="margin:0 0 4px">Messages <span id="msgBell" class="hidden" style="font-size:14px;background:var(--gold);color:#132;border-radius:20px;padding:2px 10px;vertical-align:middle;font-weight:800">🔔 0</span></h2><p style="color:var(--muted);font-size:13px;margin:0 0 12px">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. <span class="micro">Messages are member-to-member; the team admin can review them for abuse.</span></p><div id="dMsg"><div class="empty">Loading…</div></div></div>
|
||||||
<div class="table-card" style="margin-bottom:18px;border-color:rgba(78,214,203,.35)"><h2 style="margin:0 0 4px">Email me my alerts</h2><p style="color:var(--muted);font-size:13px;margin:0 0 12px">Get an email the moment this position is <strong>paid</strong>, and when it <strong>needs an upgrade</strong> to catch incoming pay — so you never miss one. Opt in with your email; unsubscribe anytime.</p><div id="dAlerts"></div></div>
|
<div class="table-card" style="margin-bottom:18px;border-color:rgba(78,214,203,.35)"><h2 style="margin:0 0 4px" id="dAlertsHead">Email me my alerts</h2><p style="color:var(--muted);font-size:13px;margin:0 0 12px" id="dAlertsIntro">Get an email the moment this position is <strong>paid</strong>, and when it <strong>needs an upgrade</strong> to catch incoming pay — so you never miss one. Opt in with your email; unsubscribe anytime.</p><div id="dAlerts"></div></div>
|
||||||
<div class="table-card" style="margin-bottom:18px;border-color:rgba(240,197,109,.45)"><h2 style="margin:0 0 4px">Share this position</h2><div id="dShare"></div><a class="btn btn-primary" href="/tools" style="display:block;text-align:center;margin-top:14px;padding:14px 18px;font-size:16px;font-weight:800">🎬 Get Your Promo Tools — posts, swipes, videos & banners →</a><a class="btn btn-secondary" href="/fast-start" style="display:block;text-align:center;margin-top:8px">🖨️ Print your 48-Hour Fast Start sheet</a><a class="btn btn-secondary" href="/weekly-rhythm" style="display:block;text-align:center;margin-top:8px">🔁 Print your Weekly Rhythm sheet</a><a class="btn btn-secondary" href="/generation-pay" style="display:block;text-align:center;margin-top:8px">📊 Print your Generation Pay chart</a><a class="btn btn-secondary" href="/flyers" style="display:block;text-align:center;margin-top:8px">🪧 Print handouts with your QR code</a><p class="micro" style="text-align:center;margin:8px 0 0">Ready-made content to share with your invite link — no writing needed.</p></div>
|
<div class="table-card" style="margin-bottom:18px;border-color:rgba(240,197,109,.45)"><h2 style="margin:0 0 4px">Share this position</h2><div id="dShare"></div><a class="btn btn-primary" href="/tools" style="display:block;text-align:center;margin-top:14px;padding:14px 18px;font-size:16px;font-weight:800">🎬 Get Your Promo Tools — posts, swipes, videos & banners →</a><a class="btn btn-secondary" href="/fast-start" style="display:block;text-align:center;margin-top:8px">🖨️ Print your 48-Hour Fast Start sheet</a><a class="btn btn-secondary" href="/weekly-rhythm" style="display:block;text-align:center;margin-top:8px">🔁 Print your Weekly Rhythm sheet</a><a class="btn btn-secondary" href="/generation-pay" style="display:block;text-align:center;margin-top:8px">📊 Print your Generation Pay chart</a><a class="btn btn-secondary" href="/flyers" style="display:block;text-align:center;margin-top:8px">🪧 Print handouts with your QR code</a><p class="micro" style="text-align:center;margin:8px 0 0">Ready-made content to share with your invite link — no writing needed.</p></div>
|
||||||
<div class="table-card" style="margin-bottom:18px;border-color:var(--gold)"><h2 style="margin:0 0 4px">Just joined under this position?</h2><p style="color:var(--muted);font-size:13px;margin:0 0 12px">Welcome to the team! Enter the <strong>new member ID</strong> the RM Circle dApp gave you — we'll verify it on the blockchain and let the team know you're in.</p><form id="dJoinForm" style="display:grid;gap:8px;max-width:480px"><input name="newId" class="input" inputmode="numeric" pattern="[0-9]{1,10}" maxlength="10" placeholder="Your NEW RM Circle ID (numbers only)" required><input name="memberName" class="input" maxlength="60" placeholder="Your name or Telegram @handle" required><button class="btn btn-primary">Submit My ID →</button></form><div id="dJoinMsg" style="margin-top:10px;font-size:14px"></div></div>
|
<div class="table-card" style="margin-bottom:18px;border-color:var(--gold)"><h2 style="margin:0 0 4px">Just joined under this position?</h2><p style="color:var(--muted);font-size:13px;margin:0 0 12px">Welcome to the team! Enter the <strong>new member ID</strong> the RM Circle dApp gave you — we'll verify it on the blockchain and let the team know you're in.</p><form id="dJoinForm" style="display:grid;gap:8px;max-width:480px"><input name="newId" class="input" inputmode="numeric" pattern="[0-9]{1,10}" maxlength="10" placeholder="Your NEW RM Circle ID (numbers only)" required><input name="memberName" class="input" maxlength="60" placeholder="Your name or Telegram @handle" required><button class="btn btn-primary">Submit My ID →</button></form><div id="dJoinMsg" style="margin-top:10px;font-size:14px"></div></div>
|
||||||
<div class="table-card" style="margin-bottom:18px"><h2 style="margin:0 0 4px">Payments received</h2><p style="color:var(--muted);font-size:13px;margin:0 0 10px">Every payment your position has received, straight from the smart contract.</p><div id="dIncome"></div></div>
|
<div class="table-card" style="margin-bottom:18px"><h2 style="margin:0 0 4px">Payments received</h2><p style="color:var(--muted);font-size:13px;margin:0 0 10px">Every payment your position has received, straight from the smart contract.</p><div id="dIncome"></div></div>
|
||||||
@@ -31,5 +31,5 @@
|
|||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<footer class="wrap disclaimer">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.<div class="footer-links"><a href="/">Home</a><a href="/contract">Contract Security</a><a href="/disclaimer">Disclaimers</a><a href="/tools">Promo Tools</a><a href="/privacy">Privacy</a><a href="/refunds">Refunds</a></div></footer>
|
<footer class="wrap disclaimer">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.<div class="footer-links"><a href="/">Home</a><a href="/contract">Contract Security</a><a href="/disclaimer">Disclaimers</a><a href="/tools">Promo Tools</a><a href="/privacy">Privacy</a><a href="/refunds">Refunds</a></div></footer>
|
||||||
<script src="/track.js"></script><script src="/qrlib.js"></script><script src="/rmc-wallet.js"></script><script src="/inapp-browser.js"></script><script src="/profile-gate.js?v=20260916c"></script><script src="/my.js"></script><script src="/payouts.js" defer></script><script src="/chat.js" defer></script><script src="/translate.js" defer></script><script src="/tg-app.js" defer></script><script src="/wallet-notice.js" defer></script> <script src="/announce.js?v=20260916a"></script>
|
<script src="/track.js"></script><script src="/qrlib.js"></script><script src="/rmc-wallet.js"></script><script src="/inapp-browser.js"></script><script src="/profile-gate.js?v=20260916d"></script><script src="/my.js"></script><script src="/payouts.js" defer></script><script src="/chat.js" defer></script><script src="/translate.js" defer></script><script src="/tg-app.js" defer></script><script src="/wallet-notice.js" defer></script> <script src="/announce.js?v=20260916a"></script>
|
||||||
</body></html>
|
</body></html>
|
||||||
|
|||||||
@@ -622,6 +622,28 @@
|
|||||||
async function renderAlerts(d){
|
async function renderAlerts(d){
|
||||||
const el=document.getElementById('dAlerts');
|
const el=document.getElementById('dAlerts');
|
||||||
if(!el)return;
|
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=`<div style="display:flex;flex-wrap:wrap;gap:10px 22px;align-items:center;margin:0 0 12px">`+
|
||||||
|
`<div><div class="micro" style="color:var(--muted)">Username</div><div style="font-weight:700">@${esc(pr.profile.username)}</div></div>`+
|
||||||
|
`<div><div class="micro" style="color:var(--muted)">Email <span style="color:var(--ok)">✓ confirmed</span></div><div style="font-weight:700">${esc(pr.profile.email)}</div></div>`+
|
||||||
|
`</div><button id="pgEditUser" class="btn btn-secondary btn-sm">Change username</button> `+
|
||||||
|
`<button id="pgEditMail" class="btn btn-secondary btn-sm" style="margin-left:6px">Change email</button>`+
|
||||||
|
`<p class="micro" style="margin:10px 0 0;color:var(--muted)">Payout alerts go to this address. Changing it here changes both.</p>`;
|
||||||
|
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};
|
let st={subscribed:false};
|
||||||
try{st=await (await fetch('/api/public/alert-status?id='+d.id)).json();}catch(e){}
|
try{st=await (await fetch('/api/public/alert-status?id='+d.id)).json();}catch(e){}
|
||||||
function subscribedView(email){
|
function subscribedView(email){
|
||||||
|
|||||||
+28
-3
@@ -9,7 +9,7 @@
|
|||||||
// Usage: await window.RMCProfile.require(); // resolves once the profile is complete
|
// Usage: await window.RMCProfile.require(); // resolves once the profile is complete
|
||||||
(function () {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
var back = null, resolveDone = null, state = null;
|
var back = null, resolveDone = null, state = null, editable = false;
|
||||||
var GOLD = '#d4af37', TEAL = '#4ed6cb';
|
var GOLD = '#d4af37', TEAL = '#4ed6cb';
|
||||||
|
|
||||||
function el(tag, css, html) {
|
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);' +
|
'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;');
|
'font-family:system-ui,Segoe UI,Arial,sans-serif;color:#e8eef6;padding:22px 22px 20px;');
|
||||||
card.id = 'pgCard';
|
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);
|
back.appendChild(card);
|
||||||
document.body.appendChild(back);
|
document.body.appendChild(back);
|
||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
|
|
||||||
function head(card, step) {
|
function head(card, step) {
|
||||||
|
var keep = card.querySelector('button[aria-label="Close"]');
|
||||||
card.innerHTML = '';
|
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;',
|
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'));
|
'Position #' + esc(state.id) + ' · step ' + step + ' of 2'));
|
||||||
return card;
|
return card;
|
||||||
@@ -79,6 +90,7 @@
|
|||||||
try {
|
try {
|
||||||
var r = await api('/api/public/profile/username', { username: i.value });
|
var r = await api('/api/public/profile/username', { username: i.value });
|
||||||
state.profile = r.profile;
|
state.profile = r.profile;
|
||||||
|
if (editable) { close(); return; }
|
||||||
next();
|
next();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
err.textContent = e.message; err.style.display = 'block'; b.disabled = false; b.textContent = 'Save and continue';
|
err.textContent = e.message; err.style.display = 'block'; b.disabled = false; b.textContent = 'Save and continue';
|
||||||
@@ -130,6 +142,7 @@
|
|||||||
try {
|
try {
|
||||||
var r = await api('/api/public/profile/email-verify', { code: ci.value });
|
var r = await api('/api/public/profile/email-verify', { code: ci.value });
|
||||||
state.profile = r.profile;
|
state.profile = r.profile;
|
||||||
|
if (editable) { close(); return; }
|
||||||
done();
|
done();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
err.textContent = e.message; err.style.display = 'block'; cb.disabled = false; cb.textContent = 'Confirm and finish';
|
err.textContent = e.message; err.style.display = 'block'; cb.disabled = false; cb.textContent = 'Confirm and finish';
|
||||||
@@ -154,7 +167,7 @@
|
|||||||
function close() {
|
function close() {
|
||||||
if (back && back.parentNode) back.parentNode.removeChild(back);
|
if (back && back.parentNode) back.parentNode.removeChild(back);
|
||||||
back = null;
|
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() {
|
function next() {
|
||||||
if (!state.profile || !state.profile.username) return stepUsername();
|
if (!state.profile || !state.profile.username) return stepUsername();
|
||||||
@@ -179,6 +192,18 @@
|
|||||||
return new Promise(function (res) { resolveDone = res; next(); });
|
return new Promise(function (res) { resolveDone = res; next(); });
|
||||||
}
|
}
|
||||||
async function status() { try { return await api('/api/public/profile'); } catch (e) { return null; } }
|
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 };
|
||||||
})();
|
})();
|
||||||
|
|||||||
Reference in New Issue
Block a user