import { browser } from "$app/environment"; import { writable, get, type Writable } from "svelte/store"; import localforage from "localforage"; interface StateBin { dueAnimeListOpen?: boolean; upcomingAnimeListOpen?: boolean; dueMangaListOpen?: boolean; completedAnimeListOpen?: boolean; completedMangaListOpen?: boolean; [key: string]: boolean | string | undefined; } const STORAGE_KEY = "stateBin"; const baseStore = writable({}); let hydrated = !browser; let state: StateBin = {}; let changedBeforeHydration = false; let initialEmission = true; let applyingStoredValue = false; let hydrationPromise = Promise.resolve(); if (browser) { hydrationPromise = localforage .getItem(STORAGE_KEY) .then(async (value): Promise => { if (value && typeof value === "object") { const nextState = changedBeforeHydration ? { ...value, ...state } : value; applyingStoredValue = true; baseStore.set(nextState); applyingStoredValue = false; } hydrated = true; await localforage.setItem(STORAGE_KEY, state); }); baseStore.subscribe((value) => { state = value; if (browser && !hydrated && !initialEmission && !applyingStoredValue) changedBeforeHydration = true; if (hydrated) localforage.setItem(STORAGE_KEY, value); initialEmission = false; }); } export const hydrateStateBin = () => hydrationPromise; const createProxyStore = (store: Writable) => { const keyStores = new Map< string, { subscribe: (run: (value: StateBin[string]) => void) => () => void; set: (value: StateBin[string]) => void; update: (updater: (value: StateBin[string]) => StateBin[string]) => void; } >(); const setKeyValue = (prop: string, value: StateBin[string]) => { const state = get(store); const updatedState = { ...state }; if (value === null || value === undefined) delete updatedState[prop]; else updatedState[prop] = value; store.set(updatedState); }; return new Proxy(store, { get(target, prop: string | symbol) { if (prop in target) return Reflect.get(target, prop); if (typeof prop !== "string") return undefined; const existingStore = keyStores.get(prop); if (existingStore) return existingStore; const keyStore = { subscribe(run: (value: StateBin[string]) => void) { return store.subscribe((value) => run(value[prop])); }, set(value: StateBin[string]) { setKeyValue(prop, value); }, update(updater: (value: StateBin[string]) => StateBin[string]) { setKeyValue(prop, updater(get(store)[prop])); }, }; keyStores.set(prop, keyStore); return keyStore; }, set(_, prop: string | symbol, value) { if (typeof prop !== "string") return false; setKeyValue(prop, value); return true; }, }); }; const stateBin = createProxyStore(baseStore); export default stateBin;