import { fetchLeaderboard, fetchPackLevels } from "../content.js"; import { localize, getFontColour, getLeaderboardLevelThumbnail } from "../util.js"; import { packScore } from "../score.js"; import Spinner from "../components/Spinner.js"; import { store } from '../main.js'; export default { components: { Spinner, }, data: () => ({ leaderboard: [], profiles: [], loading: true, selected: 0, err: [], }), template: `

Leaderboard may be incorrect, as the following levels could not be loaded: {{ err.join(', ') }}

#{{ i + 1 }}

{{ localize(ientry.total) }}

#{{ selected + 1 }} {{ entry.user }}

{{ entry.total }} points

+{{ entry.total - entry.packPoints }} from levels

+{{ entry.packPoints }} from packs

Hardest: {{ [...entry.verified, ...entry.completed].reduce((min, current) => current.rank < min.rank ? current : min).level }}

{{ localize(breakdown.total) }} pts
Verified
{{ localize(breakdown.verified) }}
Completed
{{ localize(breakdown.completed) }}
Progressed
{{ localize(breakdown.progressed) }}
Packs
{{ localize(breakdown.packs) }}
{{pack.name}}

Verified ({{ entry.verified.length}})

#{{ score.rank }}

{{ score.level }}

+{{ localize(Math.round(score.score)) }}

Completed ({{ entry.completed.length }})

#{{ score.rank }}

{{ score.level }}

+{{ localize(Math.round(score.score)) }}

Progressed ({{entry.progressed.length}})

#{{ score.rank }}

{{ score.level }}

+{{ localize(Math.round(score.score)) }}

`, computed: { entry() { return this.leaderboard[this.selected]; }, // Sums each category's scores from the raw arrays breakdown() { const e = this.entry; if (!e) return { verified: 0, completed: 0, progressed: 0, packs: 0, total: 0 }; const sum = arr => (arr || []).reduce((acc, s) => acc + Math.round(s.score || 0), 0); const verified = Math.round(sum(e.verified)); const completed = Math.round(sum(e.completed)); const progressed = Math.round(sum(e.progressed)); const packs = e.packPoints || 0; const total = verified + completed + progressed + packs; return { verified, completed, progressed, packs, total }; }, }, methods: { localize, getFontColour, getLeaderboardLevelThumbnail, // Returns stroke-dasharray string for a donut segment // circumference = 2 * π * r = 2 * π * 48 ≈ 301.59 donutDash(value, total) { const circ = 2 * Math.PI * 48; const filled = (value / total) * circ; return `${filled} ${circ - filled}`; }, // Returns stroke-dashoffset to rotate a segment into position // offset starts at 0 (top of circle after the -90deg transform) // and advances by each preceding segment's arc length donutOffset(preceding, total) { const circ = 2 * Math.PI * 48; // dashoffset is negative of how far around the circle we've gone return -((preceding / total) * circ); }, // Percentage of total, clamped 0–100 pct(value, total) { if (!total) return 0; return Math.min(100, Math.round((value / total) * 100)); }, }, async mounted() { store.leaderboard = this; await resetLeaderboard(); }, }; export async function resetLeaderboard() { store.leaderboard.loading = true; const [leaderboard, err] = await fetchLeaderboard(); // calculate pack points for (const entry of leaderboard) { let packPoints = 0; if (entry.packs && entry.packs.length) { for (const pack of entry.packs) { const packLevels = await fetchPackLevels(pack.name); const levels = packLevels .map(l => ({ listRank: l[0]?.listRank, percentToQualify: l[0]?.level?.percentToQualify ?? 0 })) .filter(l => l.listRank); packPoints += packScore(levels); } } entry.total += Math.round(packPoints); entry.packPoints = Math.round(packPoints); } // pack points resort leaderboard.sort((a, b) => b.total - a.total); for (const entry of leaderboard) { entry.verified = entry.verified.map(s => ({ ...s, score: Math.round(s.score) })); entry.completed = entry.completed.map(s => ({ ...s, score: Math.round(s.score) })); entry.progressed = entry.progressed.map(s => ({ ...s, score: Math.round(s.score) })); } store.leaderboard.leaderboard = leaderboard; store.leaderboard.err = err; store.leaderboard.loading = false; }