mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
fix: performance filters
This commit is contained in:
1 parent
4ab03e56a2
commit
a4e48a2feb
2 files changed
+65
-53
No files matched your search
@@ -1,9 +1,10 @@
|
|||||||
import { Table } from "@radix-ui/themes";
|
import { Table } from "@radix-ui/themes";
|
||||||
import { times } from "lodash-es";
|
import { times } from "lodash-es";
|
||||||
import { Fragment, Suspense, useEffect, useMemo, useState } from "react";
|
import { Fragment, Suspense, useEffect, useMemo, useState } from "react";
|
||||||
|
import usePromise from "react-promise-suspense";
|
||||||
import { awaitableAverageDefinitions } from "../../core/awaitables/averageDefinitions";
|
import { awaitableAverageDefinitions } from "../../core/awaitables/averageDefinitions";
|
||||||
import { awaitableTankDefinitions } from "../../core/awaitables/tankDefinitions";
|
import { awaitableTankDefinitions } from "../../core/awaitables/tankDefinitions";
|
||||||
import { filterTank } from "../../core/blitzkit/filterTank";
|
import { filterTanks } from "../../core/blitzkit/filterTanks";
|
||||||
import { Performance } from "../../stores/performance";
|
import { Performance } from "../../stores/performance";
|
||||||
import { TankFilters } from "../../stores/tankFilters";
|
import { TankFilters } from "../../stores/tankFilters";
|
||||||
import { TankPerformanceSort } from "../../stores/tankPerformanceSort";
|
import { TankPerformanceSort } from "../../stores/tankPerformanceSort";
|
||||||
@@ -20,12 +21,7 @@ const [tankDefinitions, averageDefinitions] = await Promise.all([
|
|||||||
awaitableAverageDefinitions,
|
awaitableAverageDefinitions,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const averageDefinitionsArray = Object.entries(averageDefinitions.averages).map(
|
const tankList = Object.values(tankDefinitions.tanks);
|
||||||
([id, average]) => ({
|
|
||||||
id: Number(id),
|
|
||||||
...average,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
export function Tanks({ skeleton }: MaybeSkeletonComponentProps) {
|
export function Tanks({ skeleton }: MaybeSkeletonComponentProps) {
|
||||||
if (skeleton) {
|
if (skeleton) {
|
||||||
@@ -39,98 +35,109 @@ export function Tanks({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sort = TankPerformanceSort.use();
|
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 tanksSorted = useMemo(() => {
|
||||||
const { playerCountPeriod } = Performance.state;
|
const { playerCountPeriod } = Performance.state;
|
||||||
|
|
||||||
switch (sort.type) {
|
switch (sort.type) {
|
||||||
case "accuracy":
|
case "accuracy":
|
||||||
return averageDefinitionsArray.sort(
|
return tanksMapped.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
sort.direction * (a.mu.hits / a.mu.shots - b.mu.hits / b.mu.shots)
|
sort.direction * (a.mu.hits / a.mu.shots - b.mu.hits / b.mu.shots)
|
||||||
);
|
);
|
||||||
case "capture_points":
|
case "capture_points":
|
||||||
return averageDefinitionsArray.sort(
|
return tanksMapped.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
sort.direction *
|
sort.direction *
|
||||||
(a.mu.capture_points / a.mu.battles -
|
(a.mu.capture_points / a.mu.battles -
|
||||||
b.mu.capture_points / b.mu.battles)
|
b.mu.capture_points / b.mu.battles)
|
||||||
);
|
);
|
||||||
case "damage":
|
case "damage":
|
||||||
return averageDefinitionsArray.sort(
|
return tanksMapped.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
sort.direction *
|
sort.direction *
|
||||||
(a.mu.damage_dealt / a.mu.battles -
|
(a.mu.damage_dealt / a.mu.battles -
|
||||||
b.mu.damage_dealt / b.mu.battles)
|
b.mu.damage_dealt / b.mu.battles)
|
||||||
);
|
);
|
||||||
case "damage_ratio":
|
case "damage_ratio":
|
||||||
return averageDefinitionsArray.sort(
|
return tanksMapped.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
sort.direction *
|
sort.direction *
|
||||||
(a.mu.damage_dealt / a.mu.damage_received -
|
(a.mu.damage_dealt / a.mu.damage_received -
|
||||||
b.mu.damage_dealt / b.mu.damage_received)
|
b.mu.damage_dealt / b.mu.damage_received)
|
||||||
);
|
);
|
||||||
case "damage_taken":
|
case "damage_taken":
|
||||||
return averageDefinitionsArray.sort(
|
return tanksMapped.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
sort.direction *
|
sort.direction *
|
||||||
(a.mu.damage_received / a.mu.battles -
|
(a.mu.damage_received / a.mu.battles -
|
||||||
b.mu.damage_received / b.mu.battles)
|
b.mu.damage_received / b.mu.battles)
|
||||||
);
|
);
|
||||||
case "hits":
|
case "hits":
|
||||||
return averageDefinitionsArray.sort(
|
return tanksMapped.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
sort.direction *
|
sort.direction *
|
||||||
(a.mu.hits / a.mu.battles - b.mu.hits / b.mu.battles)
|
(a.mu.hits / a.mu.battles - b.mu.hits / b.mu.battles)
|
||||||
);
|
);
|
||||||
case "kills":
|
case "kills":
|
||||||
return averageDefinitionsArray.sort(
|
return tanksMapped.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
sort.direction *
|
sort.direction *
|
||||||
(a.mu.frags / a.mu.battles - b.mu.frags / b.mu.battles)
|
(a.mu.frags / a.mu.battles - b.mu.frags / b.mu.battles)
|
||||||
);
|
);
|
||||||
case "shots":
|
case "shots":
|
||||||
return averageDefinitionsArray.sort(
|
return tanksMapped.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
sort.direction *
|
sort.direction *
|
||||||
(a.mu.shots / a.mu.battles - b.mu.shots / b.mu.battles)
|
(a.mu.shots / a.mu.battles - b.mu.shots / b.mu.battles)
|
||||||
);
|
);
|
||||||
case "players":
|
case "players":
|
||||||
return averageDefinitionsArray.sort(
|
return tanksMapped.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
sort.direction *
|
sort.direction *
|
||||||
(a.samples[playerCountPeriod] - b.samples[playerCountPeriod])
|
(a.samples[playerCountPeriod] - b.samples[playerCountPeriod])
|
||||||
);
|
);
|
||||||
case "spots":
|
case "spots":
|
||||||
return averageDefinitionsArray.sort(
|
return tanksMapped.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
sort.direction *
|
sort.direction *
|
||||||
(a.mu.spotted / a.mu.battles - b.mu.spotted / b.mu.battles)
|
(a.mu.spotted / a.mu.battles - b.mu.spotted / b.mu.battles)
|
||||||
);
|
);
|
||||||
case "survival":
|
case "survival":
|
||||||
return averageDefinitionsArray.sort(
|
return tanksMapped.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
sort.direction *
|
sort.direction *
|
||||||
(a.mu.survived_battles / a.mu.battles -
|
(a.mu.survived_battles / a.mu.battles -
|
||||||
b.mu.survived_battles / b.mu.battles)
|
b.mu.survived_battles / b.mu.battles)
|
||||||
);
|
);
|
||||||
case "winrate":
|
case "winrate":
|
||||||
return averageDefinitionsArray.sort(
|
return tanksMapped.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
sort.direction *
|
sort.direction *
|
||||||
(a.mu.wins / a.mu.battles - b.mu.wins / b.mu.battles)
|
(a.mu.wins / a.mu.battles - b.mu.wins / b.mu.battles)
|
||||||
);
|
);
|
||||||
case "xp":
|
case "xp":
|
||||||
return averageDefinitionsArray.sort(
|
return tanksMapped.sort(
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
sort.direction * (a.mu.xp / a.mu.battles - b.mu.xp / b.mu.battles)
|
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]);
|
}, [filters, sort]);
|
||||||
const [loadedRows, setLoadedRows] = useState(DEFAULT_LOADED_ROWS);
|
const [loadedRows, setLoadedRows] = useState(DEFAULT_LOADED_ROWS);
|
||||||
|
|
||||||
@@ -140,9 +147,9 @@ export function Tanks({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Table.Body>
|
<Table.Body>
|
||||||
<Total tanks={tanks} />
|
<Total tanks={tanksSorted} />
|
||||||
|
|
||||||
{tanks.slice(0, loadedRows).map((averages) => {
|
{tanksSorted.slice(0, loadedRows).map((averages) => {
|
||||||
const tank = tankDefinitions.tanks[averages.id];
|
const tank = tankDefinitions.tanks[averages.id];
|
||||||
|
|
||||||
if (tank === undefined) return null;
|
if (tank === undefined) return null;
|
||||||
@@ -156,16 +163,21 @@ export function Tanks({ skeleton }: MaybeSkeletonComponentProps) {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{times(Math.min(PREVIEW_COUNT, tanks.length - loadedRows), (index) => {
|
{times(
|
||||||
return (
|
Math.min(PREVIEW_COUNT, tanksSorted.length - loadedRows),
|
||||||
<RowLoader
|
(index) => {
|
||||||
key={index}
|
return (
|
||||||
onIntersection={() => {
|
<RowLoader
|
||||||
setLoadedRows((state) => Math.min(state + 2, tanks.length));
|
key={index}
|
||||||
}}
|
onIntersection={() => {
|
||||||
/>
|
setLoadedRows((state) =>
|
||||||
);
|
Math.min(state + 2, tanksSorted.length)
|
||||||
})}
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)}
|
||||||
</Table.Body>
|
</Table.Body>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2,16 +2,16 @@ import {
|
|||||||
AverageDefinitionsEntry,
|
AverageDefinitionsEntry,
|
||||||
formatCompact,
|
formatCompact,
|
||||||
type BlitzkitStats,
|
type BlitzkitStats,
|
||||||
} from '@blitzkit/core';
|
} from "@blitzkit/core";
|
||||||
import { Table } from '@radix-ui/themes';
|
import { Table } from "@radix-ui/themes";
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from "react";
|
||||||
import { awaitableAverageDefinitions } from '../../core/awaitables/averageDefinitions';
|
import { awaitableAverageDefinitions } from "../../core/awaitables/averageDefinitions";
|
||||||
import { useAveragesExclusionRatio } from '../../hooks/useAveragesExclusionRatio';
|
import { useAveragesExclusionRatio } from "../../hooks/useAveragesExclusionRatio";
|
||||||
import { useLocale } from '../../hooks/useLocale';
|
import { useLocale } from "../../hooks/useLocale";
|
||||||
import { Performance } from '../../stores/performance';
|
import { Performance } from "../../stores/performance";
|
||||||
import { StickyRowHeaderCell } from '../StickyRowHeaderCell';
|
import { StickyRowHeaderCell } from "../StickyRowHeaderCell";
|
||||||
|
|
||||||
interface AverageDefinitionsEntryWithId extends AverageDefinitionsEntry {
|
export interface AverageDefinitionsEntryWithId extends AverageDefinitionsEntry {
|
||||||
id: number;
|
id: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ const averageDefinitions = await awaitableAverageDefinitions;
|
|||||||
export const Total = memo<TotalProps>(
|
export const Total = memo<TotalProps>(
|
||||||
({ tanks }) => {
|
({ tanks }) => {
|
||||||
const playerCountPeriod = Performance.use(
|
const playerCountPeriod = Performance.use(
|
||||||
(state) => state.playerCountPeriod,
|
(state) => state.playerCountPeriod
|
||||||
);
|
);
|
||||||
const { strings, locale } = useLocale();
|
const { strings, locale } = useLocale();
|
||||||
const ratio = useAveragesExclusionRatio();
|
const ratio = useAveragesExclusionRatio();
|
||||||
@@ -32,10 +32,10 @@ export const Total = memo<TotalProps>(
|
|||||||
(slice: (tank: BlitzkitStats) => number) => {
|
(slice: (tank: BlitzkitStats) => number) => {
|
||||||
return tanks.reduce(
|
return tanks.reduce(
|
||||||
(acc, tank) => acc + tank.samples[playerCountPeriod] * slice(tank.mu),
|
(acc, tank) => acc + tank.samples[playerCountPeriod] * slice(tank.mu),
|
||||||
0,
|
0
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[tanks],
|
[tanks]
|
||||||
);
|
);
|
||||||
|
|
||||||
const battles = sum(({ battles }) => battles);
|
const battles = sum(({ battles }) => battles);
|
||||||
@@ -55,14 +55,14 @@ export const Total = memo<TotalProps>(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<StickyRowHeaderCell style={{ overflow: 'hidden', display: 'flex' }}>
|
<StickyRowHeaderCell style={{ overflow: "hidden", display: "flex" }}>
|
||||||
{strings.website.tools.performance.table.tanks.total}
|
{strings.website.tools.performance.table.tanks.total}
|
||||||
</StickyRowHeaderCell>
|
</StickyRowHeaderCell>
|
||||||
<Table.Cell align="center">{(winrate * 100).toFixed(1)}%</Table.Cell>
|
<Table.Cell align="center">{(winrate * 100).toFixed(1)}%</Table.Cell>
|
||||||
<Table.Cell align="center">
|
<Table.Cell align="center">
|
||||||
{formatCompact(
|
{formatCompact(
|
||||||
locale,
|
locale,
|
||||||
ratio * averageDefinitions.samples[playerCountPeriod],
|
ratio * averageDefinitions.samples[playerCountPeriod]
|
||||||
)}
|
)}
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell align="center">
|
<Table.Cell align="center">
|
||||||
@@ -87,5 +87,5 @@ export const Total = memo<TotalProps>(
|
|||||||
</Table.Row>
|
</Table.Row>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(prev, next) => prev.tanks === next.tanks,
|
(prev, next) => prev.tanks === next.tanks
|
||||||
);
|
);
|
||||||
Reference in new issue
Block a user