From 0fc80e931ef56a9ac809e58a2222e853f0ed5436 Mon Sep 17 00:00:00 2001
From: Ryan <7389646+ryanbarlow97@users.noreply.github.com>
Date: Fri, 17 Jul 2026 16:49:44 +0100
Subject: [PATCH] convert stats modal to embed instead of popup (#4626)
## Description:
convert stats modal to embed instead of popup
before:
after:
also has context to remember where we were in the scrolled list, so
pressing "back" keeps the position
## 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
## Please put your Discord username so you can be contacted if a bug or
regression is found:
w.o.n
---
index.html | 1 -
resources/lang/en.json | 4 +-
src/client/AccountModal.ts | 80 +++++-
src/client/GameInfoModal.ts | 202 --------------
src/client/LangSelector.ts | 3 +
src/client/Main.ts | 5 -
src/client/components/BaseModal.ts | 8 +-
src/client/components/baseComponents/Modal.ts | 16 +-
.../baseComponents/stats/GameInfoView.ts | 233 ++++++++++++++++
.../stats/PlayerGameHistoryView.ts | 24 +-
.../client/AccountGameStatsNavigation.test.ts | 231 +++++++++++++++
tests/client/GameInfoView.test.ts | 263 ++++++++++++++++++
12 files changed, 833 insertions(+), 237 deletions(-)
delete mode 100644 src/client/GameInfoModal.ts
create mode 100644 src/client/components/baseComponents/stats/GameInfoView.ts
create mode 100644 tests/client/AccountGameStatsNavigation.test.ts
create mode 100644 tests/client/GameInfoView.test.ts
diff --git a/index.html b/index.html
index fc658e653..f2e53c14f 100644
--- a/index.html
+++ b/index.html
@@ -362,7 +362,6 @@
-
diff --git a/resources/lang/en.json b/resources/lang/en.json
index cf943f929..b4352e8c9 100644
--- a/resources/lang/en.json
+++ b/resources/lang/en.json
@@ -553,6 +553,7 @@
"duration": "Duration",
"economy": "Economy",
"hydros": "Hydros",
+ "load_failed": "Couldn't load game stats.",
"loading_game_info": "Loading game stats",
"mirv": "MIRV",
"naval_trade": "Tradeship",
@@ -561,9 +562,9 @@
"num_of_conquests_humans": "Player kills",
"pirate": "Pirate",
"players": "Players",
+ "retry": "Retry",
"stolen_gold": "Stolen with warships",
"survival_time": "Survival time",
- "title": "Game info",
"total_gold": "Total",
"trade": "Trade",
"train_trade": "Train",
@@ -891,7 +892,6 @@
"copyright": "© OpenFront™ and Contributors",
"create": "Create Lobby",
"discord_avatar_alt": "Discord profile avatar",
- "game_info": "Game info",
"github": "GitHub",
"go_to_troubleshooting": "Go to our troubleshooting page",
"help": "Help",
diff --git a/src/client/AccountModal.ts b/src/client/AccountModal.ts
index 562005445..8145f3bed 100644
--- a/src/client/AccountModal.ts
+++ b/src/client/AccountModal.ts
@@ -19,6 +19,7 @@ import {
sendMagicLink,
} from "./Auth";
import "./components/baseComponents/stats/DiscordUserHeader";
+import "./components/baseComponents/stats/GameInfoView";
import "./components/baseComponents/stats/PlayerGameHistoryView";
import type { PlayerGameHistoryCache } from "./components/baseComponents/stats/PlayerGameHistoryView";
import "./components/baseComponents/stats/PlayerStatsTable";
@@ -34,6 +35,7 @@ import "./components/SubscriptionPanel";
import { modalHeader } from "./components/ui/ModalHeader";
import { fetchCosmetics } from "./Cosmetics";
import { crazyGamesSDK, type CrazyGamesUser } from "./CrazyGamesSDK";
+import { modalRouter } from "./ModalRouter";
import { translateText } from "./Utils";
@customElement("account-modal")
@@ -51,6 +53,8 @@ export class AccountModal extends BaseModal {
private statsTree: PlayerStatsTree | null = null;
// Preserves the Games tab's accumulated list + cursor across tab switches.
private gameHistoryCache: PlayerGameHistoryCache | null = null;
+ @state() private selectedGameId: string | null = null;
+ private gamesScrollTop = 0;
private cosmetics: Cosmetics | null = null;
constructor() {
@@ -68,13 +72,11 @@ export class AccountModal extends BaseModal {
// different account) so stats/history from the previous player don't
// linger.
if (this.userMeResponse?.player?.publicId !== previousPublicId) {
- this.statsTree = null;
- this.gameHistoryCache = null;
+ this.resetPlayerData();
this.requestUpdate();
}
} else {
- this.statsTree = null;
- this.gameHistoryCache = null;
+ this.resetPlayerData();
this.requestUpdate();
}
});
@@ -103,6 +105,14 @@ export class AccountModal extends BaseModal {
}
protected renderHeaderSlot() {
+ if (this.selectedGameId) {
+ return modalHeader({
+ title: translateText("game_list.stats"),
+ onBack: () => this.backToGames(),
+ ariaLabel: translateText("common.back"),
+ });
+ }
+
const isLoggedIn = !!this.userMeResponse?.user;
const publicId = this.userMeResponse?.player?.publicId ?? "";
const displayId = publicId || translateText("account_modal.not_found");
@@ -144,6 +154,9 @@ export class AccountModal extends BaseModal {
if (this.isLoadingUser || !this.isLinkedAccount()) {
return {};
}
+ if (this.selectedGameId) {
+ return {};
+ }
return {
tabs: [
{ key: "account", label: translateText("account_modal.tab_account") },
@@ -168,6 +181,15 @@ export class AccountModal extends BaseModal {
: this.renderLoginOptions()}
`;
}
+ if (this.selectedGameId) {
+ return html`
+
+ `;
+ }
return html`