From 38b2b6025f3d50e5aed0f5d794f3330d0930f570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Sat, 22 Jun 2024 16:39:31 -0500 Subject: [PATCH] feat: start of sorting --- .../tools/tankopedia/components/Results.tsx | 321 +++++++++++++++++- .../tools/tankopedia/components/SearchBar.tsx | 46 +-- .../{TankSearch/components => }/Sort.tsx | 76 +++-- .../tools/tankopedia/components/TankCard.tsx | 47 ++- .../components/TankSearch/index.tsx | 2 +- .../tools/tankopedia/components/TierCard.tsx | 22 +- src/app/tools/tankopedia/page.tsx | 7 +- src/stores/tankopediaFilters.ts | 9 + 8 files changed, 455 insertions(+), 75 deletions(-) rename src/app/tools/tankopedia/components/{TankSearch/components => }/Sort.tsx (83%) diff --git a/src/app/tools/tankopedia/components/Results.tsx b/src/app/tools/tankopedia/components/Results.tsx index 59bf5bb5a..52affb9af 100644 --- a/src/app/tools/tankopedia/components/Results.tsx +++ b/src/app/tools/tankopedia/components/Results.tsx @@ -2,12 +2,21 @@ import { Callout, Flex } from '@radix-ui/themes'; import { go } from 'fuzzysort'; import { range } from 'lodash'; import { memo, use, useMemo } from 'react'; +import { Vector2Tuple } from 'three'; import { ExperimentIcon } from '../../../../components/ExperimentIcon'; +import { resolveNearPenetration } from '../../../../core/blitz/resolveNearPenetration'; +import { modelDefinitions } from '../../../../core/blitzkit/modelDefinitions'; +import { normalizeBoundingBox } from '../../../../core/blitzkit/normalizeBoundingBox'; +import { resolveDpm } from '../../../../core/blitzkit/resolveDpm'; +import { resolveReload } from '../../../../core/blitzkit/resolveReload'; import { + TankDefinition, Tier, tankDefinitions, tankNames, + tanksDefinitionsArray, } from '../../../../core/blitzkit/tankDefinitions'; +import { unionBoundingBox } from '../../../../core/blitzkit/unionBoundingBox'; import { useTankopediaFilters } from '../../../../stores/tankopediaFilters'; import { SearchBar } from './SearchBar'; import { TankCard } from './TankCard'; @@ -15,20 +24,309 @@ import { TankCardWrapper } from './TankCardWrapper'; import { TierCard } from './TierCard'; export const Results = memo(() => { + const awaitedModelDefinitions = use(modelDefinitions); const awaitedTankDefinitions = use(tankDefinitions); + const awaitedTanksDefinitionsArray = use(tanksDefinitionsArray); const awaitedTankNames = use(tankNames); const testing = useTankopediaFilters((state) => state.testing); const search = useTankopediaFilters((state) => state.search); + const sort = useTankopediaFilters((state) => state.sort); const searchedTanks = useMemo(() => { - if (search === undefined) return undefined; + if (search !== undefined) { + const searchedRaw = go(search, awaitedTankNames, { key: 'combined' }); + const searchedTanks = searchedRaw.map( + (result) => awaitedTankDefinitions[result.obj.id], + ); + return searchedTanks; + } - const searchedRaw = go(search, awaitedTankNames, { key: 'combined' }); - const searchedTanks = searchedRaw.map( - (result) => awaitedTankDefinitions[result.obj.id], - ); + if (sort.by !== 'meta.tier') { + const filtered = awaitedTanksDefinitionsArray; + let sorted: TankDefinition[]; - return searchedTanks; - }, [search]); + switch (sort.by) { + case 'meta.name': + sorted = filtered.sort((a, b) => a.name?.localeCompare(b.name)); + break; + + case 'survivability.health': { + sorted = filtered.sort((a, b) => { + const aHealth = a.health + a.turrets.at(-1)!.health; + const bHealth = b.health + b.turrets.at(-1)!.health; + + return aHealth - bHealth; + }); + break; + } + + case 'survivability.viewRange': + sorted = filtered.sort( + (a, b) => a.turrets.at(-1)!.viewRange - b.turrets.at(-1)!.viewRange, + ); + break; + + case 'survivability.camouflageStill': + sorted = filtered.sort( + (a, b) => a.camouflage.still - b.camouflage.still, + ); + break; + + case 'survivability.camouflageMoving': + sorted = filtered.sort( + (a, b) => a.camouflage.moving - b.camouflage.moving, + ); + break; + + case 'survivability.camouflageShooting': + sorted = filtered.sort( + (a, b) => + a.turrets.at(-1)!.guns.at(-1)!.camouflageLoss - + b.turrets.at(-1)!.guns.at(-1)!.camouflageLoss, + ); + break; + + case 'survivability.volume': + sorted = filtered.sort((a, b) => { + const aTankModelDefinition = awaitedModelDefinitions[a.id]; + const bTankModelDefinition = awaitedModelDefinitions[b.id]; + const aTurretModelDefinition = + aTankModelDefinition.turrets[a.turrets.at(-1)!.id]; + const bTurretModelDefinition = + bTankModelDefinition.turrets[b.turrets.at(-1)!.id]; + const aSize = normalizeBoundingBox( + unionBoundingBox( + aTankModelDefinition.boundingBox, + aTurretModelDefinition.boundingBox, + ), + ); + const bSize = normalizeBoundingBox( + unionBoundingBox( + bTankModelDefinition.boundingBox, + bTurretModelDefinition.boundingBox, + ), + ); + const aVolume = aSize[0] * aSize[1] * aSize[2]; + const bVolume = bSize[0] * bSize[1] * bSize[2]; + + return aVolume - bVolume; + }); + break; + + case 'survivability.length': + sorted = filtered.sort((a, b) => { + const aTankModelDefinition = awaitedModelDefinitions[a.id]; + const bTankModelDefinition = awaitedModelDefinitions[b.id]; + const aTurretModelDefinition = + aTankModelDefinition.turrets[a.turrets.at(-1)!.id]; + const bTurretModelDefinition = + bTankModelDefinition.turrets[b.turrets.at(-1)!.id]; + const aSize = normalizeBoundingBox( + unionBoundingBox( + aTankModelDefinition.boundingBox, + aTurretModelDefinition.boundingBox, + ), + ); + const bSize = normalizeBoundingBox( + unionBoundingBox( + bTankModelDefinition.boundingBox, + bTurretModelDefinition.boundingBox, + ), + ); + const aLength = Math.max(aSize[0], aSize[1], aSize[2]); + const bLength = Math.max(bSize[0], bSize[1], bSize[2]); + + return aLength - bLength; + }); + break; + + case 'fire.dpm': + sorted = filtered.sort( + (a, b) => + resolveDpm( + a.turrets.at(-1)!.guns.at(-1)!, + a.turrets.at(-1)!.guns.at(-1)!.shells[0], + ) - + resolveDpm( + b.turrets.at(-1)!.guns.at(-1)!, + b.turrets.at(-1)!.guns.at(-1)!.shells[0], + ), + ); + break; + + case 'fire.reload': + sorted = filtered.sort( + (a, b) => + resolveReload(a.turrets.at(-1)!.guns.at(-1)!) - + resolveReload(b.turrets.at(-1)!.guns.at(-1)!), + ); + break; + + case 'fire.caliber': + sorted = filtered.sort( + (a, b) => + a.turrets.at(-1)!.guns.at(-1)!.shells[0].caliber - + b.turrets.at(-1)!.guns.at(-1)!.shells[0].caliber, + ); + break; + + case 'fire.standardPenetration': + sorted = filtered.sort( + (a, b) => + resolveNearPenetration( + a.turrets.at(-1)!.guns.at(-1)!.shells[0].penetration, + ) - + resolveNearPenetration( + b.turrets.at(-1)!.guns.at(-1)!.shells[0].penetration, + ), + ); + break; + + case 'fire.premiumPenetration': + sorted = filtered.sort( + (a, b) => + resolveNearPenetration( + ( + a.turrets.at(-1)!.guns.at(-1)!.shells[1] ?? + a.turrets.at(-1)!.guns.at(-1)!.shells[0] + ).penetration, + ) - + resolveNearPenetration( + ( + b.turrets.at(-1)!.guns.at(-1)!.shells[1] ?? + b.turrets.at(-1)!.guns.at(-1)!.shells[0] + ).penetration, + ), + ); + break; + + case 'fire.damage': + sorted = filtered.sort( + (a, b) => + a.turrets.at(-1)!.guns.at(-1)!.shells[0].damage.armor - + b.turrets.at(-1)!.guns.at(-1)!.shells[0].damage.armor, + ); + break; + + case 'fire.shellVelocity': + sorted = filtered.sort( + (a, b) => + a.turrets.at(-1)!.guns.at(-1)!.shells[0].speed - + b.turrets.at(-1)!.guns.at(-1)!.shells[0].speed, + ); + break; + + case 'fire.aimTime': + sorted = filtered.sort( + (a, b) => + a.turrets.at(-1)!.guns.at(-1)!.aimTime - + b.turrets.at(-1)!.guns.at(-1)!.aimTime, + ); + break; + + case 'fire.dispersionStill': + sorted = filtered.sort( + (a, b) => + a.turrets.at(-1)!.guns.at(-1)!.dispersion.base - + b.turrets.at(-1)!.guns.at(-1)!.dispersion.base, + ); + break; + + case 'fire.dispersionMoving': + sorted = filtered.sort( + (a, b) => + a.tracks.at(-1)!.dispersion.move - + b.tracks.at(-1)!.dispersion.move, + ); + break; + + case 'fire.gunDepression': + sorted = filtered.sort( + (a, b) => + awaitedModelDefinitions[a.id].turrets[a.turrets.at(-1)!.id].guns[ + a.turrets.at(-1)!.guns.at(-1)!.id + ].pitch.max - + awaitedModelDefinitions[b.id].turrets[b.turrets.at(-1)!.id].guns[ + b.turrets.at(-1)!.guns.at(-1)!.id + ].pitch.max, + ); + break; + + case 'fire.gunElevation': + sorted = filtered.sort( + (a, b) => + // reversed because of negative values + awaitedModelDefinitions[b.id].turrets[b.turrets.at(-1)!.id].guns[ + b.turrets.at(-1)!.guns.at(-1)!.id + ].pitch.min - + awaitedModelDefinitions[a.id].turrets[a.turrets.at(-1)!.id].guns[ + a.turrets.at(-1)!.guns.at(-1)!.id + ].pitch.min, + ); + break; + + case 'maneuverability.forwardsSpeed': + sorted = filtered.sort((a, b) => a.speed.forwards - b.speed.forwards); + break; + + case 'maneuverability.backwardsSpeed': + sorted = filtered.sort( + (a, b) => a.speed.backwards - b.speed.backwards, + ); + break; + + case 'maneuverability.power': + sorted = filtered.sort( + (a, b) => a.engines.at(-1)!.power - b.engines.at(-1)!.power, + ); + break; + + case 'maneuverability.powerToWeight': + sorted = filtered.sort( + (a, b) => + a.engines.at(-1)!.power / + (a.weight + + a.engines.at(-1)!.weight + + a.tracks.at(-1)!.weight + + a.turrets.at(-1)!.weight + + a.turrets.at(-1)!.guns.at(-1)!.weight) - + b.engines.at(-1)!.power / + (b.weight + + b.engines.at(-1)!.weight + + b.tracks.at(-1)!.weight + + b.turrets.at(-1)!.weight + + b.turrets.at(-1)!.guns.at(-1)!.weight), + ); + break; + + case 'maneuverability.weight': + sorted = filtered.sort( + (a, b) => + a.weight + + a.engines.at(-1)!.weight + + a.tracks.at(-1)!.weight + + a.turrets.at(-1)!.weight + + a.turrets.at(-1)!.guns.at(-1)!.weight - + (b.weight + + b.engines.at(-1)!.weight + + b.tracks.at(-1)!.weight + + b.turrets.at(-1)!.weight + + b.turrets.at(-1)!.guns.at(-1)!.weight), + ); + break; + + case 'maneuverability.traverseSpeed': + sorted = filtered.sort( + (a, b) => + a.tracks.at(-1)!.traverseSpeed - b.tracks.at(-1)!.traverseSpeed, + ); + break; + } + + return sort.direction === 'ascending' ? sorted : sorted.reverse(); + } + + return undefined; + }, [search, sort]); return ( @@ -47,9 +345,14 @@ export const Results = memo(() => { )} {!search && - range(10, 0).map((tier) => )} + sort.by === 'meta.tier' && + range( + ...(sort.direction === 'descending' + ? ([10, 0] as Vector2Tuple) + : ([1, 11] as Vector2Tuple)), + ).map((tier) => )} - {search && ( + {(search || sort.by !== 'meta.tier') && ( {searchedTanks?.map((tank) => )} diff --git a/src/app/tools/tankopedia/components/SearchBar.tsx b/src/app/tools/tankopedia/components/SearchBar.tsx index 6d48a4ef4..d7cf815b3 100644 --- a/src/app/tools/tankopedia/components/SearchBar.tsx +++ b/src/app/tools/tankopedia/components/SearchBar.tsx @@ -1,11 +1,12 @@ import { CaretRightIcon, MagnifyingGlassIcon } from '@radix-ui/react-icons'; -import { Button, Spinner, TextField } from '@radix-ui/themes'; +import { Button, Flex, Spinner, TextField } from '@radix-ui/themes'; import { debounce } from 'lodash'; import { useRouter } from 'next/navigation'; import { KeyboardEventHandler, useCallback, useRef, useState } from 'react'; import { Link } from '../../../../components/Link'; import { TankDefinition } from '../../../../core/blitzkit/tankDefinitions'; import { useTankopediaFilters } from '../../../../stores/tankopediaFilters'; +import { Sort } from './Sort'; interface SearchBarProps { topResult?: TankDefinition; @@ -44,26 +45,31 @@ export function SearchBar({ topResult }: SearchBarProps) { ); return ( - - - {searching ? : } - - - {topResult && ( + + - - - + {searching ? : } - )} - + + {topResult && ( + + + + + + )} + + + + ); } diff --git a/src/app/tools/tankopedia/components/TankSearch/components/Sort.tsx b/src/app/tools/tankopedia/components/Sort.tsx similarity index 83% rename from src/app/tools/tankopedia/components/TankSearch/components/Sort.tsx rename to src/app/tools/tankopedia/components/Sort.tsx index a2a43dc3c..3041a8929 100644 --- a/src/app/tools/tankopedia/components/TankSearch/components/Sort.tsx +++ b/src/app/tools/tankopedia/components/Sort.tsx @@ -1,23 +1,25 @@ import { CaretDownIcon } from '@radix-ui/react-icons'; import { Button, DropdownMenu } from '@radix-ui/themes'; -import mutateTankopediaPersistent, { - TankopediaSortDirection, - useTankopediaPersistent, -} from '../../../../../../stores/tankopedia'; +import { TankopediaSortDirection } from '../../../../stores/tankopedia'; +import { + mutateTankopediaFilters, + useTankopediaFilters, +} from '../../../../stores/tankopediaFilters'; export function Sort() { - const sort = useTankopediaPersistent((state) => state.sort); + const sort = useTankopediaFilters((state) => state.sort); return ( - + - - - By + + + Sort by Meta @@ -25,7 +27,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'meta.tier'; }); }} @@ -35,7 +37,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'meta.name'; }); }} @@ -51,7 +53,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'fire.dpm'; }); }} @@ -61,7 +63,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'fire.reload'; }); }} @@ -71,7 +73,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'fire.caliber'; }); }} @@ -81,7 +83,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'fire.standardPenetration'; }); }} @@ -91,7 +93,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'fire.premiumPenetration'; }); }} @@ -101,7 +103,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'fire.damage'; }); }} @@ -111,7 +113,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'fire.shellVelocity'; }); }} @@ -121,7 +123,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'fire.aimTime'; }); }} @@ -131,7 +133,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'fire.dispersionStill'; }); }} @@ -141,7 +143,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'fire.dispersionMoving'; }); }} @@ -151,7 +153,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'fire.gunDepression'; }); }} @@ -161,7 +163,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'fire.gunElevation'; }); }} @@ -177,7 +179,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'maneuverability.forwardsSpeed'; }); }} @@ -187,7 +189,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'maneuverability.backwardsSpeed'; }); }} @@ -197,7 +199,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'maneuverability.power'; }); }} @@ -207,7 +209,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'maneuverability.powerToWeight'; }); }} @@ -217,7 +219,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'maneuverability.weight'; }); }} @@ -227,7 +229,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'maneuverability.traverseSpeed'; }); }} @@ -243,7 +245,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'survivability.health'; }); }} @@ -253,7 +255,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'survivability.viewRange'; }); }} @@ -263,7 +265,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'survivability.camouflageStill'; }); }} @@ -273,7 +275,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'survivability.camouflageMoving'; }); }} @@ -283,7 +285,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'survivability.camouflageShooting'; }); }} @@ -293,7 +295,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'survivability.volume'; }); }} @@ -303,7 +305,7 @@ export function Sort() { { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.by = 'survivability.length'; }); }} @@ -319,7 +321,7 @@ export function Sort() { - mutateTankopediaPersistent((draft) => { + mutateTankopediaFilters((draft) => { draft.sort.direction = value as TankopediaSortDirection; }) } diff --git a/src/app/tools/tankopedia/components/TankCard.tsx b/src/app/tools/tankopedia/components/TankCard.tsx index e1b08d6f9..44688a6ed 100644 --- a/src/app/tools/tankopedia/components/TankCard.tsx +++ b/src/app/tools/tankopedia/components/TankCard.tsx @@ -1,7 +1,9 @@ import { Flex, Link, Text } from '@radix-ui/themes'; -import { useEffect, useRef } from 'react'; +import { use, useEffect, useMemo, useRef } from 'react'; import { classIcons } from '../../../../components/ClassIcon'; import { asset } from '../../../../core/blitzkit/asset'; +import { modelDefinitions } from '../../../../core/blitzkit/modelDefinitions'; +import { resolveDpm } from '../../../../core/blitzkit/resolveDpm'; import { TankDefinition } from '../../../../core/blitzkit/tankDefinitions'; import { tankIcon } from '../../../../core/blitzkit/tankIcon'; import { tankopediaFilterTank } from '../../../../core/blitzkit/tankopediaFilterTank'; @@ -12,8 +14,36 @@ interface TankCardProps { } export function TankCard({ tank }: TankCardProps) { + const awaitedModelDefinitions = use(modelDefinitions); const Icon = classIcons[tank.class]; const link = useRef(null); + const sortBy = useTankopediaFilters((state) => state.sort.by); + const discriminator = useMemo(() => { + if (sortBy.startsWith('meta')) return undefined; + + const turret = tank.turrets.at(-1)!; + const gun = turret.guns.at(-1)!; + const shell = gun.shells[0]; + const tracks = tank.tracks.at(-1)!; + const tankModelDefinition = awaitedModelDefinitions[tank.id]; + + switch (sortBy) { + case 'fire.aimTime': + return `${gun.aimTime.toFixed(2)}s`; + case 'fire.caliber': + return `${shell.caliber.toFixed(0)}mm`; + case 'fire.damage': + return `${shell.damage.armor.toFixed(0)}hp`; + case 'fire.dispersionMoving': + return `+ ${tracks.dispersion.move.toFixed(3)}`; + case 'fire.dispersionStill': + return gun.dispersion.base.toFixed(3); + case 'fire.dpm': + return resolveDpm(gun, shell).toFixed(0); + case 'fire.gunDepression': + return awaitedModelDefinitions; + } + }, [sortBy]); useEffect(() => { const unsubscribe = useTankopediaFilters.subscribe((filters) => { @@ -89,6 +119,21 @@ export function TankCard({ tank }: TankCardProps) { {tank.name} + + {discriminator && ( + + {discriminator} + + )} ); } diff --git a/src/app/tools/tankopedia/components/TankSearch/index.tsx b/src/app/tools/tankopedia/components/TankSearch/index.tsx index 64c8c605e..e145f67d2 100644 --- a/src/app/tools/tankopedia/components/TankSearch/index.tsx +++ b/src/app/tools/tankopedia/components/TankSearch/index.tsx @@ -31,9 +31,9 @@ import mutateTankopediaPersistent, { useTankopediaPersistent, } from '../../../../../stores/tankopedia'; import { PageTurner } from '../PageTurner'; +import { Sort } from '../Sort'; import { Options } from './components/Options'; import { Results } from './components/Results'; -import { Sort } from './components/Sort'; import { treeTypeOrder } from './constants'; interface TankSearchProps { diff --git a/src/app/tools/tankopedia/components/TierCard.tsx b/src/app/tools/tankopedia/components/TierCard.tsx index a27a3d19c..c110397e6 100644 --- a/src/app/tools/tankopedia/components/TierCard.tsx +++ b/src/app/tools/tankopedia/components/TierCard.tsx @@ -1,5 +1,7 @@ import { Flex, Heading, Separator } from '@radix-ui/themes'; import { memo, use, useEffect, useMemo, useRef } from 'react'; +import { TANK_CLASSES } from '../../../../components/Tanks/components/Item/constants'; +import { gameDefinitions } from '../../../../core/blitzkit/gameDefinitions'; import { Tier, tanksDefinitionsArray, @@ -9,6 +11,7 @@ import { tankopediaFilterTank } from '../../../../core/blitzkit/tankopediaFilter import { useTankopediaFilters } from '../../../../stores/tankopediaFilters'; import { TankCard } from './TankCard'; import { TankCardWrapper } from './TankCardWrapper'; +import { treeTypeOrder } from './TankSearch/constants'; interface TierCardProps { tier: Tier; @@ -16,10 +19,27 @@ interface TierCardProps { export const TierCard = memo( ({ tier }: TierCardProps) => { + const awaitedGameDefinitions = use(gameDefinitions); const awaitedTanksDefinitionsArray = use(tanksDefinitionsArray); const container = useRef(null); const tanks = useMemo( - () => awaitedTanksDefinitionsArray.filter((tank) => tank.tier === tier), + () => + awaitedTanksDefinitionsArray + .filter((tank) => tank.tier === tier) + .sort( + (a, b) => + treeTypeOrder.indexOf(a.treeType) - + treeTypeOrder.indexOf(b.treeType), + ) + .sort( + (a, b) => + TANK_CLASSES.indexOf(a.class) - TANK_CLASSES.indexOf(b.class), + ) + .sort( + (a, b) => + awaitedGameDefinitions.nations.indexOf(a.nation) - + awaitedGameDefinitions.nations.indexOf(b.nation), + ), [tier], ); diff --git a/src/app/tools/tankopedia/page.tsx b/src/app/tools/tankopedia/page.tsx index a5b96b0c8..5d240ef88 100644 --- a/src/app/tools/tankopedia/page.tsx +++ b/src/app/tools/tankopedia/page.tsx @@ -8,12 +8,7 @@ import { Results } from './components/Results'; export default function Page() { return ( - + diff --git a/src/stores/tankopediaFilters.ts b/src/stores/tankopediaFilters.ts index 13e65ed9f..e9c5a91bf 100644 --- a/src/stores/tankopediaFilters.ts +++ b/src/stores/tankopediaFilters.ts @@ -2,6 +2,7 @@ import { produce } from 'immer'; import { create } from 'zustand'; import { TankClass, TreeType } from '../components/Tanks'; import { Tier } from '../core/blitzkit/tankDefinitions'; +import { TankopediaSortBy, TankopediaSortDirection } from './tankopedia'; export interface TankopediaFilters { tier?: Tier; @@ -10,10 +11,18 @@ export interface TankopediaFilters { type?: TreeType; testing: 'include' | 'exclude' | 'only'; search?: string; + sort: { + by: TankopediaSortBy; + direction: TankopediaSortDirection; + }; } export const useTankopediaFilters = create()(() => ({ testing: 'include', + sort: { + by: 'meta.tier', + direction: 'descending', + }, })); export function mutateTankopediaFilters(