import { compositeStats, compositeStatsKeys, deltaTankStats, fetchAverageDefinitions, fetchTankDefinitions, formatCompositeStat, getAccountInfo, getTankStats, idToRegion, searchPlayersAcrossRegions, sumCompositeStats, type AccountListWithServer, type CompositeStatsKey, type IndividualAccountInfo, type IndividualTankStats, } from '@blitzkit/core'; import strings from '@blitzkit/core/lang/en-US.json'; import { ArrowDownIcon, MagnifyingGlassIcon, PlusIcon, } from '@radix-ui/react-icons'; import { AlertDialog, Button, DropdownMenu, Flex, Heading, IconButton, Select, Table, Text, TextField, } from '@radix-ui/themes'; import { debounce } from 'lodash-es'; import { useEffect, useMemo, useRef, useState } from 'react'; import { PageWrapper } from '../../../components/PageWrapper'; import { StickyRowHeaderCell } from '../../../components/StickyRowHeaderCell'; import { StickyTableRoot } from '../../../components/StickyTableRoot'; import { TankRowHeaderCell } from '../../../components/TankRowHeaderCell'; import { Session, type SessionTracking } from '../../../stores/session'; const tankDefinitions = await fetchTankDefinitions(); const tankAverages = await fetchAverageDefinitions(); export function Page() { return ( ); } function Content() { const [showSearch, setShowSearch] = useState(false); const [searching, setSearching] = useState(false); const [searchResults, setSearchResults] = useState([]); const [showCCInaccessibilityPrompt, setShowCCInaccessibilityPrompt] = useState(false); const input = useRef(null); const session = Session.use(); const mutateSession = Session.useMutation(); const [tankStatsB, setTankStatsB] = useState( null, ); const [accountInfo, setAccountInfo] = useState< IndividualAccountInfo | null | undefined >(undefined); useEffect(() => { (async () => { const id = session.tracking ? session.player.id : undefined; if (!id) return setAccountInfo(null); setAccountInfo( session.tracking ? await getAccountInfo(idToRegion(id), id) : null, ); })(); }, [session.tracking && session.player.id]); const delta = useMemo( () => session.tracking && tankStatsB ? deltaTankStats(session.player.stats, tankStatsB) .sort((a, b) => b.last_battle_time - a.last_battle_time) .map((entry) => { const tank = tankDefinitions.tanks[entry.tank_id]; const average = tankAverages.averages[tank.id]; const composite = compositeStats( { ...entry.all, battle_life_time: entry.battle_life_time }, average?.mu, ); return { tank, composite }; }) : undefined, [session.tracking && session.player, tankStatsB], ); const total = useMemo( () => delta ? sumCompositeStats(delta.map(({ composite }) => composite)) : undefined, [delta], ); const search = debounce(async () => { if (!input.current) return; setSearchResults(await searchPlayersAcrossRegions(input.current.value, 15)); setSearching(false); }, 500); async function track(id: number) { const region = idToRegion(id); const player = await getAccountInfo(region, id); const stats = await getTankStats(region, id); if (stats === null) { setShowCCInaccessibilityPrompt(true); return; } setShowSearch(false); mutateSession((draft) => { if (!input.current) return; draft.tracking = true; (draft as SessionTracking).player = { id, region, since: Date.now(), stats, }; input.current.value = player.nickname; }); } useEffect(() => { async function update() { if (!session.tracking) return; const tankStats = await getTankStats( idToRegion(session.player.id), session.player.id, ); setTankStatsB(tankStats); } update(); const interval = setInterval(update, 5 * 1000); return () => clearInterval(interval); }, [session.tracking && session.player.id]); return ( You're attempting to track a CC account CC (community contributor) accounts do not have public tank statistics. We are in the process of adding embeds for CC accounts, stay tuned. {!session.tracking && ( Look up a player to get started { if (!input.current) return; const sanitized = input.current.value.trim(); if (sanitized.length > 0) { setShowSearch(true); setSearching(true); search(); } else { setShowSearch(false); } }} > {showSearch && ( {searching && ( Searching... )} {!searching && searchResults.length === 0 && ( No players found )} {!searching && searchResults.length > 0 && ( {searchResults.map((player) => ( ))} )} )} )} {session.tracking && ( Tracking {accountInfo?.nickname} Since {new Date(session.player.since).toLocaleString()} )} {session.tracking && delta && delta.length > 0 && ( Tank {session.columns.map((column, index) => ( {index === 0 && session.columns.length < 4 && ( {compositeStatsKeys .filter((item) => !session.columns.includes(item)) .map((key) => ( { mutateSession((draft) => { (draft as SessionTracking).columns.unshift( key, ); }); }} > {strings.common.composite_stats[key]} ))} )} { mutateSession((draft) => { if (value === 'remove') { (draft as SessionTracking).columns.splice(index, 1); } else { (draft as SessionTracking).columns[index] = value as CompositeStatsKey; } }); }} > {compositeStatsKeys .filter( (item) => !session.columns.includes(item) || item === column, ) .map((key) => ( {strings.common.composite_stats[key]} ))} Remove ))} Total {session.columns.map((column) => ( {formatCompositeStat(total![column], column, total!)} ))} {delta.map(({ tank, composite }) => { return ( {session.columns.map((column) => ( {formatCompositeStat( composite[column], column, composite, )} ))} ); })} )} {session.tracking && delta && delta.length === 0 && ( No battles Go ahead and play a game )} ); }