feat: enhance match and lobby management with player status tracking and osu! API integration
This commit is contained in:
@@ -87,23 +87,34 @@ export const load: PageServerLoad = async () => {
|
||||
|
||||
const topTeams = teamsWithDetails.filter(Boolean);
|
||||
|
||||
// ── Recent high scores (individual map scores) ──
|
||||
const recentHighScores = await db.query.matchGameScore.findMany({
|
||||
orderBy: desc(matchGameScore.score),
|
||||
limit: 15,
|
||||
with: {
|
||||
player: true,
|
||||
game: {
|
||||
with: {
|
||||
slot: true,
|
||||
match: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// ── High scores — sorted by pp (nulls last) then score ──
|
||||
const recentHighScores = await db
|
||||
.select()
|
||||
.from(matchGameScore)
|
||||
.orderBy(
|
||||
sql`${matchGameScore.pp} DESC NULLS LAST`,
|
||||
desc(matchGameScore.score)
|
||||
)
|
||||
.limit(15)
|
||||
.then((rows) =>
|
||||
Promise.all(
|
||||
rows.map((s) =>
|
||||
db.query.matchGameScore.findFirst({
|
||||
where: eq(matchGameScore.id, s.id),
|
||||
with: {
|
||||
player: true,
|
||||
game: {
|
||||
with: { slot: true, match: true }
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
const highScoresWithNames = await Promise.all(
|
||||
recentHighScores.map(async (s, i) => {
|
||||
if (!s) return null;
|
||||
const u = await db.query.user.findFirst({
|
||||
where: eq(user.id, s.player.userId)
|
||||
});
|
||||
@@ -114,6 +125,11 @@ export const load: PageServerLoad = async () => {
|
||||
score: s.score,
|
||||
accuracy: s.accuracy,
|
||||
maxCombo: s.maxCombo,
|
||||
count300: s.count300,
|
||||
count100: s.count100,
|
||||
count50: s.count50,
|
||||
countMiss: s.countMiss,
|
||||
pp: s.pp,
|
||||
mods: s.mods,
|
||||
passed: s.passed,
|
||||
beatmapId: s.game.slot?.beatmapId ?? null,
|
||||
@@ -123,7 +139,7 @@ export const load: PageServerLoad = async () => {
|
||||
matchId: s.game.matchId
|
||||
};
|
||||
})
|
||||
);
|
||||
).then((rows) => rows.filter(Boolean));
|
||||
|
||||
return {
|
||||
topPlayers: playersWithUsers,
|
||||
|
||||
@@ -187,13 +187,17 @@
|
||||
<p class="text-sm text-text-secondary">No scores recorded yet.</p>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="px-4 py-2.5 border-b border-border">
|
||||
<p class="text-xs text-text-secondary">Ranked by pp (performance points) when available, then score</p>
|
||||
</div>
|
||||
<div
|
||||
class="grid grid-cols-[auto_auto_1fr_auto_auto_auto] gap-4 border-b border-border px-4 py-3"
|
||||
class="grid grid-cols-[auto_auto_1fr_auto_auto_auto_auto] gap-3 border-b border-border px-4 py-3"
|
||||
>
|
||||
<span class="text-xs font-600 text-text-secondary">#</span>
|
||||
<span class="text-xs font-600 text-text-secondary"></span>
|
||||
<span class="text-xs font-600 text-text-secondary">Player</span>
|
||||
<span class="text-xs font-600 text-text-secondary">Map</span>
|
||||
<span class="text-xs font-600 text-accent">PP</span>
|
||||
<span class="text-xs font-600 text-text-secondary">Score</span>
|
||||
<span class="text-xs font-600 text-text-secondary">Acc</span>
|
||||
</div>
|
||||
@@ -201,7 +205,7 @@
|
||||
{#each data.highScores as s}
|
||||
{@const medal = rankBadge(s.rank)}
|
||||
<div
|
||||
class="grid grid-cols-[auto_auto_1fr_auto_auto_auto] items-center gap-4 border-b border-border/50 px-4 py-3 last:border-b-0 {s.rank <=
|
||||
class="grid grid-cols-[auto_auto_1fr_auto_auto_auto_auto] items-center gap-3 border-b border-border/50 px-4 py-3 last:border-b-0 {s.rank <=
|
||||
3
|
||||
? 'bg-accent/[0.02]'
|
||||
: ''}"
|
||||
@@ -235,6 +239,11 @@
|
||||
{s.mapCategory}{s.mapOrder}
|
||||
</span>
|
||||
|
||||
<!-- PP -->
|
||||
<span class="text-sm font-700 tabular-nums {s.pp != null ? 'text-accent' : 'text-text-secondary/40'}">
|
||||
{s.pp != null ? `${Math.round(s.pp)}pp` : '—'}
|
||||
</span>
|
||||
|
||||
<!-- Score -->
|
||||
<span class="text-sm font-700 tabular-nums text-text-primary">
|
||||
{s.score.toLocaleString()}
|
||||
|
||||
Reference in New Issue
Block a user