Switch to Long-Polling, add Turniertabellen-Anzeige für ODJM D 2026

This commit is contained in:
2026-05-24 15:32:36 +02:00
parent 2ad3dab7f8
commit 7efa38c91a
4 changed files with 253 additions and 55 deletions

88
app.js
View File

@@ -3,37 +3,40 @@
* Haupt-Application
*/
const PGN_URL = 'https://www.deutsche-schachjugend.de/2026/odjm-d/partien/gesamt-utf8.pgn';
const REFRESH_INTERVAL = 10000; // 10 Sekunden
const PLAYER_NAME = 'Kiesewetter, Lara';
let board = null;
let chess = null;
let currentGame = null;
let allLaraGames = [];
let refreshTimer = null;
let countdown = 0;
let serverLastFetch = null;
let laraColor = null;
let currentMoveIndex = -1;
let userSelectedGame = false;
let userScrolledMoves = false;
let lastMtime = 0;
let pollId = 0;
/**
* Lädt die PGN-Datei und aktualisiert die Anzeige
*/
async function loadPGN(showOverlay = true) {
const currentPollId = ++pollId;
if (showOverlay) showLoading(true);
hideError();
try {
const [pgnResponse, statusResponse] = await Promise.all([
fetch('http://localhost:8111/pgn'),
fetch(`http://localhost:8111/pgn?since=${lastMtime}`),
fetch('http://localhost:8111/status').catch(() => null)
]);
if (currentPollId !== pollId) return;
if (!pgnResponse.ok) throw new Error(`HTTP ${pgnResponse.status}`);
const mtimeHeader = pgnResponse.headers.get('X-Cache-Mtime');
if (mtimeHeader) lastMtime = parseFloat(mtimeHeader);
if (statusResponse && statusResponse.ok) {
const status = await statusResponse.json();
serverLastFetch = status.last_fetch ? status.last_fetch * 1000 : null;
@@ -62,10 +65,12 @@ async function loadPGN(showOverlay = true) {
updateMovesList();
updateAllGamesList();
updateTimestamp();
updateStandings();
showLoading(false);
} catch (error) {
if (currentPollId !== pollId) return;
console.error('Fehler beim Laden:', error);
showError(`Fehler: ${error.message}`);
showLoading(false);
@@ -367,6 +372,49 @@ function updateAllGamesList() {
}
}
/**
* Lädt die Turniertabelle vom Proxy und zeigt Laras Platzierung an
*/
function updateStandings() {
fetch('http://localhost:8111/standings')
.then(res => {
if (!res.ok) throw new Error('Fehler beim Laden');
return res.json();
})
.then(data => {
const container = document.getElementById('standings-content');
if (!data || data.error) {
container.innerHTML = '<div class="standings-loading">Daten nicht verfügbar</div>';
return;
}
container.innerHTML = `
<div class="standings-rank">${data.rank}.</div>
<div class="standings-rank-label">Tabellenplatz</div>
<div class="standings-header">${data.round_info || 'nach Runde 1'}</div>
<div class="standings-row">
<span class="standings-label">Punkte</span>
<span class="standings-value">${data.points}</span>
</div>
<div class="standings-row">
<span class="standings-label">Siege</span>
<span class="standings-value">${data.wins}</span>
</div>
<div class="standings-row">
<span class="standings-label">Unentschieden</span>
<span class="standings-value">${data.draws}</span>
</div>
<div class="standings-row">
<span class="standings-label">Niederlagen</span>
<span class="standings-value">${data.losses}</span>
</div>
`;
})
.catch(err => {
document.getElementById('standings-content').innerHTML =
'<div class="standings-loading">Daten nicht verfügbar</div>';
});
}
/**
* Format clock string
*/
@@ -386,25 +434,19 @@ function updateTimestamp() {
}
/**
* Start auto-refresh
* Start long-polling: nach jeder Antwort sofort die nächste Anfrage stellen
*/
function startAutoRefresh() {
countdown = REFRESH_INTERVAL / 1000;
document.getElementById('refresh-timer').textContent = '● Live';
document.getElementById('refresh-timer').style.color = '#4ade80';
if (refreshTimer) clearInterval(refreshTimer);
refreshTimer = setInterval(() => {
countdown--;
const mins = Math.floor(countdown / 60);
const secs = countdown % 60;
document.getElementById('refresh-timer').textContent =
`Nächstes Update in: ${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
if (countdown <= 0) {
countdown = REFRESH_INTERVAL / 1000;
loadPGN(false);
async function poll() {
while (true) {
await loadPGN(false);
await new Promise(r => setTimeout(r, 30000));
}
}, 1000);
}
poll();
}
/**
@@ -424,11 +466,11 @@ function hideError() {
}
/**
* Manual refresh button
* Manual refresh button startet neuen Long-Poll-Zyklus
*/
document.getElementById('refresh-btn').addEventListener('click', () => {
countdown = REFRESH_INTERVAL / 1000;
loadPGN();
pollId++;
loadPGN(true);
});
/**