diff --git a/src/app/tools/tankopedia/[id]/components/Characteristics/components/Skills/index.tsx b/src/app/tools/tankopedia/[id]/components/Characteristics/components/Skills/index.tsx index 81fbb8fc0..221ae76e9 100644 --- a/src/app/tools/tankopedia/[id]/components/Characteristics/components/Skills/index.tsx +++ b/src/app/tools/tankopedia/[id]/components/Characteristics/components/Skills/index.tsx @@ -12,15 +12,13 @@ import { use } from 'react'; import { CrewSkillManager } from '../../../../../../../../components/CrewSkillManager'; import { createDefaultSkills } from '../../../../../../../../core/blitzkit/createDefaultSkills'; import { skillDefinitions } from '../../../../../../../../core/blitzkit/skillDefinitions'; -import { - mutateTankopediaTemporary, - useTankopediaTemporary, -} from '../../../../../../../../stores/tankopedia'; +import * as TankopediaEphemeral from '../../../../../../../../stores/tankopediaEphemeral'; import { ConfigurationChildWrapper } from '../ConfigurationChildWrapper'; export function Skills() { const awaitedSkillDefinitions = use(skillDefinitions); - const skillLevels = useTankopediaTemporary((state) => state.skills); + const skillLevels = TankopediaEphemeral.use((state) => state.skills); + const mutateTankopediaTemporary = TankopediaEphemeral.useMutation(); if (Object.keys(skillLevels).length === 0) { mutateTankopediaTemporary((draft) => { diff --git a/src/app/tools/tankopedia/[id]/components/Characteristics/index.tsx b/src/app/tools/tankopedia/[id]/components/Characteristics/index.tsx index 9b2fcb026..3dee62a96 100644 --- a/src/app/tools/tankopedia/[id]/components/Characteristics/index.tsx +++ b/src/app/tools/tankopedia/[id]/components/Characteristics/index.tsx @@ -29,7 +29,7 @@ import { import { useAdExempt } from '../../../../../../hooks/useAdExempt'; import { useEquipment } from '../../../../../../hooks/useEquipment'; import { mutateDuel, useDuel } from '../../../../../../stores/duel'; -import { useTankopediaTemporary } from '../../../../../../stores/tankopedia'; +import * as TankopediaEphemeral from '../../../../../../stores/tankopediaEphemeral'; import { Info } from './components/Info'; import { InfoWithDelta } from './components/InfoWithDelta'; @@ -38,7 +38,7 @@ export function Characteristics() { const awaitedEquipmentDefinitions = use(equipmentDefinitions); const awaitedModelDefinitions = use(modelDefinitions); const awaitedProvisionDefinitions = use(provisionDefinitions); - const crewSkills = useTankopediaTemporary((state) => state.skills); + const crewSkills = TankopediaEphemeral.use((state) => state.skills); const penetrationDistanceInput = useRef(null); const hasImprovedVentilation = useEquipment(102); const crewMastery = useDuel((state) => state.protagonist!.crewMastery); diff --git a/src/app/tools/tankopedia/[id]/components/Control.tsx b/src/app/tools/tankopedia/[id]/components/Control.tsx index 10ba7cd5a..5a74ffcd6 100644 --- a/src/app/tools/tankopedia/[id]/components/Control.tsx +++ b/src/app/tools/tankopedia/[id]/components/Control.tsx @@ -9,7 +9,7 @@ import { modelDefinitions } from '../../../../../core/blitzkit/modelDefinitions' import { Pose, poseEvent } from '../../../../../core/blitzkit/pose'; import { useAwait } from '../../../../../hooks/useAwait'; import { useDuel } from '../../../../../stores/duel'; -import { useTankopediaTemporary } from '../../../../../stores/tankopedia'; +import * as TankopediaEphemeral from '../../../../../stores/tankopediaEphemeral'; const poseDistances: Record = { [Pose.HullDown]: 15, @@ -18,6 +18,7 @@ const poseDistances: Record = { }; export function Controls() { + const tankopediaEphemeralStore = TankopediaEphemeral.useStore(); const camera = useThree((state) => state.camera); const canvas = useThree((state) => state.gl.domElement); const orbitControls = useRef(null); @@ -62,7 +63,7 @@ export function Controls() { }, [camera]); useEffect(() => { - const unsubscribeTankopedia = useTankopediaTemporary.subscribe( + const unsubscribeTankopediaEphemeral = tankopediaEphemeralStore.subscribe( (state) => state.controlsEnabled, (enabled) => { if (orbitControls.current) orbitControls.current.enabled = enabled; @@ -158,7 +159,7 @@ export function Controls() { poseEvent.on(handlePoseEvent); return () => { - unsubscribeTankopedia(); + unsubscribeTankopediaEphemeral(); poseEvent.off(handlePoseEvent); }; }, [camera, protagonist.tank.id, antagonist.tank.id]); @@ -182,7 +183,7 @@ export function Controls() { maxDistance={20} minDistance={5} ref={orbitControls} - enabled={useTankopediaTemporary.getState().controlsEnabled} + enabled={tankopediaEphemeralStore.getState().controlsEnabled} rotateSpeed={0.25} enableDamping={false} autoRotate={autoRotate} diff --git a/src/app/tools/tankopedia/[id]/components/Lighting.tsx b/src/app/tools/tankopedia/[id]/components/Lighting.tsx index 1cf7f6393..7814f7a8d 100644 --- a/src/app/tools/tankopedia/[id]/components/Lighting.tsx +++ b/src/app/tools/tankopedia/[id]/components/Lighting.tsx @@ -1,7 +1,7 @@ import { Environment } from '@react-three/drei'; import { useModel } from '../../../../../hooks/useModel'; import { useDuel } from '../../../../../stores/duel'; -import { useTankopediaPersistent } from '../../../../../stores/tankopedia'; +import * as TankopediaPersistent from '../../../../../stores/tankopediaPersistent'; export const ENVIRONMENTS = [ 'lobby', @@ -14,10 +14,10 @@ export const ENVIRONMENTS = [ export function Lighting() { const protagonist = useDuel((state) => state.protagonist!); const { hasPbr } = useModel(protagonist.tank.id); - const showEnvironment = useTankopediaPersistent( + const showEnvironment = TankopediaPersistent.use( (state) => state.model.visual.showEnvironment, ); - const environment = useTankopediaPersistent( + const environment = TankopediaPersistent.use( (state) => state.model.visual.environment, ); diff --git a/src/app/tools/tankopedia/[id]/components/Model/TankSandbox.tsx b/src/app/tools/tankopedia/[id]/components/Model/TankSandbox.tsx index a9c11a909..19b608297 100644 --- a/src/app/tools/tankopedia/[id]/components/Model/TankSandbox.tsx +++ b/src/app/tools/tankopedia/[id]/components/Model/TankSandbox.tsx @@ -8,7 +8,7 @@ import { modelTransformEvent } from '../../../../../../core/blitzkit/modelTransf import { Pose, poseEvent } from '../../../../../../core/blitzkit/pose'; import { useEquipment } from '../../../../../../hooks/useEquipment'; import { mutateDuel, useDuel } from '../../../../../../stores/duel'; -import { mutateTankopediaTemporary } from '../../../../../../stores/tankopedia'; +import * as TankopediaEphemeral from '../../../../../../stores/tankopediaEphemeral'; import { Controls } from '../Control'; import { Lighting } from '../Lighting'; import { SceneProps } from '../SceneProps'; @@ -16,6 +16,7 @@ import { ModelLoader } from './components/ModelLoader'; import { TankModel } from './components/TankModel'; export function TankSandbox() { + const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation(); const canvas = useRef(null); const hasImprovedVerticalStabilizer = useEquipment(122); const awaitedModelDefinitions = use(modelDefinitions); @@ -126,7 +127,7 @@ export function TankSandbox() { ref={canvas} onPointerDown={handlePointerDown} onPointerMissed={() => { - mutateTankopediaTemporary((draft) => { + mutateTankopediaEphemeral((draft) => { draft.shot = undefined; }); }} diff --git a/src/app/tools/tankopedia/[id]/components/Model/components/Options.tsx b/src/app/tools/tankopedia/[id]/components/Model/components/Options.tsx index ed9d79b1f..8e086fbec 100644 --- a/src/app/tools/tankopedia/[id]/components/Model/components/Options.tsx +++ b/src/app/tools/tankopedia/[id]/components/Model/components/Options.tsx @@ -33,36 +33,36 @@ import { useEquipment } from '../../../../../../../hooks/useEquipment'; import { useFullScreen } from '../../../../../../../hooks/useFullScreen'; import { useApp } from '../../../../../../../stores/app'; import { mutateDuel, useDuel } from '../../../../../../../stores/duel'; -import mutateTankopediaPersistent, { - mutateTankopediaTemporary, - useTankopediaPersistent, -} from '../../../../../../../stores/tankopedia'; +import * as TankopediaEphemeral from '../../../../../../../stores/tankopediaEphemeral'; +import * as TankopediaPersistent from '../../../../../../../stores/tankopediaPersistent'; import { TankSearch } from '../../../../components/TankSearch'; import { ENVIRONMENTS } from '../../Lighting'; import { RotationInputs } from '../../QuickInputs'; export function Options() { - const mode = useTankopediaPersistent((state) => state.mode); + const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation(); + const mutateTankopediaPersistent = TankopediaPersistent.useMutation(); + const mode = TankopediaPersistent.use((state) => state.mode); const isFullScreen = useFullScreen(); - const showGrid = useTankopediaPersistent( + const showGrid = TankopediaPersistent.use( (state) => state.model.visual.showGrid, ); - const showEnvironment = useTankopediaPersistent( + const showEnvironment = TankopediaPersistent.use( (state) => state.model.visual.showEnvironment, ); - const greenPenetration = useTankopediaPersistent( + const greenPenetration = TankopediaPersistent.use( (state) => state.model.visual.greenPenetration, ); - const wireframe = useTankopediaPersistent( + const wireframe = TankopediaPersistent.use( (state) => state.model.visual.wireframe, ); - const showSpacedArmor = useTankopediaPersistent( + const showSpacedArmor = TankopediaPersistent.use( (state) => state.model.visual.showSpacedArmor, ); - const opaque = useTankopediaPersistent((state) => state.model.visual.opaque); + const opaque = TankopediaPersistent.use((state) => state.model.visual.opaque); const fullScreenAvailable = typeof document !== 'undefined' && document.fullscreenEnabled; - const environment = useTankopediaPersistent( + const environment = TankopediaPersistent.use( (state) => state.model.visual.environment, ); const developerMode = useApp((state) => state.developerMode); @@ -152,7 +152,7 @@ export function Options() { mutateDuel((draft) => { draft.antagonist!.shell = thisShell; }); - mutateTankopediaTemporary((draft) => { + mutateTankopediaEphemeral((draft) => { draft.shot = undefined; }); }} @@ -186,7 +186,7 @@ export function Options() { ? 0 : 1; }); - mutateTankopediaTemporary((draft) => { + mutateTankopediaEphemeral((draft) => { draft.shot = undefined; }); }} @@ -218,7 +218,7 @@ export function Options() { ? 0 : -1; }); - mutateTankopediaTemporary((draft) => { + mutateTankopediaEphemeral((draft) => { draft.shot = undefined; }); }} @@ -337,7 +337,7 @@ export function Options() { draft.antagonist!.shell = draft.antagonist!.gun.shells[0]; }); - mutateTankopediaTemporary((draft) => { + mutateTankopediaEphemeral((draft) => { draft.shot = undefined; }); }} @@ -466,7 +466,7 @@ export function Options() { mutateTankopediaPersistent((draft) => { draft.model.visual.showSpacedArmor = checked; }); - mutateTankopediaTemporary((draft) => { + mutateTankopediaEphemeral((draft) => { draft.shot = undefined; }); }} diff --git a/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx b/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx index 60f5c7531..71f335a56 100644 --- a/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx +++ b/src/app/tools/tankopedia/[id]/components/Model/components/TankModel.tsx @@ -22,7 +22,7 @@ import { normalizeAngleRad } from '../../../../../../../core/math/normalizeAngle import { useAwait } from '../../../../../../../hooks/useAwait'; import { useModel } from '../../../../../../../hooks/useModel'; import { mutateDuel, useDuel } from '../../../../../../../stores/duel'; -import { mutateTankopediaTemporary } from '../../../../../../../stores/tankopedia'; +import * as TankopediaEphemeral from '../../../../../../../stores/tankopediaEphemeral'; export const TankModel = memo(() => { const awaitedModelDefinitions = useAwait(modelDefinitions); @@ -31,6 +31,7 @@ export const TankModel = memo(() => { const hullContainer = useRef(null); const turretContainer = useRef(null); const gunContainer = useRef(null); + const mutateTankopediaTemporary = TankopediaEphemeral.useMutation(); useEffect(() => { const hullOrigin = new Vector3( diff --git a/src/app/tools/tankopedia/[id]/components/SceneProps.tsx b/src/app/tools/tankopedia/[id]/components/SceneProps.tsx index 203d41d05..b267dbdfe 100644 --- a/src/app/tools/tankopedia/[id]/components/SceneProps.tsx +++ b/src/app/tools/tankopedia/[id]/components/SceneProps.tsx @@ -2,10 +2,10 @@ import { useFrame, useLoader } from '@react-three/fiber'; import { clamp } from 'lodash'; import { useRef } from 'react'; import { MeshStandardMaterial, TextureLoader } from 'three'; -import { useTankopediaPersistent } from '../../../../../stores/tankopedia'; +import * as TankopediaPersistent from '../../../../../stores/tankopediaPersistent'; export function SceneProps() { - const show = useTankopediaPersistent( + const show = TankopediaPersistent.use( (state) => state.model.visual.showGrid && !state.model.visual.showEnvironment, ); diff --git a/src/app/tools/tankopedia/[id]/page.tsx b/src/app/tools/tankopedia/[id]/page.tsx index d618d94c0..c02a3f6ce 100644 --- a/src/app/tools/tankopedia/[id]/page.tsx +++ b/src/app/tools/tankopedia/[id]/page.tsx @@ -6,7 +6,7 @@ import PageWrapper from '../../../../components/PageWrapper'; import { assignDuelMember } from '../../../../core/blitzkit/assignDuelMember'; import { useAdExempt } from '../../../../hooks/useAdExempt'; import { mutateDuel, useDuel } from '../../../../stores/duel'; -import { mutateTankopediaTemporary } from '../../../../stores/tankopedia'; +import * as TankopediaEphemeral from '../../../../stores/tankopediaEphemeral'; import { HistorySection } from './components/HistorySection'; import { CharacteristicsSection } from './components/Model/CharacteristicsSection'; import { HeroSection } from './components/Model/HeroSection'; @@ -18,6 +18,7 @@ export default function Page({ params }: { params: { id: string } }) { const initialId = parseInt(params.id); const assigned = useDuel((state) => state.assigned); const exempt = useAdExempt(); + const mutateTankopediaEphemeral = TankopediaEphemeral.useMutation(); useEffect(() => { assignDuelMember('both', initialId); @@ -26,7 +27,7 @@ export default function Page({ params }: { params: { id: string } }) { if (document.activeElement instanceof HTMLInputElement) return; function wipeShot() { - mutateTankopediaTemporary((draft) => { + mutateTankopediaEphemeral((draft) => { draft.shot = undefined; }); } @@ -64,7 +65,7 @@ export default function Page({ params }: { params: { id: string } }) { const unsubscribe = useDuel.subscribe( (state) => state.protagonist?.tank.id, () => { - mutateTankopediaTemporary((draft) => { + mutateTankopediaEphemeral((draft) => { draft.shot = undefined; }); }, diff --git a/src/app/tools/tankopedia/components/PageTurner.tsx b/src/app/tools/tankopedia/components/PageTurner.tsx index 4aa76fff1..90acf6014 100644 --- a/src/app/tools/tankopedia/components/PageTurner.tsx +++ b/src/app/tools/tankopedia/components/PageTurner.tsx @@ -2,9 +2,7 @@ import { CaretLeftIcon, CaretRightIcon } from '@radix-ui/react-icons'; import { Flex, IconButton, TextField } from '@radix-ui/themes'; import { useEffect, useRef } from 'react'; import { TankDefinition } from '../../../../core/blitzkit/tankDefinitions'; -import mutateTankopediaPersistent, { - useTankopediaPersistent, -} from '../../../../stores/tankopedia'; +import * as TankopediaPersistent from '../../../../stores/tankopediaPersistent'; interface PageTurnerProps { searchedList: TankDefinition[]; @@ -12,7 +10,8 @@ interface PageTurnerProps { } export function PageTurner({ searchedList, tanksPerPage }: PageTurnerProps) { - const page = useTankopediaPersistent((state) => state.filters.page); + const page = TankopediaPersistent.use((state) => state.filters.page); + const mutateTankopediaPersistent = TankopediaPersistent.useMutation(); const pageInput = useRef(null); useEffect(() => { diff --git a/src/app/tools/tankopedia/components/Sort.tsx b/src/app/tools/tankopedia/components/Sort.tsx index daa45dad3..2a0db3ff1 100644 --- a/src/app/tools/tankopedia/components/Sort.tsx +++ b/src/app/tools/tankopedia/components/Sort.tsx @@ -1,6 +1,6 @@ import { CaretDownIcon } from '@radix-ui/react-icons'; import { Button, DropdownMenu } from '@radix-ui/themes'; -import { TankopediaSortDirection } from '../../../../stores/tankopedia'; +import { TankopediaSortDirection } from '../../../../stores/tankopediaPersistent'; import * as TankopediaSort from '../../../../stores/tankopediaSort'; export function Sort() { diff --git a/src/app/tools/tankopedia/components/TankSearch/index.tsx b/src/app/tools/tankopedia/components/TankSearch/index.tsx index 36d8f4a96..1d7f728a5 100644 --- a/src/app/tools/tankopedia/components/TankSearch/index.tsx +++ b/src/app/tools/tankopedia/components/TankSearch/index.tsx @@ -21,7 +21,10 @@ import { import { unionBoundingBox } from '../../../../../core/blitzkit/unionBoundingBox'; import { useAdExempt } from '../../../../../hooks/useAdExempt'; import * as TankFilters from '../../../../../stores/tankFilters'; -import { SORT_NAMES, SORT_UNITS } from '../../../../../stores/tankopedia'; +import { + SORT_NAMES, + SORT_UNITS, +} from '../../../../../stores/tankopediaPersistent/constants'; import * as TankopediaSort from '../../../../../stores/tankopediaSort'; import { FilterControl } from '../FilterControl'; import { NoResults } from '../NoResults'; diff --git a/src/components/Armor/components/CoreArmorScene.tsx b/src/components/Armor/components/CoreArmorScene.tsx index 769d94799..75a408994 100644 --- a/src/components/Armor/components/CoreArmorScene.tsx +++ b/src/components/Armor/components/CoreArmorScene.tsx @@ -11,7 +11,7 @@ import { resolveArmor } from '../../../core/blitzkit/resolveThickness'; import { useArmor } from '../../../hooks/useArmor'; import { useModelDefinitions } from '../../../hooks/useModelDefinitions'; import { useDuel } from '../../../stores/duel'; -import { useTankopediaPersistent } from '../../../stores/tankopedia'; +import * as TankopediaPersistent from '../../../stores/tankopediaPersistent'; import { CoreArmorSceneComponent } from './CoreArmorSceneComponent'; import { ShotDisplay } from './ShotDisplay'; @@ -20,7 +20,8 @@ export const CoreArmorScene = memo(() => { const modelDefinitions = useModelDefinitions(); const turretContainer = useRef(null); const gunContainer = useRef(null); - const initialTankopediaState = useTankopediaPersistent.getState(); + const tankopediaPersistentStore = TankopediaPersistent.useStore(); + const initialTankopediaState = tankopediaPersistentStore.getState(); const protagonist = useDuel((draft) => draft.protagonist!); useEffect(() => { @@ -99,7 +100,7 @@ export const CoreArmorScene = memo(() => { }); useEffect(() => { - const unsubscribe = useTankopediaPersistent.subscribe( + const unsubscribe = tankopediaPersistentStore.subscribe( (state) => state.mode, (mode) => { if (wrapper.current) wrapper.current.visible = mode === 'armor'; diff --git a/src/components/Armor/components/CoreArmorSceneComponent/index.tsx b/src/components/Armor/components/CoreArmorSceneComponent/index.tsx index 06b00bbf5..bab6af1d1 100644 --- a/src/components/Armor/components/CoreArmorSceneComponent/index.tsx +++ b/src/components/Armor/components/CoreArmorSceneComponent/index.tsx @@ -10,10 +10,7 @@ import { hasEquipment } from '../../../../core/blitzkit/hasEquipment'; import { jsxTree } from '../../../../core/blitzkit/jsxTree'; import { ShellDefinition } from '../../../../core/blitzkit/tankDefinitions'; import { EquipmentMatrix, useDuel } from '../../../../stores/duel'; -import { - TankopediaPersistent, - useTankopediaPersistent, -} from '../../../../stores/tankopedia'; +import * as TankopediaPersistent from '../../../../stores/tankopediaPersistent'; import fragmentShader from './shaders/fragment.glsl'; import vertexShader from './shaders/vertex.glsl'; import { spacedArmorRenderTarget } from './target'; @@ -31,6 +28,7 @@ export function CoreArmorSceneComponent({ node, thickness, }: CoreArmorSceneComponentProps) { + const tankopediaPersistentStore = TankopediaPersistent.useStore(); const material = new ShaderMaterial({ fragmentShader, vertexShader, @@ -77,7 +75,7 @@ export function CoreArmorSceneComponent({ await handleAntagonistEquipmentChange(duel.antagonist!.equipmentMatrix); } function handleVisualChange( - visual: TankopediaPersistent['model']['visual'], + visual: TankopediaPersistent.TankopediaPersistent['model']['visual'], ) { material.uniforms.greenPenetration.value = visual.greenPenetration; material.uniforms.opaque.value = visual.opaque || visual.wireframe; @@ -113,7 +111,7 @@ export function CoreArmorSceneComponent({ } handleShellChange(useDuel.getState().antagonist!.shell); - handleVisualChange(useTankopediaPersistent.getState().model.visual); + handleVisualChange(tankopediaPersistentStore.getState().model.visual); handleProtagonistEquipmentChange( useDuel.getState().protagonist!.equipmentMatrix, ); @@ -123,7 +121,7 @@ export function CoreArmorSceneComponent({ const unsubscribes = [ useDuel.subscribe((state) => state.antagonist!.shell, handleShellChange), - useTankopediaPersistent.subscribe( + tankopediaPersistentStore.subscribe( (state) => state.model.visual, handleVisualChange, ), diff --git a/src/components/Armor/components/ShotDisplay.tsx b/src/components/Armor/components/ShotDisplay.tsx index 877eec688..7faaa6813 100644 --- a/src/components/Armor/components/ShotDisplay.tsx +++ b/src/components/Armor/components/ShotDisplay.tsx @@ -4,15 +4,11 @@ import { useFrame } from '@react-three/fiber'; import { ComponentProps, useRef } from 'react'; import { Euler, Mesh, Quaternion } from 'three'; import { J_HAT } from '../../../constants/axis'; -import { - ShotLayerNonExternal, - ShotStatus, - useTankopediaTemporary, -} from '../../../stores/tankopedia'; +import * as TankopediaEphemeral from '../../../stores/tankopediaEphemeral'; import { ShotDisplayCard } from './ShotDisplayCard'; export const shotStatusColors: Record< - ShotStatus, + TankopediaEphemeral.ShotStatus, ComponentProps['color'] > = { blocked: 'red', @@ -27,7 +23,7 @@ const TRACER_THICK = 1 / 32; const TRACER_THIN = TRACER_THICK / 2; export function ShotDisplay() { - const shot = useTankopediaTemporary((state) => state.shot); + const shot = TankopediaEphemeral.use((state) => state.shot); const inTracer = useRef(null); const outTracer = useRef(null); @@ -55,13 +51,16 @@ export function ShotDisplay() { }, 0) + SHOT_DISPLAY_LENGTH_INFINITY; const outLength = shot.out && shot.out.layers.length > 0 - ? (shot.in.layers.at(-1) as ShotLayerNonExternal).point.distanceTo( - (shot.out.layers.at(-1) as ShotLayerNonExternal).point, + ? ( + shot.in.layers.at(-1) as TankopediaEphemeral.ShotLayerNonExternal + ).point.distanceTo( + (shot.out.layers.at(-1) as TankopediaEphemeral.ShotLayerNonExternal) + .point, ) : SHOT_DISPLAY_LENGTH_INFINITY; const inLast = shot.in.layers.findLast( (layer) => layer.type !== null, - ) as ShotLayerNonExternal; + ) as TankopediaEphemeral.ShotLayerNonExternal; const outTitleColor = shot.out ? shotStatusColors[shot.out.status] : undefined; diff --git a/src/components/Armor/components/ShotDisplayCard.tsx b/src/components/Armor/components/ShotDisplayCard.tsx index 3b67d740a..be661972c 100644 --- a/src/components/Armor/components/ShotDisplayCard.tsx +++ b/src/components/Armor/components/ShotDisplayCard.tsx @@ -1,11 +1,7 @@ import { Card, Flex, Inset, Separator, Text } from '@radix-ui/themes'; import { ComponentProps } from 'react'; import { radToDeg } from 'three/src/math/MathUtils'; -import { - ShotLayer, - ShotStatus, - useTankopediaTemporary, -} from '../../../stores/tankopedia'; +import * as TankopediaEphemeral from '../../../stores/tankopediaEphemeral'; import { shotStatusColors } from './ShotDisplay'; import { ArmorType } from './SpacedArmorScene'; import { ExternalModuleVariant } from './SpacedArmorSceneComponent'; @@ -22,7 +18,7 @@ const externalLayerNames: Record = { track: 'Tracks', }; -const shotStatusNames: Record = { +const shotStatusNames: Record = { blocked: 'Blocked', ricochet: 'Ricochet', penetration: 'penetration', @@ -39,7 +35,7 @@ function LayerEntry({ layerIndex?: string; shotStatusColor: ComponentProps['color']; layerName: string; - layer: ShotLayer; + layer: TankopediaEphemeral.ShotLayer; angle?: number; }) { return ( @@ -113,7 +109,7 @@ function LayerEntry({ } export function ShotDisplayCard() { - const shot = useTankopediaTemporary((state) => state.shot); + const shot = TankopediaEphemeral.use((state) => state.shot); if (!shot) return null; diff --git a/src/components/Armor/components/SpacedArmorScene.tsx b/src/components/Armor/components/SpacedArmorScene.tsx index 84783370f..7663f8484 100644 --- a/src/components/Armor/components/SpacedArmorScene.tsx +++ b/src/components/Armor/components/SpacedArmorScene.tsx @@ -12,7 +12,7 @@ import { useArmor } from '../../../hooks/useArmor'; import { useModel } from '../../../hooks/useModel'; import { useModelDefinitions } from '../../../hooks/useModelDefinitions'; import { useDuel } from '../../../stores/duel'; -import { useTankopediaPersistent } from '../../../stores/tankopedia'; +import * as TankopediaPersistent from '../../../stores/tankopediaPersistent'; import { SpacedArmorSceneComponent } from './SpacedArmorSceneComponent'; export enum ArmorType { @@ -30,7 +30,8 @@ export const SpacedArmorScene = memo(({ scene }) => { const modelDefinitions = useModelDefinitions(); const turretContainer = useRef(null); const gunContainer = useRef(null); - const initialTankopediaState = useTankopediaPersistent.getState(); + const tankopediaPersistentStore = TankopediaPersistent.useStore(); + const initialTankopediaState = tankopediaPersistentStore.getState(); const protagonist = useDuel((draft) => draft.protagonist!); useEffect(() => { @@ -109,7 +110,7 @@ export const SpacedArmorScene = memo(({ scene }) => { }); useEffect(() => { - const unsubscribe = useTankopediaPersistent.subscribe( + const unsubscribe = tankopediaPersistentStore.subscribe( (state) => state.mode, (mode) => { if (wrapper.current) wrapper.current.visible = mode === 'armor'; diff --git a/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx b/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx index abd98b9de..b40f4384e 100644 --- a/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx +++ b/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx @@ -17,12 +17,7 @@ import { hasEquipment } from '../../../../core/blitzkit/hasEquipment'; import { jsxTree } from '../../../../core/blitzkit/jsxTree'; import { ShellType } from '../../../../core/blitzkit/tankDefinitions'; import { useDuel } from '../../../../stores/duel'; -import { - Shot, - ShotLayerBase, - ShotLayerNonExternal, - useTankopediaTemporary, -} from '../../../../stores/tankopedia'; +import * as TankopediaEphemeral from '../../../../stores/tankopediaEphemeral'; import { ArmorType } from '../SpacedArmorScene'; import { SpacedArmorSubExternal } from './components/SpacedArmorSubExternal'; import { SpacedArmorSubSpaced } from './components/SpacedArmorSubSpaced'; @@ -77,6 +72,7 @@ export function SpacedArmorSceneComponent({ clip, ...props }: SpacedArmorSceneComponentProps) { + const tankopediaEphemeralStore = TankopediaEphemeral.useStore(); const camera = useThree((state) => state.camera); async function shoot( @@ -88,7 +84,7 @@ export function SpacedArmorSceneComponent({ const { antagonist, protagonist } = useDuel.getState(); const shell = antagonist!.shell; const cameraNormal = camera.position.clone().sub(point).normalize(); - const shot: Shot = { + const shot: TankopediaEphemeral.Shot = { damage: -1, containsGaps: shell.type === ShellType.HEAT, in: { @@ -150,7 +146,9 @@ export function SpacedArmorSceneComponent({ ); if (shell.type === ShellType.HEAT && index > 0) { - const lastLayer = shot.in.layers.at(-1) as ShotLayerBase; + const lastLayer = shot.in.layers.at( + -1, + ) as TankopediaEphemeral.ShotLayerBase; const distance = lastLayer.point.distanceTo(intersection.point); remainingPenetration -= 0.5 * remainingPenetration * distance; const blocked = remainingPenetration <= 0; @@ -244,8 +242,12 @@ export function SpacedArmorSceneComponent({ index++; } - const lastLayer = shot.in.layers.at(-1) as ShotLayerNonExternal | undefined; - const firstLayer = shot.in.layers[0] as ShotLayerBase | undefined; + const lastLayer = shot.in.layers.at(-1) as + | TankopediaEphemeral.ShotLayerNonExternal + | undefined; + const firstLayer = shot.in.layers[0] as + | TankopediaEphemeral.ShotLayerBase + | undefined; if (!lastLayer || !firstLayer) { return null; @@ -370,7 +372,7 @@ export function SpacedArmorSceneComponent({ true, ))!; - useTankopediaTemporary.setState({ shot }); + tankopediaEphemeralStore.setState({ shot }); }, }, node.uuid, diff --git a/src/core/zustand/createNextSafeStore.tsx b/src/core/zustand/createNextSafeStore.tsx index 0fb75f015..d9afcbb5d 100644 --- a/src/core/zustand/createNextSafeStore.tsx +++ b/src/core/zustand/createNextSafeStore.tsx @@ -1,39 +1,28 @@ import { Draft, produce } from 'immer'; -import { Context, createContext, ReactNode, useContext, useRef } from 'react'; -import { createStore, StoreApi, useStore as useStoreZustand } from 'zustand'; +import { createContext, ReactNode, useContext, useRef } from 'react'; +import { StoreApi, useStore as useStoreZustand } from 'zustand'; interface StoreProviderProps { children: ReactNode; } -export function createStoreContext() { - return createContext | undefined>(undefined); +// idk why zustand doesn't export this +type ExtractState = S extends { + getState: () => infer T; } + ? T + : never; -export function createStoreProvider( - initialState: Type, - Context: Context | undefined>, +export function createNextSafeStore>( + initial: () => Store, ) { - return function Provider({ children }: StoreProviderProps) { - const storeRef = useRef>(); - if (!storeRef.current) { - storeRef.current = createStore()(() => ({ ...initialState })); - } + type Type = Store extends StoreApi ? T : never; - return ( - {children} - ); - }; -} - -export function createNextSafeStore(initialState: Type) { - const Context = createContext | undefined>(undefined); + const Context = createContext(undefined); function Provider({ children }: StoreProviderProps) { - const storeRef = useRef>(); - if (!storeRef.current) { - storeRef.current = createStore()(() => ({ ...initialState })); - } + const storeRef = useRef(); + if (!storeRef.current) storeRef.current = initial() as Store; return ( {children} @@ -42,17 +31,18 @@ export function createNextSafeStore(initialState: Type) { function useStore() { const store = useContext(Context); - if (!store) throw new Error('useStore must be used within Provider'); - - return store; + return store as Store; } function use( - selector: (store: Type) => Slice = (state) => state as unknown as Slice, - ): Slice { + selector: (store: Type) => Slice = (state) => state as Slice, + ) { const store = useStore(); - return useStoreZustand(store, selector); + return useStoreZustand( + store, + selector as (state: ExtractState) => Slice, + ); } function useMutation() { diff --git a/src/stores/tankFilters.ts b/src/stores/tankFilters.ts index 6d87247b5..489bb0aaa 100644 --- a/src/stores/tankFilters.ts +++ b/src/stores/tankFilters.ts @@ -1,5 +1,6 @@ 'use client'; +import { create } from 'zustand'; import { TankClass, TreeType } from '../components/Tanks'; import { Tier } from '../core/blitzkit/tankDefinitions'; import { createNextSafeStore } from '../core/zustand/createNextSafeStore'; @@ -14,13 +15,15 @@ export interface TankFilters { searching: boolean; } -export const { Provider, use, useMutation, useStore } = - createNextSafeStore({ - tiers: [], - nations: [], - classes: [], - types: [], - testing: 'include', - search: undefined, - searching: false, - }); +export const { Provider, use, useMutation, useStore } = createNextSafeStore( + () => + create()(() => ({ + tiers: [], + nations: [], + classes: [], + types: [], + testing: 'include', + search: undefined, + searching: false, + })), +); diff --git a/src/stores/tankPerformanceSort/index.ts b/src/stores/tankPerformanceSort/index.ts index 717815e57..76fe22353 100644 --- a/src/stores/tankPerformanceSort/index.ts +++ b/src/stores/tankPerformanceSort/index.ts @@ -1,5 +1,6 @@ 'use client'; +import { create } from 'zustand'; import { createNextSafeStore } from '../../core/zustand/createNextSafeStore'; import { tankPerformanceSortTypeNames } from './constants'; @@ -10,8 +11,10 @@ export interface TankPerformanceSort { direction: -1 | 1; } -export const { Provider, use, useMutation, useStore } = - createNextSafeStore({ - type: 'winrate', - direction: -1, - }); +export const { Provider, use, useMutation, useStore } = createNextSafeStore( + () => + create()(() => ({ + type: 'winrate', + direction: -1, + })), +); diff --git a/src/stores/tankopedia.ts b/src/stores/tankopedia.ts deleted file mode 100644 index 97f4e2af1..000000000 --- a/src/stores/tankopedia.ts +++ /dev/null @@ -1,218 +0,0 @@ -import { produce } from 'immer'; -import { merge } from 'lodash'; -import { Vector3 } from 'three'; -import { create } from 'zustand'; -import { persist, subscribeWithSelector } from 'zustand/middleware'; -import { ENVIRONMENTS } from '../app/tools/tankopedia/[id]/components/Lighting'; -import { ArmorType } from '../components/Armor/components/SpacedArmorScene'; -import { ExternalModuleVariant } from '../components/Armor/components/SpacedArmorSceneComponent'; -import { TankClass, TreeType } from '../components/Tanks'; -import { Tier } from '../core/blitzkit/tankDefinitions'; - -export type TankopediaSortBy = keyof typeof SORT_NAMES; -export type TankopediaSortDirection = 'ascending' | 'descending'; -export type TankopediaTestTankDisplay = 'include' | 'exclude' | 'only'; -export type TankopediaMode = 'model' | 'armor'; - -export interface TankopediaPersistent { - model: { - visual: { - wireframe: boolean; - opaque: boolean; - environment: (typeof ENVIRONMENTS)[number]; - showGrid: boolean; - greenPenetration: boolean; - showEnvironment: boolean; - showSpacedArmor: boolean; - }; - }; - sort: { - by: TankopediaSortBy; - direction: TankopediaSortDirection; - }; - filters: { - tiers: Tier[]; - types: TankClass[]; - treeTypes: TreeType[]; - nations: string[]; - test: TankopediaTestTankDisplay; - page: number; - }; - mode: TankopediaMode; -} - -interface TankopediaTemporary { - shot?: Shot; - skills: Record; - controlsEnabled: boolean; -} - -export interface ShotLayerBase { - index: number; - thickness: number; - point: Vector3; - surfaceNormal: Vector3; - status: 'blocked' | 'penetration' | 'ricochet'; -} - -interface ShotLayerExternal extends ShotLayerBase { - type: ArmorType.External; - variant: ExternalModuleVariant; -} - -export interface ShotLayerNonExternal extends ShotLayerBase { - type: Exclude; - angle: number; - thicknessAngled: number; -} - -export interface ShotLayerGap { - type: null; - status: 'penetration' | 'blocked'; - distance: number; -} - -export type ShotLayer = ShotLayerExternal | ShotLayerNonExternal | ShotLayerGap; - -export type Shot = { - containsGaps: boolean; - damage: number; - - in: { - surfaceNormal: Vector3; - status: ShotStatus; - layers: ShotLayer[]; - }; - out?: { - surfaceNormal: Vector3; - status: ShotStatus; - layers: ShotLayer[]; - }; -}; - -export type ShotStatus = 'penetration' | 'blocked' | 'ricochet' | 'splash'; - -export type ArmorPiercingLayer = - | { - distance: number; - nominal: number; - angled: number; - ricochet: boolean; - type: 'core' | 'spaced' | 'external'; - } - | { - type: 'gap'; - gap: number; - }; - -export const SORT_NAMES = { - 'meta.none': 'none', - 'survivability.health': 'health', - 'survivability.viewRange': 'view range', - 'survivability.camouflageStill': 'camouflage still', - 'survivability.camouflageMoving': 'camouflage moving', - 'survivability.camouflageShooting': 'camouflage shooting', - 'survivability.volume': 'volume', - 'survivability.length': 'length', - 'fire.dpm': 'standard DPM', - 'fire.dpmPremium': 'premium DPM', - 'fire.reload': 'reload', - 'fire.caliber': 'caliber', - 'fire.standardPenetration': 'standard penetration', - 'fire.premiumPenetration': 'premium penetration', - 'fire.damage': 'damage', - 'fire.shellVelocity': 'shell velocity', - 'fire.aimTime': 'aim time', - 'fire.dispersionStill': 'dispersion still', - 'fire.dispersionMoving': 'dispersion moving', - 'fire.gunDepression': 'gun depression', - 'fire.gunElevation': 'gun elevation', - 'maneuverability.forwardsSpeed': 'forwards speed', - 'maneuverability.backwardsSpeed': 'backwards speed', - 'maneuverability.power': 'power', - 'maneuverability.powerToWeight': 'power to weight', - 'maneuverability.weight': 'weight', - 'maneuverability.traverseSpeed': 'traverse speed', -} as const; - -export const SORT_UNITS: Record = { - 'meta.none': undefined, - 'survivability.health': 'hp', - 'fire.aimTime': 's', - 'fire.caliber': 'mm', - 'fire.damage': 'hp', - 'fire.dispersionMoving': 'm', - 'fire.dispersionStill': 'm', - 'fire.dpm': undefined, - 'fire.dpmPremium': undefined, - 'fire.gunDepression': '°', - 'fire.gunElevation': '°', - 'fire.premiumPenetration': 'mm', - 'fire.reload': 's', - 'fire.shellVelocity': 'm/s', - 'fire.standardPenetration': 'mm', - 'maneuverability.backwardsSpeed': 'kph', - 'maneuverability.forwardsSpeed': 'kph', - 'maneuverability.power': 'hp', - 'maneuverability.powerToWeight': 'hp/tn', - 'maneuverability.traverseSpeed': '°/s', - 'maneuverability.weight': 'tn', - 'survivability.camouflageMoving': '%', - 'survivability.camouflageShooting': '%', - 'survivability.camouflageStill': '%', - 'survivability.length': 'm', - 'survivability.viewRange': 'm', - 'survivability.volume': 'm^3', -}; - -export const useTankopediaPersistent = create()( - persist( - subscribeWithSelector(() => ({ - model: { - visual: { - showSpacedArmor: true, - wireframe: false, - opaque: false, - environment: ENVIRONMENTS[0], - controlsEnabled: true, - showGrid: true, - greenPenetration: false, - showEnvironment: false, - }, - }, - sort: { - by: 'meta.none', - direction: 'descending', - }, - filters: { - tiers: [], - types: [], - treeTypes: [], - nations: [], - test: 'include', - page: 0, - }, - mode: 'model', - })), - { name: 'tankopedia', merge: (a, b) => merge(b, a) }, - ), -); - -export const useTankopediaTemporary = create()( - subscribeWithSelector(() => ({ - skills: {}, - controlsEnabled: true, - })), -); - -export default function mutateTankopediaPersistent( - recipe: (draft: TankopediaPersistent) => void, -) { - useTankopediaPersistent.setState(produce(recipe)); -} - -export function mutateTankopediaTemporary( - recipe: (draft: TankopediaTemporary) => void, -) { - useTankopediaTemporary.setState(produce(recipe)); -} diff --git a/src/stores/tankopediaEphemeral.ts b/src/stores/tankopediaEphemeral.ts new file mode 100644 index 000000000..1d21efc74 --- /dev/null +++ b/src/stores/tankopediaEphemeral.ts @@ -0,0 +1,80 @@ +import { Vector3 } from 'three'; +import { create } from 'zustand'; +import { subscribeWithSelector } from 'zustand/middleware'; +import { ArmorType } from '../components/Armor/components/SpacedArmorScene'; +import { ExternalModuleVariant } from '../components/Armor/components/SpacedArmorSceneComponent'; +import { createNextSafeStore } from '../core/zustand/createNextSafeStore'; + +interface TankopediaEphemeral { + shot?: Shot; + skills: Record; + controlsEnabled: boolean; +} + +export interface ShotLayerBase { + index: number; + thickness: number; + point: Vector3; + surfaceNormal: Vector3; + status: 'blocked' | 'penetration' | 'ricochet'; +} + +interface ShotLayerExternal extends ShotLayerBase { + type: ArmorType.External; + variant: ExternalModuleVariant; +} + +export interface ShotLayerNonExternal extends ShotLayerBase { + type: Exclude; + angle: number; + thicknessAngled: number; +} + +export interface ShotLayerGap { + type: null; + status: 'penetration' | 'blocked'; + distance: number; +} + +export type ShotLayer = ShotLayerExternal | ShotLayerNonExternal | ShotLayerGap; + +export type Shot = { + containsGaps: boolean; + damage: number; + + in: { + surfaceNormal: Vector3; + status: ShotStatus; + layers: ShotLayer[]; + }; + out?: { + surfaceNormal: Vector3; + status: ShotStatus; + layers: ShotLayer[]; + }; +}; + +export type ShotStatus = 'penetration' | 'blocked' | 'ricochet' | 'splash'; + +export type ArmorPiercingLayer = + | { + distance: number; + nominal: number; + angled: number; + ricochet: boolean; + type: 'core' | 'spaced' | 'external'; + } + | { + type: 'gap'; + gap: number; + }; + +export const { Provider, use, useMutation, useStore } = createNextSafeStore( + () => + create()( + subscribeWithSelector(() => ({ + skills: {}, + controlsEnabled: true, + })), + ), +); diff --git a/src/stores/tankopediaPersistent/constants.ts b/src/stores/tankopediaPersistent/constants.ts new file mode 100644 index 000000000..d0e039418 --- /dev/null +++ b/src/stores/tankopediaPersistent/constants.ts @@ -0,0 +1,60 @@ +import { TankopediaSortBy } from '.'; + +export const SORT_UNITS: Record = { + 'meta.none': undefined, + 'survivability.health': 'hp', + 'fire.aimTime': 's', + 'fire.caliber': 'mm', + 'fire.damage': 'hp', + 'fire.dispersionMoving': 'm', + 'fire.dispersionStill': 'm', + 'fire.dpm': undefined, + 'fire.dpmPremium': undefined, + 'fire.gunDepression': '°', + 'fire.gunElevation': '°', + 'fire.premiumPenetration': 'mm', + 'fire.reload': 's', + 'fire.shellVelocity': 'm/s', + 'fire.standardPenetration': 'mm', + 'maneuverability.backwardsSpeed': 'kph', + 'maneuverability.forwardsSpeed': 'kph', + 'maneuverability.power': 'hp', + 'maneuverability.powerToWeight': 'hp/tn', + 'maneuverability.traverseSpeed': '°/s', + 'maneuverability.weight': 'tn', + 'survivability.camouflageMoving': '%', + 'survivability.camouflageShooting': '%', + 'survivability.camouflageStill': '%', + 'survivability.length': 'm', + 'survivability.viewRange': 'm', + 'survivability.volume': 'm^3', +}; +export const SORT_NAMES = { + 'meta.none': 'none', + 'survivability.health': 'health', + 'survivability.viewRange': 'view range', + 'survivability.camouflageStill': 'camouflage still', + 'survivability.camouflageMoving': 'camouflage moving', + 'survivability.camouflageShooting': 'camouflage shooting', + 'survivability.volume': 'volume', + 'survivability.length': 'length', + 'fire.dpm': 'standard DPM', + 'fire.dpmPremium': 'premium DPM', + 'fire.reload': 'reload', + 'fire.caliber': 'caliber', + 'fire.standardPenetration': 'standard penetration', + 'fire.premiumPenetration': 'premium penetration', + 'fire.damage': 'damage', + 'fire.shellVelocity': 'shell velocity', + 'fire.aimTime': 'aim time', + 'fire.dispersionStill': 'dispersion still', + 'fire.dispersionMoving': 'dispersion moving', + 'fire.gunDepression': 'gun depression', + 'fire.gunElevation': 'gun elevation', + 'maneuverability.forwardsSpeed': 'forwards speed', + 'maneuverability.backwardsSpeed': 'backwards speed', + 'maneuverability.power': 'power', + 'maneuverability.powerToWeight': 'power to weight', + 'maneuverability.weight': 'weight', + 'maneuverability.traverseSpeed': 'traverse speed', +} as const; diff --git a/src/stores/tankopediaPersistent/index.ts b/src/stores/tankopediaPersistent/index.ts new file mode 100644 index 000000000..5ce3ba0fd --- /dev/null +++ b/src/stores/tankopediaPersistent/index.ts @@ -0,0 +1,75 @@ +import { merge } from 'lodash'; +import { create } from 'zustand'; +import { persist, subscribeWithSelector } from 'zustand/middleware'; +import { ENVIRONMENTS } from '../../app/tools/tankopedia/[id]/components/Lighting'; +import { TankClass, TreeType } from '../../components/Tanks'; +import { Tier } from '../../core/blitzkit/tankDefinitions'; +import { createNextSafeStore } from '../../core/zustand/createNextSafeStore'; +import { SORT_NAMES } from './constants'; + +export type TankopediaSortBy = keyof typeof SORT_NAMES; +export type TankopediaSortDirection = 'ascending' | 'descending'; +export type TankopediaTestTankDisplay = 'include' | 'exclude' | 'only'; +export type TankopediaMode = 'model' | 'armor'; + +export interface TankopediaPersistent { + model: { + visual: { + wireframe: boolean; + opaque: boolean; + environment: (typeof ENVIRONMENTS)[number]; + showGrid: boolean; + greenPenetration: boolean; + showEnvironment: boolean; + showSpacedArmor: boolean; + }; + }; + sort: { + by: TankopediaSortBy; + direction: TankopediaSortDirection; + }; + filters: { + tiers: Tier[]; + types: TankClass[]; + treeTypes: TreeType[]; + nations: string[]; + test: TankopediaTestTankDisplay; + page: number; + }; + mode: TankopediaMode; +} + +export const { Provider, use, useMutation, useStore } = createNextSafeStore( + () => + create()( + persist( + subscribeWithSelector(() => ({ + model: { + visual: { + showSpacedArmor: true, + wireframe: false, + opaque: false, + environment: ENVIRONMENTS[0], + showGrid: true, + greenPenetration: false, + showEnvironment: false, + }, + }, + sort: { + by: 'meta.none', + direction: 'descending', + }, + filters: { + tiers: [], + types: [], + treeTypes: [], + nations: [], + test: 'include', + page: 0, + }, + mode: 'model', + })), + { name: 'tankopedia', merge: (a, b) => merge(b, a) }, + ), + ), +); diff --git a/src/stores/tankopediaSort.ts b/src/stores/tankopediaSort.ts index 578f2df11..0eb0a0e71 100644 --- a/src/stores/tankopediaSort.ts +++ b/src/stores/tankopediaSort.ts @@ -1,15 +1,21 @@ 'use client'; +import { create } from 'zustand'; import { createNextSafeStore } from '../core/zustand/createNextSafeStore'; -import { TankopediaSortBy, TankopediaSortDirection } from './tankopedia'; +import { + TankopediaSortBy, + TankopediaSortDirection, +} from './tankopediaPersistent'; export interface TankopediaSort { by: TankopediaSortBy; direction: TankopediaSortDirection; } -export const { Provider, use, useMutation, useStore } = - createNextSafeStore({ - by: 'meta.none', - direction: 'descending', - }); +export const { Provider, use, useMutation, useStore } = createNextSafeStore( + () => + create()(() => ({ + by: 'meta.none', + direction: 'descending', + })), +);