Advertise the jackpot that will actually be paid (B-65)
participant_count and jackpot_sats were computed over every round_participants row, while the draw only picks from confirmed participants and the payout only spends their sats. So the advertised jackpot could exceed the one paid out, and a player whose bet was later abandoned appeared in the count and then vanished again. Counting only confirmed rows would have fixed the arithmetic and broken something else: the player who just bet would see neither themselves nor their money for a whole block. So this is the same confirmed/in-flight split the balance already exposes (balance_sats vs pending_balance_sats): participant_count and jackpot_sats are now the confirmed, authoritative figures, and pending_participant_count/pending_jackpot_sats/has_pending_bets report what is in flight — inclusive figures, not deltas, matching the balance pair's convention. /'s round card shows the confirmed numbers big and the difference as an amber "+N in attesa" suffix, reusing .balance-pending's colour for the same "not settled yet" meaning. The two new spans render from server data through t(), so they carry no data-i18n and onLanguageChange() repaints them from the last response — the one-mechanism-per-element rule. Both strings are in all 7 languages. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+48
-10
@@ -368,6 +368,49 @@ async function checkLastRoundResult() {
|
||||
|
||||
let lastJackpotValue = null;
|
||||
|
||||
// The round's own confirmed/in-flight split (B-65). The big numbers are the
|
||||
// confirmed ones — the players the draw will pick from and the pool the payout
|
||||
// will actually spend — because a jackpot advertised larger than the one paid out
|
||||
// is the kind of gap nobody forgives. What has been bet but hasn't confirmed yet
|
||||
// is shown next to them instead of being folded in, so the player who just bet
|
||||
// still sees their own bet immediately (the same reasoning as the amber
|
||||
// pending balance, see setBalanceDisplay).
|
||||
//
|
||||
// Kept out of the [data-i18n] mechanism on purpose: these come from server data,
|
||||
// so onLanguageChange() re-renders them through t() like every other dynamic bit.
|
||||
let lastRoundStats = null;
|
||||
|
||||
function renderRoundStats(data) {
|
||||
lastRoundStats = data;
|
||||
document.getElementById('round-players').textContent = data.participant_count;
|
||||
|
||||
const jackpotEl = document.getElementById('round-jackpot');
|
||||
const jackpotValue = data.jackpot_sats / SATS_PER_PLM;
|
||||
jackpotEl.textContent = formatPlm(data.jackpot_sats);
|
||||
if (lastJackpotValue !== null && jackpotValue !== lastJackpotValue) {
|
||||
jackpotEl.classList.remove('jackpot-bump');
|
||||
void jackpotEl.offsetWidth; // restart the animation
|
||||
jackpotEl.classList.add('jackpot-bump');
|
||||
}
|
||||
lastJackpotValue = jackpotValue;
|
||||
|
||||
// pending_* are inclusive of the confirmed figures (like pending_balance_sats),
|
||||
// so what's shown alongside is the difference.
|
||||
const pendingPlayers = (data.pending_participant_count || 0) - data.participant_count;
|
||||
const pendingJackpotSats = (data.pending_jackpot_sats || 0) - data.jackpot_sats;
|
||||
const show = !!data.has_pending_bets && pendingPlayers > 0;
|
||||
|
||||
const playersPendingEl = document.getElementById('round-players-pending');
|
||||
playersPendingEl.textContent = show ? t('round.playersPending', { n: pendingPlayers }) : '';
|
||||
playersPendingEl.classList.toggle('hidden', !show);
|
||||
|
||||
const jackpotPendingEl = document.getElementById('round-jackpot-pending');
|
||||
jackpotPendingEl.textContent = show
|
||||
? t('round.jackpotPending', { amount: formatPlm(pendingJackpotSats) })
|
||||
: '';
|
||||
jackpotPendingEl.classList.toggle('hidden', !show);
|
||||
}
|
||||
|
||||
let timerHitZero = false;
|
||||
|
||||
function updateRoundTimer() {
|
||||
@@ -451,16 +494,7 @@ async function refreshRound() {
|
||||
: t('round.none');
|
||||
betAmountSats = data.bet_amount_sats;
|
||||
renderBetButton();
|
||||
document.getElementById('round-players').textContent = data.participant_count;
|
||||
const jackpotEl = document.getElementById('round-jackpot');
|
||||
const jackpotValue = data.jackpot_sats / SATS_PER_PLM;
|
||||
jackpotEl.textContent = formatPlm(data.jackpot_sats);
|
||||
if (lastJackpotValue !== null && jackpotValue !== lastJackpotValue) {
|
||||
jackpotEl.classList.remove('jackpot-bump');
|
||||
void jackpotEl.offsetWidth; // restart the animation
|
||||
jackpotEl.classList.add('jackpot-bump');
|
||||
}
|
||||
lastJackpotValue = jackpotValue;
|
||||
renderRoundStats(data);
|
||||
if (data.server_time) serverTimeOffsetMs = new Date(data.server_time) - new Date();
|
||||
roundCloseAt = data.closes_at ? new Date(data.closes_at) : null;
|
||||
updateRoundTimer();
|
||||
@@ -858,6 +892,10 @@ function connectRoundEvents() {
|
||||
function onLanguageChange() {
|
||||
renderBetButton();
|
||||
renderChainStatusBar(); // repaints from remembered state, without waiting for the next poll
|
||||
// Same reason, for the round's "+N in attesa" suffixes (B-65): repaint from what
|
||||
// was last received rather than leaving them in the old language until the
|
||||
// refreshRound() below happens to come back.
|
||||
if (lastRoundStats) renderRoundStats(lastRoundStats);
|
||||
if (token) {
|
||||
refreshRound();
|
||||
refreshMe();
|
||||
|
||||
@@ -63,6 +63,8 @@ const TRANSLATIONS = {
|
||||
|
||||
'round.players': 'Players',
|
||||
'round.jackpot': 'Jackpot',
|
||||
'round.playersPending': '+{n} pending',
|
||||
'round.jackpotPending': '+{amount} pending',
|
||||
'round.status.open': 'open',
|
||||
'round.status.closing': 'closing',
|
||||
'round.status.drawing': 'drawing in progress',
|
||||
@@ -228,6 +230,8 @@ const TRANSLATIONS = {
|
||||
|
||||
'round.players': 'Giocatori',
|
||||
'round.jackpot': 'Jackpot',
|
||||
'round.playersPending': '+{n} in attesa',
|
||||
'round.jackpotPending': '+{amount} in attesa',
|
||||
'round.status.open': 'aperto',
|
||||
'round.status.closing': 'in chiusura',
|
||||
'round.status.drawing': 'estrazione in corso',
|
||||
@@ -390,6 +394,8 @@ const TRANSLATIONS = {
|
||||
|
||||
'round.players': 'Jugadores',
|
||||
'round.jackpot': 'Bote',
|
||||
'round.playersPending': '+{n} pendientes',
|
||||
'round.jackpotPending': '+{amount} pendientes',
|
||||
'round.status.open': 'abierta',
|
||||
'round.status.closing': 'cerrando',
|
||||
'round.status.drawing': 'sorteo en curso',
|
||||
@@ -552,6 +558,8 @@ const TRANSLATIONS = {
|
||||
|
||||
'round.players': 'Joueurs',
|
||||
'round.jackpot': 'Jackpot',
|
||||
'round.playersPending': '+{n} en attente',
|
||||
'round.jackpotPending': '+{amount} en attente',
|
||||
'round.status.open': 'ouvert',
|
||||
'round.status.closing': 'en fermeture',
|
||||
'round.status.drawing': 'tirage en cours',
|
||||
@@ -714,6 +722,8 @@ const TRANSLATIONS = {
|
||||
|
||||
'round.players': 'Spieler',
|
||||
'round.jackpot': 'Jackpot',
|
||||
'round.playersPending': '+{n} ausstehend',
|
||||
'round.jackpotPending': '+{amount} ausstehend',
|
||||
'round.status.open': 'offen',
|
||||
'round.status.closing': 'wird geschlossen',
|
||||
'round.status.drawing': 'Ziehung läuft',
|
||||
@@ -876,6 +886,8 @@ const TRANSLATIONS = {
|
||||
|
||||
'round.players': 'Игроки',
|
||||
'round.jackpot': 'Джекпот',
|
||||
'round.playersPending': '+{n} в ожидании',
|
||||
'round.jackpotPending': '+{amount} в ожидании',
|
||||
'round.status.open': 'открыт',
|
||||
'round.status.closing': 'закрывается',
|
||||
'round.status.drawing': 'идёт розыгрыш',
|
||||
@@ -1038,6 +1050,8 @@ const TRANSLATIONS = {
|
||||
|
||||
'round.players': '参与人数',
|
||||
'round.jackpot': '奖池',
|
||||
'round.playersPending': '+{n} 待确认',
|
||||
'round.jackpotPending': '+{amount} 待确认',
|
||||
'round.status.open': '进行中',
|
||||
'round.status.closing': '即将结束',
|
||||
'round.status.drawing': '正在开奖',
|
||||
|
||||
@@ -150,11 +150,15 @@
|
||||
<div class="row-between" style="margin-top:10px" id="round-stats-row">
|
||||
<div>
|
||||
<div class="hint" style="margin-bottom:2px" data-i18n="round.players">Giocatori</div>
|
||||
<span class="mono" id="round-players">—</span>
|
||||
<!-- The two -pending spans are filled from server data by renderRoundStats()
|
||||
(B-65), so they carry no data-i18n: an element belongs to one
|
||||
translation mechanism or the other, never both. -->
|
||||
<span class="mono" id="round-players">—</span> <span class="pending-suffix hidden" id="round-players-pending"></span>
|
||||
</div>
|
||||
<div style="text-align:right">
|
||||
<div class="hint" style="margin-bottom:2px" data-i18n="round.jackpot">Jackpot</div>
|
||||
<span class="mono" id="round-jackpot">—</span> <span class="balance-unit">PLM</span>
|
||||
<span class="pending-suffix hidden" id="round-jackpot-pending"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -215,6 +215,12 @@ button.link:hover, a.link:hover { filter: none; color: var(--color-foreground);
|
||||
.balance-confirmed { color: var(--color-success); }
|
||||
.balance-pending { color: var(--color-primary); }
|
||||
|
||||
/* The in-flight part of the round's own figures (B-65): the players/jackpot next
|
||||
to it are the confirmed ones the draw and the payout will actually use, and this
|
||||
is what has been bet but hasn't confirmed yet. Same amber as .balance-pending,
|
||||
for the same "not settled" meaning. */
|
||||
.pending-suffix { color: var(--color-primary); font-size: 0.8rem; font-weight: 600; }
|
||||
|
||||
.icon { width: 16px; height: 16px; flex-shrink: 0; }
|
||||
|
||||
.dash-panel { display: none; }
|
||||
|
||||
Reference in New Issue
Block a user