feat : add profile page
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
# ADR 0003: Personal guild profile read model
|
||||
|
||||
- Status: Accepted
|
||||
- Date: 2026-08-06
|
||||
|
||||
## Context
|
||||
|
||||
Signed-in members need one private page that combines their Discord identity with XP,
|
||||
level progress, voice time, and leaderboard standing. The Discord bot remains the
|
||||
authoritative writer for `user_levels` and `voice_time`, while the website has only a
|
||||
read-only connection to those tables. XP values use `numeric(40,0)`, which may exceed
|
||||
JavaScript's safe integer range.
|
||||
|
||||
## Decision
|
||||
|
||||
1. `/profile` identifies the current member from the server-side NextAuth session and
|
||||
Discord provider account. Anonymous visitors receive a Discord login prompt that
|
||||
returns them to `/profile`; profiles cannot be selected by URL parameter.
|
||||
2. Discord identity, XP, and voice data load independently. Session identity is the
|
||||
fallback when Discord member metadata is unavailable, and unavailable activity
|
||||
sources report section-level errors.
|
||||
3. The stored bot level remains authoritative. Next-level progress duplicates the
|
||||
bot's documented XP formula and performs all XP arithmetic with `bigint` before
|
||||
converting the bounded percentage to a number. Level 200 is the maximum and does
|
||||
not display a progress bar.
|
||||
4. XP and voice ranks use the public leaderboards' shared competition ranking. XP
|
||||
rank excludes level-0 members. Voice time and rank include only voice and Stage
|
||||
channels that currently exist in the guild.
|
||||
5. Missing activity rows display zero activity and no rank. The website does not
|
||||
create rows or write inferred values back to the bot database.
|
||||
6. One public leaderboard SSE subscription refreshes an open profile after either an
|
||||
XP event or a voice-time event. Redis remains a notification path rather than the
|
||||
source of truth.
|
||||
|
||||
## Consequences
|
||||
|
||||
- A signed-in member sees only the profile associated with their own Discord account.
|
||||
- Profile totals and public leaderboard values share ranking and voice-channel scope.
|
||||
- The bot formula is a cross-service contract. Formula or maximum-level changes must
|
||||
update the bot integration document and website helper together.
|
||||
- Large cumulative XP values remain exact through database reads, calculations, and
|
||||
formatting.
|
||||
@@ -63,3 +63,15 @@ tables, combined with current Discord channel metadata.
|
||||
|
||||
A minimal Redis notification on `erika:sse:leaderboards` that tells an open XP or VC
|
||||
page to refresh its Server Component data after a bot transaction commits.
|
||||
|
||||
## Guild profile
|
||||
|
||||
The private `/profile` view for the currently authenticated Discord member. It joins
|
||||
Discord identity and roles with that member's level, XP, current-channel voice time,
|
||||
and leaderboard ranks without exposing a public member-profile URL.
|
||||
|
||||
## Level progress
|
||||
|
||||
The member's XP accumulated since reaching their stored level, measured against the
|
||||
bot's XP requirement for the next level. Level 200 is the maximum and has no next-level
|
||||
progress bar.
|
||||
|
||||
@@ -3,6 +3,28 @@
|
||||
The Discord bot owns all writes to `user_levels` and `voice_time`. The website only
|
||||
subscribes to notifications and then reads the committed database state.
|
||||
|
||||
## Level formula contract
|
||||
|
||||
The website's personal profile calculates next-level progress from the bot's stored
|
||||
level and cumulative XP. Both services must use this exact formula:
|
||||
|
||||
```text
|
||||
required(0) = 500
|
||||
required(level) = required(level - 1) + 100 + (level * 25)
|
||||
total(0) = 0
|
||||
total(level) = total(level - 1) + required(level - 1)
|
||||
```
|
||||
|
||||
`required(level)` is the XP needed to advance from that level to the next. `total(level)`
|
||||
is the cumulative XP needed to reach that level. Level 200 is the maximum; the XP
|
||||
requirement caps at the level-199 value and the profile does not show next-level
|
||||
progress at the cap.
|
||||
|
||||
Store cumulative XP as an integer value and update `xp` and `level` in the same bot
|
||||
transaction. Changing the formula or maximum level requires a coordinated website
|
||||
update because the website intentionally does not infer the formula from database
|
||||
metadata.
|
||||
|
||||
## Redis channel
|
||||
|
||||
Publish leaderboard notifications to exactly:
|
||||
|
||||
Reference in New Issue
Block a user