fix mini rankings
All checks were successful
Deploy Website / deploy (push) Successful in 4m2s

This commit is contained in:
Lee 2024-10-10 01:52:05 +01:00
parent f75897007c
commit 526167d4f1

@ -36,6 +36,7 @@ type Variants = {
itemsPerPage: number; itemsPerPage: number;
icon: (player: ScoreSaberPlayer) => ReactElement; icon: (player: ScoreSaberPlayer) => ReactElement;
getPage: (player: ScoreSaberPlayer, itemsPerPage: number) => number; getPage: (player: ScoreSaberPlayer, itemsPerPage: number) => number;
getRank: (player: ScoreSaberPlayer) => number;
query: (page: number, country: string) => Promise<ScoreSaberPlayersPageToken | undefined>; query: (page: number, country: string) => Promise<ScoreSaberPlayersPageToken | undefined>;
}; };
}; };
@ -47,6 +48,9 @@ const miniVariants: Variants = {
getPage: (player: ScoreSaberPlayer, itemsPerPage: number) => { getPage: (player: ScoreSaberPlayer, itemsPerPage: number) => {
return Math.floor((player.rank - 1) / itemsPerPage) + 1; return Math.floor((player.rank - 1) / itemsPerPage) + 1;
}, },
getRank: (player: ScoreSaberPlayer) => {
return player.rank;
},
query: (page: number) => { query: (page: number) => {
return scoresaberService.lookupPlayers(page); return scoresaberService.lookupPlayers(page);
}, },
@ -59,6 +63,9 @@ const miniVariants: Variants = {
getPage: (player: ScoreSaberPlayer, itemsPerPage: number) => { getPage: (player: ScoreSaberPlayer, itemsPerPage: number) => {
return Math.floor((player.countryRank - 1) / itemsPerPage) + 1; return Math.floor((player.countryRank - 1) / itemsPerPage) + 1;
}, },
getRank: (player: ScoreSaberPlayer) => {
return player.countryRank;
},
query: (page: number, country: string) => { query: (page: number, country: string) => {
return scoresaberService.lookupPlayersByCountry(page, country); return scoresaberService.lookupPlayersByCountry(page, country);
}, },
@ -67,69 +74,74 @@ const miniVariants: Variants = {
export default function Mini({ type, player, shouldUpdate }: MiniProps) { export default function Mini({ type, player, shouldUpdate }: MiniProps) {
if (shouldUpdate == undefined) { if (shouldUpdate == undefined) {
// Default to true
shouldUpdate = true; shouldUpdate = true;
} }
const variant = miniVariants[type]; const variant = miniVariants[type];
const icon = variant.icon(player); const icon = variant.icon(player);
const itemsPerPage = variant.itemsPerPage; const itemsPerPage = variant.itemsPerPage;
// Calculate the page and the rank of the player within that page
const page = variant.getPage(player, itemsPerPage); const page = variant.getPage(player, itemsPerPage);
const rankWithinPage = player.rank % itemsPerPage; const rankWithinPage = variant.getRank(player) % itemsPerPage;
const { data, isLoading, isError } = useQuery({ const { data, isLoading, isError } = useQuery({
queryKey: ["player-" + type, player.id, type, page], queryKey: ["mini-ranking-" + type, player.id, type, page],
queryFn: async () => { queryFn: async () => {
// Determine pages to search based on player's rank within the page
const pagesToSearch = [page]; const pagesToSearch = [page];
if (rankWithinPage < 5 && page > 1) { if (rankWithinPage < 5 && page > 1) {
// Allow page 1 to be valid
// Player is near the start of the page, so search the previous page too
pagesToSearch.push(page - 1); pagesToSearch.push(page - 1);
} }
if (rankWithinPage > itemsPerPage - 5) { if (rankWithinPage > itemsPerPage - 5) {
// Player is near the end of the page, so search the next page too
pagesToSearch.push(page + 1); pagesToSearch.push(page + 1);
} }
// Fetch players from the determined pages
const players: ScoreSaberPlayerToken[] = []; const players: ScoreSaberPlayerToken[] = [];
for (const p of pagesToSearch) { for (const p of pagesToSearch) {
const response = await variant.query(p, player.country); const response = await variant.query(p, player.country);
if (response === undefined) { if (response) {
return undefined;
}
players.push(...response.players); players.push(...response.players);
} }
}
return players; // Sort players by rank to ensure correct order
return players.sort((a, b) => {
// This is the wrong type but it still works.
return variant.getRank(a as unknown as ScoreSaberPlayer) - variant.getRank(b as unknown as ScoreSaberPlayer);
});
}, },
enabled: shouldUpdate, enabled: shouldUpdate,
}); });
let players = data; // So we can update it later if (isLoading) {
if (players && (!isLoading || !isError)) { return <PlayerRankingSkeleton />;
// Find the player's position in the list }
if (isError || !data) {
return <p className="text-red-500">Error loading ranking</p>;
}
let players = data;
const playerPosition = players.findIndex(p => p.id === player.id); const playerPosition = players.findIndex(p => p.id === player.id);
// Ensure we always show 5 players // Always show 5 players: 3 above and 1 below the player
const start = Math.max(0, playerPosition - 3); // Start showing 3 players above the player, but not less than index 0 const start = Math.max(0, playerPosition - 3);
const end = Math.min(players.length, start + 5); // Ensure there are 5 players shown const end = Math.min(players.length, start + 5);
players = players.slice(start, end); players = players.slice(start, end);
// If there are less than 5 players at the top, append more players from below // If fewer than 5 players, append/prepend more
if (players.length < 5 && start === 0) { if (players.length < 5) {
const additionalPlayers = players.slice(playerPosition + 1, playerPosition + (5 - players.length + 1)); const missingPlayers = 5 - players.length;
if (start === 0) {
const additionalPlayers = players.slice(playerPosition + 1, playerPosition + 1 + missingPlayers);
players = [...players, ...additionalPlayers]; players = [...players, ...additionalPlayers];
} else if (end === players.length) {
const additionalPlayers = players.slice(Math.max(0, start - missingPlayers), start);
players = [...additionalPlayers, ...players];
} }
} }
if (isLoading) {
return <PlayerRankingSkeleton />;
}
return ( return (
<Card className="w-full flex gap-2 sticky select-none"> <Card className="w-full flex gap-2 sticky select-none">
<div className="flex gap-2"> <div className="flex gap-2">
@ -137,8 +149,7 @@ export default function Mini({ type, player, shouldUpdate }: MiniProps) {
<p>{type} Ranking</p> <p>{type} Ranking</p>
</div> </div>
<div className="flex flex-col text-sm"> <div className="flex flex-col text-sm">
{isError && <p className="text-red-500">Error</p>} {players.map((playerRanking, index) => {
{players?.map((playerRanking, index) => {
const rank = type == "Global" ? playerRanking.rank : playerRanking.countryRank; const rank = type == "Global" ? playerRanking.rank : playerRanking.countryRank;
const playerName = const playerName =
playerRanking.name.length > PLAYER_NAME_MAX_LENGTH playerRanking.name.length > PLAYER_NAME_MAX_LENGTH