react-native-nitro-storage 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -22
- package/android/src/main/cpp/AndroidStorageAdapterCpp.cpp +10 -0
- package/android/src/main/cpp/AndroidStorageAdapterCpp.hpp +13 -0
- package/android/src/main/java/com/nitrostorage/AndroidStorageAdapter.kt +10 -0
- package/cpp/bindings/HybridStorage.cpp +50 -0
- package/cpp/bindings/HybridStorage.hpp +4 -0
- package/cpp/core/NativeStorageAdapter.hpp +3 -0
- package/ios/IOSStorageAdapterCpp.hpp +3 -0
- package/ios/IOSStorageAdapterCpp.mm +13 -0
- package/lib/commonjs/Storage.nitro.js +0 -7
- package/lib/commonjs/Storage.nitro.js.map +1 -1
- package/lib/commonjs/Storage.types.js +13 -0
- package/lib/commonjs/Storage.types.js.map +1 -0
- package/lib/commonjs/index.js +69 -8
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +268 -0
- package/lib/commonjs/index.web.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/module/Storage.nitro.js +1 -6
- package/lib/module/Storage.nitro.js.map +1 -1
- package/lib/module/Storage.types.js +9 -0
- package/lib/module/Storage.types.js.map +1 -0
- package/lib/module/index.js +66 -8
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +257 -0
- package/lib/module/index.web.js.map +1 -0
- package/lib/typescript/Storage.nitro.d.ts +5 -6
- package/lib/typescript/Storage.nitro.d.ts.map +1 -1
- package/lib/typescript/Storage.types.d.ts +6 -0
- package/lib/typescript/Storage.types.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +12 -3
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +50 -0
- package/lib/typescript/index.web.d.ts.map +1 -0
- package/nitrogen/generated/android/NitroStorage+autolinking.cmake +1 -1
- package/nitrogen/generated/android/NitroStorage+autolinking.gradle +1 -1
- package/nitrogen/generated/android/NitroStorageOnLoad.cpp +1 -1
- package/nitrogen/generated/android/NitroStorageOnLoad.hpp +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/com/nitrostorage/NitroStorageOnLoad.kt +1 -1
- package/nitrogen/generated/ios/NitroStorage+autolinking.rb +2 -2
- package/nitrogen/generated/ios/NitroStorage-Swift-Cxx-Bridge.cpp +1 -1
- package/nitrogen/generated/ios/NitroStorage-Swift-Cxx-Bridge.hpp +1 -1
- package/nitrogen/generated/ios/NitroStorage-Swift-Cxx-Umbrella.hpp +1 -1
- package/nitrogen/generated/ios/NitroStorageAutolinking.mm +1 -1
- package/nitrogen/generated/ios/NitroStorageAutolinking.swift +1 -1
- package/nitrogen/generated/shared/c++/HybridStorageSpec.cpp +5 -1
- package/nitrogen/generated/shared/c++/HybridStorageSpec.hpp +6 -1
- package/package.json +6 -25
- package/src/Storage.nitro.ts +6 -7
- package/src/Storage.types.ts +5 -0
- package/src/index.ts +88 -14
- package/src/index.web.ts +341 -0
package/src/Storage.nitro.ts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import type
|
|
2
|
-
|
|
3
|
-
export enum StorageScope {
|
|
4
|
-
Memory = 0,
|
|
5
|
-
Disk = 1,
|
|
6
|
-
Secure = 2,
|
|
7
|
-
}
|
|
1
|
+
import { type HybridObject } from "react-native-nitro-modules";
|
|
2
|
+
import { StorageScope } from "./Storage.types";
|
|
8
3
|
|
|
9
4
|
export interface Storage extends HybridObject<{ ios: "c++"; android: "c++" }> {
|
|
10
5
|
set(key: string, value: string, scope: number): void;
|
|
11
6
|
get(key: string, scope: number): string | undefined;
|
|
12
7
|
remove(key: string, scope: number): void;
|
|
8
|
+
clear(scope: number): void;
|
|
9
|
+
setBatch(keys: string[], values: string[], scope: number): void;
|
|
10
|
+
getBatch(keys: string[], scope: number): (string | undefined)[];
|
|
11
|
+
removeBatch(keys: string[], scope: number): void;
|
|
13
12
|
addOnChange(
|
|
14
13
|
scope: number,
|
|
15
14
|
callback: (key: string, value: string | undefined) => void
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { useSyncExternalStore
|
|
1
|
+
import { useSyncExternalStore } from "react";
|
|
2
2
|
import { NitroModules } from "react-native-nitro-modules";
|
|
3
|
-
import type { Storage
|
|
4
|
-
import { StorageScope
|
|
3
|
+
import type { Storage } from "./Storage.nitro";
|
|
4
|
+
import { StorageScope } from "./Storage.types";
|
|
5
5
|
|
|
6
|
-
export { StorageScope } from "./Storage.
|
|
6
|
+
export { StorageScope } from "./Storage.types";
|
|
7
7
|
export type { Storage } from "./Storage.nitro";
|
|
8
8
|
|
|
9
9
|
let _storageModule: Storage | null = null;
|
|
@@ -23,11 +23,18 @@ function notifyMemoryListeners(key: string, value: any) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export const storage = {
|
|
26
|
-
clear: (scope: StorageScope
|
|
27
|
-
|
|
26
|
+
clear: (scope: StorageScope) => {
|
|
27
|
+
if (scope === StorageScope.Memory) {
|
|
28
|
+
memoryStore.clear();
|
|
29
|
+
notifyMemoryListeners("", undefined);
|
|
30
|
+
} else {
|
|
31
|
+
getStorageModule().clear(scope);
|
|
32
|
+
}
|
|
28
33
|
},
|
|
29
34
|
clearAll: () => {
|
|
30
|
-
storage.clear(
|
|
35
|
+
storage.clear(StorageScope.Memory);
|
|
36
|
+
storage.clear(StorageScope.Disk);
|
|
37
|
+
storage.clear(StorageScope.Secure);
|
|
31
38
|
},
|
|
32
39
|
};
|
|
33
40
|
|
|
@@ -44,6 +51,9 @@ export interface StorageItem<T> {
|
|
|
44
51
|
set: (value: T | ((prev: T) => T)) => void;
|
|
45
52
|
delete: () => void;
|
|
46
53
|
subscribe: (callback: () => void) => () => void;
|
|
54
|
+
serialize: (value: T) => string;
|
|
55
|
+
deserialize: (value: string) => T;
|
|
56
|
+
_triggerListeners: () => void;
|
|
47
57
|
scope: StorageScope;
|
|
48
58
|
key: string;
|
|
49
59
|
}
|
|
@@ -61,7 +71,7 @@ export function createStorageItem<T = undefined>(
|
|
|
61
71
|
): StorageItem<T> {
|
|
62
72
|
const serialize = config.serialize ?? defaultSerialize;
|
|
63
73
|
const deserialize = config.deserialize ?? defaultDeserialize;
|
|
64
|
-
const isMemory = config.scope ===
|
|
74
|
+
const isMemory = config.scope === StorageScope.Memory;
|
|
65
75
|
|
|
66
76
|
const listeners = new Set<() => void>();
|
|
67
77
|
let unsubscribe: (() => void) | null = null;
|
|
@@ -70,7 +80,9 @@ export function createStorageItem<T = undefined>(
|
|
|
70
80
|
if (!unsubscribe) {
|
|
71
81
|
if (isMemory) {
|
|
72
82
|
const listener = (key: string) => {
|
|
73
|
-
if (key === config.key) {
|
|
83
|
+
if (key === "" || key === config.key) {
|
|
84
|
+
lastRaw = undefined;
|
|
85
|
+
lastValue = undefined;
|
|
74
86
|
listeners.forEach((l) => l());
|
|
75
87
|
}
|
|
76
88
|
};
|
|
@@ -78,7 +90,9 @@ export function createStorageItem<T = undefined>(
|
|
|
78
90
|
unsubscribe = () => memoryListeners.delete(listener);
|
|
79
91
|
} else {
|
|
80
92
|
unsubscribe = getStorageModule().addOnChange(config.scope, (key) => {
|
|
81
|
-
if (key === config.key) {
|
|
93
|
+
if (key === "" || key === config.key) {
|
|
94
|
+
lastRaw = undefined;
|
|
95
|
+
lastValue = undefined;
|
|
82
96
|
listeners.forEach((listener) => listener());
|
|
83
97
|
}
|
|
84
98
|
});
|
|
@@ -86,11 +100,11 @@ export function createStorageItem<T = undefined>(
|
|
|
86
100
|
}
|
|
87
101
|
};
|
|
88
102
|
|
|
89
|
-
let lastRaw: string |
|
|
103
|
+
let lastRaw: string | undefined;
|
|
90
104
|
let lastValue: T | undefined;
|
|
91
105
|
|
|
92
106
|
const get = (): T => {
|
|
93
|
-
let raw: string |
|
|
107
|
+
let raw: string | undefined;
|
|
94
108
|
|
|
95
109
|
if (isMemory) {
|
|
96
110
|
raw = memoryStore.get(config.key);
|
|
@@ -120,8 +134,8 @@ export function createStorageItem<T = undefined>(
|
|
|
120
134
|
const set = (valueOrFn: T | ((prev: T) => T)): void => {
|
|
121
135
|
const currentValue = get();
|
|
122
136
|
const newValue =
|
|
123
|
-
valueOrFn
|
|
124
|
-
? (valueOrFn as
|
|
137
|
+
typeof valueOrFn === "function"
|
|
138
|
+
? (valueOrFn as (prev: T) => T)(currentValue)
|
|
125
139
|
: valueOrFn;
|
|
126
140
|
|
|
127
141
|
if (isMemory) {
|
|
@@ -159,6 +173,13 @@ export function createStorageItem<T = undefined>(
|
|
|
159
173
|
set,
|
|
160
174
|
delete: deleteItem,
|
|
161
175
|
subscribe,
|
|
176
|
+
serialize,
|
|
177
|
+
deserialize,
|
|
178
|
+
_triggerListeners: () => {
|
|
179
|
+
lastRaw = undefined;
|
|
180
|
+
lastValue = undefined;
|
|
181
|
+
listeners.forEach((l) => l());
|
|
182
|
+
},
|
|
162
183
|
scope: config.scope,
|
|
163
184
|
key: config.key,
|
|
164
185
|
};
|
|
@@ -174,3 +195,56 @@ export function useStorage<T>(
|
|
|
174
195
|
export function useSetStorage<T>(item: StorageItem<T>) {
|
|
175
196
|
return item.set;
|
|
176
197
|
}
|
|
198
|
+
|
|
199
|
+
export function getBatch(
|
|
200
|
+
items: StorageItem<any>[],
|
|
201
|
+
scope: StorageScope
|
|
202
|
+
): any[] {
|
|
203
|
+
if (scope === StorageScope.Memory) {
|
|
204
|
+
return items.map((item) => item.get());
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const keys = items.map((item) => item.key);
|
|
208
|
+
const rawValues = getStorageModule().getBatch(keys, scope);
|
|
209
|
+
|
|
210
|
+
return items.map((item, idx) => {
|
|
211
|
+
const raw = rawValues[idx];
|
|
212
|
+
if (raw === undefined) {
|
|
213
|
+
return item.get();
|
|
214
|
+
}
|
|
215
|
+
return item.deserialize(raw);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function setBatch(
|
|
220
|
+
items: { item: StorageItem<any>; value: any }[],
|
|
221
|
+
scope: StorageScope
|
|
222
|
+
): void {
|
|
223
|
+
if (scope === StorageScope.Memory) {
|
|
224
|
+
items.forEach(({ item, value }) => item.set(value));
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const keys = items.map((i) => i.item.key);
|
|
229
|
+
const values = items.map((i) => i.item.serialize(i.value));
|
|
230
|
+
|
|
231
|
+
getStorageModule().setBatch(keys, values, scope);
|
|
232
|
+
|
|
233
|
+
items.forEach(({ item }) => {
|
|
234
|
+
item._triggerListeners();
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export function removeBatch(
|
|
239
|
+
items: StorageItem<any>[],
|
|
240
|
+
scope: StorageScope
|
|
241
|
+
): void {
|
|
242
|
+
if (scope === StorageScope.Memory) {
|
|
243
|
+
items.forEach((item) => item.delete());
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const keys = items.map((item) => item.key);
|
|
248
|
+
getStorageModule().removeBatch(keys, scope);
|
|
249
|
+
items.forEach((item) => item.delete());
|
|
250
|
+
}
|
package/src/index.web.ts
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { useSyncExternalStore } from "react";
|
|
2
|
+
|
|
3
|
+
export enum StorageScope {
|
|
4
|
+
Memory = 0,
|
|
5
|
+
Disk = 1,
|
|
6
|
+
Secure = 2,
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface Storage {
|
|
10
|
+
name: string;
|
|
11
|
+
equals: (other: any) => boolean;
|
|
12
|
+
dispose: () => void;
|
|
13
|
+
set(key: string, value: string, scope: number): void;
|
|
14
|
+
get(key: string, scope: number): string | undefined;
|
|
15
|
+
remove(key: string, scope: number): void;
|
|
16
|
+
clear(scope: number): void;
|
|
17
|
+
setBatch(keys: string[], values: string[], scope: number): void;
|
|
18
|
+
getBatch(keys: string[], scope: number): (string | undefined)[];
|
|
19
|
+
removeBatch(keys: string[], scope: number): void;
|
|
20
|
+
addOnChange(
|
|
21
|
+
scope: number,
|
|
22
|
+
callback: (key: string, value: string | undefined) => void
|
|
23
|
+
): () => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const diskListeners = new Map<string, Set<() => void>>();
|
|
27
|
+
const secureListeners = new Map<string, Set<() => void>>();
|
|
28
|
+
|
|
29
|
+
function notifyDiskListeners(key: string) {
|
|
30
|
+
diskListeners.get(key)?.forEach((cb) => cb());
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function notifySecureListeners(key: string) {
|
|
34
|
+
secureListeners.get(key)?.forEach((cb) => cb());
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const WebStorage: Storage = {
|
|
38
|
+
name: "Storage",
|
|
39
|
+
equals: (other) => other === WebStorage,
|
|
40
|
+
dispose: () => {},
|
|
41
|
+
set: (key: string, value: string, scope: number) => {
|
|
42
|
+
if (scope === StorageScope.Disk) {
|
|
43
|
+
localStorage?.setItem(key, value);
|
|
44
|
+
notifyDiskListeners(key);
|
|
45
|
+
} else if (scope === StorageScope.Secure) {
|
|
46
|
+
sessionStorage?.setItem(key, value);
|
|
47
|
+
notifySecureListeners(key);
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
get: (key: string, scope: number) => {
|
|
52
|
+
if (scope === StorageScope.Disk) {
|
|
53
|
+
return localStorage?.getItem(key) ?? undefined;
|
|
54
|
+
} else if (scope === StorageScope.Secure) {
|
|
55
|
+
return sessionStorage?.getItem(key) ?? undefined;
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
},
|
|
59
|
+
remove: (key: string, scope: number) => {
|
|
60
|
+
if (scope === StorageScope.Disk) {
|
|
61
|
+
localStorage?.removeItem(key);
|
|
62
|
+
notifyDiskListeners(key);
|
|
63
|
+
} else if (scope === StorageScope.Secure) {
|
|
64
|
+
sessionStorage?.removeItem(key);
|
|
65
|
+
notifySecureListeners(key);
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
clear: (scope: number) => {
|
|
70
|
+
if (scope === StorageScope.Disk) {
|
|
71
|
+
localStorage?.clear();
|
|
72
|
+
diskListeners.forEach((listeners) => {
|
|
73
|
+
listeners.forEach((cb) => cb());
|
|
74
|
+
});
|
|
75
|
+
} else if (scope === StorageScope.Secure) {
|
|
76
|
+
sessionStorage?.clear();
|
|
77
|
+
secureListeners.forEach((listeners) => {
|
|
78
|
+
listeners.forEach((cb) => cb());
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
setBatch: (keys: string[], values: string[], scope: number) => {
|
|
83
|
+
keys.forEach((key, i) => WebStorage.set(key, values[i], scope));
|
|
84
|
+
},
|
|
85
|
+
getBatch: (keys: string[], scope: number) => {
|
|
86
|
+
return keys.map((key) => WebStorage.get(key, scope));
|
|
87
|
+
},
|
|
88
|
+
removeBatch: (keys: string[], scope: number) => {
|
|
89
|
+
keys.forEach((key) => WebStorage.remove(key, scope));
|
|
90
|
+
},
|
|
91
|
+
addOnChange: (
|
|
92
|
+
_scope: number,
|
|
93
|
+
_callback: (key: string, value: string | undefined) => void
|
|
94
|
+
) => {
|
|
95
|
+
return () => {};
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const memoryStore = new Map<string, any>();
|
|
100
|
+
const memoryListeners = new Set<(key: string, value: any) => void>();
|
|
101
|
+
|
|
102
|
+
function notifyMemoryListeners(key: string, value: any) {
|
|
103
|
+
memoryListeners.forEach((listener) => listener(key, value));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export const storage = {
|
|
107
|
+
clear: (scope: StorageScope) => {
|
|
108
|
+
if (scope === StorageScope.Memory) {
|
|
109
|
+
memoryStore.clear();
|
|
110
|
+
notifyMemoryListeners("", undefined);
|
|
111
|
+
} else {
|
|
112
|
+
WebStorage.clear(scope);
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
clearAll: () => {
|
|
116
|
+
storage.clear(StorageScope.Memory);
|
|
117
|
+
storage.clear(StorageScope.Disk);
|
|
118
|
+
storage.clear(StorageScope.Secure);
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export interface StorageItemConfig<T> {
|
|
123
|
+
key: string;
|
|
124
|
+
scope: StorageScope;
|
|
125
|
+
defaultValue?: T;
|
|
126
|
+
serialize?: (value: T) => string;
|
|
127
|
+
deserialize?: (value: string) => T;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface StorageItem<T> {
|
|
131
|
+
get: () => T;
|
|
132
|
+
set: (value: T | ((prev: T) => T)) => void;
|
|
133
|
+
delete: () => void;
|
|
134
|
+
subscribe: (callback: () => void) => () => void;
|
|
135
|
+
serialize: (value: T) => string;
|
|
136
|
+
deserialize: (value: string) => T;
|
|
137
|
+
_triggerListeners: () => void;
|
|
138
|
+
scope: StorageScope;
|
|
139
|
+
key: string;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function defaultSerialize<T>(value: T): string {
|
|
143
|
+
return JSON.stringify(value);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function defaultDeserialize<T>(value: string): T {
|
|
147
|
+
return JSON.parse(value) as T;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function createStorageItem<T = undefined>(
|
|
151
|
+
config: StorageItemConfig<T>
|
|
152
|
+
): StorageItem<T> {
|
|
153
|
+
const serialize = config.serialize ?? defaultSerialize;
|
|
154
|
+
const deserialize = config.deserialize ?? defaultDeserialize;
|
|
155
|
+
const isMemory = config.scope === StorageScope.Memory;
|
|
156
|
+
|
|
157
|
+
const listeners = new Set<() => void>();
|
|
158
|
+
let unsubscribe: (() => void) | null = null;
|
|
159
|
+
let lastRaw: string | undefined;
|
|
160
|
+
let lastValue: T | undefined;
|
|
161
|
+
|
|
162
|
+
const ensureSubscription = () => {
|
|
163
|
+
if (!unsubscribe) {
|
|
164
|
+
if (isMemory) {
|
|
165
|
+
const listener = (key: string) => {
|
|
166
|
+
if (key === config.key) {
|
|
167
|
+
lastRaw = undefined;
|
|
168
|
+
lastValue = undefined;
|
|
169
|
+
listeners.forEach((l) => l());
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
memoryListeners.add(listener);
|
|
173
|
+
unsubscribe = () => memoryListeners.delete(listener);
|
|
174
|
+
} else if (config.scope === StorageScope.Disk) {
|
|
175
|
+
const listener = () => {
|
|
176
|
+
lastRaw = undefined;
|
|
177
|
+
lastValue = undefined;
|
|
178
|
+
listeners.forEach((l) => l());
|
|
179
|
+
};
|
|
180
|
+
if (!diskListeners.has(config.key)) {
|
|
181
|
+
diskListeners.set(config.key, new Set());
|
|
182
|
+
}
|
|
183
|
+
diskListeners.get(config.key)!.add(listener);
|
|
184
|
+
unsubscribe = () => diskListeners.get(config.key)?.delete(listener);
|
|
185
|
+
} else if (config.scope === StorageScope.Secure) {
|
|
186
|
+
const listener = () => {
|
|
187
|
+
lastRaw = undefined;
|
|
188
|
+
lastValue = undefined;
|
|
189
|
+
listeners.forEach((l) => l());
|
|
190
|
+
};
|
|
191
|
+
if (!secureListeners.has(config.key)) {
|
|
192
|
+
secureListeners.set(config.key, new Set());
|
|
193
|
+
}
|
|
194
|
+
secureListeners.get(config.key)!.add(listener);
|
|
195
|
+
unsubscribe = () => secureListeners.get(config.key)?.delete(listener);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const get = (): T => {
|
|
201
|
+
let raw: string | undefined;
|
|
202
|
+
if (isMemory) {
|
|
203
|
+
raw = memoryStore.get(config.key);
|
|
204
|
+
} else {
|
|
205
|
+
raw = WebStorage.get(config.key, config.scope);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (raw === lastRaw && lastValue !== undefined) {
|
|
209
|
+
return lastValue;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
lastRaw = raw;
|
|
213
|
+
|
|
214
|
+
if (raw === undefined) {
|
|
215
|
+
lastValue = config.defaultValue as T;
|
|
216
|
+
} else {
|
|
217
|
+
lastValue = isMemory ? (raw as T) : deserialize(raw);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return lastValue;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const set = (valueOrFn: T | ((prev: T) => T)): void => {
|
|
224
|
+
const currentValue = get();
|
|
225
|
+
const newValue =
|
|
226
|
+
typeof valueOrFn === "function"
|
|
227
|
+
? (valueOrFn as (prev: T) => T)(currentValue)
|
|
228
|
+
: valueOrFn;
|
|
229
|
+
|
|
230
|
+
lastRaw = undefined;
|
|
231
|
+
|
|
232
|
+
if (isMemory) {
|
|
233
|
+
memoryStore.set(config.key, newValue);
|
|
234
|
+
notifyMemoryListeners(config.key, newValue);
|
|
235
|
+
} else {
|
|
236
|
+
WebStorage.set(config.key, serialize(newValue), config.scope);
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const deleteItem = (): void => {
|
|
241
|
+
lastRaw = undefined;
|
|
242
|
+
|
|
243
|
+
if (isMemory) {
|
|
244
|
+
memoryStore.delete(config.key);
|
|
245
|
+
notifyMemoryListeners(config.key, undefined);
|
|
246
|
+
} else {
|
|
247
|
+
WebStorage.remove(config.key, config.scope);
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const subscribe = (callback: () => void): (() => void) => {
|
|
252
|
+
ensureSubscription();
|
|
253
|
+
listeners.add(callback);
|
|
254
|
+
return () => {
|
|
255
|
+
listeners.delete(callback);
|
|
256
|
+
if (listeners.size === 0 && unsubscribe) {
|
|
257
|
+
unsubscribe();
|
|
258
|
+
unsubscribe = null;
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
get,
|
|
265
|
+
set,
|
|
266
|
+
delete: deleteItem,
|
|
267
|
+
subscribe,
|
|
268
|
+
serialize,
|
|
269
|
+
deserialize,
|
|
270
|
+
_triggerListeners: () => {
|
|
271
|
+
lastRaw = undefined;
|
|
272
|
+
lastValue = undefined;
|
|
273
|
+
listeners.forEach((l) => l());
|
|
274
|
+
},
|
|
275
|
+
scope: config.scope,
|
|
276
|
+
key: config.key,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function useStorage<T>(
|
|
281
|
+
item: StorageItem<T>
|
|
282
|
+
): [T, (value: T | ((prev: T) => T)) => void] {
|
|
283
|
+
const value = useSyncExternalStore(item.subscribe, item.get, item.get);
|
|
284
|
+
return [value, item.set];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function useSetStorage<T>(item: StorageItem<T>) {
|
|
288
|
+
return item.set;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function getBatch(
|
|
292
|
+
items: StorageItem<any>[],
|
|
293
|
+
scope: StorageScope
|
|
294
|
+
): any[] {
|
|
295
|
+
if (scope === StorageScope.Memory) {
|
|
296
|
+
return items.map((item) => item.get());
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const keys = items.map((item) => item.key);
|
|
300
|
+
const rawValues = WebStorage.getBatch(keys, scope);
|
|
301
|
+
|
|
302
|
+
return items.map((item, idx) => {
|
|
303
|
+
const raw = rawValues[idx];
|
|
304
|
+
if (raw === undefined) {
|
|
305
|
+
return item.get();
|
|
306
|
+
}
|
|
307
|
+
return item.deserialize(raw);
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export function setBatch(
|
|
312
|
+
items: { item: StorageItem<any>; value: any }[],
|
|
313
|
+
scope: StorageScope
|
|
314
|
+
): void {
|
|
315
|
+
if (scope === StorageScope.Memory) {
|
|
316
|
+
items.forEach(({ item, value }) => item.set(value));
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const keys = items.map((i) => i.item.key);
|
|
321
|
+
const values = items.map((i) => i.item.serialize(i.value));
|
|
322
|
+
WebStorage.setBatch(keys, values, scope);
|
|
323
|
+
|
|
324
|
+
items.forEach(({ item }) => {
|
|
325
|
+
item._triggerListeners();
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export function removeBatch(
|
|
330
|
+
items: StorageItem<any>[],
|
|
331
|
+
scope: StorageScope
|
|
332
|
+
): void {
|
|
333
|
+
if (scope === StorageScope.Memory) {
|
|
334
|
+
items.forEach((item) => item.delete());
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const keys = items.map((item) => item.key);
|
|
339
|
+
WebStorage.removeBatch(keys, scope);
|
|
340
|
+
items.forEach((item) => item.delete());
|
|
341
|
+
}
|