Skip to content

Commit

Permalink
Storing state in the url
Browse files Browse the repository at this point in the history
  • Loading branch information
iwoplaza committed Dec 29, 2024
1 parent a6de81e commit a289dff
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 30 deletions.
1 change: 1 addition & 0 deletions apps/phoure-demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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:*",
Expand Down
68 changes: 68 additions & 0 deletions apps/phoure-demo/src/atomWithUrl.ts
Original file line number Diff line number Diff line change
@@ -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 = <T>(
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: <really annoying>
locationAtom as any,
{
...prev,
searchParams: searchParams,
},
{ replace: false },
);
},
);
};
53 changes: 23 additions & 30 deletions apps/phoure-demo/src/controlAtoms.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { atom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';
import { atomWithUrl } from './atomWithUrl';

export const DisplayModes = [
{ key: 'upscaled', label: 'Upscaled' },
Expand All @@ -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),
Expand All @@ -50,19 +45,17 @@ export const autoRotateControlAtom = (() => {
);
})();

export const targetResolutionAtom = atomWithStorage('TARGET_RESOLUTION', 256);
// -------------------
// Rendering controls
// -------------------

export const displayModeAtom = atomWithStorage<DisplayMode>(
'DISPLAY_MODE',
'upscaled',
);
export const targetResolutionAtom = atomWithUrl('res', 256);

export const fixedTimestepEnabledAtom = atomWithStorage(
'FIXED_TIMESTEP_ENABLED',
true,
);
export const displayModeAtom = atomWithUrl<DisplayMode>('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 */);
11 changes: 11 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit a289dff

Please sign in to comment.