Dashboard: community toasts for joins, tank arrivals, adoptions, purchases, payouts and qualifications
A second, quieter toast (bottom-left, one per 4 s) shows site-wide activity from the live feed for every signed-in member; personal payout toasts are unchanged. The server pushes Joined (at first username, with tank flag) and Adopted events onto the feed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
+27
-1
@@ -275,8 +275,34 @@
|
|||||||
clearTimeout(liveRefreshT);
|
clearTimeout(liveRefreshT);
|
||||||
liveRefreshT = setTimeout(() => { loadDashboard(); try { loadLineage(); } catch (e) {} }, 600);
|
liveRefreshT = setTimeout(() => { loadDashboard(); try { loadLineage(); } catch (e) {} }, 600);
|
||||||
}
|
}
|
||||||
|
// community toasts (Marty, 2026-09-12: keep the dashboard feeling alive): everyone else's joins,
|
||||||
|
// purchases, payouts, qualifications, tank arrivals and adoptions, in a second, quieter toast so
|
||||||
|
// they never replace a personal one. At most one every 4 s; a burst shows the latest.
|
||||||
|
let liveT = 0, liveQ = null;
|
||||||
|
function communityToast(msg) {
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - liveT < 4000) { liveQ = msg; if (!communityToast._q) communityToast._q = setTimeout(() => { communityToast._q = null; const m = liveQ; liveQ = null; if (m) communityToast(m); }, 4200 - (now - liveT)); return; }
|
||||||
|
liveT = now;
|
||||||
|
let el = document.getElementById('liveToast');
|
||||||
|
if (!el) { el = document.createElement('div'); el.id = 'liveToast'; el.setAttribute('role', 'status'); el.style.cssText = 'position:fixed;left:16px;bottom:16px;z-index:90;max-width:min(360px,calc(100vw - 32px));background:var(--panel-solid);border:1px solid var(--line-strong);border-left:3px solid var(--mint);border-radius:12px;padding:10px 14px;font-size:14px;box-shadow:0 10px 30px rgba(0,0,0,.4);transition:opacity .3s'; document.body.appendChild(el); }
|
||||||
|
el.textContent = msg; el.hidden = false; el.style.opacity = '1';
|
||||||
|
clearTimeout(communityToast._t); communityToast._t = setTimeout(() => { el.style.opacity = '0'; setTimeout(() => { el.hidden = true; }, 350); }, 6000);
|
||||||
|
}
|
||||||
function handleLiveEvent(ev) {
|
function handleLiveEvent(ev) {
|
||||||
if (!MYID || !ev || !ev.type) return;
|
if (!ev || !ev.type) return;
|
||||||
|
const nm = id => (ev.names && ev.names[id]) ? '@' + ev.names[id] : 'member #' + id;
|
||||||
|
// site-wide activity (not about me): joins, tank, adoptions, purchases, payouts, qualifications
|
||||||
|
if (ev.type === 'Joined') { communityToast('👋 ' + ev.name + ' just joined' + (ev.tank ? ' and is waiting for a sponsor in the holding tank' : '')); liveRefresh(); return; }
|
||||||
|
if (ev.type === 'Adopted') { communityToast('🤝 ' + ev.sponsor + ' picked up ' + ev.member + ' from the holding tank'); liveRefresh(); return; }
|
||||||
|
const mine = MYID && (ev.recipientId === MYID || ev.toId === MYID || ev.sponsorId === MYID || ev.skippedId === MYID || ev.buyerId === MYID);
|
||||||
|
if (!mine) {
|
||||||
|
if (ev.type === 'Purchase' && ev.buyerId) communityToast('🧾 ' + nm(ev.buyerId) + ' just bought a $' + Math.round((ev.priceCents || 0) / 100) + ' package');
|
||||||
|
else if (ev.type === 'TierPaid' && ev.recipientId) communityToast('💸 ' + nm(ev.recipientId) + ' just got paid ' + IAP.fmtPol(ev.amountWei) + ' POL');
|
||||||
|
else if (ev.type === 'BuyerCounted' && ev.sponsorId) communityToast('🎯 ' + nm(ev.sponsorId) + ' now has ' + ev.newCount + ' qualifying buyer' + (ev.newCount === 1 ? '' : 's'));
|
||||||
|
else if (ev.type === 'MemberActivated' && ev.id) communityToast('⚡ ' + nm(ev.id) + ' switched on payouts');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!MYID) return;
|
||||||
let toast = null, kind = 'ok';
|
let toast = null, kind = 'ok';
|
||||||
let sound = null;
|
let sound = null;
|
||||||
if (ev.type === 'TierPaid' && ev.recipientId === MYID) { toast = '💸 You earned a level-' + ev.tier + ' payout of ' + IAP.fmtPol(ev.amountWei) + ' POL!'; sound = 'chaching'; }
|
if (ev.type === 'TierPaid' && ev.recipientId === MYID) { toast = '💸 You earned a level-' + ev.tier + ' payout of ' + IAP.fmtPol(ev.amountWei) + ' POL!'; sound = 'chaching'; }
|
||||||
|
|||||||
+1
-1
@@ -915,7 +915,7 @@
|
|||||||
<script src="/assets/common.js?v=20260912b"></script>
|
<script src="/assets/common.js?v=20260912b"></script>
|
||||||
<script src="/assets/wallet.js?v=20260911a"></script>
|
<script src="/assets/wallet.js?v=20260911a"></script>
|
||||||
<script src="/assets/promo.js?v=20260911a"></script>
|
<script src="/assets/promo.js?v=20260911a"></script>
|
||||||
<script src="/assets/my.js?v=20260912f"></script>
|
<script src="/assets/my.js?v=20260912g"></script>
|
||||||
<script src="/assets/chat.js?v=20260907l"></script>
|
<script src="/assets/chat.js?v=20260907l"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1130,6 +1130,7 @@ const server = http.createServer(async (req, res) => {
|
|||||||
const b = await readBody(req);
|
const b = await readBody(req);
|
||||||
const r = await tank.adopt(s.email, b.who, b.note);
|
const r = await tank.adopt(s.email, b.who, b.note);
|
||||||
if (r.ok) { // tell the payments topic who picked whom up (Marty, 2026-09-12)
|
if (r.ok) { // tell the payments topic who picked whom up (Marty, 2026-09-12)
|
||||||
|
pushFeed({ type: 'Adopted', sponsor: r.adopterName, member: r.adopteeName, ts: Date.now() });
|
||||||
try {
|
try {
|
||||||
const sc = siteConfig();
|
const sc = siteConfig();
|
||||||
const clean = t => String(t || '').replace(/[<>&]/g, '');
|
const clean = t => String(t || '').replace(/[<>&]/g, '');
|
||||||
@@ -1235,6 +1236,8 @@ const server = http.createServer(async (req, res) => {
|
|||||||
if (!r.error && before && !before.username && b.username) {
|
if (!r.error && before && !before.username && b.username) {
|
||||||
const acct = await accounts.byEmail(s.email);
|
const acct = await accounts.byEmail(s.email);
|
||||||
notifyNewReferral(acct && acct.sponsorRef, acct).catch(() => {});
|
notifyNewReferral(acct && acct.sponsorRef, acct).catch(() => {});
|
||||||
|
// everyone's dashboard: "@name just joined" (and whether they are waiting in the tank)
|
||||||
|
if (acct && acct.username) pushFeed({ type: 'Joined', name: '@' + acct.username, tank: !acct.sponsorRef && !acct.memberId, ts: Date.now() });
|
||||||
}
|
}
|
||||||
return json(res, r.error ? 400 : 200, r);
|
return json(res, r.error ? 400 : 200, r);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user