From a289dff1d9381346fb917bf055554df033dbd378 Mon Sep 17 00:00:00 2001 From: Iwo Plaza Date: Sun, 29 Dec 2024 13:05:10 +0100 Subject: [PATCH] Storing state in the url --- apps/phoure-demo/package.json | 1 + apps/phoure-demo/src/atomWithUrl.ts | 68 ++++++++++++++++++++++++++++ apps/phoure-demo/src/controlAtoms.ts | 53 ++++++++++------------ pnpm-lock.yaml | 11 +++++ 4 files changed, 103 insertions(+), 30 deletions(-) create mode 100644 apps/phoure-demo/src/atomWithUrl.ts diff --git a/apps/phoure-demo/package.json b/apps/phoure-demo/package.json index 01ba279..09f5b29 100644 --- a/apps/phoure-demo/package.json +++ b/apps/phoure-demo/package.json @@ -44,6 +44,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "jotai": "^2.11.0", + "jotai-location": "^0.5.5", "lucide-react": "^0.435.0", "motion": "^11.15.0", "phoure": "workspace:*", diff --git a/apps/phoure-demo/src/atomWithUrl.ts b/apps/phoure-demo/src/atomWithUrl.ts new file mode 100644 index 0000000..bc4a372 --- /dev/null +++ b/apps/phoure-demo/src/atomWithUrl.ts @@ -0,0 +1,68 @@ +import { atom } from 'jotai'; +import { atomWithLocation } from 'jotai-location'; + +const locationAtom = atomWithLocation(); + +export const stringParam = { + encode: (val: string) => val, + decode: (val: string) => val, +}; + +export const boolParam = { + encode: (val: boolean) => (val ? '1' : '0'), + decode: (val: string) => val === '1', +}; + +export const numberParam = { + encode: (val: number) => String(val), + decode: (val: string) => Number.parseFloat(val), +}; + +export const objParam = { + encode: JSON.stringify, + decode: JSON.parse, +}; + +export const typeToParam = { + string: stringParam, + boolean: boolParam, + number: numberParam, + object: objParam, +}; + +export const atomWithUrl = ( + key: string, + defaultValue: T, + options?: { encode: (val: T) => string; decode: (val: string) => unknown }, +) => { + const optionsOrInferred = + options ?? + typeToParam[typeof defaultValue as keyof typeof typeToParam] ?? + objParam; + + const { encode, decode } = optionsOrInferred; + + return atom( + (get) => { + const location = get(locationAtom); + return location.searchParams?.has(key) + ? (decode(location.searchParams.get(key) ?? '') as T) + : defaultValue; + }, + (get, set, newValue: T) => { + const prev = get(locationAtom); + const searchParams = new URLSearchParams(prev.searchParams); + searchParams.set(key, encode(newValue)); + + set( + // biome-ignore lint/suspicious/noExplicitAny: + locationAtom as any, + { + ...prev, + searchParams: searchParams, + }, + { replace: false }, + ); + }, + ); +}; diff --git a/apps/phoure-demo/src/controlAtoms.ts b/apps/phoure-demo/src/controlAtoms.ts index da0683d..a98bd6b 100644 --- a/apps/phoure-demo/src/controlAtoms.ts +++ b/apps/phoure-demo/src/controlAtoms.ts @@ -1,5 +1,5 @@ import { atom } from 'jotai'; -import { atomWithStorage } from 'jotai/utils'; +import { atomWithUrl } from './atomWithUrl'; export const DisplayModes = [ { key: 'upscaled', label: 'Upscaled' }, @@ -12,31 +12,26 @@ export const DisplayModes = [ export type DisplayMode = (typeof DisplayModes)[number]['key']; -export const measurePerformanceAtom = atomWithStorage( - 'MEASURE_PERFORMANCE', - false, -); - -export const cameraOrientationControlAtom = atomWithStorage( - 'CAMERA_ORIENTATION', - 0, -); - -export const cameraYControlAtom = atomWithStorage('CAMERA_Y', 0); +export const measurePerformanceAtom = atomWithUrl('perf', false); -export const cameraZoomControlAtom = atomWithStorage('CAMERA_ZOOM', 2); +// ------------------- +// Camera controls +// ------------------- -export const cameraFovControlAtom = atomWithStorage('CAMERA_FOV', 90); +export const cameraOrientationControlAtom = atomWithUrl('cyaw', 0); +export const cameraYControlAtom = atomWithUrl('cy', 0); +export const cameraZoomControlAtom = atomWithUrl('cd', 2); +export const cameraFovControlAtom = atomWithUrl('fov', 90); export const autoCameraOrientation = atom(0); -export const autoRotateSpeedAtom = atomWithStorage( - 'AUTO_ROTATE_SPEED', +export const autoRotateSpeedAtom = atomWithUrl( + 'crot', 0.5, // degrees per second ); export const autoRotateControlAtom = (() => { - const innerAtom = atomWithStorage('AUTO_ROTATE', true); + const innerAtom = atomWithUrl('cauto', true); return atom( (get) => get(innerAtom), @@ -50,19 +45,17 @@ export const autoRotateControlAtom = (() => { ); })(); -export const targetResolutionAtom = atomWithStorage('TARGET_RESOLUTION', 256); +// ------------------- +// Rendering controls +// ------------------- -export const displayModeAtom = atomWithStorage( - 'DISPLAY_MODE', - 'upscaled', -); +export const targetResolutionAtom = atomWithUrl('res', 256); -export const fixedTimestepEnabledAtom = atomWithStorage( - 'FIXED_TIMESTEP_ENABLED', - true, -); +export const displayModeAtom = atomWithUrl('mode', 'upscaled'); -export const fixedTimestepAtom = atomWithStorage( - 'FIXED_TIMESTEP', - 0.3 /* seconds */, -); +// ------------------- +// Time controls +// ------------------- + +export const fixedTimestepEnabledAtom = atomWithUrl('tfix', true); +export const fixedTimestepAtom = atomWithUrl('tstep', 0.3 /* seconds */); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6829d2d..3e6c6f6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,6 +74,9 @@ importers: jotai: specifier: ^2.11.0 version: 2.11.0(@types/react@18.3.4)(react@18.3.1) + jotai-location: + specifier: ^0.5.5 + version: 0.5.5(jotai@2.11.0) lucide-react: specifier: ^0.435.0 version: 0.435.0(react@18.3.1) @@ -2582,6 +2585,14 @@ packages: resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} hasBin: true + /jotai-location@0.5.5(jotai@2.11.0): + resolution: {integrity: sha512-6QW/7W9IJHjhbn7gRgAw4sC30k0/G6JiC4uPlKi8ZPZGYk7R7r9PyMD2eVhL4XSxxag89JxS1iSyr6BIXsB4Sw==} + peerDependencies: + jotai: '>=1.11.0' + dependencies: + jotai: 2.11.0(@types/react@18.3.4)(react@18.3.1) + dev: false + /jotai@2.11.0(@types/react@18.3.4)(react@18.3.1): resolution: {integrity: sha512-zKfoBBD1uDw3rljwHkt0fWuja1B76R7CjznuBO+mSX6jpsO1EBeWNRKpeaQho9yPI/pvCv4recGfgOXGxwPZvQ==} engines: {node: '>=12.20.0'}