1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
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<StateBin>({});
let hydrated = !browser;
let state: StateBin = {};
let changedBeforeHydration = false;
let initialEmission = true;
let applyingStoredValue = false;
let hydrationPromise = Promise.resolve();
if (browser) {
hydrationPromise = localforage
.getItem<StateBin>(STORAGE_KEY)
.then(async (value): Promise<void> => {
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<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);
export default stateBin;
|