mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-08-18 00:28:14 +00:00
Add PlayerInfoModal (#2058)
## Description: This PR adds the Player Status display to the AccountModal. <img width="573" height="640" alt="スクリーンショット 2025-10-11 6 44 25" src="https://github.com/user-attachments/assets/54c36bde-7c9c-4431-8ac9-e7f4089971f4" /> (origin pr:https://github.com/openfrontio/OpenFrontIO/pull/1758) ## 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: aotumuri --------- Co-authored-by: evanpelle <evanpelle@gmail.com>
This commit is contained in:
@@ -155,6 +155,7 @@
|
|||||||
"account_modal": {
|
"account_modal": {
|
||||||
"title": "Account",
|
"title": "Account",
|
||||||
"logged_in_as": "Logged in as {email}",
|
"logged_in_as": "Logged in as {email}",
|
||||||
|
"fetching_account": "Fetching account information...",
|
||||||
"logged_in_with_discord": "Logged in with Discord",
|
"logged_in_with_discord": "Logged in with Discord",
|
||||||
"recovery_email_sent": "Recovery email sent to {email}"
|
"recovery_email_sent": "Recovery email sent to {email}"
|
||||||
},
|
},
|
||||||
|
|||||||
+115
-11
@@ -1,9 +1,23 @@
|
|||||||
import { html, LitElement, TemplateResult } from "lit";
|
import { html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, query, state } from "lit/decorators.js";
|
import { customElement, query, state } from "lit/decorators.js";
|
||||||
import { UserMeResponse } from "../core/ApiSchemas";
|
import {
|
||||||
|
PlayerGame,
|
||||||
|
PlayerStatsTree,
|
||||||
|
UserMeResponse,
|
||||||
|
} from "../core/ApiSchemas";
|
||||||
|
import "./components/baseComponents/stats/DiscordUserHeader";
|
||||||
|
import "./components/baseComponents/stats/GameList";
|
||||||
|
import "./components/baseComponents/stats/PlayerStatsTable";
|
||||||
|
import "./components/baseComponents/stats/PlayerStatsTree";
|
||||||
import "./components/Difficulties";
|
import "./components/Difficulties";
|
||||||
import "./components/PatternButton";
|
import "./components/PatternButton";
|
||||||
import { discordLogin, getApiBase, getUserMe, logOut } from "./jwt";
|
import {
|
||||||
|
discordLogin,
|
||||||
|
fetchPlayerById,
|
||||||
|
getApiBase,
|
||||||
|
getUserMe,
|
||||||
|
logOut,
|
||||||
|
} from "./jwt";
|
||||||
import { isInIframe, translateText } from "./Utils";
|
import { isInIframe, translateText } from "./Utils";
|
||||||
|
|
||||||
@customElement("account-modal")
|
@customElement("account-modal")
|
||||||
@@ -14,12 +28,33 @@ export class AccountModal extends LitElement {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@state() private email: string = "";
|
@state() private email: string = "";
|
||||||
|
@state() private isLoadingUser: boolean = false;
|
||||||
|
|
||||||
private loggedInEmail: string | null = null;
|
private loggedInEmail: string | null = null;
|
||||||
private loggedInDiscord: string | null = null;
|
private loggedInDiscord: string | null = null;
|
||||||
|
private userMeResponse: UserMeResponse | null = null;
|
||||||
|
private playerId: string | null = null;
|
||||||
|
private statsTree: PlayerStatsTree | null = null;
|
||||||
|
private recentGames: PlayerGame[] = [];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
document.addEventListener("userMeResponse", (event: Event) => {
|
||||||
|
const customEvent = event as CustomEvent;
|
||||||
|
if (customEvent.detail) {
|
||||||
|
this.userMeResponse = customEvent.detail as UserMeResponse;
|
||||||
|
this.playerId = this.userMeResponse?.player?.publicId;
|
||||||
|
if (this.playerId === undefined) {
|
||||||
|
this.statsTree = null;
|
||||||
|
this.recentGames = [];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.statsTree = null;
|
||||||
|
this.recentGames = [];
|
||||||
|
this.requestUpdate();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
createRenderRoot() {
|
createRenderRoot() {
|
||||||
@@ -38,6 +73,16 @@ export class AccountModal extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private renderInner() {
|
private renderInner() {
|
||||||
|
if (this.isLoadingUser) {
|
||||||
|
return html`
|
||||||
|
<div class="flex flex-col items-center justify-center p-6 text-white">
|
||||||
|
<p class="mb-2">${translateText("account_modal.fetching_account")}</p>
|
||||||
|
<div
|
||||||
|
class="w-6 h-6 border-4 border-blue-500 border-t-transparent rounded-full animate-spin"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
if (this.loggedInDiscord) {
|
if (this.loggedInDiscord) {
|
||||||
return this.renderLoggedInDiscord();
|
return this.renderLoggedInDiscord();
|
||||||
} else if (this.loggedInEmail) {
|
} else if (this.loggedInEmail) {
|
||||||
@@ -47,15 +92,39 @@ export class AccountModal extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private viewGame(gameId: string): void {
|
||||||
|
this.close();
|
||||||
|
const path = location.pathname;
|
||||||
|
const { search } = location;
|
||||||
|
const hash = `#join=${encodeURIComponent(gameId)}`;
|
||||||
|
const newUrl = `${path}${search}${hash}`;
|
||||||
|
|
||||||
|
history.pushState({ join: gameId }, "", newUrl);
|
||||||
|
window.dispatchEvent(new HashChangeEvent("hashchange"));
|
||||||
|
}
|
||||||
|
|
||||||
private renderLoggedInDiscord() {
|
private renderLoggedInDiscord() {
|
||||||
return html`
|
return html`
|
||||||
<div class="p-6">
|
<div class="p-6">
|
||||||
<div class="mb-4">
|
<div class="mb-4 text-center">
|
||||||
<p class="text-white text-center mb-4">
|
<p class="text-white mb-4">
|
||||||
Logged in with Discord as ${this.loggedInDiscord}
|
Logged in with Discord as ${this.loggedInDiscord}
|
||||||
</p>
|
</p>
|
||||||
|
${this.logoutButton()}
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col items-center mt-2 mb-4">
|
||||||
|
<discord-user-header
|
||||||
|
.data=${this.userMeResponse?.user?.discord ?? null}
|
||||||
|
></discord-user-header>
|
||||||
|
<player-stats-tree-view
|
||||||
|
.statsTree=${this.statsTree}
|
||||||
|
></player-stats-tree-view>
|
||||||
|
<hr class="w-2/3 border-gray-600 my-2" />
|
||||||
|
<game-list
|
||||||
|
.games=${this.recentGames}
|
||||||
|
.onViewGame=${(id: string) => this.viewGame(id)}
|
||||||
|
></game-list>
|
||||||
</div>
|
</div>
|
||||||
${this.logoutButton()}
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -208,13 +277,30 @@ export class AccountModal extends LitElement {
|
|||||||
discordLogin();
|
discordLogin();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async open() {
|
public open() {
|
||||||
const userMe = await getUserMe();
|
|
||||||
if (userMe) {
|
|
||||||
this.loggedInEmail = userMe.user.email ?? null;
|
|
||||||
this.loggedInDiscord = userMe.user.discord?.global_name ?? null;
|
|
||||||
}
|
|
||||||
this.modalEl?.open();
|
this.modalEl?.open();
|
||||||
|
this.isLoadingUser = true;
|
||||||
|
|
||||||
|
void getUserMe()
|
||||||
|
.then((userMe) => {
|
||||||
|
if (userMe) {
|
||||||
|
this.loggedInEmail = userMe.user.email ?? null;
|
||||||
|
this.loggedInDiscord = userMe.user.discord?.global_name ?? null;
|
||||||
|
if (this.playerId) {
|
||||||
|
this.loadFromApi(this.playerId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.loggedInEmail = null;
|
||||||
|
this.loggedInDiscord = null;
|
||||||
|
}
|
||||||
|
this.isLoadingUser = false;
|
||||||
|
this.requestUpdate();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.warn("Failed to fetch user info in AccountModal.open():", err);
|
||||||
|
this.isLoadingUser = false;
|
||||||
|
this.requestUpdate();
|
||||||
|
});
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,6 +314,24 @@ export class AccountModal extends LitElement {
|
|||||||
// Refresh the page after logout to update the UI state
|
// Refresh the page after logout to update the UI state
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async loadFromApi(playerId: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const data = await fetchPlayerById(playerId);
|
||||||
|
if (!data) {
|
||||||
|
this.requestUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.recentGames = data.games;
|
||||||
|
this.statsTree = data.stats;
|
||||||
|
|
||||||
|
this.requestUpdate();
|
||||||
|
} catch (err) {
|
||||||
|
console.warn("Failed to load player data:", err);
|
||||||
|
this.requestUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@customElement("account-button")
|
@customElement("account-button")
|
||||||
|
|||||||
Reference in New Issue
Block a user