Seasons
This commit is contained in:
+61
-1
@@ -218,6 +218,7 @@ function renderSections() {
|
||||
const available = {
|
||||
"account-section": true,
|
||||
"leaderboard-section": true,
|
||||
"seasons-section": true,
|
||||
"chat-section": true,
|
||||
"alerts-section": true,
|
||||
"stats-section": statsLoaded,
|
||||
@@ -251,6 +252,7 @@ $("section-nav").addEventListener("click", (event) => {
|
||||
if (selectedSection === "account-section") loadMe();
|
||||
if (selectedSection === "leaderboard-section") loadLeaderboard();
|
||||
if (selectedSection === "chat-section") loadChat();
|
||||
if (selectedSection === "seasons-section") loadSeasons();
|
||||
syncPresence();
|
||||
});
|
||||
|
||||
@@ -387,8 +389,20 @@ async function refreshState() {
|
||||
const t0 = Date.now();
|
||||
const s = await api("GET", "/api/state");
|
||||
const first = !game;
|
||||
const changedSeason = game && game.season?.id !== s.season?.id;
|
||||
if (changedSeason) {
|
||||
statsLoaded = false;
|
||||
currentShareText = "";
|
||||
alertObservation = null;
|
||||
$("press-result").textContent = "";
|
||||
$("me-section").hidden = true;
|
||||
}
|
||||
clockOffset = s.now - (t0 + Date.now()) / 2;
|
||||
game = s;
|
||||
if (s.season) {
|
||||
$("season-subtitle").textContent = s.season.name;
|
||||
$("season-label").textContent = `${s.ended ? "Season finished. " : ""}Next season: ${new Date(s.season.ends_at).toISOString().slice(0, 10)} UTC.`;
|
||||
}
|
||||
observeZoneAlerts(s);
|
||||
$("history-global-summary").textContent =
|
||||
`There have been ${s.total_presses.toLocaleString()} total presses and there are ${s.total_players.toLocaleString()} total players.`;
|
||||
@@ -399,6 +413,7 @@ async function refreshState() {
|
||||
await loadLeaderboard();
|
||||
if (selectedSection === "chat-section") await loadChat();
|
||||
if (selectedSection === "account-section") await loadMe();
|
||||
if (selectedSection === "seasons-section") await loadSeasons();
|
||||
}
|
||||
|
||||
function updateButton() {
|
||||
@@ -417,7 +432,7 @@ function tick() {
|
||||
cd.textContent = formatMs(remaining);
|
||||
setZoneClass(cd, zone.name);
|
||||
setZoneClass($("the-button"), zone.name);
|
||||
$("zone-label").textContent = game.ended ? "game over" : `${zone.name} zone \u00b7 ${zone.points} pt${zone.points === 1 ? "" : "s"}`;
|
||||
$("zone-label").textContent = game.ended ? "season finished" : `${zone.name} zone \u00b7 ${zone.points} pt${zone.points === 1 ? "" : "s"}`;
|
||||
const pct = Math.min(100, Math.max(0, ((game.duration_ms - remaining) / game.duration_ms) * 100));
|
||||
$("zone-marker").style.left = `${pct}%`;
|
||||
if (!game.ended && remaining <= 0) refreshState().catch(() => {});
|
||||
@@ -513,6 +528,49 @@ async function loadMe() {
|
||||
}
|
||||
}
|
||||
|
||||
function seasonDuration(ms) {
|
||||
const days = Math.floor(ms / 86400000);
|
||||
return `${days ? `${days}d ` : ""}${formatMs(ms % 86400000).slice(0, -3)}`;
|
||||
}
|
||||
|
||||
async function loadSeasons() {
|
||||
try {
|
||||
const result = await api("GET", "/api/seasons");
|
||||
const current = result.current;
|
||||
$("season-current").replaceChildren(
|
||||
el("p", "CURRENT SEASON", "eyebrow"), el("h3", current.name),
|
||||
el("p", `${current.ended ? "Finished - next countdown" : "Next season"}: ${new Date(current.ends_at).toISOString().slice(0, 10)} at 00:00 UTC`, "fine-print"),
|
||||
);
|
||||
$("seasons-status").textContent = result.seasons.length ? "" : "No finished seasons yet. This chapter is still being written.";
|
||||
$("season-winners").hidden = result.seasons.length === 0;
|
||||
fillTable($("season-winners"), ["Season", "Winner(s)", "Points each", "Lasted"], result.seasons.map((season) => [
|
||||
season.name, season.winners.map((p) => p.name).join(", ") || "No winner (no presses)",
|
||||
season.winners.length ? season.winners[0].total_points.toLocaleString() : "--", seasonDuration(season.duration_ms),
|
||||
]));
|
||||
const container = $("season-standings");
|
||||
// Do not replace focused/open archive details on every poll.
|
||||
const signature = JSON.stringify(result.seasons);
|
||||
if (container.dataset.signature !== signature) {
|
||||
container.dataset.signature = signature;
|
||||
container.replaceChildren(...result.seasons.map((season) => {
|
||||
const details = el("details");
|
||||
details.append(el("summary", `${season.name} - final standings`));
|
||||
details.append(el("p", `Played from ${new Date(season.played_from).toLocaleString()} until ${new Date(season.finished_at).toLocaleString()}. Ended by ${season.reason === "boundary" ? "the next season" : "the countdown"}.`, "fine-print"));
|
||||
const wrapper = el("div", undefined, "table-scroll");
|
||||
const table = el("table");
|
||||
fillTable(table, ["#", "Player", "Points", "Presses"], season.leaderboard.map((player, index) => [
|
||||
index + 1, player.name, player.total_points.toLocaleString(), player.press_count.toLocaleString(),
|
||||
]));
|
||||
wrapper.append(table);
|
||||
details.append(wrapper);
|
||||
return details;
|
||||
}));
|
||||
}
|
||||
} catch {
|
||||
$("seasons-status").textContent = "Seasons could not be refreshed. Retrying automatically...";
|
||||
}
|
||||
}
|
||||
|
||||
async function loadLeaderboard() {
|
||||
try {
|
||||
const { leaderboard } = await api("GET", "/api/leaderboard");
|
||||
@@ -592,8 +650,10 @@ $("chat-form").addEventListener("submit", async (event) => {
|
||||
});
|
||||
|
||||
async function loadStats() {
|
||||
const seasonId = game?.season?.id;
|
||||
try {
|
||||
const s = await api("GET", "/api/stats");
|
||||
if (game?.season?.id !== seasonId) return;
|
||||
statsLoaded = true;
|
||||
renderSections();
|
||||
const summary = $("stats-summary");
|
||||
|
||||
Reference in New Issue
Block a user