Leaderboard and dynamic layout
This commit is contained in:
+58
-5
@@ -9,6 +9,40 @@ let token = localStorage.getItem(TOKEN_KEY);
|
||||
let statsLoaded = false;
|
||||
let subscribed = false;
|
||||
let currentShareText = "";
|
||||
let selectedSection = null;
|
||||
|
||||
function renderSections() {
|
||||
const available = {
|
||||
"account-section": true,
|
||||
"leaderboard-section": true,
|
||||
"stats-section": statsLoaded,
|
||||
rules: true,
|
||||
"mailing-list": !!token && !subscribed,
|
||||
"share-controls": !!currentShareText,
|
||||
more: true,
|
||||
donate: true,
|
||||
};
|
||||
if (!available[selectedSection]) selectedSection = null;
|
||||
document.querySelectorAll("[data-section]").forEach((button) => {
|
||||
const id = button.dataset.section;
|
||||
button.hidden = !available[id];
|
||||
button.setAttribute("aria-expanded", String(selectedSection === id));
|
||||
if (id === "account-section") button.textContent = token ? "Your score" : "Log in / Join";
|
||||
});
|
||||
document.querySelectorAll("[data-panel]").forEach((panel) => {
|
||||
panel.hidden = panel.id !== selectedSection;
|
||||
});
|
||||
$("section-hint").hidden = selectedSection !== null;
|
||||
}
|
||||
|
||||
$("section-nav").addEventListener("click", (event) => {
|
||||
const button = event.target.closest("[data-section]");
|
||||
if (!button) return;
|
||||
selectedSection = selectedSection === button.dataset.section ? null : button.dataset.section;
|
||||
renderSections();
|
||||
if (selectedSection === "account-section") loadMe();
|
||||
if (selectedSection === "leaderboard-section") loadLeaderboard();
|
||||
});
|
||||
|
||||
async function api(method, path, body) {
|
||||
const headers = { "Content-Type": "application/json" };
|
||||
@@ -150,13 +184,15 @@ async function refreshState() {
|
||||
if (first) buildStaticUi();
|
||||
if (game.ended && !statsLoaded) loadStats();
|
||||
updateButton();
|
||||
await loadLeaderboard();
|
||||
}
|
||||
|
||||
function updateButton() {
|
||||
$("the-button").disabled = !token || !game || game.ended;
|
||||
$("register-section").hidden = !!token;
|
||||
$("mailing-list").hidden = !token || subscribed;
|
||||
$("me-section").hidden = !token;
|
||||
$("my-subscribed").hidden = !token || !subscribed;
|
||||
renderSections();
|
||||
}
|
||||
|
||||
function tick() {
|
||||
@@ -179,7 +215,6 @@ async function loadMe() {
|
||||
if (!token) return;
|
||||
try {
|
||||
const me = await api("GET", "/api/me");
|
||||
$("me-section").hidden = false;
|
||||
$("my-name").textContent = me.name;
|
||||
$("my-points").textContent = me.total_points.toLocaleString();
|
||||
$("my-presses").textContent = me.press_count;
|
||||
@@ -202,11 +237,28 @@ async function loadMe() {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadLeaderboard() {
|
||||
try {
|
||||
const { leaderboard } = await api("GET", "/api/leaderboard");
|
||||
fillTable(
|
||||
$("leaderboard"),
|
||||
["Rank", "Player", "Points", "Presses"],
|
||||
leaderboard.map((player, index) => [
|
||||
index + 1, player.name, player.total_points.toLocaleString(), player.press_count.toLocaleString(),
|
||||
]),
|
||||
);
|
||||
$("leaderboard").hidden = leaderboard.length === 0;
|
||||
$("leaderboard-status").textContent = leaderboard.length ? "" : "No players yet. Be the first to join!";
|
||||
} catch {
|
||||
$("leaderboard-status").textContent = "Scores could not be refreshed. Retrying automatically...";
|
||||
}
|
||||
}
|
||||
|
||||
async function loadStats() {
|
||||
try {
|
||||
const s = await api("GET", "/api/stats");
|
||||
statsLoaded = true;
|
||||
$("stats-section").hidden = false;
|
||||
renderSections();
|
||||
const summary = $("stats-summary");
|
||||
summary.replaceChildren(
|
||||
el("p", `Lasted ${formatMs(s.ended_at - s.started_at)} \u00b7 ${s.total_players} players \u00b7 ${s.total_presses} presses`),
|
||||
@@ -242,8 +294,8 @@ async function loadStats() {
|
||||
$("the-button").addEventListener("click", async () => {
|
||||
const btn = $("the-button");
|
||||
btn.disabled = true;
|
||||
$("share-controls").hidden = true;
|
||||
currentShareText = "";
|
||||
renderSections();
|
||||
try {
|
||||
const r = await api("POST", "/api/press");
|
||||
game.deadline = r.deadline;
|
||||
@@ -251,8 +303,8 @@ $("the-button").addEventListener("click", async () => {
|
||||
$("press-result").textContent =
|
||||
`You pressed in the ${r.press.zone} zone with ${formatPrecise(r.press.remaining_ms)} remaining: +${r.press.points} points`;
|
||||
updateShareLinks();
|
||||
$("share-controls").hidden = false;
|
||||
await loadMe();
|
||||
await loadLeaderboard();
|
||||
} catch (e) {
|
||||
$("press-result").textContent = e.message;
|
||||
await refreshState().catch(() => {});
|
||||
@@ -277,6 +329,7 @@ $("register-form").addEventListener("submit", async (ev) => {
|
||||
$("password-input").value = "";
|
||||
updateButton();
|
||||
await loadMe();
|
||||
await loadLeaderboard();
|
||||
} catch (e) {
|
||||
$("register-error").textContent = e.message;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user