diff --git a/bun.lockb b/bun.lockb index 634ca645f..dcdaec92b 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/packages/varuna/package.json b/packages/varuna/package.json new file mode 100644 index 000000000..837eda1cd --- /dev/null +++ b/packages/varuna/package.json @@ -0,0 +1,8 @@ +{ + "name": "varuna", + "type": "module", + "main": "src/index.ts", + "dependencies": { + "react": "^18.3.1" + } +} diff --git a/packages/varuna/src/index.ts b/packages/varuna/src/index.ts new file mode 100644 index 000000000..2d077cafc --- /dev/null +++ b/packages/varuna/src/index.ts @@ -0,0 +1,55 @@ +import { produce } from 'immer'; +import { merge } from 'lodash-es'; +import { useEffect, useState } from 'react'; + +export class Varuna { + private listeners = new Set<(state: Type) => void>(); + public initial: Type; + + constructor( + public state: Type, + persistence?: string, + ) { + if (persistence) { + const dehydrated = localStorage.getItem(persistence); + + if (dehydrated) { + const rehydrated = JSON.parse(dehydrated) as unknown; + this.state = merge(this.state, rehydrated); + } + } + + this.initial = this.state; + } + + private dispatch() { + const state = this.state; + this.listeners.forEach((callback) => callback(state)); + } + + on(slicer: (state: Type) => Slice, callback: (state: Slice) => void) { + const last = slicer(this.state); + + function listener(state: Type) { + const next = slicer(state); + if (next !== last) callback(next); + } + + this.listeners.add(listener); + + return () => { + this.listeners.delete(listener); + }; + } + + mutate(mutator: (state: Type) => void) { + this.state = produce(this.state, mutator); + this.dispatch(); + } + + use(slicer: (state: Type) => Slice) { + const [slice, setSlice] = useState(slicer(this.state)); + useEffect(() => this.on(slicer, setSlice), []); + return slice; + } +} diff --git a/packages/varuna/tsconfig.json b/packages/varuna/tsconfig.json new file mode 100644 index 000000000..4082f16a5 --- /dev/null +++ b/packages/varuna/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig.json" +} diff --git a/packages/website/package.json b/packages/website/package.json index 782764a2c..c6dcff2ab 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -13,6 +13,7 @@ "@astrolib/seo": "^1.0.0-beta.8", "@blitzkit/core": "workspace:*", "@blitzkit/i18n": "workspace:*", + "varuna": "workspace:*", "@fontsource/inter": "^5.2.5", "@nanostores/persistent": "^0.10.2", "@nanostores/react": "^0.7.3", diff --git a/packages/website/src/components/TierList/Controls/index.tsx b/packages/website/src/components/TierList/Controls/index.tsx index 7b629f341..fd1cd56e3 100644 --- a/packages/website/src/components/TierList/Controls/index.tsx +++ b/packages/website/src/components/TierList/Controls/index.tsx @@ -1,11 +1,10 @@ import { ReloadIcon } from '@radix-ui/react-icons'; import { Button, Flex } from '@radix-ui/themes'; import { useLocale } from '../../../hooks/useLocale'; -import { TierList, tierListInitialState } from '../../../stores/tierList'; +import { TierList } from '../../../stores/tierList'; import { Share } from './components/Share'; export function TierListControls() { - const mutateTierList = TierList.useMutation(); const { strings } = useLocale(); return ( @@ -13,8 +12,8 @@ export function TierListControls() {