mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 02:37:05 +02:00
feat: variable player count
This commit is contained in:
1 parent
d6fc037d4f
commit
03ce75c15d
15 files changed
+149
-28
No files matched your search
@@ -0,0 +1,15 @@
|
||||
# BlitzKit 1.21.0
|
||||
|
||||
Introducing the Tank performance tool!
|
||||
|
||||

|
||||
|
||||
## 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
|
||||
+1
-1
@@ -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",
|
||||
|
||||
@@ -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"]
|
||||
["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"]
|
||||
@@ -0,0 +1,11 @@
|
||||
import { FilterControl } from '../../tankopedia/components/FilterControl';
|
||||
import { PlayerCountControl } from './PlayerCountControl';
|
||||
|
||||
export function Controls() {
|
||||
return (
|
||||
<>
|
||||
<FilterControl />
|
||||
<PlayerCountControl />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -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() {
|
||||
<Table.Header>
|
||||
<Table.Row align="center">
|
||||
<StickyColumnHeaderCell>Tank</StickyColumnHeaderCell>
|
||||
{tankPerformanceSortTypeNamesArray.map((type) => {
|
||||
{TankPerformanceSortTypeNamesArray.map((type) => {
|
||||
const isSelected = sort.type === type;
|
||||
|
||||
return (
|
||||
@@ -32,7 +32,7 @@ export function Header() {
|
||||
justify="center"
|
||||
>
|
||||
<Flex align="center" gap="1">
|
||||
{tankPerformanceSortTypeNames[type]}
|
||||
{TankPerformanceSortTypeNames[type]}
|
||||
|
||||
<IconButton
|
||||
size={isSelected ? '1' : '2'}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
'use client';
|
||||
|
||||
import { Flex, Select, Text } from '@radix-ui/themes';
|
||||
import * as TankPerformancePersistent from '../../../../stores/tankPerformancePersistent';
|
||||
import { PlayerCountPeriod } from '../../../../stores/tankPerformancePersistent/constants';
|
||||
|
||||
export function PlayerCountControl() {
|
||||
const playerCountPeriod = TankPerformancePersistent.use(
|
||||
(state) => state.playerCountPeriod,
|
||||
);
|
||||
const mutateTankPerformanceSort = TankPerformancePersistent.useMutation();
|
||||
|
||||
return (
|
||||
<Flex justify="center" align="center" gap="2">
|
||||
<Text>Player count</Text>
|
||||
<Select.Root
|
||||
value={playerCountPeriod}
|
||||
onValueChange={(period) => {
|
||||
mutateTankPerformanceSort((draft) => {
|
||||
draft.playerCountPeriod = period as PlayerCountPeriod;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Select.Trigger />
|
||||
<Select.Content>
|
||||
<Select.Item value={PlayerCountPeriod.Past120Days}>
|
||||
Past 120 days
|
||||
</Select.Item>
|
||||
<Select.Item value={PlayerCountPeriod.Past90Days}>
|
||||
Past 90 days
|
||||
</Select.Item>
|
||||
<Select.Item value={PlayerCountPeriod.Past60Days}>
|
||||
Past 60 days
|
||||
</Select.Item>
|
||||
<Select.Item value={PlayerCountPeriod.ThisMonth}>
|
||||
This month
|
||||
</Select.Item>
|
||||
<Select.Item value={PlayerCountPeriod.ThisWeek}>
|
||||
This week
|
||||
</Select.Item>
|
||||
<Select.Item value={PlayerCountPeriod.Yesterday}>
|
||||
Yesterday
|
||||
</Select.Item>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@@ -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<RowLoaderProps>(({ onIntersection }) => {
|
||||
<Skeleton height="100%" width="128px" />
|
||||
</Table.RowHeaderCell>
|
||||
|
||||
{times(tankPerformanceSortTypeNamesArray.length, (index) => (
|
||||
{times(TankPerformanceSortTypeNamesArray.length, (index) => (
|
||||
<Table.Cell key={index} align="center">
|
||||
<Skeleton height="1em" width="32px" />
|
||||
</Table.Cell>
|
||||
|
||||
@@ -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<TankRowProps>(
|
||||
const awaitedAverageDefinitions = use(averageDefinitions);
|
||||
const averages = awaitedAverageDefinitions.averages[tank.id];
|
||||
const ratio = useAveragesExclusionRatio();
|
||||
const playerCountPeriod = TankPerformancePersistent.use(
|
||||
(state) => state.playerCountPeriod,
|
||||
);
|
||||
|
||||
return (
|
||||
<Table.Row>
|
||||
@@ -24,7 +28,9 @@ export const TankRow = memo<TankRowProps>(
|
||||
{((averages.mu.wins / averages.mu.battles) * 100).toFixed(1)}%
|
||||
</Table.Cell>
|
||||
<Table.Cell align="center">
|
||||
{formatCompact(Math.round(ratio * averages.samples.d_30))}
|
||||
{formatCompact(
|
||||
Math.round(ratio * averages.samples[playerCountPeriod]),
|
||||
)}
|
||||
</Table.Cell>
|
||||
<Table.Cell align="center">
|
||||
{formatCompact(
|
||||
|
||||
@@ -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<TotalProps>(
|
||||
({ 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<TotalProps>(
|
||||
[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<TotalProps>(
|
||||
|
||||
return (
|
||||
<Table.Row>
|
||||
<StickyRowHeaderCell
|
||||
// maxWidth={{ initial: '128px', sm: '240px' }}
|
||||
style={{ overflow: 'hidden', display: 'flex' }}
|
||||
>
|
||||
<StickyRowHeaderCell style={{ overflow: 'hidden', display: 'flex' }}>
|
||||
Total
|
||||
</StickyRowHeaderCell>
|
||||
<Table.Cell align="center">{(winrate * 100).toFixed(1)}%</Table.Cell>
|
||||
<Table.Cell align="center">{formatCompact(ratio * players)}</Table.Cell>
|
||||
<Table.Cell align="center">
|
||||
{formatCompact(
|
||||
ratio * awaitedAverageDefinitions.samples[playerCountPeriod],
|
||||
)}
|
||||
</Table.Cell>
|
||||
<Table.Cell align="center">{formatCompact(ratio * battles)}</Table.Cell>
|
||||
<Table.Cell align="center">
|
||||
{Math.round(damage).toLocaleString()}
|
||||
|
||||
@@ -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 (
|
||||
<TankPerformanceSort.Provider>
|
||||
<TankFilters.Provider>{children}</TankFilters.Provider>
|
||||
</TankPerformanceSort.Provider>
|
||||
<TankPerformancePersistent.Provider>
|
||||
<TankPerformanceSort.Provider>
|
||||
<TankFilters.Provider>{children}</TankFilters.Provider>
|
||||
</TankPerformanceSort.Provider>
|
||||
</TankPerformancePersistent.Provider>
|
||||
);
|
||||
}
|
||||
@@ -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() {
|
||||
<PageWrapper color="jade" noMaxWidth>
|
||||
<Info />
|
||||
{/* <TierBreakdown /> */}
|
||||
<FilterControl />
|
||||
<Controls />
|
||||
<TankTable />
|
||||
</PageWrapper>
|
||||
);
|
||||
|
||||
@@ -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',
|
||||
}
|
||||
@@ -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<TankPerformancePersistent>()(
|
||||
persist(
|
||||
subscribeWithSelector<TankPerformancePersistent>(() => ({
|
||||
playerCountPeriod: PlayerCountPeriod.ThisMonth,
|
||||
})),
|
||||
{ name: 'tank-performance', merge: (a, b) => merge(b, a) },
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -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[];
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user