From fbde9ec64f3b12376c88cd742d12e221ac3a4eb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Tue, 7 Nov 2023 18:58:19 -0600 Subject: [PATCH] fix: no double fetch and caching --- scripts/buildRatingsLeaderboard.ts | 19 +- src/app/tools/ratings/page.tsx | 307 ++++++++++++++++++---------- src/core/blitz/getRatingsLeague.ts | 23 +++ src/core/blitzkrieg/patientFetch.ts | 14 ++ 4 files changed, 243 insertions(+), 120 deletions(-) create mode 100644 src/core/blitz/getRatingsLeague.ts create mode 100644 src/core/blitzkrieg/patientFetch.ts diff --git a/scripts/buildRatingsLeaderboard.ts b/scripts/buildRatingsLeaderboard.ts index 07c477fc9..4f989f0d8 100644 --- a/scripts/buildRatingsLeaderboard.ts +++ b/scripts/buildRatingsLeaderboard.ts @@ -6,6 +6,7 @@ import { RatingsNeighbors, RatingsPlayer, } from '../src/commands/ratings'; +import { patientFetch } from '../src/core/blitzkrieg/patientFetch'; import commitMultipleFiles from './commitMultipleFiles.js'; /* @@ -18,22 +19,8 @@ import commitMultipleFiles from './commitMultipleFiles.js'; * [region]/ratings/[season]/midnight.json: last midnight's leaderboard relative to the region */ -async function patientFetch(url: string) { - let patientResponse: Response; - - while (patientResponse! === undefined) { - try { - patientResponse = await fetch(url); - } catch (error) { - console.warn(`Failed to fetch ${url}; retrying in 1 minute...`); - await new Promise((resolve) => setTimeout(resolve, 1 * 60 * 1000)); - } - } - - return patientResponse; -} - -export type BlitzkriegRatingsLeaderboard = { id: number; score: number }[]; +export type BlitzkriegRatingsLeaderboardEntry = { id: number; score: number }; +export type BlitzkriegRatingsLeaderboard = BlitzkriegRatingsLeaderboardEntry[]; const publish = argv.includes('--publish'); const latest = argv.includes('--latest'); diff --git a/src/app/tools/ratings/page.tsx b/src/app/tools/ratings/page.tsx index 9efb65575..cfee3da4d 100644 --- a/src/app/tools/ratings/page.tsx +++ b/src/app/tools/ratings/page.tsx @@ -14,10 +14,11 @@ import { TextField, } from '@radix-ui/themes'; import { produce } from 'immer'; -import { debounce, findLastIndex, range } from 'lodash'; +import { debounce, range } from 'lodash'; import { ChangeEvent, useEffect, useRef, useState } from 'react'; import useSWR from 'swr'; import { create } from 'zustand'; +import { BlitzkriegRatingsLeaderboardEntry } from '../../../../scripts/buildRatingsLeaderboard'; import * as Leaderboard from '../../../components/Leaderboard'; import PageWrapper from '../../../components/PageWrapper'; import { LEAGUES } from '../../../constants/leagues'; @@ -28,13 +29,15 @@ import fetchBlitz from '../../../core/blitz/fetchBlitz'; import { getAccountInfo } from '../../../core/blitz/getAccountInfo'; import { getClanAccountInfo } from '../../../core/blitz/getClanAccountInfo'; import getRatingsInfo from '../../../core/blitz/getRatingsInfo'; +import { getRatingsLeague } from '../../../core/blitz/getRatingsLeague'; import { AccountList } from '../../../core/blitz/searchPlayersAcrossRegions'; import { getArchivedLatestSeasonNumber } from '../../../core/blitzkrieg/getArchivedLatestSeasonNumber'; import getArchivedRatingsInfo from '../../../core/blitzkrieg/getArchivedRatingsInfo'; import { getArchivedRatingsLeaderboard } from '../../../core/blitzkrieg/getArchivedRatingsLeaderboard'; import { noArrows } from './page.css'; -const ROWS_OPTIONS = [5, 10, 25, 50, 100]; +const ROWS_OPTIONS_ARCHIVED = [5, 10, 15, 25, 30, 50, 100]; +const ROWS_OPTIONS_CURRENT = [5, 10, 15, 25, 30]; type UsernameCache = Record>; type ClanCache = Record>; @@ -51,7 +54,7 @@ const useClanCache = create(() => ({ })); export default function Page() { - const [rowsPerPage, setRowsPerPage] = useState(ROWS_OPTIONS[2]); + const [rowsPerPage, setRowsPerPage] = useState(25); const usernameCache = useUsernameCache(); const clanCache = useClanCache(); const [region, setRegion] = useState('com'); @@ -69,29 +72,44 @@ export default function Page() { getArchivedLatestSeasonNumber.name, getArchivedLatestSeasonNumber, ); - const players = useSWR(`ratings-players-${region}-${season}`, () => { + const players = useSWR(`ratings-players-${region}-${season}`, async () => { if (season === null) { - // i'll deal with this later - return null; + // const players = await getRatingsLeague(region, 0); + // useUsernameCache.setState( + // produce((draft: UsernameCache) => { + // players.result.forEach((player) => { + // draft[region][player.spa_id] = player.nickname; + // }); + // }), + // ); + // useClanCache.setState( + // produce((draft: ClanCache) => { + // players.result.forEach((player) => { + // draft[region][player.spa_id] = player.clan_tag; + // }); + // }), + // ); + // return players.result.map( + // (player) => + // ({ + // id: player.spa_id, + // score: player.score, + // }) satisfies BlitzkriegRatingsLeaderboardEntry, + // ); + + return {}; } else { - return getArchivedRatingsLeaderboard(region, season); + return { + ...(await getArchivedRatingsLeaderboard(region, season)), + } as Record; } }); const [page, setPage] = useState(0); const [highlightedPlayerId, setHighlightedPlayerId] = useState( null, ); - const playerSlice = players.data?.slice( - page * rowsPerPage, - page * rowsPerPage + rowsPerPage, - ); const positionInput = useRef(null); const scoreInput = useRef(null); - const previousPage = () => setPage((page) => Math.max(page - 1, 0)); - const nextPage = () => - setPage((page) => - Math.min(page + 1, Math.floor((players.data?.length ?? 0) / rowsPerPage)), - ); const [searchResults, setSearchResults] = useState( undefined, ); @@ -108,10 +126,7 @@ export default function Page() { setSearchResults( accountList.filter( (searchedPlayer) => - players.data?.some( - (leaderboardPlayer) => - leaderboardPlayer.id === searchedPlayer.account_id, - ), + players.data && searchedPlayer.account_id in players.data, ), ); } else { @@ -121,52 +136,59 @@ export default function Page() { 500, ); - cachePage(page - 1); - cachePage(page); - cachePage(page + 1); + useEffect(() => { + // TODO: caches twice on initial load + cachePage(page - 1); + cachePage(page); + cachePage(page + 1); + }, [page, players.isLoading]); function cachePage(page: number) { - const ids = players.data - ?.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) - .map(({ id }) => id) - .filter((id) => !(id in usernameCache[region])); + if (season === null) { + } else { + if (players.isLoading || !ratingsInfo || ratingsInfo?.detail) return; - if (ids && ids.length > 0) { - getAccountInfo(region, ids).then((data) => { - data.map((player, index) => { - useUsernameCache.setState( - produce((draft: UsernameCache) => { - if (player) { - draft[region][ids[index]] = player.nickname; - } else { - draft[region][ids[index]] = null; - } - }), - ); - }); - }); + const ids = range( + Math.max(0, page * rowsPerPage), + Math.min(ratingsInfo.count - 1, page * rowsPerPage + rowsPerPage), + ) + .map((index) => players.data![index].id) + .filter((id) => !(id in usernameCache[region])); - getClanAccountInfo(region, ids, ['clan']).then((data) => { - data.map((player, index) => { - useClanCache.setState( - produce((draft: ClanCache) => { - if (player) { - draft[region][ids[index]] = player.clan?.tag; - } - }), - ); + if (ids && ids.length > 0) { + getAccountInfo(region, ids).then((data) => { + data.map((player, index) => { + useUsernameCache.setState( + produce((draft: UsernameCache) => { + if (player) { + draft[region][ids[index]] = player.nickname; + } else { + draft[region][ids[index]] = null; + } + }), + ); + }); }); - }); + + getClanAccountInfo(region, ids, ['clan']).then((data) => { + data.map((player, index) => { + useClanCache.setState( + produce((draft: ClanCache) => { + if (player) { + draft[region][ids[index]] = player.clan?.tag; + } + }), + ); + }); + }); + } } } const handleJumpToPosition = () => { - if (!players.data) return; + if (!players.data || !ratingsInfo || ratingsInfo?.detail) return; const rawPosition = positionInput.current!.valueAsNumber - 1; - const position = Math.max( - 0, - Math.min(rawPosition, (players.data?.length ?? 0) - 1), - ); + const position = Math.max(0, Math.min(rawPosition, ratingsInfo.count - 1)); setPage(Math.floor(position / rowsPerPage)); if (players.data) { @@ -175,11 +197,16 @@ export default function Page() { }; const [jumpToPositionOpen, setJumpToPositionOpen] = useState(false); const handleJumpToScore = () => { - if (!players.data) return; + if (!players.data || !ratingsInfo || ratingsInfo?.detail) return; - const playerIndex = findLastIndex(players.data, (player) => { - return player.score >= scoreInput.current!.valueAsNumber; - }); + let playerIndex = -1; + + for (let index = ratingsInfo.count - 1; index >= 0; index--) { + if (players.data[index].score >= scoreInput.current!.valueAsNumber) { + playerIndex = index; + break; + } + } if (playerIndex === -1) return; @@ -190,10 +217,17 @@ export default function Page() { } }; const [jumpToScoreOpen, setJumpToScoreOpen] = useState(false); + const pages = Math.ceil( + (ratingsInfo && ratingsInfo.detail === undefined ? ratingsInfo?.count : 1) / + rowsPerPage, + ); useEffect(() => { setPage(0); }, [season, region]); + useEffect(() => { + if (season === null && rowsPerPage > 30) setRowsPerPage(25); + }, [season]); function PageTurner() { const pageInput = useRef(null); @@ -205,7 +239,10 @@ export default function Page() { return ( - @@ -219,10 +256,7 @@ export default function Page() { setPage( Math.max( 0, - Math.min( - Math.floor((players.data?.length ?? 0) / rowsPerPage), - event.target.valueAsNumber - 1, - ), + Math.min(pages - 1, event.target.valueAsNumber - 1), ), ); }} @@ -232,11 +266,14 @@ export default function Page() { } }} /> - - out of {Math.ceil((players.data?.length ?? 0) / rowsPerPage)} - + out of {pages} - @@ -247,7 +284,7 @@ export default function Page() { return ( - + setRegion(value as Region)} @@ -296,12 +333,15 @@ export default function Page() { setRowsPerPage(parseInt(value))} - defaultValue={`${rowsPerPage}`} + value={`${rowsPerPage}`} > - {ROWS_OPTIONS.map((size) => ( + {(season === null + ? ROWS_OPTIONS_CURRENT + : ROWS_OPTIONS_ARCHIVED + ).map((size) => ( {size} per page @@ -406,22 +446,68 @@ export default function Page() {