feat: upgrade TankSort

This commit is contained in:
TrèsAbhi committed 2025-09-01 18:13:41 -05:00
1 parent 87b6a36520
commit 4efdaaf7c9
9 files changed
+39 -42

No files matched your search

+8 -2
View File
@@ -43,11 +43,17 @@ export class Varuna<Type> {
}
mutate(mutator: (state: Type) => void) {
this.state = produce(this.state, mutator);
this.set(produce(this.state, mutator));
}
set(state: Type) {
this.state = state;
this.dispatch();
}
use<Slice>(slicer: (state: Type) => Slice) {
use<Slice = Type>(
slicer: (state: Type) => Slice = (state) => state as unknown as Slice,
) {
const [slice, setSlice] = useState(slicer(this.state));
useEffect(() => this.on(slicer, setSlice), []);
return slice;
@@ -4,11 +4,10 @@ import {
unionBoundingBox,
type TankDefinition,
} from '@blitzkit/core';
import { useStore } from '@nanostores/react';
import { useMemo } from 'react';
import { awaitableModelDefinitions } from '../../../core/awaitables/modelDefinitions';
import { resolveReload } from '../../../core/blitzkit/resolveReload';
import { $tankopediaSort } from '../../../stores/tankopediaSort';
import { TankSort } from '../../../stores/tankopediaSort';
import { TankCard } from '../../TankCard';
interface TankSearchCardProps {
@@ -19,9 +18,9 @@ interface TankSearchCardProps {
const modelDefinitions = await awaitableModelDefinitions;
export function TankSearchCard({ tank, onSelect }: TankSearchCardProps) {
const tankopediaSort = useStore($tankopediaSort);
const by = TankSort.use((state) => state.by);
const discriminator = useMemo(() => {
if (tankopediaSort.by.startsWith('meta')) return undefined;
if (by.startsWith('meta')) return undefined;
const turret = tank.turrets.at(-1)!;
const gun = turret.guns.at(-1)!;
@@ -34,7 +33,7 @@ export function TankSearchCard({ tank, onSelect }: TankSearchCardProps) {
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
const gunModelDefinition = turretModelDefinition.guns[gun.id];
switch (tankopediaSort.by) {
switch (by) {
case 'fire.aimTime':
return gun.aim_time.toFixed(2);
case 'fire.caliber':
@@ -133,7 +132,7 @@ export function TankSearchCard({ tank, onSelect }: TankSearchCardProps) {
return Math.max(bounds.x, bounds.y, bounds.z).toFixed(0);
}
}
}, [tankopediaSort.by]);
}, [by]);
return (
<TankCard
@@ -24,10 +24,7 @@ import {
initialTankFilters,
type TankFilters,
} from '../../../stores/tankFilters';
import {
$tankopediaSort,
initialTankopediaSort,
} from '../../../stores/tankopediaSort';
import { TankSort } from '../../../stores/tankopediaSort';
import { classIcons } from '../../ClassIcon';
import { ExperimentIcon } from '../../ExperimentIcon';
import { GunAutoloaderIcon } from '../../GunAutoloaderIcon';
@@ -620,7 +617,7 @@ export function FilterControl({ compact }: FilterControlProps) {
onClick={(event) => {
event.preventDefault();
$tankFilters.set(initialTankFilters);
$tankopediaSort.set(initialTankopediaSort);
TankSort.set(TankSort.initial);
}}
>
{strings.website.common.tank_search.clear_filters}
@@ -6,7 +6,7 @@ import { awaitableTankDefinitions } from '../../../core/awaitables/tankDefinitio
import { useLocale } from '../../../hooks/useLocale';
import { $tankFilters, initialTankFilters } from '../../../stores/tankFilters';
import { TankopediaPersistent } from '../../../stores/tankopediaPersistent';
import { $tankopediaSort } from '../../../stores/tankopediaSort';
import { TankSort } from '../../../stores/tankopediaSort';
import { TankCard } from '../../TankCard';
import { TankCardWrapper } from './TankCardWrapper';
@@ -19,7 +19,6 @@ export function RecentlyViewed() {
const recentlyViewed = tankopediaPersistentStore
.getState()
.recentlyViewed.filter((id) => id in tankDefinitions.tanks);
const sort = useStore($tankopediaSort);
const hasFilters = useMemo(
() =>
Object.entries(filters).some(([key, value]) => {
@@ -31,8 +30,9 @@ export function RecentlyViewed() {
[filters],
);
const { strings } = useLocale();
const by = TankSort.use((state) => state.by);
if (recentlyViewed.length === 0 || sort.by !== 'meta.none' || hasFilters) {
if (recentlyViewed.length === 0 || by !== 'meta.none' || hasFilters) {
return null;
}
@@ -1,4 +1,3 @@
import { useStore } from '@nanostores/react';
import { CaretDownIcon } from '@radix-ui/react-icons';
import { Button, DropdownMenu } from '@radix-ui/themes';
import { useLocale } from '../../../hooks/useLocale';
@@ -6,7 +5,7 @@ import type {
TankopediaSortBy,
TankopediaSortDirection,
} from '../../../stores/tankopediaPersistent';
import { $tankopediaSort } from '../../../stores/tankopediaSort';
import { TankSort } from '../../../stores/tankopediaSort';
interface ItemProps {
by: TankopediaSortBy;
@@ -18,13 +17,15 @@ interface ItemProps {
function Item({ by }: ItemProps) {
const { strings } = useLocale();
const tankopediaSort = useStore($tankopediaSort);
const _by = TankSort.use((state) => state.by);
return (
<DropdownMenu.CheckboxItem
checked={tankopediaSort.by === by}
checked={_by === by}
onClick={() => {
$tankopediaSort.setKey('by', by);
TankSort.mutate((draft) => {
draft.by = by;
});
}}
>
{strings.website.common.tank_search.sort[by]}
@@ -33,7 +34,7 @@ function Item({ by }: ItemProps) {
}
export function Sort() {
const tankopediaSort = useStore($tankopediaSort);
const direction = TankSort.use((state) => state.direction);
const { strings } = useLocale();
return (
@@ -119,12 +120,11 @@ export function Sort() {
}
</DropdownMenu.Label>
<DropdownMenu.RadioGroup
value={tankopediaSort.direction}
value={direction}
onValueChange={(value) => {
$tankopediaSort.setKey(
'direction',
value as TankopediaSortDirection,
);
TankSort.mutate((draft) => {
draft.direction = value as TankopediaSortDirection;
});
}}
>
<DropdownMenu.RadioItem value="ascending">
@@ -4,13 +4,13 @@ import { Flex, IconButton, Text } from '@radix-ui/themes';
import { times } from 'lodash-es';
import { memo } from 'react';
import { $tankFilters } from '../../../stores/tankFilters';
import { $tankopediaSort } from '../../../stores/tankopediaSort';
import { TankSort } from '../../../stores/tankopediaSort';
export const TierFilter = memo(() => {
const tankFilters = useStore($tankFilters);
const tankopediaSort = useStore($tankopediaSort);
const by = TankSort.use((state) => state.by);
if (tankopediaSort.by !== 'meta.none' || tankFilters.search) return null;
if (by !== 'meta.none' || tankFilters.search) return null;
return (
<Flex justify="center">
@@ -22,7 +22,7 @@ import { useLocale } from '../../hooks/useLocale';
import { $tankFilters } from '../../stores/tankFilters';
import { TankopediaPersistent } from '../../stores/tankopediaPersistent';
import { SORT_UNITS } from '../../stores/tankopediaPersistent/constants';
import { $tankopediaSort } from '../../stores/tankopediaSort';
import { TankSort } from '../../stores/tankopediaSort';
import type { MaybeSkeletonComponentProps } from '../../types/maybeSkeletonComponentProps';
import { ExperimentIcon } from '../ExperimentIcon';
import { TankSearchCard } from './components/Card';
@@ -58,7 +58,7 @@ export const TankSearch = memo<TankSearchProps>(
const mutateTankopediaPersistent = TankopediaPersistent.useMutation();
const awaitedTanksDefinitionsArray = Object.values(tankDefinitions.tanks);
const tankFilters = useStore($tankFilters);
const tankopediaSort = useStore($tankopediaSort);
const tankopediaSort = TankSort.use();
const tanksFiltered = useMemo(() => {
if (tankFilters.search === null) {
const filtered = awaitedTanksDefinitionsArray.filter((tank) =>
@@ -7,7 +7,6 @@ import {
LocaleProvider,
type LocaleAcceptorProps,
} from '../../../hooks/useLocale';
import { TankPerformanceEphemeral } from '../../../stores/tankPerformanceEphemeral';
import { TankPerformanceSort } from '../../../stores/tankPerformanceSort';
import type { MaybeSkeletonComponentProps } from '../../../types/maybeSkeletonComponentProps';
@@ -17,9 +16,7 @@ export function Page({ skeleton, locale }: PageProps) {
return (
<LocaleProvider locale={locale}>
<TankPerformanceSort.Provider>
<TankPerformanceEphemeral.Provider>
<Content skeleton={skeleton} />
</TankPerformanceEphemeral.Provider>
<Content skeleton={skeleton} />
</TankPerformanceSort.Provider>
</LocaleProvider>
);
@@ -1,17 +1,15 @@
import { map } from 'nanostores';
import { Varuna } from 'varuna';
import {
type TankopediaSortBy,
type TankopediaSortDirection,
} from './tankopediaPersistent';
export interface TankopediaSort {
export interface TankSort {
by: TankopediaSortBy;
direction: TankopediaSortDirection;
}
export const initialTankopediaSort: TankopediaSort = {
export const TankSort = new Varuna<TankSort>({
by: 'meta.none',
direction: 'descending',
};
export const $tankopediaSort = map<TankopediaSort>(initialTankopediaSort);
});