mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 15:06:20 +00:00
Merge branch 'Dev' into feat/react19-modernization
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
import { GetConfiguration } from '@nitrots/nitro-renderer';
|
||||
import { getAccessToken } from '../auth';
|
||||
|
||||
export type BadgeRarityKey = 'common' | 'rare' | 'epic' | 'legendary' | 'mythical' | 'unique';
|
||||
|
||||
export interface BadgeLeaderboardStat
|
||||
{
|
||||
badgeCode: string;
|
||||
ownerCount: number;
|
||||
rarity: BadgeRarityKey;
|
||||
}
|
||||
|
||||
export interface BadgeLeaderboardEntry
|
||||
{
|
||||
userId: number;
|
||||
username: string;
|
||||
figure: string;
|
||||
score: number;
|
||||
rank: number;
|
||||
}
|
||||
|
||||
export interface BadgeLeaderboardBoard
|
||||
{
|
||||
entries: BadgeLeaderboardEntry[];
|
||||
totalPlayers: number;
|
||||
viewerEntry?: Partial<BadgeLeaderboardEntry>;
|
||||
}
|
||||
|
||||
export interface BadgeLeaderboardResponse
|
||||
{
|
||||
viewerUserId: number;
|
||||
badgeStats: BadgeLeaderboardStat[];
|
||||
thresholds: {
|
||||
commonMinOwners: number;
|
||||
rareMinOwners: number;
|
||||
epicMinOwners: number;
|
||||
legendaryMinOwners: number;
|
||||
mythicalMinOwners: number;
|
||||
uniqueOwners: number;
|
||||
};
|
||||
leaderboards: {
|
||||
totalBadges: BadgeLeaderboardBoard;
|
||||
achievementLevel: BadgeLeaderboardBoard;
|
||||
rarity: Record<BadgeRarityKey, BadgeLeaderboardBoard>;
|
||||
};
|
||||
}
|
||||
|
||||
const interpolate = (value: string): string =>
|
||||
{
|
||||
try { return GetConfiguration().interpolate(value); }
|
||||
catch { return value; }
|
||||
};
|
||||
|
||||
const getUrl = (): string =>
|
||||
{
|
||||
const configured = GetConfiguration().getValue<string>('badges.leaderboard.endpoint', '/api/badges/leaderboard');
|
||||
|
||||
return interpolate(configured);
|
||||
};
|
||||
|
||||
const authHeaders = (): Record<string, string> =>
|
||||
{
|
||||
const headers: Record<string, string> = {
|
||||
'Accept': 'application/json',
|
||||
'X-Requested-With': 'NitroBadgeLeaderboard'
|
||||
};
|
||||
|
||||
const token = getAccessToken();
|
||||
|
||||
if(token) headers.Authorization = `Bearer ${ token }`;
|
||||
|
||||
return headers;
|
||||
};
|
||||
|
||||
const parseJson = async <T>(response: Response): Promise<T> =>
|
||||
{
|
||||
const text = await response.text();
|
||||
|
||||
if(!text) return {} as T;
|
||||
|
||||
try { return JSON.parse(text) as T; }
|
||||
catch { throw new Error('Invalid response from badge leaderboard endpoint.'); }
|
||||
};
|
||||
|
||||
const throwOnError = async (response: Response): Promise<void> =>
|
||||
{
|
||||
if(response.ok) return;
|
||||
|
||||
const payload = await parseJson<{ error?: string }>(response);
|
||||
const message = payload?.error || `Request failed (${ response.status }).`;
|
||||
const error = new Error(message) as Error & { status?: number };
|
||||
|
||||
error.status = response.status;
|
||||
|
||||
throw error;
|
||||
};
|
||||
|
||||
let cachePromise: Promise<BadgeLeaderboardResponse> = null;
|
||||
let cacheValue: BadgeLeaderboardResponse = null;
|
||||
|
||||
const buildStatsMap = (response: BadgeLeaderboardResponse | null): Map<string, BadgeLeaderboardStat> =>
|
||||
{
|
||||
const map = new Map<string, BadgeLeaderboardStat>();
|
||||
|
||||
if(!response?.badgeStats?.length) return map;
|
||||
|
||||
for(const stat of response.badgeStats)
|
||||
{
|
||||
if(!stat?.badgeCode) continue;
|
||||
|
||||
map.set(stat.badgeCode, stat);
|
||||
}
|
||||
|
||||
return map;
|
||||
};
|
||||
|
||||
let cacheStatsMap: Map<string, BadgeLeaderboardStat> = new Map();
|
||||
|
||||
export const fetchBadgeLeaderboard = async (force = false): Promise<BadgeLeaderboardResponse> =>
|
||||
{
|
||||
if(!force)
|
||||
{
|
||||
if(cacheValue) return cacheValue;
|
||||
if(cachePromise) return cachePromise;
|
||||
}
|
||||
|
||||
cachePromise = (async () =>
|
||||
{
|
||||
const response = await fetch(getUrl(), {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
headers: authHeaders()
|
||||
});
|
||||
|
||||
await throwOnError(response);
|
||||
|
||||
const payload = await parseJson<BadgeLeaderboardResponse>(response);
|
||||
|
||||
cacheValue = payload;
|
||||
cacheStatsMap = buildStatsMap(payload);
|
||||
|
||||
return payload;
|
||||
})();
|
||||
|
||||
try
|
||||
{
|
||||
return await cachePromise;
|
||||
}
|
||||
finally
|
||||
{
|
||||
cachePromise = null;
|
||||
}
|
||||
};
|
||||
|
||||
export const getCachedBadgeLeaderboard = (): BadgeLeaderboardResponse =>
|
||||
{
|
||||
return cacheValue;
|
||||
};
|
||||
|
||||
export const getCachedBadgeRarityStat = (badgeCode: string): BadgeLeaderboardStat =>
|
||||
{
|
||||
if(!badgeCode) return null;
|
||||
|
||||
return cacheStatsMap.get(badgeCode) || null;
|
||||
};
|
||||
|
||||
export const ensureBadgeLeaderboardLoaded = async (): Promise<BadgeLeaderboardResponse> =>
|
||||
{
|
||||
return fetchBadgeLeaderboard(false);
|
||||
};
|
||||
@@ -1 +1,2 @@
|
||||
export * from './CustomBadgeApi';
|
||||
export * from './BadgeLeaderboardApi';
|
||||
|
||||
Reference in New Issue
Block a user