aboutsummaryrefslogtreecommitdiff
path: root/src/stores/stateBin.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/stores/stateBin.ts')
-rw-r--r--src/stores/stateBin.ts135
1 files changed, 93 insertions, 42 deletions
diff --git a/src/stores/stateBin.ts b/src/stores/stateBin.ts
index f724860a..3fd38a99 100644
--- a/src/stores/stateBin.ts
+++ b/src/stores/stateBin.ts
@@ -1,61 +1,112 @@
-import { browser } from '$app/environment';
-import { writable, get, type Writable } from 'svelte/store';
-import localforage from 'localforage';
+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;
+ dueAnimeListOpen?: boolean;
+ upcomingAnimeListOpen?: boolean;
+ dueMangaListOpen?: boolean;
+ completedAnimeListOpen?: boolean;
+ completedMangaListOpen?: boolean;
+ [key: string]: boolean | string | undefined;
}
-const STORAGE_KEY = 'stateBin';
+const STORAGE_KEY = "stateBin";
const baseStore = writable<StateBin>({});
+let hydrated = !browser;
+let state: StateBin = {};
+let changedBeforeHydration = false;
+let initialEmission = true;
+let applyingStoredValue = false;
+let hydrationPromise = Promise.resolve();
if (browser) {
- localforage.getItem<StateBin>(STORAGE_KEY).then((value) => {
- if (value && typeof value === 'object') baseStore.set(value);
- });
+ hydrationPromise = localforage
+ .getItem<StateBin>(STORAGE_KEY)
+ .then(async (value): Promise<void> => {
+ if (value && typeof value === "object") {
+ const nextState = changedBeforeHydration
+ ? { ...value, ...state }
+ : value;
- baseStore.subscribe((value) => {
- localforage.setItem(STORAGE_KEY, value);
- });
-}
-
-const createProxyStore = (store: Writable<StateBin>) => {
- return new Proxy(store, {
- get(target, prop: string) {
- if (prop in target) return (target as unknown as Record<string, unknown>)[prop];
+ applyingStoredValue = true;
+ baseStore.set(nextState);
+ applyingStoredValue = false;
+ }
- const derivedKey = writable(get(store)[prop]);
+ hydrated = true;
- derivedKey.subscribe((value) => {
- const state = get(store);
- const updatedState = { ...state };
+ await localforage.setItem(STORAGE_KEY, state);
+ });
- if (value === null || value === undefined) delete updatedState[prop];
- else updatedState[prop] = value;
+ baseStore.subscribe((value) => {
+ state = value;
- store.set(updatedState);
- });
+ if (browser && !hydrated && !initialEmission && !applyingStoredValue)
+ changedBeforeHydration = true;
- return derivedKey;
- },
+ if (hydrated) localforage.setItem(STORAGE_KEY, value);
- set(_, prop: string, value) {
- const state = get(store);
- const updatedState = { ...state };
-
- if (value === null || value === undefined) delete updatedState[prop];
- else updatedState[prop] = value;
+ initialEmission = false;
+ });
+}
- store.set(updatedState);
+export const hydrateStateBin = () => hydrationPromise;
- return true;
- }
- });
+const createProxyStore = (store: Writable<StateBin>) => {
+ 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);