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:
martbost
2026-09-12 16:03:43 -05:00
parent a9f10f861d
commit 6395ad3a7e
3 changed files with 31 additions and 2 deletions
+27 -1
View File
@@ -275,8 +275,34 @@
clearTimeout(liveRefreshT);
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) {
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 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'; }
+1 -1
View File
@@ -915,7 +915,7 @@
<script src="/assets/common.js?v=20260912b"></script>
<script src="/assets/wallet.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>
</body>
</html>