Skip to content

✨ Add XP leaderboard: global/friends, month/year (G7) - #190

Merged
Logan2234 merged 8 commits into
mainfrom
feat/g7-leaderboard
Sep 5, 2026
Merged

✨ Add XP leaderboard: global/friends, month/year (G7)#190
Logan2234 merged 8 commits into
mainfrom
feat/g7-leaderboard

Conversation

@Logan2234

Copy link
Copy Markdown
Owner

Summary

Ranks by summed XP over a calendar month or year, in two scopes: global and friends. Gated behind both SOCIAL_ENABLED and GAMIFICATION_ENABLED (new GamificationFeatureGuard, same 404-not-403 pattern as SocialFeatureGuard) — a leaderboard needs XP to exist and other users to rank against, so either flag being off hides the whole surface.

Ranking happens in plain JS rather than a SQL window function (no raw query exists anywhere else in this codebase): ties share a rank and the next distinct rank skips ahead by the tie's size (1, 2, 2, 4), older account wins a tie. Top 100 shown; the viewer's own row is returned separately when it falls outside that cut, never duplicated. A PRIVATE row's real avatar only shows to a viewer who follows them — everyone else sees the same identicon a public account without a photo would show, so a row never signals that an account is private.

Frontend: new /app/leaderboard page (tabs, month/year filter, loading skeleton, animate:flip), a crown nav entry between Stats and Feed (added a gamification gate alongside the existing social one across every nav skin — rail, bottom bar, filmstrip, launcher sheet), a "Voir le classement" link in the profile's XP card (own profile only), and "Nouveau" badges on both the achievements and leaderboard page titles.

Test plan

  • 9 new unit tests on LeaderboardService (tie ranking, Top 100 cutoff, private-avatar masking, GHOST/hideProgression exclusion, friends scope, period boundaries) — all passing.
  • pnpm --recursive check (pre-push hook) passes clean across packages/shared, apps/api, apps/web.
  • Manual click-through of /app/leaderboard (tabs, period filter, own-row highlight, deep link to /app/u/[username]) and the new profile card link, both themes, mobile viewport.

Related

[G7] on Quackback's internal roadmap.

Checklist

  • Tests added/updated for the behavior this changes
  • New user-facing strings added to both fr and en message catalogs
  • pnpm build:package run (packages/shared changed)
  • A Prisma migration is included if schema.prisma changed — n/a, no schema change
  • No --no-verify — hooks ran clean

🤖 Generated with Claude Code

Logan2234 and others added 4 commits September 5, 2026 00:33
Ranks by summed XP over a calendar month or year, gated behind both
SOCIAL_ENABLED and GAMIFICATION_ENABLED (new GamificationFeatureGuard,
same 404-not-403 pattern as SocialFeatureGuard). Lives in SocialModule
rather than GamificationModule to avoid a circular import (Social
already imports Gamification for AchievementService), and leans on a
new FollowService.listFriendIds() for the friends scope.

Ranking happens in JS rather than a SQL window function (no raw query
exists elsewhere in this codebase): ties share a rank and the next
distinct rank skips ahead by the tie's size, older account wins a tie.
Top 100 shown, viewer's own row returned separately when it falls
outside that cut. A PRIVATE row's real avatar only shows to a viewer
who follows them — everyone else sees the same identicon a public
account without a photo would show, so a leaderboard row never signals
that an account is private.

Frontend: new /app/leaderboard page (tabs, month/year filter, skeleton,
animate:flip), a crown nav entry between Stats and Feed (gamification
flag added alongside the existing social one across every nav skin), a
"Voir le classement" link in the profile's XP card, and "Nouveau"
badges on both the achievements and leaderboard page titles.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation ci CI/CD, GitHub Actions workflows api apps/api (NestJS) web apps/web (SvelteKit) shared packages/shared labels Sep 5, 2026
Logan2234 and others added 4 commits September 5, 2026 12:14
The approved mockup ("Bobine") had a gold/silver/bronze gradient tint
+ colored rank number on the top 3, and a left accent stripe on your
own row — both were lost between the mockup and the actual page, which
shipped with a plain flat list. Reuses the existing tier-gold/silver/
bronze tokens (same ones the achievement medallions use) rather than
inventing new colors. Divider switched to dashed to match the
"pellicule" look too.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The XP figure was one flat string ("4 820 XP") — split it into a bold,
larger number and a small dim "xp" unit, matching the mockup. Row
hover also had no transition (background snapped instantly); added a
150ms color transition.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A brand-new account got to ~level 2.8 the moment it added its very
first item: WORK_ADDED (2 XP) + DOMAIN_STARTED (100 XP, a one-off
per-domain milestone) = 102 XP, comfortably past the old level 2
threshold (52 XP). Rather than shrink DOMAIN_STARTED, raised
LEVEL_BASE 40 -> 100 (LEVEL_STEP unchanged), so level 2 now costs 112
XP — the single biggest freebie in the game no longer levels you up
by itself. LEVEL_CAP_LEVEL/XP_AT_CAP_LEVEL recomputed to keep the
quadratic and capped branches meeting exactly at the boundary (now
75 -> 76 instead of 80 -> 81), and level.util.spec.ts's thresholds
updated to match — all 11 cases still pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@Logan2234
Logan2234 merged commit 040b4b9 into main Sep 5, 2026
19 of 20 checks passed
@Logan2234
Logan2234 deleted the feat/g7-leaderboard branch September 5, 2026 11:28
@Logan2234
Logan2234 restored the feat/g7-leaderboard branch September 5, 2026 11:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api apps/api (NestJS) ci CI/CD, GitHub Actions workflows documentation Improvements or additions to documentation shared packages/shared web apps/web (SvelteKit)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant