diff --git a/docs/changelogs/1.21.0.md b/docs/changelogs/1.21.0.md new file mode 100644 index 000000000..41433d8a2 --- /dev/null +++ b/docs/changelogs/1.21.0.md @@ -0,0 +1,15 @@ +# BlitzKit 1.21.0 + +Introducing the Tank performance tool! + +![](https://i.imgur.com/1TZzVnp.png) + +## Features + +- Added the Tank performance tool +- The tankopedia search page now loads much faster + - Not all tanks are loaded at once; rather, more tanks are loaded as you scroll down the page + +## Fixes + +- The footer is now better centered diff --git a/package.json b/package.json index 76c3e4176..fde4d6437 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "blitzkit", - "version": "1.20.5", + "version": "1.21.0", "description": "🎉 Tools for everything World of Tanks Blitz", "main": "src/server.ts", "type": "module", diff --git a/public/assets/versions.json b/public/assets/versions.json index 92635bc2f..bd566573f 100644 --- a/public/assets/versions.json +++ b/public/assets/versions.json @@ -1 +1 @@ -["1.10.0","1.10.1","1.10.2","1.11.0","1.12.0","1.12.1","1.12.2","1.12.3","1.12.4","1.12.5","1.12.6","1.12.7","1.12.8","1.12.9","1.13.0","1.13.1","1.13.2","1.13.3","1.14.0","1.14.1","1.14.2","1.14.3","1.14.4","1.15.0","1.15.1","1.15.10","1.15.11","1.15.12","1.15.13","1.15.2","1.15.3","1.15.4","1.15.5","1.15.6","1.15.7","1.15.8","1.15.9","1.16.0","1.16.1","1.16.2","1.16.3","1.16.4","1.17.0","1.17.1","1.17.2","1.18.0","1.18.1","1.18.2","1.18.3","1.19.0","1.19.1","1.19.2","1.19.3","1.19.4","1.19.5","1.19.6","1.20.0","1.20.1","1.20.2","1.20.3","1.20.4","1.20.5","1.3.0","1.3.1","1.3.2","1.3.3","1.4.0","1.4.1","1.5.0","1.5.1","1.5.2","1.5.3","1.5.4","1.6.0","1.7.0","1.7.1","1.7.2","1.7.3-1.7.4","1.7.5","1.7.6","1.7.7","1.8.0","1.8.1","1.9.0","1.9.1","1.9.2","1.9.3","1.9.4","1.9.5"] \ No newline at end of file +["1.10.0","1.10.1","1.10.2","1.11.0","1.12.0","1.12.1","1.12.2","1.12.3","1.12.4","1.12.5","1.12.6","1.12.7","1.12.8","1.12.9","1.13.0","1.13.1","1.13.2","1.13.3","1.14.0","1.14.1","1.14.2","1.14.3","1.14.4","1.15.0","1.15.1","1.15.10","1.15.11","1.15.12","1.15.13","1.15.2","1.15.3","1.15.4","1.15.5","1.15.6","1.15.7","1.15.8","1.15.9","1.16.0","1.16.1","1.16.2","1.16.3","1.16.4","1.17.0","1.17.1","1.17.2","1.18.0","1.18.1","1.18.2","1.18.3","1.19.0","1.19.1","1.19.2","1.19.3","1.19.4","1.19.5","1.19.6","1.20.0","1.20.1","1.20.2","1.20.3","1.20.4","1.20.5","1.21.0","1.3.0","1.3.1","1.3.2","1.3.3","1.4.0","1.4.1","1.5.0","1.5.1","1.5.2","1.5.3","1.5.4","1.6.0","1.7.0","1.7.1","1.7.2","1.7.3-1.7.4","1.7.5","1.7.6","1.7.7","1.8.0","1.8.1","1.9.0","1.9.1","1.9.2","1.9.3","1.9.4","1.9.5"] \ No newline at end of file diff --git a/src/app/tools/tank-performance/components/Controls.tsx b/src/app/tools/tank-performance/components/Controls.tsx new file mode 100644 index 000000000..86a34b01d --- /dev/null +++ b/src/app/tools/tank-performance/components/Controls.tsx @@ -0,0 +1,11 @@ +import { FilterControl } from '../../tankopedia/components/FilterControl'; +import { PlayerCountControl } from './PlayerCountControl'; + +export function Controls() { + return ( + <> + + + + ); +} diff --git a/src/app/tools/tank-performance/components/Header.tsx b/src/app/tools/tank-performance/components/Header.tsx index d4cf02e22..3c3693ffc 100644 --- a/src/app/tools/tank-performance/components/Header.tsx +++ b/src/app/tools/tank-performance/components/Header.tsx @@ -9,8 +9,8 @@ import { Flex, IconButton, Table } from '@radix-ui/themes'; import { StickyColumnHeaderCell } from '../../../../components/StickyColumnHeaderCell'; import * as TankPerformanceSort from '../../../../stores/tankPerformanceSort'; import { - tankPerformanceSortTypeNames, - tankPerformanceSortTypeNamesArray, + TankPerformanceSortTypeNames, + TankPerformanceSortTypeNamesArray, } from '../../../../stores/tankPerformanceSort/constants'; export function Header() { @@ -21,7 +21,7 @@ export function Header() { Tank - {tankPerformanceSortTypeNamesArray.map((type) => { + {TankPerformanceSortTypeNamesArray.map((type) => { const isSelected = sort.type === type; return ( @@ -32,7 +32,7 @@ export function Header() { justify="center" > - {tankPerformanceSortTypeNames[type]} + {TankPerformanceSortTypeNames[type]} state.playerCountPeriod, + ); + const mutateTankPerformanceSort = TankPerformancePersistent.useMutation(); + + return ( + + Player count + { + mutateTankPerformanceSort((draft) => { + draft.playerCountPeriod = period as PlayerCountPeriod; + }); + }} + > + + + + Past 120 days + + + Past 90 days + + + Past 60 days + + + This month + + + This week + + + Yesterday + + + + + ); +} diff --git a/src/app/tools/tank-performance/components/RowLoader.tsx b/src/app/tools/tank-performance/components/RowLoader.tsx index bf0b9ad6e..a019b5285 100644 --- a/src/app/tools/tank-performance/components/RowLoader.tsx +++ b/src/app/tools/tank-performance/components/RowLoader.tsx @@ -2,7 +2,7 @@ import { Skeleton, Table } from '@radix-ui/themes'; import { times } from 'lodash'; import { memo, useRef } from 'react'; import { useIntersection } from '../../../../hooks/useIntersection'; -import { tankPerformanceSortTypeNamesArray } from '../../../../stores/tankPerformanceSort/constants'; +import { TankPerformanceSortTypeNamesArray } from '../../../../stores/tankPerformanceSort/constants'; interface RowLoaderProps { onIntersection?: () => void; @@ -21,7 +21,7 @@ export const RowLoader = memo(({ onIntersection }) => { - {times(tankPerformanceSortTypeNamesArray.length, (index) => ( + {times(TankPerformanceSortTypeNamesArray.length, (index) => ( diff --git a/src/app/tools/tank-performance/components/TankRow.tsx b/src/app/tools/tank-performance/components/TankRow.tsx index 06d7df79e..c39a891c9 100644 --- a/src/app/tools/tank-performance/components/TankRow.tsx +++ b/src/app/tools/tank-performance/components/TankRow.tsx @@ -5,6 +5,7 @@ import { averageDefinitions } from '../../../../core/blitzkit/averageDefinitions import { TankDefinition } from '../../../../core/blitzkit/tankDefinitions'; import { formatCompact } from '../../../../core/math/formatCompact'; import { useAveragesExclusionRatio } from '../../../../hooks/useAveragesExclusionRatio'; +import * as TankPerformancePersistent from '../../../../stores/tankPerformancePersistent'; interface TankRowProps { tank: TankDefinition; @@ -15,6 +16,9 @@ export const TankRow = memo( const awaitedAverageDefinitions = use(averageDefinitions); const averages = awaitedAverageDefinitions.averages[tank.id]; const ratio = useAveragesExclusionRatio(); + const playerCountPeriod = TankPerformancePersistent.use( + (state) => state.playerCountPeriod, + ); return ( @@ -24,7 +28,9 @@ export const TankRow = memo( {((averages.mu.wins / averages.mu.battles) * 100).toFixed(1)}% - {formatCompact(Math.round(ratio * averages.samples.d_30))} + {formatCompact( + Math.round(ratio * averages.samples[playerCountPeriod]), + )} {formatCompact( diff --git a/src/app/tools/tank-performance/components/Total.tsx b/src/app/tools/tank-performance/components/Total.tsx index 4a2656763..543800ba0 100644 --- a/src/app/tools/tank-performance/components/Total.tsx +++ b/src/app/tools/tank-performance/components/Total.tsx @@ -1,12 +1,14 @@ import { Table } from '@radix-ui/themes'; -import { memo, useCallback } from 'react'; +import { memo, use, useCallback } from 'react'; import { StickyRowHeaderCell } from '../../../../components/StickyRowHeaderCell'; import { + averageDefinitions, AverageDefinitionsAllStats, AverageDefinitionsEntryWithId, } from '../../../../core/blitzkit/averageDefinitions'; import { formatCompact } from '../../../../core/math/formatCompact'; import { useAveragesExclusionRatio } from '../../../../hooks/useAveragesExclusionRatio'; +import * as TankPerformancePersistent from '../../../../stores/tankPerformancePersistent'; interface TotalProps { tanks: AverageDefinitionsEntryWithId[]; @@ -14,6 +16,10 @@ interface TotalProps { export const Total = memo( ({ tanks }) => { + const awaitedAverageDefinitions = use(averageDefinitions); + const playerCountPeriod = TankPerformancePersistent.use( + (state) => state.playerCountPeriod, + ); const ratio = useAveragesExclusionRatio(); const sum = useCallback( (slice: (tank: AverageDefinitionsAllStats) => number) => { @@ -25,7 +31,6 @@ export const Total = memo( [tanks], ); - const players = sum(() => 1); const battles = sum(({ battles }) => battles); const winrate = sum(({ wins }) => wins) / battles; const damage = sum(({ damage_dealt }) => damage_dealt) / battles; @@ -43,14 +48,15 @@ export const Total = memo( return ( - + Total {(winrate * 100).toFixed(1)}% - {formatCompact(ratio * players)} + + {formatCompact( + ratio * awaitedAverageDefinitions.samples[playerCountPeriod], + )} + {formatCompact(ratio * battles)} {Math.round(damage).toLocaleString()} diff --git a/src/app/tools/tank-performance/layout.tsx b/src/app/tools/tank-performance/layout.tsx index 8a0c5b6b2..21c9944c7 100644 --- a/src/app/tools/tank-performance/layout.tsx +++ b/src/app/tools/tank-performance/layout.tsx @@ -1,11 +1,14 @@ import { ReactNode } from 'react'; import * as TankFilters from '../../../stores/tankFilters'; +import * as TankPerformancePersistent from '../../../stores/tankPerformancePersistent'; import * as TankPerformanceSort from '../../../stores/tankPerformanceSort'; export default function Layout({ children }: { children: ReactNode }) { return ( - - {children} - + + + {children} + + ); } diff --git a/src/app/tools/tank-performance/page.tsx b/src/app/tools/tank-performance/page.tsx index 9a119a1f5..0a7538698 100644 --- a/src/app/tools/tank-performance/page.tsx +++ b/src/app/tools/tank-performance/page.tsx @@ -1,5 +1,5 @@ import PageWrapper from '../../../components/PageWrapper'; -import { FilterControl } from '../tankopedia/components/FilterControl'; +import { Controls } from './components/Controls'; import { Info } from './components/Info'; import { TankTable } from './components/Table'; @@ -8,7 +8,7 @@ export default function Page() { {/* */} - + ); diff --git a/src/stores/tankPerformancePersistent/constants.ts b/src/stores/tankPerformancePersistent/constants.ts new file mode 100644 index 000000000..34d2c4e07 --- /dev/null +++ b/src/stores/tankPerformancePersistent/constants.ts @@ -0,0 +1,8 @@ +export enum PlayerCountPeriod { + Past120Days = 'd_120', + Past90Days = 'd_90', + Past60Days = 'd_60', + ThisMonth = 'd_30', + ThisWeek = 'd_7', + Yesterday = 'd_1', +} diff --git a/src/stores/tankPerformancePersistent/index.ts b/src/stores/tankPerformancePersistent/index.ts new file mode 100644 index 000000000..6cebe3476 --- /dev/null +++ b/src/stores/tankPerformancePersistent/index.ts @@ -0,0 +1,23 @@ +'use client'; + +import { merge } from 'lodash'; +import { create } from 'zustand'; +import { persist, subscribeWithSelector } from 'zustand/middleware'; +import { createNextSafeStore } from '../../core/zustand/createNextSafeStore'; +import { PlayerCountPeriod } from './constants'; + +export interface TankPerformancePersistent { + playerCountPeriod: PlayerCountPeriod; +} + +export const { Provider, use, useMutation, useStore } = createNextSafeStore( + () => + create()( + persist( + subscribeWithSelector(() => ({ + playerCountPeriod: PlayerCountPeriod.ThisMonth, + })), + { name: 'tank-performance', merge: (a, b) => merge(b, a) }, + ), + ), +); diff --git a/src/stores/tankPerformanceSort/constants.ts b/src/stores/tankPerformanceSort/constants.ts index 461fd4a5c..01f23929a 100644 --- a/src/stores/tankPerformanceSort/constants.ts +++ b/src/stores/tankPerformanceSort/constants.ts @@ -1,9 +1,9 @@ import { TankPerformanceSortType } from '.'; -export const tankPerformanceSortTypeNames = { +export const TankPerformanceSortTypeNames = { winrate: 'Winrate', - players: 'Players (30d)', - battles: 'Battles (career)', + players: 'Players', + battles: 'Battles (total)', damage: 'Damage', survival: 'Survival', xp: 'XP', @@ -16,6 +16,7 @@ export const tankPerformanceSortTypeNames = { damageTaken: 'Dmg. taken', capturePoints: 'Cap points', } as const; -export const tankPerformanceSortTypeNamesArray = Object.keys( - tankPerformanceSortTypeNames, + +export const TankPerformanceSortTypeNamesArray = Object.keys( + TankPerformanceSortTypeNames, ) as TankPerformanceSortType[]; diff --git a/src/stores/tankPerformanceSort/index.ts b/src/stores/tankPerformanceSort/index.ts index 76fe22353..86893965d 100644 --- a/src/stores/tankPerformanceSort/index.ts +++ b/src/stores/tankPerformanceSort/index.ts @@ -2,9 +2,9 @@ import { create } from 'zustand'; import { createNextSafeStore } from '../../core/zustand/createNextSafeStore'; -import { tankPerformanceSortTypeNames } from './constants'; +import { TankPerformanceSortTypeNames } from './constants'; -export type TankPerformanceSortType = keyof typeof tankPerformanceSortTypeNames; +export type TankPerformanceSortType = keyof typeof TankPerformanceSortTypeNames; export interface TankPerformanceSort { type: TankPerformanceSortType;