diff --git a/src/app/api/tank-averages/route.ts b/src/app/api/tank-averages/route.ts new file mode 100644 index 000000000..7f5ab96bf --- /dev/null +++ b/src/app/api/tank-averages/route.ts @@ -0,0 +1,6 @@ +import { NextResponse } from 'next/server'; +import { tankAverages } from '../../../core/blitzstars/tankAverages'; + +export async function GET() { + return NextResponse.json(await tankAverages); +} diff --git a/src/app/tools/session/page.tsx b/src/app/tools/session/page.tsx index 83c750054..e698fbbeb 100644 --- a/src/app/tools/session/page.tsx +++ b/src/app/tools/session/page.tsx @@ -7,14 +7,18 @@ import * as Breakdown from '../../../components/Breakdown'; import { Button } from '../../../components/Button'; import PageWrapper from '../../../components/PageWrapper'; import { SearchBar } from '../../../components/SearchBar'; +import { TreeTypeEnum } from '../../../components/Tanks'; import { REGION_NAMES } from '../../../constants/regions'; import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID'; +import calculateWN8 from '../../../core/blitz/calculateWN8'; import { diffNormalizedTankStats } from '../../../core/blitz/diffNormalizedTankStats'; +import getWN8Percentile from '../../../core/blitz/getWN8Percentile'; import getWargamingResponse from '../../../core/blitz/getWargamingResponse'; import listPlayers, { AccountListWithServer, } from '../../../core/blitz/listPlayers'; -import { tankopedia } from '../../../core/blitz/tankopedia'; +import { TankopediaEntry, tankopedia } from '../../../core/blitz/tankopedia'; +import { tankAverages } from '../../../core/blitzstars/tankAverages'; import { theme } from '../../../stitches.config'; import { useSession } from '../../../stores/session'; import { @@ -24,8 +28,6 @@ import { } from '../../../types/tanksStats'; import * as styles from './page.css'; -(async () => console.log(await tankopedia))(); - export default function Page() { const input = useRef(null); const [searchResults, setSearchResults] = useState< @@ -43,16 +45,25 @@ export default function Page() { 500, ); const session = useSession(); - const [diff, setDiff] = useState(undefined); + const [diff, setDiff] = useState< + | { + stats: IndividualTankStats; + tankopedia?: TankopediaEntry; + career: IndividualTankStats; + currentWN8?: number; + careerWN8?: number; + }[] + | undefined + >(undefined); useEffect(() => { (async () => { if (session.isTracking) { const { id, region } = session; - const currentRaw = await getWargamingResponse( + const careerRaw = await getWargamingResponse( `https://api.wotblitz.${region}/wotb/tanks/stats/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`, ); - const current = currentRaw[id].reduce< + const career = careerRaw[id].reduce< Record >( (accumulator, curr) => ({ @@ -61,8 +72,34 @@ export default function Page() { }), {}, ); + const awaitedTankopedia = await tankopedia; + const awaitedTankAverages = await tankAverages; - setDiff(diffNormalizedTankStats(session.tankStats, current)); + setDiff( + ( + Object.values( + diffNormalizedTankStats(session.tankStats, career), + ) as IndividualTankStats[] + ).map((stats) => { + return { + stats, + tankopedia: awaitedTankopedia[stats.tank_id], + career: career[stats.tank_id], + currentWN8: awaitedTankAverages[stats.tank_id] + ? calculateWN8( + awaitedTankAverages[stats.tank_id].all, + stats.all, + ) + : undefined, + careerWN8: awaitedTankAverages[stats.tank_id] + ? calculateWN8( + awaitedTankAverages[stats.tank_id].all, + career[stats.tank_id].all, + ) + : undefined, + }; + }), + ); } })(); }, []); @@ -208,9 +245,68 @@ export default function Page() { {diff && - (Object.entries(diff) as [string, IndividualTankStats][]) - .sort(([, a], [, b]) => b.last_battle_time - a.last_battle_time) - .map(([id, tankStats]) => )} + diff + .sort((a, b) => b.stats.last_battle_time - a.stats.last_battle_time) + .map(({ stats, tankopedia, career, careerWN8, currentWN8 }) => ( + { + if (tankopedia?.is_collectible) return TreeTypeEnum.Collector; + if (tankopedia?.is_premium) return TreeTypeEnum.Premium; + })()} + rows={[ + { + title: 'Battles', + current: stats.all.battles.toLocaleString(), + career: career.all.battles.toLocaleString(), + }, + { + title: 'Winrate', + current: `${( + 100 * + (stats.all.wins / stats.all.battles) + ).toFixed(2)}%`, + career: `${( + 100 * + (career.all.wins / career.all.battles) + ).toFixed(2)}%`, + delta: + stats.all.wins / stats.all.battles - + career.all.wins / career.all.battles, + }, + { + title: 'WN8', + current: + currentWN8 === undefined + ? undefined + : Math.round(currentWN8).toLocaleString(), + career: + careerWN8 === undefined + ? undefined + : Math.round(careerWN8).toLocaleString(), + percentile: + currentWN8 === undefined + ? undefined + : getWN8Percentile(currentWN8), + }, + { + title: 'Damage', + current: Math.round( + stats.all.damage_dealt / stats.all.battles, + ).toLocaleString(), + career: Math.round( + career.all.damage_dealt / career.all.battles, + ).toLocaleString(), + delta: + stats.all.damage_dealt / stats.all.battles - + career.all.damage_dealt / career.all.battles, + }, + ]} + /> + ))} ); diff --git a/src/components/Breakdown/components/Row.tsx b/src/components/Breakdown/components/Row.tsx index fbbaa2717..0ee489c51 100644 --- a/src/components/Breakdown/components/Row.tsx +++ b/src/components/Breakdown/components/Row.tsx @@ -41,14 +41,16 @@ export function Row(props: RowProps) { backgroundColor: theme.colors.componentInteractive, }} > - {props.type === 'tank' && - props.treeType !== undefined && - props.tankType !== undefined && ( - - )} + {props.type === 'tank' && props.tankType !== undefined && ( + + )} response.json()) .then((wrapperTankopedia) => { console.log('Tankopedia cached'); - return (wrapperTankopedia as { data: Tankopedia }).data; + return context === 'bot' + ? (wrapperTankopedia as { data: Tankopedia }).data + : (wrapperTankopedia as Tankopedia); }); const entries = new Promise(async (resolve) => { resolve(Object.entries(await tankopedia).map(([, entry]) => entry)); diff --git a/src/core/blitzkrieg/context.ts b/src/core/blitzkrieg/context.ts new file mode 100644 index 000000000..07ba4b209 --- /dev/null +++ b/src/core/blitzkrieg/context.ts @@ -0,0 +1,4 @@ +type Context = 'website' | 'bot'; + +export const context: Context = + typeof window === 'undefined' ? 'bot' : 'website'; diff --git a/src/core/blitzstars/tankAverages.ts b/src/core/blitzstars/tankAverages.ts index c36b554ca..8fd37a864 100644 --- a/src/core/blitzstars/tankAverages.ts +++ b/src/core/blitzstars/tankAverages.ts @@ -1,4 +1,5 @@ import { AllStats } from '../../types/accountInfo'; +import { context } from '../blitzkrieg/context'; export interface SpecialStats { winrate: number; @@ -82,20 +83,24 @@ export type TankAverages = Record; console.log('Caching tank averages...'); export const tankAverages = fetch( - 'https://www.blitzstars.com/api/tankaverages.json', + context === 'bot' + ? 'https://www.blitzstars.com/api/tankaverages.json' + : '/api/tank-averages', ) .then((response) => response.json()) - .then((individualTankAverages) => { - const partialTankAverages: TankAverages = {}; + .then((json) => { + if (context === 'bot') { + const partialTankAverages: TankAverages = {}; - (individualTankAverages as IndividualTankAverage[]).forEach( - (individualTankAverage) => { + (json as IndividualTankAverage[]).forEach((individualTankAverage) => { partialTankAverages[individualTankAverage.tank_id] = individualTankAverage; - }, - ); + }); - console.log('Tank averages cached'); + console.log('Tank averages cached'); - return partialTankAverages; + return partialTankAverages; + } else { + return json as TankAverages; + } });