Wire up the SSE push channel in both frontend dashboards
app/static/index.html: opens an EventSource against /rounds/stream (no auth needed, see the previous commit) alongside the existing polling loops. On an "update" notification, immediately re-runs the same refreshes polling would eventually do (refreshRound/refreshMe/checkLastRoundResult when logged in, refreshChainStatusOnly when logged out). Also reacts to the browser's "open" event, which fires on the initial connection and on every automatic reconnect — this re-syncs right away instead of leaving the page on stale state until the next event or poll tick, which matters most right after a dropped connection comes back. app/static/admin.html: same channel, refreshing the chain-status bar and whichever admin section is currently open (Utenti/Round/Transazioni pendenti/Audit log) instead of requiring a manual tab switch to see new data. Polling intervals are untouched in both pages — this is purely additive, so a blocked/dropped SSE connection just degrades to the pre-existing behavior. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+29
-2
@@ -241,14 +241,16 @@ function stopChainStatusPolling() {
|
||||
}
|
||||
|
||||
const VIEWS = ['parametri', 'utenti', 'round', 'pending', 'audit'];
|
||||
const VIEW_LOADERS = { utenti: loadUsers, round: loadRounds, pending: loadPending, audit: loadAuditLog };
|
||||
let currentAdminView = 'parametri';
|
||||
|
||||
function switchView(name) {
|
||||
currentAdminView = name;
|
||||
for (const key of VIEWS) {
|
||||
document.getElementById('nav-' + key).classList.toggle('active', key === name);
|
||||
document.getElementById('view-' + key).classList.toggle('active', key === name);
|
||||
}
|
||||
const loaders = { utenti: loadUsers, round: loadRounds, pending: loadPending, audit: loadAuditLog };
|
||||
if (loaders[name]) loaders[name]();
|
||||
if (VIEW_LOADERS[name]) VIEW_LOADERS[name]();
|
||||
}
|
||||
|
||||
function showDashboard() {
|
||||
@@ -521,6 +523,31 @@ window.addEventListener('pageshow', (event) => {
|
||||
if (event.persisted) initAuthState();
|
||||
});
|
||||
|
||||
// Same server-push channel as app/static/index.html (see app/rounds/events.py):
|
||||
// a content-free "something changed" ping. Here it refreshes the chain-status
|
||||
// bar immediately, and reloads whichever admin section is currently open
|
||||
// (Utenti/Round/Transazioni pendenti/Audit log) so it doesn't need a manual
|
||||
// switch-away-and-back to pick up a new row. Polling stays in place as a
|
||||
// fallback if this connection is ever blocked or drops.
|
||||
let adminEventSource = null;
|
||||
|
||||
function onAdminServerEvent() {
|
||||
if (!adminToken) return;
|
||||
refreshChainStatus();
|
||||
if (VIEW_LOADERS[currentAdminView]) VIEW_LOADERS[currentAdminView]();
|
||||
}
|
||||
|
||||
function connectAdminEvents() {
|
||||
if (adminEventSource) return;
|
||||
adminEventSource = new EventSource('/rounds/stream');
|
||||
adminEventSource.addEventListener('update', onAdminServerEvent);
|
||||
// Fires on the initial connection AND every successful auto-reconnect —
|
||||
// re-syncs immediately instead of waiting for the next event or poll tick
|
||||
// to notice whatever changed while this connection was down.
|
||||
adminEventSource.addEventListener('open', onAdminServerEvent);
|
||||
}
|
||||
|
||||
connectAdminEvents();
|
||||
initAuthState();
|
||||
</script>
|
||||
|
||||
|
||||
+40
-1
@@ -682,8 +682,14 @@ async function showDashboard() {
|
||||
// data.winner_user_id === myUserId — otherwise that comparison could race
|
||||
// against an unset myUserId right after a reload.
|
||||
await refreshMe();
|
||||
// Awaited too, and before refreshRound(): on a brand-new browser/device that
|
||||
// never saw this round live (nothing in localStorage), this is the only
|
||||
// thing that knows the outcome once the round has fully closed. Resolving
|
||||
// it first means refreshRound() finds the answer already in place instead
|
||||
// of momentarily rendering "no result" and then flipping to the win/lose
|
||||
// box a moment later once this backstop catches up.
|
||||
await checkLastRoundResult();
|
||||
refreshRound();
|
||||
checkLastRoundResult();
|
||||
clearInterval(lastResultInterval);
|
||||
lastResultInterval = setInterval(checkLastRoundResult, 20000);
|
||||
clearInterval(roundTimerInterval);
|
||||
@@ -906,6 +912,39 @@ async function withdraw() {
|
||||
refreshMe();
|
||||
}
|
||||
|
||||
// Server push: an SSE channel that notifies the instant round/bet/balance
|
||||
// state changes anywhere (see app/rounds/events.py), instead of everyone
|
||||
// waiting for their next poll tick. The message carries no payload — it just
|
||||
// means "something changed", so we react by immediately re-running the same
|
||||
// refreshes the polling loop would eventually do on its own. Polling is left
|
||||
// completely in place as a fallback: if this connection is blocked/dropped
|
||||
// (proxy, browser setting, flaky network), the page keeps working exactly as
|
||||
// before, just without the instant nudge.
|
||||
let roundEventSource = null;
|
||||
|
||||
function onRoundServerEvent() {
|
||||
if (token) {
|
||||
refreshRound();
|
||||
refreshMe();
|
||||
checkLastRoundResult();
|
||||
} else {
|
||||
refreshChainStatusOnly();
|
||||
}
|
||||
}
|
||||
|
||||
function connectRoundEvents() {
|
||||
if (roundEventSource) return;
|
||||
roundEventSource = new EventSource('/rounds/stream');
|
||||
roundEventSource.addEventListener('update', onRoundServerEvent);
|
||||
// Fires on the initial connection AND every successful auto-reconnect (the
|
||||
// browser retries this on its own after a drop) — re-syncs immediately
|
||||
// instead of leaving the page on whatever it last knew until the next event
|
||||
// or poll tick, which would otherwise widen the "missed while disconnected"
|
||||
// window to the full reconnect gap.
|
||||
roundEventSource.addEventListener('open', onRoundServerEvent);
|
||||
}
|
||||
|
||||
connectRoundEvents();
|
||||
initAuthState();
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user