Files
wownerogue/html/js/ui/publicStats.js
T
jwinterm 63e3006aca launch features: win feed, public stats, share-a-win, live CTA
Four pre-launch additions to make the game feel alive and shareable:

- Live win feed: server broadcasts 'win_feed' on every escape; client shows a
  transient toast ('X escaped with the bag') so the room feels active.
- Public stats: GET /api/stats (10s-cached) returns games + escapes in the last
  24h and total paid out; a status-area strip polls it for social proof.
- Share-a-win: game-over screen gets 'Share to X' + 'Copy link' buttons pointing
  at the /verify page, which now renders OpenGraph/Twitter-card meta (brand OG
  image per instance) so shared links unfurl with a preview.
- Land-on-action: a dismissible 'N games live now — watch' CTA appears on connect
  when games are in progress, opening the existing spectator panel. Non-intrusive:
  hidden while playing/spectating and never hijacks the canvas.
2026-06-01 09:17:48 -04:00

46 lines
1.6 KiB
JavaScript

/**
* Public stats strip — light "social proof" shown in the status area: games and escapes in
* the last 24h, and total paid out (only where payouts are enabled). Polls /api/stats, which
* is cached server-side (10s), so a 30s poll is cheap. Online-player count is intentionally
* NOT shown here — it already updates live via the 'user_count' socket event.
*/
(function () {
var EL_ID = 'publicStats';
function fmtAmount(n) {
if (!isFinite(n)) return '0';
// Trim to a tidy magnitude: whole numbers plain, otherwise up to 4 dp without trailing zeros.
if (n >= 1000) return Math.round(n).toLocaleString();
var s = (Math.round(n * 10000) / 10000).toString();
return s;
}
function render(data) {
var el = document.getElementById(EL_ID);
if (!el || !data) return;
var parts = [];
parts.push('🎮 ' + (data.gamesToday || 0) + ' games (24h)');
parts.push('🏆 ' + (data.escapesToday || 0) + ' escapes');
if (data.payoutsEnabled && data.totalPaidOut > 0) {
parts.push('💰 ' + fmtAmount(data.totalPaidOut) + ' ' + (data.currencyLabel || '') + ' paid out');
}
el.innerHTML = parts.join('  ·  ');
el.style.display = 'block';
}
function poll() {
fetch('/api/stats')
.then(function (r) { return r.json(); })
.then(render)
.catch(function () { /* ignore; transient */ });
}
function start() {
poll();
setInterval(poll, 30000);
}
if (document.readyState !== 'loading') start();
else document.addEventListener('DOMContentLoaded', start);
})();