From 458d04e2788ca129c3f63dad9ac29c3dc7db58be Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 22 May 2026 15:12:01 +0100 Subject: [PATCH] split account modal into multiple tabs (#3986) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description: The account modal was getting too large, too much scrolling. Screenshot 2026-05-22 at 2 23 23 PM Screenshot 2026-05-22 at 2 23 33 PM Screenshot 2026-05-22 at 2 23 42 PM ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: evan --- resources/lang/en.json | 7 +- src/client/AccountModal.ts | 159 +++++++++++++++++++++++-------------- 2 files changed, 107 insertions(+), 59 deletions(-) diff --git a/resources/lang/en.json b/resources/lang/en.json index 80df945c6..9bc8fe3f6 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -380,7 +380,12 @@ "clear_session": "Clear Session", "failed_to_send_recovery_email": "Failed to send recovery email", "enter_email_address": "Please enter an email address", - "public_player_id": "Public Player ID:" + "public_player_id": "Public Player ID:", + "tab_account": "Account", + "tab_stats": "Stats", + "tab_games": "Games", + "no_stats": "No stats available yet. Play some games to start tracking.", + "no_games": "No games played yet." }, "leaderboard_modal": { "title": "Leaderboard", diff --git a/src/client/AccountModal.ts b/src/client/AccountModal.ts index 2bfae22b3..2b547720d 100644 --- a/src/client/AccountModal.ts +++ b/src/client/AccountModal.ts @@ -93,81 +93,124 @@ export class AccountModal extends BaseModal { }); } - protected renderBody() { + private isLinkedAccount(): boolean { + const me = this.userMeResponse?.user; + return !!(me?.discord ?? me?.email); + } + + protected modalConfig() { + if (this.isLoadingUser || !this.isLinkedAccount()) { + return {}; + } + return { + tabs: [ + { key: "account", label: translateText("account_modal.tab_account") }, + { key: "stats", label: translateText("account_modal.tab_stats") }, + { key: "games", label: translateText("account_modal.tab_games") }, + ], + }; + } + + protected renderBody(tab: string) { if (this.isLoadingUser) { return this.renderLoadingSpinner( translateText("account_modal.fetching_account"), ); } - const isLoggedIn = !!this.userMeResponse?.user; + if (!this.isLinkedAccount()) { + return html`
+ ${this.renderLoginOptions()} +
`; + } return html`
- ${isLoggedIn ? this.renderAccountInfo() : this.renderLoginOptions()} +
${this.renderTab(tab)}
`; } - private renderAccountInfo() { - const me = this.userMeResponse?.user; - const isLinked = me?.discord ?? me?.email; - - if (!isLinked) { - return this.renderLoginOptions(); + private renderTab(tab: string): TemplateResult { + switch (tab) { + case "stats": + return this.renderStatsTab(); + case "games": + return this.renderGamesTab(); + default: + return this.renderAccountTab(); } + } + private renderAccountTab(): TemplateResult { return html` -
-
- -
-
-
- ${translateText("account_modal.connected_as")} -
-
- - ${this.renderLoggedInAs()} -
+
+
+
+
+ ${translateText("account_modal.connected_as")} +
+
+ + ${this.renderLoggedInAs()}
- - ${this.renderSubscriptionPanel()} - - - ${this.hasAnyStats() - ? html`
-

- 📊 - ${translateText("account_modal.stats_overview")} -

- -
` - : ""} - - -
-

- 🎮 - ${translateText("game_list.recent_games")} -

- void this.viewGame(id)} - > -
+ ${this.renderSubscriptionPanel()} +
+ `; + } + + private renderStatsTab(): TemplateResult { + if (!this.hasAnyStats()) { + return this.renderEmptyState( + "📊", + translateText("account_modal.no_stats"), + ); + } + return html` +
+

+ 📊 + ${translateText("account_modal.stats_overview")} +

+ +
+ `; + } + + private renderGamesTab(): TemplateResult { + if (this.recentGames.length === 0) { + return this.renderEmptyState( + "🎮", + translateText("account_modal.no_games"), + ); + } + return html` +
+

+ 🎮 + ${translateText("game_list.recent_games")} +

+ void this.viewGame(id)} + > +
+ `; + } + + private renderEmptyState(icon: string, message: string): TemplateResult { + return html` +
+
${icon}
+

${message}

`; }