diff --git a/packages/website/src/components/Performance/Tanks.tsx b/packages/website/src/components/Performance/Tanks.tsx index e1dfeba0d..8c095c0b0 100644 --- a/packages/website/src/components/Performance/Tanks.tsx +++ b/packages/website/src/components/Performance/Tanks.tsx @@ -1,9 +1,10 @@ import { Table } from "@radix-ui/themes"; import { times } from "lodash-es"; import { Fragment, Suspense, useEffect, useMemo, useState } from "react"; +import usePromise from "react-promise-suspense"; import { awaitableAverageDefinitions } from "../../core/awaitables/averageDefinitions"; import { awaitableTankDefinitions } from "../../core/awaitables/tankDefinitions"; -import { filterTank } from "../../core/blitzkit/filterTank"; +import { filterTanks } from "../../core/blitzkit/filterTanks"; import { Performance } from "../../stores/performance"; import { TankFilters } from "../../stores/tankFilters"; import { TankPerformanceSort } from "../../stores/tankPerformanceSort"; @@ -20,12 +21,7 @@ const [tankDefinitions, averageDefinitions] = await Promise.all([ awaitableAverageDefinitions, ]); -const averageDefinitionsArray = Object.entries(averageDefinitions.averages).map( - ([id, average]) => ({ - id: Number(id), - ...average, - }) -); +const tankList = Object.values(tankDefinitions.tanks); export function Tanks({ skeleton }: MaybeSkeletonComponentProps) { if (skeleton) { @@ -39,98 +35,109 @@ export function Tanks({ skeleton }: MaybeSkeletonComponentProps) { } const sort = TankPerformanceSort.use(); + const filters = TankFilters.use(); + const filteredTanks = usePromise( + () => + filterTanks(filters, tankList).then((tanks) => + tanks.filter(({ id }) => id in averageDefinitions.averages) + ), + // react-promise-suspense has awful type annotations + [filters] as any + ); + const tanksMapped = useMemo( + () => + filteredTanks.map(({ id }) => ({ + id, + ...averageDefinitions.averages[id], + })), + [filters] + ); const tanksSorted = useMemo(() => { const { playerCountPeriod } = Performance.state; switch (sort.type) { case "accuracy": - return averageDefinitionsArray.sort( + return tanksMapped.sort( (a, b) => sort.direction * (a.mu.hits / a.mu.shots - b.mu.hits / b.mu.shots) ); case "capture_points": - return averageDefinitionsArray.sort( + return tanksMapped.sort( (a, b) => sort.direction * (a.mu.capture_points / a.mu.battles - b.mu.capture_points / b.mu.battles) ); case "damage": - return averageDefinitionsArray.sort( + return tanksMapped.sort( (a, b) => sort.direction * (a.mu.damage_dealt / a.mu.battles - b.mu.damage_dealt / b.mu.battles) ); case "damage_ratio": - return averageDefinitionsArray.sort( + return tanksMapped.sort( (a, b) => sort.direction * (a.mu.damage_dealt / a.mu.damage_received - b.mu.damage_dealt / b.mu.damage_received) ); case "damage_taken": - return averageDefinitionsArray.sort( + return tanksMapped.sort( (a, b) => sort.direction * (a.mu.damage_received / a.mu.battles - b.mu.damage_received / b.mu.battles) ); case "hits": - return averageDefinitionsArray.sort( + return tanksMapped.sort( (a, b) => sort.direction * (a.mu.hits / a.mu.battles - b.mu.hits / b.mu.battles) ); case "kills": - return averageDefinitionsArray.sort( + return tanksMapped.sort( (a, b) => sort.direction * (a.mu.frags / a.mu.battles - b.mu.frags / b.mu.battles) ); case "shots": - return averageDefinitionsArray.sort( + return tanksMapped.sort( (a, b) => sort.direction * (a.mu.shots / a.mu.battles - b.mu.shots / b.mu.battles) ); case "players": - return averageDefinitionsArray.sort( + return tanksMapped.sort( (a, b) => sort.direction * (a.samples[playerCountPeriod] - b.samples[playerCountPeriod]) ); case "spots": - return averageDefinitionsArray.sort( + return tanksMapped.sort( (a, b) => sort.direction * (a.mu.spotted / a.mu.battles - b.mu.spotted / b.mu.battles) ); case "survival": - return averageDefinitionsArray.sort( + return tanksMapped.sort( (a, b) => sort.direction * (a.mu.survived_battles / a.mu.battles - b.mu.survived_battles / b.mu.battles) ); case "winrate": - return averageDefinitionsArray.sort( + return tanksMapped.sort( (a, b) => sort.direction * (a.mu.wins / a.mu.battles - b.mu.wins / b.mu.battles) ); case "xp": - return averageDefinitionsArray.sort( + return tanksMapped.sort( (a, b) => sort.direction * (a.mu.xp / a.mu.battles - b.mu.xp / b.mu.battles) ); } - }, [sort]); - const filters = TankFilters.use(); - const tanks = useMemo(() => { - return tanksSorted.filter((tank) => - filterTank(filters, tankDefinitions.tanks[tank.id]) - ); }, [filters, sort]); const [loadedRows, setLoadedRows] = useState(DEFAULT_LOADED_ROWS); @@ -140,9 +147,9 @@ export function Tanks({ skeleton }: MaybeSkeletonComponentProps) { return ( - + - {tanks.slice(0, loadedRows).map((averages) => { + {tanksSorted.slice(0, loadedRows).map((averages) => { const tank = tankDefinitions.tanks[averages.id]; if (tank === undefined) return null; @@ -156,16 +163,21 @@ export function Tanks({ skeleton }: MaybeSkeletonComponentProps) { ); })} - {times(Math.min(PREVIEW_COUNT, tanks.length - loadedRows), (index) => { - return ( - { - setLoadedRows((state) => Math.min(state + 2, tanks.length)); - }} - /> - ); - })} + {times( + Math.min(PREVIEW_COUNT, tanksSorted.length - loadedRows), + (index) => { + return ( + { + setLoadedRows((state) => + Math.min(state + 2, tanksSorted.length) + ); + }} + /> + ); + } + )} ); } diff --git a/packages/website/src/components/Performance/Total.tsx b/packages/website/src/components/Performance/Total.tsx index fbe3e1eed..dbb544ddf 100644 --- a/packages/website/src/components/Performance/Total.tsx +++ b/packages/website/src/components/Performance/Total.tsx @@ -2,16 +2,16 @@ import { AverageDefinitionsEntry, formatCompact, type BlitzkitStats, -} from '@blitzkit/core'; -import { Table } from '@radix-ui/themes'; -import { memo, useCallback } from 'react'; -import { awaitableAverageDefinitions } from '../../core/awaitables/averageDefinitions'; -import { useAveragesExclusionRatio } from '../../hooks/useAveragesExclusionRatio'; -import { useLocale } from '../../hooks/useLocale'; -import { Performance } from '../../stores/performance'; -import { StickyRowHeaderCell } from '../StickyRowHeaderCell'; +} from "@blitzkit/core"; +import { Table } from "@radix-ui/themes"; +import { memo, useCallback } from "react"; +import { awaitableAverageDefinitions } from "../../core/awaitables/averageDefinitions"; +import { useAveragesExclusionRatio } from "../../hooks/useAveragesExclusionRatio"; +import { useLocale } from "../../hooks/useLocale"; +import { Performance } from "../../stores/performance"; +import { StickyRowHeaderCell } from "../StickyRowHeaderCell"; -interface AverageDefinitionsEntryWithId extends AverageDefinitionsEntry { +export interface AverageDefinitionsEntryWithId extends AverageDefinitionsEntry { id: number; } @@ -24,7 +24,7 @@ const averageDefinitions = await awaitableAverageDefinitions; export const Total = memo( ({ tanks }) => { const playerCountPeriod = Performance.use( - (state) => state.playerCountPeriod, + (state) => state.playerCountPeriod ); const { strings, locale } = useLocale(); const ratio = useAveragesExclusionRatio(); @@ -32,10 +32,10 @@ export const Total = memo( (slice: (tank: BlitzkitStats) => number) => { return tanks.reduce( (acc, tank) => acc + tank.samples[playerCountPeriod] * slice(tank.mu), - 0, + 0 ); }, - [tanks], + [tanks] ); const battles = sum(({ battles }) => battles); @@ -55,14 +55,14 @@ export const Total = memo( return ( - + {strings.website.tools.performance.table.tanks.total} {(winrate * 100).toFixed(1)}% {formatCompact( locale, - ratio * averageDefinitions.samples[playerCountPeriod], + ratio * averageDefinitions.samples[playerCountPeriod] )} @@ -87,5 +87,5 @@ export const Total = memo( ); }, - (prev, next) => prev.tanks === next.tanks, + (prev, next) => prev.tanks === next.tanks );