feat: safe sort

This commit is contained in:
TrèsAbhi committed 2024-07-08 19:59:15 -05:00
1 parent 4d99e61bfb
commit f0fc23712a
13 files changed
+73 -65

No files matched your search

@@ -6,14 +6,15 @@ import {
CaretUpIcon, CaretUpIcon,
} from '@radix-ui/react-icons'; } from '@radix-ui/react-icons';
import { Flex, IconButton, Table } from '@radix-ui/themes'; import { Flex, IconButton, Table } from '@radix-ui/themes';
import * as TankPerformanceSort from '../../../../stores/tankPerformanceSort';
import { import {
tankPerformanceSortTypeNames, tankPerformanceSortTypeNames,
tankPerformanceSortTypeNamesArray, tankPerformanceSortTypeNamesArray,
useTankPerformanceSort, } from '../../../../stores/tankPerformanceSort/constants';
} from '../../../../stores/tankPerformanceSort';
export function Header() { export function Header() {
const sort = useTankPerformanceSort(); const sort = TankPerformanceSort.use();
const store = TankPerformanceSort.useStore();
return ( return (
<Table.Header> <Table.Header>
@@ -40,11 +41,9 @@ export function Header() {
highContrast={!isSelected} highContrast={!isSelected}
onClick={() => { onClick={() => {
if (isSelected) { if (isSelected) {
useTankPerformanceSort.setState({ store.setState({ direction: -sort.direction as 1 | -1 });
direction: -sort.direction as 1 | -1,
});
} else { } else {
useTankPerformanceSort.setState({ type, direction: -1 }); store.setState({ type, direction: -1 });
} }
}} }}
> >
@@ -20,7 +20,7 @@ export function Info() {
</Callout.Icon> </Callout.Icon>
<Callout.Text> <Callout.Text>
Career stats based on {numberFormat.format(Math.round(samples))}{' '} Career stats based on {numberFormat.format(Math.round(samples))}{' '}
players with at least 5K career battles and 1 battle in the past 30 players with at least 5K career battles and 1 battle in the past 120
days; updated daily. days; updated daily.
</Callout.Text> </Callout.Text>
</Callout.Root> </Callout.Root>
@@ -2,7 +2,7 @@ import { Skeleton, Table } from '@radix-ui/themes';
import { times } from 'lodash'; import { times } from 'lodash';
import { memo, useRef } from 'react'; import { memo, useRef } from 'react';
import { useIntersection } from '../../../../hooks/useIntersection'; import { useIntersection } from '../../../../hooks/useIntersection';
import { tankPerformanceSortTypeNamesArray } from '../../../../stores/tankPerformanceSort'; import { tankPerformanceSortTypeNamesArray } from '../../../../stores/tankPerformanceSort/constants';
interface RowLoaderProps { interface RowLoaderProps {
onIntersection?: () => void; onIntersection?: () => void;
@@ -7,7 +7,7 @@ import { averageDefinitionsArray } from '../../../../core/blitzkit/averageDefini
import { filterTank } from '../../../../core/blitzkit/filterTank'; import { filterTank } from '../../../../core/blitzkit/filterTank';
import { tankDefinitions } from '../../../../core/blitzkit/tankDefinitions'; import { tankDefinitions } from '../../../../core/blitzkit/tankDefinitions';
import * as TankFilters from '../../../../stores/tankFilters'; import * as TankFilters from '../../../../stores/tankFilters';
import { useTankPerformanceSort } from '../../../../stores/tankPerformanceSort'; import * as TankPerformanceSort from '../../../../stores/tankPerformanceSort';
import { RowLoader } from './RowLoader'; import { RowLoader } from './RowLoader';
import { TankRow } from './TankRow'; import { TankRow } from './TankRow';
import { Total } from './Total'; import { Total } from './Total';
@@ -19,7 +19,7 @@ export function Tanks() {
const awaitedTankDefinitions = use(tankDefinitions); const awaitedTankDefinitions = use(tankDefinitions);
const awaitedAverageDefinitionsArray = use(averageDefinitionsArray); const awaitedAverageDefinitionsArray = use(averageDefinitionsArray);
const filters = TankFilters.use(); const filters = TankFilters.use();
const sort = useTankPerformanceSort(); const sort = TankPerformanceSort.use();
const tanksSorted = useMemo(() => { const tanksSorted = useMemo(() => {
switch (sort.type) { switch (sort.type) {
case 'accuracy': case 'accuracy':
@@ -126,6 +126,9 @@ export function Tanks() {
{tanks.slice(0, loadedRows).map((averages) => { {tanks.slice(0, loadedRows).map((averages) => {
const tank = awaitedTankDefinitions[averages.id]; const tank = awaitedTankDefinitions[averages.id];
if (tank === undefined) return null;
return ( return (
<Suspense key={tank.id} fallback={<RowLoader />}> <Suspense key={tank.id} fallback={<RowLoader />}>
<TankRow tank={tank} /> <TankRow tank={tank} />
@@ -33,7 +33,7 @@ export function TierBreakdown() {
data={times(10, (index) => { data={times(10, (index) => {
const tier = (index + 1) as Tier; const tier = (index + 1) as Tier;
const tierTanks = awaitedAverageDefinitionsArray.filter( const tierTanks = awaitedAverageDefinitionsArray.filter(
({ id }) => awaitedTankDefinitions[id].tier === tier, ({ id }) => awaitedTankDefinitions[id]?.tier === tier,
); );
const populations: Record<TankClass, number> = { const populations: Record<TankClass, number> = {
'AT-SPG': 0, 'AT-SPG': 0,
+11
View File
@@ -0,0 +1,11 @@
import { ReactNode } from 'react';
import * as TankFilters from '../../../stores/tankFilters';
import * as TankPerformanceSort from '../../../stores/tankPerformanceSort';
export default function Layout({ children }: { children: ReactNode }) {
return (
<TankPerformanceSort.Provider>
<TankFilters.Provider>{children}</TankFilters.Provider>
</TankPerformanceSort.Provider>
);
}
+6 -9
View File
@@ -1,5 +1,4 @@
import PageWrapper from '../../../components/PageWrapper'; import PageWrapper from '../../../components/PageWrapper';
import * as TankFilters from '../../../stores/tankFilters';
import { FilterControl } from '../tankopedia/components/FilterControl'; import { FilterControl } from '../tankopedia/components/FilterControl';
import { Info } from './components/Info'; import { Info } from './components/Info';
import { TankTable } from './components/Table'; import { TankTable } from './components/Table';
@@ -7,13 +6,11 @@ import { TierBreakdown } from './components/TierBreakdown';
export default function Page() { export default function Page() {
return ( return (
<TankFilters.Provider> <PageWrapper color="jade" noMaxWidth>
<PageWrapper color="jade" noMaxWidth> <Info />
<Info /> <TierBreakdown />
<TierBreakdown /> <FilterControl />
<FilterControl /> <TankTable />
<TankTable /> </PageWrapper>
</PageWrapper>
</TankFilters.Provider>
); );
} }
+1
View File
@@ -1,3 +1,4 @@
import { TankFilters } from '../../stores/tankFilters';
import { TankDefinition } from './tankDefinitions'; import { TankDefinition } from './tankDefinitions';
export function filterTank(filters: TankFilters, tank: TankDefinition) { export function filterTank(filters: TankFilters, tank: TankDefinition) {
+3 -3
View File
@@ -42,9 +42,9 @@ export function createNextSafeStore<Type>(initialState: Type) {
function useStore() { function useStore() {
const store = useContext(Context); const store = useContext(Context);
if (!store) {
throw new Error('useStoreContext must be used within Provider'); if (!store) throw new Error('useStore must be used within Provider');
}
return store; return store;
} }
File renamed without changes.
-41
View File
@@ -1,41 +0,0 @@
import { produce } from 'immer';
import { create } from 'zustand';
export const tankPerformanceSortTypeNames = {
winrate: 'Winrate',
players: 'Players (30d)',
battles: 'Battles (career)',
damage: 'Damage',
survival: 'Survival',
xp: 'XP',
kills: 'Kills',
spots: 'Spots',
accuracy: 'Accuracy',
shots: 'Shots',
hits: 'Hits',
damageRatio: 'Dmg. ratio',
damageTaken: 'Dmg. taken',
capturePoints: 'Cap points',
} as const;
export const tankPerformanceSortTypeNamesArray = Object.keys(
tankPerformanceSortTypeNames,
) as TankPerformanceSortType[];
type TankPerformanceSortType = keyof typeof tankPerformanceSortTypeNames;
export interface TankPerformanceSort {
type: TankPerformanceSortType;
direction: -1 | 1;
}
export const useTankPerformanceSort = create<TankPerformanceSort>()(() => ({
type: 'winrate',
direction: -1,
}));
export function mutateTankPerformanceSort(
recipe: (draft: TankPerformanceSort) => void,
) {
useTankPerformanceSort.setState(produce(recipe));
}
@@ -0,0 +1,21 @@
import { TankPerformanceSortType } from '.';
export const tankPerformanceSortTypeNames = {
winrate: 'Winrate',
players: 'Players (30d)',
battles: 'Battles (career)',
damage: 'Damage',
survival: 'Survival',
xp: 'XP',
kills: 'Kills',
spots: 'Spots',
accuracy: 'Accuracy',
shots: 'Shots',
hits: 'Hits',
damageRatio: 'Dmg. ratio',
damageTaken: 'Dmg. taken',
capturePoints: 'Cap points',
} as const;
export const tankPerformanceSortTypeNamesArray = Object.keys(
tankPerformanceSortTypeNames,
) as TankPerformanceSortType[];
+17
View File
@@ -0,0 +1,17 @@
'use client';
import { createNextSafeStore } from '../../core/zustand/createNextSafeStore';
import { tankPerformanceSortTypeNames } from './constants';
export type TankPerformanceSortType = keyof typeof tankPerformanceSortTypeNames;
export interface TankPerformanceSort {
type: TankPerformanceSortType;
direction: -1 | 1;
}
export const { Provider, use, useMutation, useStore } =
createNextSafeStore<TankPerformanceSort>({
type: 'winrate',
direction: -1,
});