| |
| |
| |
|
|
| import { Auth } from '../auth/Auth.js'; |
| import { Leaderboard } from '../auth/Leaderboard.js'; |
|
|
| export class LeaderboardUI { |
| constructor() { |
| this._interval = null; |
| } |
|
|
| $(id) { return document.getElementById(id); } |
|
|
| startAutoRefresh() { |
| if (this._interval) clearInterval(this._interval); |
| this._interval = setInterval(() => { Leaderboard.bumpPeers(); this.render(); }, 12000); |
| } |
|
|
| refreshUserChip() { |
| const u = Auth.current(); |
| if (u) { |
| this.$('userName').textContent = u.name || u.email.split('@')[0]; |
| this.$('userAvatar').textContent = (u.name || u.email).slice(0, 1).toUpperCase(); |
| this.$('signOutBtn').style.display = ''; |
| |
| this.$('t-stars').textContent = (u.stars || 0).toLocaleString(); |
| } else { |
| this.$('userName').textContent = 'Guest'; |
| this.$('userAvatar').textContent = 'G'; |
| this.$('signOutBtn').style.display = 'none'; |
| this.$('t-stars').textContent = '0'; |
| } |
| } |
|
|
| render() { |
| const u = Auth.current(); |
| const rows = Leaderboard.combinedRanks(u); |
| const top = rows.slice(0, 8); |
| const meRow = rows.find(r => r.me); |
| if (meRow && !top.find(r => r.me)) top.push(meRow); |
|
|
| const html = top.map(r => { |
| const rankCls = r.rank <= 3 ? ` r${r.rank}` : ''; |
| const meCls = r.me ? ' me' : ''; |
| const rankIcon = r.rank <= 3 ? ['🥇', '🥈', '🥉'][r.rank - 1] : r.rank; |
| return `<div class="lb-row${rankCls}${meCls}"> |
| <div class="rank">${rankIcon}</div> |
| <div class="who"><div class="a">${r.avatar || r.name.slice(0, 1).toUpperCase()}</div><div class="n">${r.me ? '<b>You</b>' : r.name}</div></div> |
| <div class="pts">⭐ ${r.stars.toLocaleString()}</div> |
| </div>`; |
| }).join(''); |
| this.$('lbList').innerHTML = html; |
| this.$('lb-total').textContent = rows.length.toLocaleString(); |
| this.$('lb-globalStars').textContent = rows.reduce((s, r) => s + r.stars, 0).toLocaleString(); |
| this.$('onlineCount').textContent = Leaderboard.onlineCount(); |
| if (u && meRow) this.$('userRank').textContent = `Rank #${meRow.rank} · ⭐ ${u.stars || 0}`; |
| else this.$('userRank').textContent = u ? `⭐ ${u.stars || 0}` : 'Guest mode'; |
| } |
|
|
| bumpUserStats(deltaStars, deltaMastered, currentLen) { |
| const u = Auth.current(); |
| if (!u) return; |
| const patch = { |
| stars: (u.stars || 0) + deltaStars, |
| mastered: (u.mastered || 0) + deltaMastered, |
| grade: currentLen, |
| }; |
| Auth.update(patch); |
| |
| Auth.saveProgress(patch).catch(() => {}); |
| this.refreshUserChip(); |
| this.render(); |
| const fresh = Auth.current(); |
| const el = document.getElementById('t-stars'); |
| el.textContent = (fresh.stars || 0).toLocaleString(); |
| el.classList.remove('bump'); void el.offsetWidth; el.classList.add('bump'); |
| } |
| } |
|
|