@buoy-gg/storage 3.0.2 → 4.0.1
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 +1 -1
- package/lib/commonjs/storage/components/GameUIStorageBrowser.js +24 -3
- package/lib/commonjs/storage/components/SelectionActionBar.js +16 -3
- package/lib/commonjs/storage/components/StorageBrowserMode.js +6 -2
- package/lib/commonjs/storage/components/StorageKeyRow.js +95 -6
- package/lib/commonjs/storage/components/StorageKeySection.js +8 -2
- package/lib/commonjs/storage/components/StorageModalWithTabs.js +47 -1
- package/lib/commonjs/storage/hooks/useAsyncStorageKeys.js +3 -2
- package/lib/commonjs/storage/stores/storageEventStore.js +7 -4
- package/lib/commonjs/storage/sync/storageSyncAdapter.js +7 -3
- package/lib/commonjs/storage/utils/AsyncStorageListener.js +148 -160
- package/lib/commonjs/storage/utils/asyncStorageCompat.js +89 -0
- package/lib/commonjs/storage/utils/clearAllStorage.js +2 -1
- package/lib/commonjs/storage/utils/mmkvTypeDetection.js +20 -5
- package/lib/commonjs/storage/utils/storageTimeTravelUtils.js +3 -2
- package/lib/commonjs/storage/utils/valueType.js +41 -0
- package/lib/module/storage/components/GameUIStorageBrowser.js +24 -3
- package/lib/module/storage/components/SelectionActionBar.js +17 -3
- package/lib/module/storage/components/StorageBrowserMode.js +6 -2
- package/lib/module/storage/components/StorageKeyRow.js +97 -8
- package/lib/module/storage/components/StorageKeySection.js +8 -2
- package/lib/module/storage/components/StorageModalWithTabs.js +47 -1
- package/lib/module/storage/hooks/useAsyncStorageKeys.js +3 -2
- package/lib/module/storage/stores/storageEventStore.js +7 -4
- package/lib/module/storage/sync/storageSyncAdapter.js +7 -3
- package/lib/module/storage/utils/AsyncStorageListener.js +124 -135
- package/lib/module/storage/utils/asyncStorageCompat.js +81 -0
- package/lib/module/storage/utils/clearAllStorage.js +2 -1
- package/lib/module/storage/utils/mmkvTypeDetection.js +20 -5
- package/lib/module/storage/utils/storageTimeTravelUtils.js +3 -2
- package/lib/module/storage/utils/valueType.js +39 -0
- package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts +5 -1
- package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts.map +1 -1
- package/lib/typescript/storage/components/SelectionActionBar.d.ts +3 -1
- package/lib/typescript/storage/components/SelectionActionBar.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageBrowserMode.d.ts +3 -1
- package/lib/typescript/storage/components/StorageBrowserMode.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageKeyRow.d.ts +7 -1
- package/lib/typescript/storage/components/StorageKeyRow.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageKeySection.d.ts +7 -1
- package/lib/typescript/storage/components/StorageKeySection.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageModalWithTabs.d.ts.map +1 -1
- package/lib/typescript/storage/hooks/useAsyncStorageKeys.d.ts.map +1 -1
- package/lib/typescript/storage/stores/storageEventStore.d.ts.map +1 -1
- package/lib/typescript/storage/sync/storageSyncAdapter.d.ts +1 -1
- package/lib/typescript/storage/sync/storageSyncAdapter.d.ts.map +1 -1
- package/lib/typescript/storage/utils/AsyncStorageListener.d.ts +20 -0
- package/lib/typescript/storage/utils/AsyncStorageListener.d.ts.map +1 -1
- package/lib/typescript/storage/utils/asyncStorageCompat.d.ts +30 -0
- package/lib/typescript/storage/utils/asyncStorageCompat.d.ts.map +1 -0
- package/lib/typescript/storage/utils/clearAllStorage.d.ts.map +1 -1
- package/lib/typescript/storage/utils/mmkvTypeDetection.d.ts.map +1 -1
- package/lib/typescript/storage/utils/storageTimeTravelUtils.d.ts.map +1 -1
- package/lib/typescript/storage/utils/valueType.d.ts +13 -0
- package/lib/typescript/storage/utils/valueType.d.ts.map +1 -1
- package/package.json +6 -6
|
@@ -62,7 +62,9 @@ export function GameUIStorageBrowser({
|
|
|
62
62
|
storageDataRef,
|
|
63
63
|
eventCountByKey,
|
|
64
64
|
onViewHistory,
|
|
65
|
-
enabledStorageTypes
|
|
65
|
+
enabledStorageTypes,
|
|
66
|
+
pinnedKeys,
|
|
67
|
+
onTogglePin
|
|
66
68
|
}) {
|
|
67
69
|
const isPro = useIsPro();
|
|
68
70
|
const [showUpgradeModal, setShowUpgradeModal] = useState(false);
|
|
@@ -172,6 +174,15 @@ export function GameUIStorageBrowser({
|
|
|
172
174
|
refresh();
|
|
173
175
|
}, [refresh]);
|
|
174
176
|
|
|
177
|
+
// Hide the selected keys by adding each to the ignored-pattern filter store,
|
|
178
|
+
// then exit selection mode. Non-destructive — keys can be unhidden from the
|
|
179
|
+
// filters UI.
|
|
180
|
+
const handleHideKeys = useCallback(keys => {
|
|
181
|
+
keys.forEach(k => onAddPattern?.(k.key));
|
|
182
|
+
setSelectedKeyIds(new Set());
|
|
183
|
+
setIsSelectMode(false);
|
|
184
|
+
}, [onAddPattern]);
|
|
185
|
+
|
|
175
186
|
// Memoized export data for copy functionality
|
|
176
187
|
const copyExportData = useMemo(() => {
|
|
177
188
|
const allKeys = allStorageKeys;
|
|
@@ -394,8 +405,14 @@ export function GameUIStorageBrowser({
|
|
|
394
405
|
// NOTE: MMKV instance filtering is now handled in mmkvStorageKeys memo (line 128)
|
|
395
406
|
// No need to filter again here
|
|
396
407
|
|
|
408
|
+
// Float pinned keys to the top (stable — preserves existing order otherwise)
|
|
409
|
+
if (pinnedKeys && pinnedKeys.size > 0) {
|
|
410
|
+
const pinned = keys.filter(k => pinnedKeys.has(k.key));
|
|
411
|
+
const rest = keys.filter(k => !pinnedKeys.has(k.key));
|
|
412
|
+
keys = [...pinned, ...rest];
|
|
413
|
+
}
|
|
397
414
|
return keys;
|
|
398
|
-
}, [sortedKeys, activeFilter, activeStorageType, ignoredPatterns, searchQuery, selectedMMKVInstance, enabledStorageTypes]);
|
|
415
|
+
}, [sortedKeys, activeFilter, activeStorageType, ignoredPatterns, searchQuery, selectedMMKVInstance, enabledStorageTypes, pinnedKeys]);
|
|
399
416
|
|
|
400
417
|
// For free users, limit visible keys to FREE_TIER_KEY_LIMIT
|
|
401
418
|
const visibleKeys = useMemo(() => {
|
|
@@ -617,6 +634,7 @@ export function GameUIStorageBrowser({
|
|
|
617
634
|
instance: inst.instance
|
|
618
635
|
})),
|
|
619
636
|
onDeleteComplete: handleDeleteComplete,
|
|
637
|
+
onHideKeys: handleHideKeys,
|
|
620
638
|
onSelectAll: handleSelectAll,
|
|
621
639
|
onClearSelection: handleClearSelection,
|
|
622
640
|
totalVisibleKeys: visibleKeys.length
|
|
@@ -629,7 +647,10 @@ export function GameUIStorageBrowser({
|
|
|
629
647
|
selectedKeys: selectedKeyIds,
|
|
630
648
|
onSelectionChange: handleSelectionChange,
|
|
631
649
|
eventCountByKey: eventCountByKey,
|
|
632
|
-
onViewHistory: onViewHistory
|
|
650
|
+
onViewHistory: onViewHistory,
|
|
651
|
+
onHideKey: key => handleHideKeys([key]),
|
|
652
|
+
pinnedKeys: pinnedKeys,
|
|
653
|
+
onTogglePin: onTogglePin
|
|
633
654
|
}), hasLockedKeys && /*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
634
655
|
style: styles.limitBanner,
|
|
635
656
|
onPress: () => setShowUpgradeModal(true),
|
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect } from "react";
|
|
4
4
|
import { View, Text, StyleSheet, TouchableOpacity, Alert } from "react-native";
|
|
5
|
-
import { Trash2, CopyButton, CheckSquare, ProUpgradeModal } from "@buoy-gg/shared-ui";
|
|
5
|
+
import { Trash2, CopyButton, CheckSquare, EyeOff, ProUpgradeModal } from "@buoy-gg/shared-ui";
|
|
6
6
|
import { macOSColors } from "@buoy-gg/shared-ui";
|
|
7
7
|
import { useIsPro } from "@buoy-gg/license";
|
|
8
|
-
import
|
|
8
|
+
import { removeMany } from "../utils/asyncStorageCompat";
|
|
9
9
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
10
|
export function SelectionActionBar({
|
|
11
11
|
selectedKeys,
|
|
12
12
|
mmkvInstances,
|
|
13
13
|
onDeleteComplete,
|
|
14
|
+
onHideKeys,
|
|
14
15
|
onSelectAll,
|
|
15
16
|
onClearSelection,
|
|
16
17
|
totalVisibleKeys
|
|
@@ -41,6 +42,12 @@ export function SelectionActionBar({
|
|
|
41
42
|
};
|
|
42
43
|
};
|
|
43
44
|
|
|
45
|
+
// Hide selected keys (adds them to the filter store; non-destructive)
|
|
46
|
+
const handleHideSelected = () => {
|
|
47
|
+
if (selectedCount === 0) return;
|
|
48
|
+
onHideKeys?.(selectedKeys);
|
|
49
|
+
};
|
|
50
|
+
|
|
44
51
|
// Handle delete selected keys
|
|
45
52
|
const handleDeleteSelected = () => {
|
|
46
53
|
if (selectedCount === 0) return;
|
|
@@ -64,7 +71,7 @@ export function SelectionActionBar({
|
|
|
64
71
|
|
|
65
72
|
// Delete AsyncStorage keys
|
|
66
73
|
if (asyncKeys.length > 0) {
|
|
67
|
-
await
|
|
74
|
+
await removeMany(asyncKeys.map(k => k.key));
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
// Delete MMKV keys
|
|
@@ -127,6 +134,13 @@ export function SelectionActionBar({
|
|
|
127
134
|
success: macOSColors.semantic.success,
|
|
128
135
|
error: macOSColors.semantic.error
|
|
129
136
|
}
|
|
137
|
+
}), onHideKeys && /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
138
|
+
style: styles.actionButton,
|
|
139
|
+
onPress: handleHideSelected,
|
|
140
|
+
children: /*#__PURE__*/_jsx(EyeOff, {
|
|
141
|
+
size: 14,
|
|
142
|
+
color: macOSColors.semantic.warning
|
|
143
|
+
})
|
|
130
144
|
}), /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
131
145
|
style: styles.actionButton,
|
|
132
146
|
onPress: handleDeleteSelected,
|
|
@@ -16,7 +16,9 @@ export function StorageBrowserMode({
|
|
|
16
16
|
storageDataRef,
|
|
17
17
|
eventCountByKey,
|
|
18
18
|
onViewHistory,
|
|
19
|
-
enabledStorageTypes
|
|
19
|
+
enabledStorageTypes,
|
|
20
|
+
pinnedKeys,
|
|
21
|
+
onTogglePin
|
|
20
22
|
}) {
|
|
21
23
|
return /*#__PURE__*/_jsx(GameUIStorageBrowser, {
|
|
22
24
|
requiredStorageKeys: requiredStorageKeys,
|
|
@@ -28,6 +30,8 @@ export function StorageBrowserMode({
|
|
|
28
30
|
storageDataRef: storageDataRef,
|
|
29
31
|
eventCountByKey: eventCountByKey,
|
|
30
32
|
onViewHistory: onViewHistory,
|
|
31
|
-
enabledStorageTypes: enabledStorageTypes
|
|
33
|
+
enabledStorageTypes: enabledStorageTypes,
|
|
34
|
+
pinnedKeys: pinnedKeys,
|
|
35
|
+
onTogglePin: onTogglePin
|
|
32
36
|
});
|
|
33
37
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
|
|
4
|
-
import { macOSColors, CompactRow, TypeBadge, HardDrive, Square, CheckSquare, ExpandedInfoRow, PillBadge } from "@buoy-gg/shared-ui";
|
|
4
|
+
import { macOSColors, CompactRow, TypeBadge, HardDrive, Square, CheckSquare, EyeOff, Pin, ExpandedInfoRow, PillBadge } from "@buoy-gg/shared-ui";
|
|
5
5
|
import { getStorageTypeLabel } from "../utils/storageQueryUtils";
|
|
6
|
-
import { getValueTypeLabel } from "../utils/valueType";
|
|
6
|
+
import { getValueTypeLabel, getValueTypeWithPreview } from "../utils/valueType";
|
|
7
7
|
import { DataViewer } from "@buoy-gg/shared-ui/dataViewer";
|
|
8
8
|
|
|
9
9
|
// MMKV Instance color palette - consistent colors per instance
|
|
@@ -78,11 +78,18 @@ export function StorageKeyRow({
|
|
|
78
78
|
isSelected = false,
|
|
79
79
|
onSelectionChange,
|
|
80
80
|
eventCount,
|
|
81
|
-
onViewHistory
|
|
81
|
+
onViewHistory,
|
|
82
|
+
onHideKey,
|
|
83
|
+
isPinned = false,
|
|
84
|
+
onTogglePin
|
|
82
85
|
}) {
|
|
83
86
|
const config = getStatusConfig(storageKey.status);
|
|
84
87
|
const hasValue = storageKey.value !== undefined && storageKey.value !== null;
|
|
85
88
|
|
|
89
|
+
// Booleans get a dedicated colored badge instead of an inline text preview so
|
|
90
|
+
// their value is scannable at a glance.
|
|
91
|
+
const isBoolean = hasValue && typeof storageKey.value === "boolean";
|
|
92
|
+
|
|
86
93
|
// Format primary text - show the key
|
|
87
94
|
const primaryText = storageKey.key;
|
|
88
95
|
|
|
@@ -195,6 +202,45 @@ export function StorageKeyRow({
|
|
|
195
202
|
style: styles.expandedDescription,
|
|
196
203
|
children: storageKey.description
|
|
197
204
|
})]
|
|
205
|
+
}) : null, onTogglePin || onHideKey ? /*#__PURE__*/_jsxs(View, {
|
|
206
|
+
style: styles.actionRow,
|
|
207
|
+
children: [onTogglePin ? /*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
208
|
+
style: [styles.actionChip, isPinned && styles.actionChipActive],
|
|
209
|
+
onPress: () => onTogglePin(storageKey.key),
|
|
210
|
+
hitSlop: {
|
|
211
|
+
top: 6,
|
|
212
|
+
bottom: 6,
|
|
213
|
+
left: 6,
|
|
214
|
+
right: 6
|
|
215
|
+
},
|
|
216
|
+
children: [/*#__PURE__*/_jsx(Pin, {
|
|
217
|
+
size: 12,
|
|
218
|
+
color: macOSColors.semantic.info
|
|
219
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
220
|
+
style: [styles.actionChipText, {
|
|
221
|
+
color: macOSColors.semantic.info
|
|
222
|
+
}],
|
|
223
|
+
children: isPinned ? "Unpin" : "Pin to top"
|
|
224
|
+
})]
|
|
225
|
+
}) : null, onHideKey ? /*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
226
|
+
style: styles.actionChip,
|
|
227
|
+
onPress: () => onHideKey(storageKey),
|
|
228
|
+
hitSlop: {
|
|
229
|
+
top: 6,
|
|
230
|
+
bottom: 6,
|
|
231
|
+
left: 6,
|
|
232
|
+
right: 6
|
|
233
|
+
},
|
|
234
|
+
children: [/*#__PURE__*/_jsx(EyeOff, {
|
|
235
|
+
size: 12,
|
|
236
|
+
color: macOSColors.semantic.warning
|
|
237
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
238
|
+
style: [styles.actionChipText, {
|
|
239
|
+
color: macOSColors.semantic.warning
|
|
240
|
+
}],
|
|
241
|
+
children: "Hide from list"
|
|
242
|
+
})]
|
|
243
|
+
}) : null]
|
|
198
244
|
}) : null]
|
|
199
245
|
});
|
|
200
246
|
|
|
@@ -230,17 +276,28 @@ export function StorageKeyRow({
|
|
|
230
276
|
statusLabel: config.label,
|
|
231
277
|
statusSublabel: config.sublabel,
|
|
232
278
|
primaryText: primaryText,
|
|
233
|
-
secondaryText: hasValue ? getValueTypeLabel(storageKey.value) : undefined,
|
|
279
|
+
secondaryText: hasValue ? isBoolean ? getValueTypeLabel(storageKey.value) : getValueTypeWithPreview(storageKey.value) : undefined,
|
|
280
|
+
secondaryAccessory: isBoolean ? /*#__PURE__*/_jsx(PillBadge, {
|
|
281
|
+
size: "sm",
|
|
282
|
+
color: storageKey.value ? macOSColors.semantic.success : macOSColors.semantic.error,
|
|
283
|
+
children: storageKey.value ? "true" : "false"
|
|
284
|
+
}) : undefined,
|
|
234
285
|
expandedContent: expandedContent,
|
|
235
286
|
isExpanded: isExpanded,
|
|
236
287
|
expandedGlowColor: config.color,
|
|
237
|
-
customBadge: /*#__PURE__*/
|
|
238
|
-
|
|
239
|
-
children:
|
|
288
|
+
customBadge: /*#__PURE__*/_jsxs(View, {
|
|
289
|
+
style: styles.badgeRow,
|
|
290
|
+
children: [isPinned && /*#__PURE__*/_jsx(Pin, {
|
|
291
|
+
size: 12,
|
|
292
|
+
color: macOSColors.semantic.info
|
|
293
|
+
}), /*#__PURE__*/_jsx(PillBadge, {
|
|
294
|
+
color: getStorageTypeColor(storageKey.storageType),
|
|
295
|
+
children: storageTypeLabel
|
|
296
|
+
})]
|
|
240
297
|
}),
|
|
241
298
|
showChevron: !isSelectMode,
|
|
242
299
|
onPress: isSelectMode ? handleCheckboxPress : onPress ? () => onPress(storageKey) : undefined
|
|
243
|
-
}), eventCount != null && eventCount >
|
|
300
|
+
}), eventCount != null && eventCount > 1 && /*#__PURE__*/_jsx(View, {
|
|
244
301
|
style: [styles.absCountBadge, {
|
|
245
302
|
backgroundColor: macOSColors.semantic.warning + "22",
|
|
246
303
|
borderColor: macOSColors.semantic.warning + "55"
|
|
@@ -269,6 +326,11 @@ const getStorageTypeColor = storageType => {
|
|
|
269
326
|
}
|
|
270
327
|
};
|
|
271
328
|
const styles = StyleSheet.create({
|
|
329
|
+
badgeRow: {
|
|
330
|
+
flexDirection: "row",
|
|
331
|
+
alignItems: "center",
|
|
332
|
+
gap: 6
|
|
333
|
+
},
|
|
272
334
|
expandedContainer: {
|
|
273
335
|
gap: 8
|
|
274
336
|
},
|
|
@@ -342,6 +404,33 @@ const styles = StyleSheet.create({
|
|
|
342
404
|
fontWeight: "700",
|
|
343
405
|
fontFamily: "monospace"
|
|
344
406
|
},
|
|
407
|
+
actionRow: {
|
|
408
|
+
flexDirection: "row",
|
|
409
|
+
alignItems: "center",
|
|
410
|
+
flexWrap: "wrap",
|
|
411
|
+
gap: 8,
|
|
412
|
+
marginTop: 4
|
|
413
|
+
},
|
|
414
|
+
actionChip: {
|
|
415
|
+
flexDirection: "row",
|
|
416
|
+
alignItems: "center",
|
|
417
|
+
gap: 6,
|
|
418
|
+
paddingVertical: 6,
|
|
419
|
+
paddingHorizontal: 10,
|
|
420
|
+
borderRadius: 6,
|
|
421
|
+
backgroundColor: macOSColors.background.card,
|
|
422
|
+
borderWidth: 1,
|
|
423
|
+
borderColor: macOSColors.border.default
|
|
424
|
+
},
|
|
425
|
+
actionChipActive: {
|
|
426
|
+
backgroundColor: macOSColors.semantic.info + "15",
|
|
427
|
+
borderColor: macOSColors.semantic.info + "44"
|
|
428
|
+
},
|
|
429
|
+
actionChipText: {
|
|
430
|
+
fontSize: 11,
|
|
431
|
+
fontWeight: "600",
|
|
432
|
+
fontFamily: "monospace"
|
|
433
|
+
},
|
|
345
434
|
viewHistoryButton: {
|
|
346
435
|
marginLeft: 4
|
|
347
436
|
},
|
|
@@ -23,7 +23,10 @@ export function StorageKeySection({
|
|
|
23
23
|
selectedKeys = new Set(),
|
|
24
24
|
onSelectionChange,
|
|
25
25
|
eventCountByKey,
|
|
26
|
-
onViewHistory
|
|
26
|
+
onViewHistory,
|
|
27
|
+
onHideKey,
|
|
28
|
+
pinnedKeys,
|
|
29
|
+
onTogglePin
|
|
27
30
|
}) {
|
|
28
31
|
const [expandedKey, setExpandedKey] = useState(null);
|
|
29
32
|
const handleKeyPress = useCallback(storageKey => {
|
|
@@ -76,7 +79,10 @@ export function StorageKeySection({
|
|
|
76
79
|
isSelected: selectedKeys.has(uniqueKey),
|
|
77
80
|
onSelectionChange: onSelectionChange,
|
|
78
81
|
eventCount: eventCountByKey?.[storageKey.key],
|
|
79
|
-
onViewHistory: onViewHistory ? () => onViewHistory(storageKey.key) : undefined
|
|
82
|
+
onViewHistory: onViewHistory ? () => onViewHistory(storageKey.key) : undefined,
|
|
83
|
+
onHideKey: onHideKey,
|
|
84
|
+
isPinned: pinnedKeys?.has(storageKey.key),
|
|
85
|
+
onTogglePin: onTogglePin
|
|
80
86
|
}, uniqueKey);
|
|
81
87
|
})
|
|
82
88
|
})]
|
|
@@ -56,8 +56,10 @@ export function StorageModalWithTabs({
|
|
|
56
56
|
);
|
|
57
57
|
const [enabledStorageTypes, setEnabledStorageTypes] = useState(new Set(['async', 'mmkv', 'secure']) // All enabled by default
|
|
58
58
|
);
|
|
59
|
+
const [pinnedKeys, setPinnedKeys] = useState(new Set());
|
|
59
60
|
const lastEventRef = useRef(null);
|
|
60
61
|
const hasLoadedFilters = useRef(false);
|
|
62
|
+
const hasLoadedPins = useRef(false);
|
|
61
63
|
const hasLoadedTabState = useRef(false);
|
|
62
64
|
const hasLoadedMonitoringState = useRef(false);
|
|
63
65
|
|
|
@@ -172,6 +174,37 @@ export function StorageModalWithTabs({
|
|
|
172
174
|
saveFilters();
|
|
173
175
|
}, [ignoredPatterns]);
|
|
174
176
|
|
|
177
|
+
// Load persisted pinned keys on mount
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
if (!visible || hasLoadedPins.current) return;
|
|
180
|
+
const loadPins = async () => {
|
|
181
|
+
try {
|
|
182
|
+
const stored = await AsyncStorage.getItem(devToolsStorageKeys.storage.pinnedKeys());
|
|
183
|
+
if (stored) {
|
|
184
|
+
setPinnedKeys(new Set(JSON.parse(stored)));
|
|
185
|
+
}
|
|
186
|
+
hasLoadedPins.current = true;
|
|
187
|
+
} catch (error) {
|
|
188
|
+
// Failed to load pinned keys
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
loadPins();
|
|
192
|
+
}, [visible]);
|
|
193
|
+
|
|
194
|
+
// Save pinned keys when they change
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
if (!hasLoadedPins.current) return; // Don't save on initial load
|
|
197
|
+
|
|
198
|
+
const savePins = async () => {
|
|
199
|
+
try {
|
|
200
|
+
await AsyncStorage.setItem(devToolsStorageKeys.storage.pinnedKeys(), JSON.stringify(Array.from(pinnedKeys)));
|
|
201
|
+
} catch (error) {
|
|
202
|
+
// Failed to save pinned keys
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
savePins();
|
|
206
|
+
}, [pinnedKeys]);
|
|
207
|
+
|
|
175
208
|
// Event listener setup - now handled by useStorageEvents hook
|
|
176
209
|
// The hook subscribes to the centralized storageEventStore
|
|
177
210
|
|
|
@@ -206,6 +239,17 @@ export function StorageModalWithTabs({
|
|
|
206
239
|
const handleAddPattern = useCallback(pattern => {
|
|
207
240
|
setIgnoredPatterns(prev => new Set([...prev, pattern]));
|
|
208
241
|
}, []);
|
|
242
|
+
const handleTogglePin = useCallback(key => {
|
|
243
|
+
setPinnedKeys(prev => {
|
|
244
|
+
const next = new Set(prev);
|
|
245
|
+
if (next.has(key)) {
|
|
246
|
+
next.delete(key);
|
|
247
|
+
} else {
|
|
248
|
+
next.add(key);
|
|
249
|
+
}
|
|
250
|
+
return next;
|
|
251
|
+
});
|
|
252
|
+
}, []);
|
|
209
253
|
const handleToggleFilters = useCallback(() => {
|
|
210
254
|
setShowFilters(!showFilters);
|
|
211
255
|
}, [showFilters]);
|
|
@@ -504,7 +548,9 @@ export function StorageModalWithTabs({
|
|
|
504
548
|
storageDataRef: storageDataRef,
|
|
505
549
|
eventCountByKey: eventCountByKey,
|
|
506
550
|
onViewHistory: handleViewHistoryFromBrowser,
|
|
507
|
-
enabledStorageTypes: enabledStorageTypes
|
|
551
|
+
enabledStorageTypes: enabledStorageTypes,
|
|
552
|
+
pinnedKeys: pinnedKeys,
|
|
553
|
+
onTogglePin: handleTogglePin
|
|
508
554
|
});
|
|
509
555
|
}
|
|
510
556
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
4
4
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
5
|
+
import { readMany } from '../utils/asyncStorageCompat';
|
|
5
6
|
export function useAsyncStorageKeys(requiredStorageKeys = []) {
|
|
6
7
|
// State management
|
|
7
8
|
const [storageKeys, setStorageKeys] = useState([]);
|
|
@@ -31,8 +32,8 @@ export function useAsyncStorageKeys(requiredStorageKeys = []) {
|
|
|
31
32
|
return;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
// 2. Get all values
|
|
35
|
-
const allKeyValuePairs = await
|
|
35
|
+
// 2. Get all values (compat helper normalizes v2 multiGet / v3 getMany)
|
|
36
|
+
const allKeyValuePairs = await readMany(allKeys);
|
|
36
37
|
|
|
37
38
|
// 3. Process keys into StorageKeyInfo format
|
|
38
39
|
const allStorageKeys = [];
|
|
@@ -111,11 +111,14 @@ class StorageEventStore extends BaseEventStore {
|
|
|
111
111
|
|
|
112
112
|
// ── AsyncStorage: async scan ──
|
|
113
113
|
try {
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
const {
|
|
115
|
+
asyncStorage,
|
|
116
|
+
readMany
|
|
117
|
+
} = require("../utils/asyncStorageCompat");
|
|
118
|
+
if (asyncStorage) {
|
|
119
|
+
const allKeys = await asyncStorage.getAllKeys();
|
|
117
120
|
if (allKeys.length > 0) {
|
|
118
|
-
const pairs = await
|
|
121
|
+
const pairs = await readMany(allKeys);
|
|
119
122
|
for (const [key, rawValue] of pairs) {
|
|
120
123
|
this.addEvent({
|
|
121
124
|
action: "setItem",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
4
|
+
import { readMany, writeMany, removeMany } from "../utils/asyncStorageCompat";
|
|
4
5
|
import { storageEventStore } from "../stores/storageEventStore";
|
|
5
6
|
import { clearAllAppStorage } from "../utils/clearAllStorage";
|
|
6
7
|
/**
|
|
@@ -26,8 +27,11 @@ export const storageSyncAdapter = {
|
|
|
26
27
|
/** Clears all app storage keys, preserving dev tools settings. */
|
|
27
28
|
clearAppStorage: () => clearAllAppStorage(),
|
|
28
29
|
// ── Remote AsyncStorage proxy (desktop browse/edit mode) ──
|
|
30
|
+
// Single-item methods share the same signature across async-storage v2/v3;
|
|
31
|
+
// batch methods go through the compat helpers (which translate to v3's
|
|
32
|
+
// getMany/setMany/removeMany) and keep the v2 tuple/pairs shape on the wire.
|
|
29
33
|
"async.getAllKeys": () => AsyncStorage.getAllKeys(),
|
|
30
|
-
"async.multiGet": params =>
|
|
34
|
+
"async.multiGet": params => readMany(params.keys),
|
|
31
35
|
"async.getItem": params => AsyncStorage.getItem(params.key),
|
|
32
36
|
"async.setItem": params => {
|
|
33
37
|
const {
|
|
@@ -37,8 +41,8 @@ export const storageSyncAdapter = {
|
|
|
37
41
|
return AsyncStorage.setItem(key, value);
|
|
38
42
|
},
|
|
39
43
|
"async.removeItem": params => AsyncStorage.removeItem(params.key),
|
|
40
|
-
"async.multiRemove": params =>
|
|
41
|
-
"async.multiSet": params =>
|
|
44
|
+
"async.multiRemove": params => removeMany(params.keys),
|
|
45
|
+
"async.multiSet": params => writeMany(params.pairs),
|
|
42
46
|
"async.clear": () => AsyncStorage.clear()
|
|
43
47
|
}
|
|
44
48
|
};
|