lorem_ipsum/js/leaderboard.js
2026-04-22 10:23:35 +02:00

165 lines
4.5 KiB
JavaScript

// Formatiert Sekunden als m:ss. Bei ungültigem Wert wird ein Platzhalter angezeigt.
function formatTime(seconds) {
if (typeof seconds !== "number" || Number.isNaN(seconds)) {
return "-";
}
const minutes = Math.floor(seconds / 60);
const remainingSeconds = Math.floor(seconds % 60);
return `${minutes}:${String(remainingSeconds).padStart(2, "0")}`;
}
// Liefert den aktuellen Login-Kontext, falls Auth global verfügbar ist.
function getLoggedInAuth() {
if (!window.AppAuth || typeof window.AppAuth.getAuth !== "function") {
return null;
}
const auth = window.AppAuth.getAuth();
if (!auth || !auth.username) {
return null;
}
return auth;
}
// Vereinheitlicht Benutzernamen für robuste Vergleiche (z. B. Groß-/Kleinschreibung).
function normalizeUsername(username) {
return String(username ?? "").trim().toLowerCase();
}
function getLoggedInUsername() {
const auth = getLoggedInAuth();
if (!auth) {
return null;
}
return normalizeUsername(auth.username);
}
// Nutzt den vom Backend gelieferten Rang, fallback auf die aktuelle Listenposition.
function getDisplayedRank(entry, index) {
const place = Number(entry?.place);
if (!Number.isNaN(place) && place > 0) {
return place;
}
return index + 1;
}
// Bestes Ergebnis: höchste Punktzahl, bei Gleichstand die geringere Zeit.
function getBestScoreEntry(entries) {
return entries
.slice()
.sort((a, b) => {
const scoreA = Number(a.score ?? 0);
const scoreB = Number(b.score ?? 0);
if (scoreB !== scoreA) {
return scoreB - scoreA;
}
const timeA = Number(a.time ?? Number.MAX_SAFE_INTEGER);
const timeB = Number(b.time ?? Number.MAX_SAFE_INTEGER);
return timeA - timeB;
})[0] ?? null;
}
async function getCurrentUserLeaderboardEntry(username) {
if (!window.ScoreService || !username) {
return null;
}
const scoreService = new window.ScoreService(window.config);
const result = await scoreService.getScoreByName(username);
if (!result.ok || !Array.isArray(result.body) || result.body.length === 0) {
return null;
}
return getBestScoreEntry(result.body);
}
// Rendert die Top-Liste und markiert den eingeloggten Nutzer visuell.
function renderLeaderboard(entries, extraUserEntry = null) {
const tableBody = document.getElementById("leaderboard-body");
if (!tableBody) {
return;
}
const loggedInUsername = getLoggedInUsername();
tableBody.innerHTML = "";
entries.forEach((entry, index) => {
const row = document.createElement("tr");
const rowUsername = normalizeUsername(entry.username);
if (loggedInUsername && rowUsername === loggedInUsername) {
row.classList.add("leaderboard-row-current-user");
}
row.innerHTML = `
<td>${getDisplayedRank(entry, index)}</td>
<td>${entry.username ?? "-"}</td>
<td>${formatTime(entry.time)} min</td>
<td>${entry.score ?? "-"}</td>
`;
tableBody.appendChild(row);
});
if (extraUserEntry) {
// Trennt Top-10 und eigenen Eintrag optisch, wenn der Nutzer nicht in den Top-10 ist.
const spacerRow = document.createElement("tr");
spacerRow.classList.add("leaderboard-row-gap");
spacerRow.innerHTML = '<td colspan="4"></td>';
tableBody.appendChild(spacerRow);
const userRow = document.createElement("tr");
userRow.classList.add("leaderboard-row-current-user");
userRow.classList.add("leaderboard-row-current-user-extra");
userRow.innerHTML = `
<td>${getDisplayedRank(extraUserEntry, 0)}</td>
<td>${extraUserEntry.username ?? "-"}</td>
<td>${formatTime(extraUserEntry.time)} min</td>
<td>${extraUserEntry.score ?? "-"}</td>
`;
tableBody.appendChild(userRow);
}
}
async function loadTopTenLeaderboard() {
const leaderboardService = new window.LeaderboardService(window.config);
const result = await leaderboardService.getLeaderboard(0, 10);
if (!result.ok || !Array.isArray(result.body)) {
renderLeaderboard([]);
return;
}
const auth = getLoggedInAuth();
let extraUserEntry = null;
if (auth && auth.username) {
const loggedInUsername = normalizeUsername(auth.username);
// Falls der Nutzer nicht in den Top-10 erscheint, wird sein bestes Ergebnis separat gezeigt.
const isInTopTen = result.body.some(
(entry) => normalizeUsername(entry.username) === loggedInUsername,
);
if (!isInTopTen) {
extraUserEntry = await getCurrentUserLeaderboardEntry(auth.username);
}
}
renderLeaderboard(result.body, extraUserEntry);
}
window.initLeaderboardPage = function initLeaderboardPage() {
loadTopTenLeaderboard().catch((error) => {
console.error("Fehler beim Laden des Leaderboards:", error);
renderLeaderboard([]);
});
};