scoreboard-js/scoreboard/public/javascripts/indexScore.js
Julian Appel cd4877bdd2 Frontend: Reconnect-Sync, Dropdowns ohne String-Interpolation, Fehlerpfad
Alle vier Frontends horchten auf ein Event 'connected', das nie gesendet wurde;
der Server verschickte stattdessen ein Event mit dem Namen "Hello user from
server" und leerem Payload. Beides ist entfernt. An seine Stelle tritt das
Socket.IO-eigene 'connect', das auch nach einem Reconnect feuert: Adminpanel und
Monitor holen sich dort den aktuellen Stand. Da der Pi das WLAN selbst
aufspannt, sind kurze Abbrueche im Betrieb normal — bisher blieb die Anzeige
danach auf einem veralteten Stand stehen, bis das naechste Ereignis eintraf.

Die Teamnamen-Dropdowns bauten ihre Eintraege als HTML-String zusammen und
interpolierten den Namen in einen onclick-Aufruf. Ein Name mit Apostroph, etwa
"SG D'Horn", zerlegte den Handler und machte den Eintrag unbrauchbar. Die
Eintraege entstehen jetzt ueber createTeamDropdownItem() mit textContent und
addEventListener, der Name kommt per Closure statt als String. Die
Schleifenvariable ist zudem korrekt deklariert, vorher war sie global.

fetchJson() kapselt Anfragen und liefert null, wenn der Server nicht erreichbar
ist oder mit einem Fehler antwortet, statt still in der Konsole zu scheitern.
timerStart und timerPause behalten ihre eigene Auswertung des 406-Status.

Das Verbindungs-Handling ist von routes/index.js nach controllers/socketio.js
gewandert, wo der Socket.IO-Server erzeugt wird, und protokolliert jetzt auch
Verbindungsabbrueche.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 21:06:45 +02:00

123 lines
No EOL
5.9 KiB
JavaScript

let wss = "ws://" + window.location.hostname + ":3001" // Build socketio endpoint from window.location
const socket = io(wss); // Connect to socketio server
// ######################################################################################################################################################
// Websockets event handler
// ######################################################################################################################################################
// 'connect' feuert bei jeder Verbindung, auch nach einem Reconnect. Da der Pi das WLAN
// selbst aufspannt, sind kurze Abbrueche normal: danach werden Timer und Spielstand neu
// geholt, damit die Anzeige nicht auf einem veralteten Stand haengen bleibt.
socket.on('connect', () => {
console.log("Verbunden");
initialUpdate();
});
socket.on('timerDurationLeft', (message) => {
console.log(message) // Log durationLeft in client console
document.getElementById("durationLeft").innerHTML = message; // Display durationLeft in corresponding html Element
});
socket.on('timerEnded', (message) => {
document.getElementById("durationLeft").innerHTML = "ENDE"; // Display "STOP" in same html element as the duration
});
socket.on('refresh', (message) => {
document.location.reload() // Reload page on received 'refresh' messagen
});
socket.on('score', (message) => {
console.log(message) // Log score in client console
document.getElementById("score").innerHTML = message; // Display score in corresponding html Element
});
socket.on('scoreSideswitch', (message) => {
scoreGetValues(); // Update frontend when sides have been switch (switches team name sides)
});
// ######################################################################################################################################################
// General functions
// ######################################################################################################################################################
// Holt JSON von einem Endpunkt. Liefert null statt zu werfen, wenn der Server nicht
// erreichbar ist oder mit einem Fehler antwortet.
async function fetchJson(url) {
try {
const response = await fetch(url);
if(!response.ok) {
console.error("Anfrage fehlgeschlagen:", url, response.status);
return null;
}
return await response.json();
} catch (err) {
console.error("Server nicht erreichbar:", url, err);
return null;
}
}
// Update DOM timer elements from passed values
function updateTimerFrontend(values) {
document.getElementById("durationLeft").innerHTML = values.print; // Display durationLeft as prettyfied string
}
// Update DOM score elements from passed values
function updateScoreFrontend(values) {
document.getElementById("score").innerHTML = values.print; // Set score on admin interface
if(!values.sideswitch) {
// Normale Seite: teamA links, teamB rechts
if(values.teamA.isSpielgemeinschaft) {
document.getElementById("teamA").innerHTML = values.teamA.name + '<br>' + values.teamA.name2;
} else {
document.getElementById("teamA").innerHTML = values.teamA.name
}
if(values.teamB.isSpielgemeinschaft) {
document.getElementById("teamB").innerHTML = values.teamB.name + '<br>' + values.teamB.name2;
} else {
document.getElementById("teamB").innerHTML = values.teamB.name
}
} else {
// Seitenwechsel nach Halbzeit: teamA und teamB tauschen ihre Anzeigeseite
if(values.teamA.isSpielgemeinschaft) {
document.getElementById("teamB").innerHTML = values.teamA.name + '<br>' + values.teamA.name2;
} else {
document.getElementById("teamB").innerHTML = values.teamA.name
}
if(values.teamB.isSpielgemeinschaft) {
document.getElementById("teamA").innerHTML = values.teamB.name + '<br>' + values.teamB.name2;
} else {
document.getElementById("teamA").innerHTML = values.teamB.name
}
}
}
// Wird beim Laden der Seite aufgerufen, um Timer und Score sofort mit dem aktuellen
// Serverstand zu synchronisieren (verhindert leere Anzeige nach Seiten-Reload)
async function initialUpdate() {
timerGetValues(); // Request new values for timer
scoreGetValues(); // Request new values for score
}
// ######################################################################################################################################################
// Timerfunctions
// ######################################################################################################################################################
// Request important timer values
async function timerGetValues() {
const data = await fetchJson("/admin/timerGetValues"); // Call API Endpoint /admin/timerGetValues
if(data) updateTimerFrontend(data); // Update admin frontend
}
// ######################################################################################################################################################
// Scoreboardfunctions
// ######################################################################################################################################################
// Request important values for score
async function scoreGetValues() {
const data = await fetchJson("/admin/scoreGetValues"); // Call API Endpoint /admin/scoreGetValues
console.log(data); // Print received data
if(data) updateScoreFrontend(data); // Update admin frontend with received values for scoreboard
}