mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-28 01:34:34 +00:00
f532dab704
Resolves #1664 ## Description: Add a game ranking window, accessible through the player game history: <img width="508" height="140" alt="image" src="https://github.com/user-attachments/assets/51a628d9-628d-44c3-9776-d9b359b94e65" /> There is a lot of data players could be ranked with. Three main ranking categories with their own sub-categories: <img width="371" height="264" alt="image" src="https://github.com/user-attachments/assets/8b3b7c53-c52f-4b96-8039-23180c9181cf" /> ### Duration: Rank players according to their survival time <img width="284" height="281" alt="image" src="https://github.com/user-attachments/assets/6dfa0d11-7f5b-4f4f-81f8-f31e24ade6bf" /> ### War: #### Conquests: Number of conquered players and bots #### Bombs: Show all bomb launched by each players. Can be sorted with each category. <img width="289" height="193" alt="image" src="https://github.com/user-attachments/assets/fc0f9663-9a50-4098-b5c6-f434354accff" /> ### Economy: Show all gold earned by each players with trade, conquests, pirate or total: <img width="276" height="195" alt="image" src="https://github.com/user-attachments/assets/a925249d-b2d2-4c61-92a5-4dbf5922b32b" /> ### Responsiveness:  ## 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: IngloriousTom
180 lines
4.5 KiB
TypeScript
180 lines
4.5 KiB
TypeScript
import { z } from "zod";
|
|
import {
|
|
PlayerProfile,
|
|
PlayerProfileSchema,
|
|
UserMeResponse,
|
|
UserMeResponseSchema,
|
|
} from "../core/ApiSchemas";
|
|
import { AnalyticsRecord, AnalyticsRecordSchema } from "../core/Schemas";
|
|
import { getAuthHeader, logOut, userAuth } from "./Auth";
|
|
|
|
export async function fetchPlayerById(
|
|
playerId: string,
|
|
): Promise<PlayerProfile | false> {
|
|
try {
|
|
const userAuthResult = await userAuth();
|
|
if (!userAuthResult) return false;
|
|
const { jwt } = userAuthResult;
|
|
|
|
const url = `${getApiBase()}/player/${encodeURIComponent(playerId)}`;
|
|
|
|
const res = await fetch(url, {
|
|
headers: {
|
|
Accept: "application/json",
|
|
Authorization: `Bearer ${jwt}`,
|
|
},
|
|
});
|
|
|
|
if (res.status !== 200) {
|
|
console.warn(
|
|
"fetchPlayerById: unexpected status",
|
|
res.status,
|
|
res.statusText,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
const json = await res.json();
|
|
const parsed = PlayerProfileSchema.safeParse(json);
|
|
if (!parsed.success) {
|
|
console.warn("fetchPlayerById: Zod validation failed", parsed.error);
|
|
return false;
|
|
}
|
|
|
|
return parsed.data;
|
|
} catch (err) {
|
|
console.warn("fetchPlayerById: request failed", err);
|
|
return false;
|
|
}
|
|
}
|
|
export async function getUserMe(): Promise<UserMeResponse | false> {
|
|
try {
|
|
const userAuthResult = await userAuth();
|
|
if (!userAuthResult) return false;
|
|
const { jwt } = userAuthResult;
|
|
|
|
// Get the user object
|
|
const response = await fetch(getApiBase() + "/users/@me", {
|
|
headers: {
|
|
authorization: `Bearer ${jwt}`,
|
|
},
|
|
});
|
|
if (response.status === 401) {
|
|
await logOut();
|
|
return false;
|
|
}
|
|
if (response.status !== 200) return false;
|
|
const body = await response.json();
|
|
const result = UserMeResponseSchema.safeParse(body);
|
|
if (!result.success) {
|
|
const error = z.prettifyError(result.error);
|
|
console.error("Invalid response", error);
|
|
return false;
|
|
}
|
|
return result.data;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export async function createCheckoutSession(
|
|
priceId: string,
|
|
colorPaletteName: string | null,
|
|
): Promise<string | false> {
|
|
try {
|
|
const response = await fetch(
|
|
`${getApiBase()}/stripe/create-checkout-session`,
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Authorization: await getAuthHeader(),
|
|
},
|
|
body: JSON.stringify({
|
|
priceId: priceId,
|
|
hostname: window.location.origin,
|
|
colorPaletteName: colorPaletteName,
|
|
}),
|
|
},
|
|
);
|
|
if (!response.ok) {
|
|
console.error(
|
|
"createCheckoutSession: request failed",
|
|
response.status,
|
|
response.statusText,
|
|
);
|
|
return false;
|
|
}
|
|
const json = await response.json();
|
|
return json.url;
|
|
} catch (e) {
|
|
console.error("createCheckoutSession: request failed", e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export function getApiBase() {
|
|
const domainname = getAudience();
|
|
|
|
if (domainname === "localhost") {
|
|
const apiDomain = process?.env?.API_DOMAIN;
|
|
if (apiDomain) {
|
|
return `https://${apiDomain}`;
|
|
}
|
|
return localStorage.getItem("apiHost") ?? "http://localhost:8787";
|
|
}
|
|
|
|
return `https://api.${domainname}`;
|
|
}
|
|
|
|
export function getAudience() {
|
|
const { hostname } = new URL(window.location.href);
|
|
const domainname = hostname.split(".").slice(-2).join(".");
|
|
return domainname;
|
|
}
|
|
|
|
// Check if the user's account is linked to a Discord or email account.
|
|
export function hasLinkedAccount(
|
|
userMeResponse: UserMeResponse | false,
|
|
): boolean {
|
|
return (
|
|
userMeResponse !== false &&
|
|
(userMeResponse.user?.discord !== undefined ||
|
|
userMeResponse.user?.email !== undefined)
|
|
);
|
|
}
|
|
|
|
export async function fetchGameById(
|
|
gameId: string,
|
|
): Promise<AnalyticsRecord | false> {
|
|
try {
|
|
const url = `${getApiBase()}/game/${encodeURIComponent(gameId)}`;
|
|
const res = await fetch(url, {
|
|
headers: {
|
|
Accept: "application/json",
|
|
},
|
|
});
|
|
|
|
if (res.status !== 200) {
|
|
console.warn(
|
|
"fetchGameById: unexpected status",
|
|
res.status,
|
|
res.statusText,
|
|
);
|
|
return false;
|
|
}
|
|
|
|
const json = await res.json();
|
|
const parsed = AnalyticsRecordSchema.safeParse(json);
|
|
if (!parsed.success) {
|
|
console.warn("fetchGameById: Zod validation failed", parsed.error);
|
|
return false;
|
|
}
|
|
|
|
return parsed.data;
|
|
} catch (err) {
|
|
console.warn("fetchGameById: request failed", err);
|
|
return false;
|
|
}
|
|
}
|