diff --git a/public/assets/my.js b/public/assets/my.js
index 75b8c06..ca8460d 100644
--- a/public/assets/my.js
+++ b/public/assets/my.js
@@ -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'; }
diff --git a/public/my.html b/public/my.html
index 6f68605..858a7d7 100644
--- a/public/my.html
+++ b/public/my.html
@@ -915,7 +915,7 @@
-
+