@buoy-gg/storage 7.0.4 → 7.0.5
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/lib/commonjs/index.js +77 -1
- package/lib/commonjs/storage/components/DiffViewer/TreeDiffViewer.js +10 -3
- package/lib/commonjs/storage/components/DiffViewer/modes/ThemedSplitView.js +26 -8
- package/lib/commonjs/storage/components/GameUIStorageBrowser.js +263 -115
- package/lib/commonjs/storage/components/GameUIStorageStats.js +7 -7
- package/lib/commonjs/storage/components/MMKVInstanceInfoPanel.js +1 -1
- package/lib/commonjs/storage/components/MMKVInstanceSelector.js +1 -1
- package/lib/commonjs/storage/components/SelectionActionBar.js +2 -36
- package/lib/commonjs/storage/components/StorageActions.js +6 -6
- package/lib/commonjs/storage/components/StorageBrowserMode.js +16 -2
- package/lib/commonjs/storage/components/StorageEventDetailContent.js +106 -149
- package/lib/commonjs/storage/components/StorageEventListener.js +13 -13
- package/lib/commonjs/storage/components/StorageFilterCards.js +13 -135
- package/lib/commonjs/storage/components/StorageKeyCard.js +1 -1
- package/lib/commonjs/storage/components/StorageKeyEditorScreen.js +392 -0
- package/lib/commonjs/storage/components/StorageKeyRow.js +53 -15
- package/lib/commonjs/storage/components/StorageKeySection.js +10 -4
- package/lib/commonjs/storage/components/StorageKeyStats.js +10 -10
- package/lib/commonjs/storage/components/StorageModalWithTabs.js +272 -129
- package/lib/commonjs/storage/components/StorageScalarKeyEditor.js +46 -0
- package/lib/commonjs/storage/components/StorageTypeFilterBar.js +141 -0
- package/lib/commonjs/storage/components/StorageValueEditModal.js +361 -0
- package/lib/commonjs/storage/hooks/useKeyEditor.js +104 -0
- package/lib/commonjs/storage/hooks/useMeasuredWidth.js +74 -0
- package/lib/commonjs/storage/hooks/useSecureStoreKeys.js +70 -15
- package/lib/commonjs/storage/hooks/useTreeDraft.js +107 -0
- package/lib/commonjs/storage/index.js +29 -0
- package/lib/commonjs/storage/stores/storageEventStore.js +4 -2
- package/lib/commonjs/storage/sync/storageSyncAdapter.js +125 -3
- package/lib/commonjs/storage/utils/AsyncStorageListener.js +76 -12
- package/lib/commonjs/storage/utils/MMKVInstanceRegistry.js +10 -9
- package/lib/commonjs/storage/utils/MMKVListener.js +40 -4
- package/lib/commonjs/storage/utils/SecureStoreRegistry.js +52 -1
- package/lib/commonjs/storage/utils/deleteStorageKeys.js +53 -0
- package/lib/commonjs/storage/utils/index.js +13 -0
- package/lib/commonjs/storage/utils/lineDiff.js +21 -354
- package/lib/commonjs/storage/utils/setStorageValue.js +218 -0
- package/lib/commonjs/storage/utils/storageBrowserRefresh.js +49 -0
- package/lib/commonjs/storage/utils/storageOps.js +53 -0
- package/lib/commonjs/storage/utils/storageTimeTravelUtils.js +60 -38
- package/lib/commonjs/storage/utils/valueType.js +24 -0
- package/lib/module/index.js +18 -1
- package/lib/module/storage/components/DiffViewer/TreeDiffViewer.js +10 -3
- package/lib/module/storage/components/DiffViewer/modes/ThemedSplitView.js +26 -8
- package/lib/module/storage/components/GameUIStorageBrowser.js +266 -118
- package/lib/module/storage/components/GameUIStorageStats.js +7 -7
- package/lib/module/storage/components/MMKVInstanceInfoPanel.js +1 -1
- package/lib/module/storage/components/MMKVInstanceSelector.js +1 -1
- package/lib/module/storage/components/SelectionActionBar.js +2 -36
- package/lib/module/storage/components/StorageActions.js +7 -7
- package/lib/module/storage/components/StorageBrowserMode.js +16 -2
- package/lib/module/storage/components/StorageEventDetailContent.js +110 -151
- package/lib/module/storage/components/StorageEventListener.js +14 -14
- package/lib/module/storage/components/StorageFilterCards.js +13 -135
- package/lib/module/storage/components/StorageKeyCard.js +1 -1
- package/lib/module/storage/components/StorageKeyEditorScreen.js +388 -0
- package/lib/module/storage/components/StorageKeyRow.js +55 -17
- package/lib/module/storage/components/StorageKeySection.js +11 -5
- package/lib/module/storage/components/StorageKeyStats.js +11 -10
- package/lib/module/storage/components/StorageModalWithTabs.js +272 -130
- package/lib/module/storage/components/StorageScalarKeyEditor.js +42 -0
- package/lib/module/storage/components/StorageTypeFilterBar.js +136 -0
- package/lib/module/storage/components/StorageValueEditModal.js +357 -0
- package/lib/module/storage/hooks/useKeyEditor.js +100 -0
- package/lib/module/storage/hooks/useMeasuredWidth.js +71 -0
- package/lib/module/storage/hooks/useSecureStoreKeys.js +71 -16
- package/lib/module/storage/hooks/useTreeDraft.js +103 -0
- package/lib/module/storage/index.js +3 -0
- package/lib/module/storage/stores/storageEventStore.js +4 -2
- package/lib/module/storage/sync/storageSyncAdapter.js +125 -3
- package/lib/module/storage/utils/AsyncStorageListener.js +76 -12
- package/lib/module/storage/utils/MMKVInstanceRegistry.js +8 -7
- package/lib/module/storage/utils/MMKVListener.js +40 -4
- package/lib/module/storage/utils/SecureStoreRegistry.js +52 -1
- package/lib/module/storage/utils/deleteStorageKeys.js +49 -0
- package/lib/module/storage/utils/index.js +3 -0
- package/lib/module/storage/utils/lineDiff.js +7 -354
- package/lib/module/storage/utils/setStorageValue.js +209 -0
- package/lib/module/storage/utils/storageBrowserRefresh.js +44 -0
- package/lib/module/storage/utils/storageOps.js +46 -0
- package/lib/module/storage/utils/storageTimeTravelUtils.js +61 -40
- package/lib/module/storage/utils/valueType.js +23 -0
- package/lib/typescript/index.d.ts +12 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/storage/components/DiffViewer/TreeDiffViewer.d.ts +4 -1
- package/lib/typescript/storage/components/DiffViewer/TreeDiffViewer.d.ts.map +1 -1
- package/lib/typescript/storage/components/DiffViewer/modes/ThemedSplitView.d.ts +7 -1
- package/lib/typescript/storage/components/DiffViewer/modes/ThemedSplitView.d.ts.map +1 -1
- package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts +27 -1
- package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts.map +1 -1
- package/lib/typescript/storage/components/SelectionActionBar.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageBrowserMode.d.ts +17 -1
- package/lib/typescript/storage/components/StorageBrowserMode.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageEventDetailContent.d.ts +4 -1
- package/lib/typescript/storage/components/StorageEventDetailContent.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageFilterCards.d.ts +9 -3
- package/lib/typescript/storage/components/StorageFilterCards.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageKeyEditorScreen.d.ts +42 -0
- package/lib/typescript/storage/components/StorageKeyEditorScreen.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageKeyRow.d.ts +17 -0
- 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/StorageKeyStats.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageModalWithTabs.d.ts +28 -1
- package/lib/typescript/storage/components/StorageModalWithTabs.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageScalarKeyEditor.d.ts +24 -0
- package/lib/typescript/storage/components/StorageScalarKeyEditor.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageTypeFilterBar.d.ts +29 -0
- package/lib/typescript/storage/components/StorageTypeFilterBar.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageValueEditModal.d.ts +36 -0
- package/lib/typescript/storage/components/StorageValueEditModal.d.ts.map +1 -0
- package/lib/typescript/storage/hooks/useKeyEditor.d.ts +51 -0
- package/lib/typescript/storage/hooks/useKeyEditor.d.ts.map +1 -0
- package/lib/typescript/storage/hooks/useMeasuredWidth.d.ts +37 -0
- package/lib/typescript/storage/hooks/useMeasuredWidth.d.ts.map +1 -0
- package/lib/typescript/storage/hooks/useSecureStoreKeys.d.ts +8 -1
- package/lib/typescript/storage/hooks/useSecureStoreKeys.d.ts.map +1 -1
- package/lib/typescript/storage/hooks/useTreeDraft.d.ts +67 -0
- package/lib/typescript/storage/hooks/useTreeDraft.d.ts.map +1 -0
- package/lib/typescript/storage/index.d.ts +2 -0
- package/lib/typescript/storage/index.d.ts.map +1 -1
- package/lib/typescript/storage/sync/storageSyncAdapter.d.ts +41 -0
- package/lib/typescript/storage/sync/storageSyncAdapter.d.ts.map +1 -1
- package/lib/typescript/storage/utils/AsyncStorageListener.d.ts +15 -0
- package/lib/typescript/storage/utils/AsyncStorageListener.d.ts.map +1 -1
- package/lib/typescript/storage/utils/MMKVInstanceRegistry.d.ts.map +1 -1
- package/lib/typescript/storage/utils/MMKVListener.d.ts +7 -0
- package/lib/typescript/storage/utils/MMKVListener.d.ts.map +1 -1
- package/lib/typescript/storage/utils/SecureStoreRegistry.d.ts +20 -0
- package/lib/typescript/storage/utils/SecureStoreRegistry.d.ts.map +1 -1
- package/lib/typescript/storage/utils/deleteStorageKeys.d.ts +27 -0
- package/lib/typescript/storage/utils/deleteStorageKeys.d.ts.map +1 -0
- package/lib/typescript/storage/utils/index.d.ts +1 -0
- package/lib/typescript/storage/utils/index.d.ts.map +1 -1
- package/lib/typescript/storage/utils/lineDiff.d.ts +7 -31
- package/lib/typescript/storage/utils/lineDiff.d.ts.map +1 -1
- package/lib/typescript/storage/utils/setStorageValue.d.ts +64 -0
- package/lib/typescript/storage/utils/setStorageValue.d.ts.map +1 -0
- package/lib/typescript/storage/utils/storageBrowserRefresh.d.ts +28 -0
- package/lib/typescript/storage/utils/storageBrowserRefresh.d.ts.map +1 -0
- package/lib/typescript/storage/utils/storageOps.d.ts +25 -0
- package/lib/typescript/storage/utils/storageOps.d.ts.map +1 -0
- package/lib/typescript/storage/utils/storageTimeTravelUtils.d.ts +15 -5
- 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 +5 -5
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import { useMemo, useCallback, useState, useEffect } from "react";
|
|
4
|
-
import { StyleSheet, Text, View, TouchableOpacity, ScrollView } from "react-native";
|
|
4
|
+
import { StyleSheet, Text, View, TouchableOpacity, ScrollView, Alert } from "react-native";
|
|
5
5
|
import { absoluteFill } from "@buoy-gg/shared-ui";
|
|
6
6
|
import { Database, Zap, ProBadge, ProUpgradeModal } from "@buoy-gg/shared-ui";
|
|
7
7
|
import { isDevToolsStorageKey } from "@buoy-gg/shared-ui";
|
|
8
8
|
import { StorageKeySection } from "./StorageKeySection";
|
|
9
|
+
import { StorageKeyEditorScreen } from "./StorageKeyEditorScreen";
|
|
9
10
|
import { StorageFilterCards } from "./StorageFilterCards";
|
|
10
11
|
import { useAsyncStorageKeys } from "../hooks/useAsyncStorageKeys";
|
|
11
12
|
import { addListener } from "../utils/AsyncStorageListener";
|
|
@@ -17,13 +18,15 @@ import { SelectionActionBar } from "./SelectionActionBar";
|
|
|
17
18
|
// Pure JS (no react-native-mmkv dependency) — works with any registered
|
|
18
19
|
// instance, including remote-backed wrappers on the desktop dashboard.
|
|
19
20
|
import { addMMKVListener } from "../utils/MMKVListener";
|
|
21
|
+
import { onStorageBrowserRefresh } from "../utils/storageBrowserRefresh";
|
|
22
|
+
import { setStorageValue, storageEditBlockedReason } from "../utils/setStorageValue";
|
|
20
23
|
|
|
21
24
|
// Import shared Game UI components
|
|
22
|
-
import { gameUIColors, macOSColors, HardDrive } from "@buoy-gg/shared-ui";
|
|
25
|
+
import { gameUIColors, macOSColors, buoyColors, HardDrive } from "@buoy-gg/shared-ui";
|
|
23
26
|
import { useIsPro } from "@buoy-gg/license";
|
|
24
27
|
|
|
25
28
|
// MMKV Instance color palette - consistent colors per instance
|
|
26
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
29
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
27
30
|
const INSTANCE_COLORS = [macOSColors.semantic.info,
|
|
28
31
|
// Blue
|
|
29
32
|
macOSColors.semantic.success,
|
|
@@ -57,10 +60,17 @@ export function GameUIStorageBrowser({
|
|
|
57
60
|
searchQuery = "",
|
|
58
61
|
storageDataRef,
|
|
59
62
|
eventCountByKey,
|
|
63
|
+
lastWriteByKey,
|
|
60
64
|
onViewHistory,
|
|
61
65
|
enabledStorageTypes,
|
|
66
|
+
statusFilter,
|
|
67
|
+
onStatusFilterChange,
|
|
68
|
+
onStatusCountsChange,
|
|
62
69
|
pinnedKeys,
|
|
63
|
-
onTogglePin
|
|
70
|
+
onTogglePin,
|
|
71
|
+
storageTypeFilter,
|
|
72
|
+
onStorageTypeFilterChange,
|
|
73
|
+
applySafeAreaInset
|
|
64
74
|
}) {
|
|
65
75
|
const isPro = useIsPro();
|
|
66
76
|
const [showUpgradeModal, setShowUpgradeModal] = useState(false);
|
|
@@ -71,10 +81,29 @@ export function GameUIStorageBrowser({
|
|
|
71
81
|
setShowUpgradeModal(false);
|
|
72
82
|
}
|
|
73
83
|
}, [showUpgradeModal, isPro]);
|
|
74
|
-
const [
|
|
75
|
-
|
|
84
|
+
const [internalFilter, setInternalFilter] = useState("all");
|
|
85
|
+
// Controlled when the host supplies both halves; uncontrolled otherwise.
|
|
86
|
+
const isFilterControlled = statusFilter != null && onStatusFilterChange != null;
|
|
87
|
+
const activeFilter = isFilterControlled ? statusFilter : internalFilter;
|
|
88
|
+
const setActiveFilter = isFilterControlled ? onStatusFilterChange : setInternalFilter;
|
|
89
|
+
const [internalStorageType, setInternalStorageType] = useState("all");
|
|
90
|
+
// Controlled when the host supplies both halves — the modal does, so the
|
|
91
|
+
// Events tab shows the same backend you picked here.
|
|
92
|
+
const isStorageTypeControlled = storageTypeFilter != null && onStorageTypeFilterChange != null;
|
|
93
|
+
const activeStorageType = isStorageTypeControlled ? storageTypeFilter : internalStorageType;
|
|
94
|
+
const setActiveStorageType = isStorageTypeControlled ? onStorageTypeFilterChange : setInternalStorageType;
|
|
76
95
|
const [selectedMMKVInstance, setSelectedMMKVInstance] = useState(null);
|
|
77
96
|
|
|
97
|
+
/**
|
|
98
|
+
* The key the full-screen editor is open on, if any.
|
|
99
|
+
*
|
|
100
|
+
* Held here rather than in the row because the editor covers the whole
|
|
101
|
+
* browser, and because the write it eventually performs is `handleSaveValue`
|
|
102
|
+
* — which needs the MMKV instance registry and the refresh fan-out that live
|
|
103
|
+
* at this level.
|
|
104
|
+
*/
|
|
105
|
+
const [editingKey, setEditingKey] = useState(null);
|
|
106
|
+
|
|
78
107
|
// Selection mode state
|
|
79
108
|
const [isSelectMode, setIsSelectMode] = useState(false);
|
|
80
109
|
const [selectedKeyIds, setSelectedKeyIds] = useState(new Set());
|
|
@@ -188,6 +217,40 @@ export function GameUIStorageBrowser({
|
|
|
188
217
|
setIsSelectMode(false);
|
|
189
218
|
}, [onAddPattern]);
|
|
190
219
|
|
|
220
|
+
/**
|
|
221
|
+
* Write an edited value back to the device.
|
|
222
|
+
*
|
|
223
|
+
* Pro is checked here rather than on the Edit button because the technical
|
|
224
|
+
* blocks (`storageEditBlockedReason`) and the licence are different kinds of
|
|
225
|
+
* "no": the former means this key can never be edited and the row says so
|
|
226
|
+
* permanently, the latter is an upsell. Throwing lets the editor keep the
|
|
227
|
+
* draft on screen instead of discarding what was typed.
|
|
228
|
+
*/
|
|
229
|
+
const handleSaveValue = useCallback(async (storageKey, raw) => {
|
|
230
|
+
if (!isPro) {
|
|
231
|
+
setShowUpgradeModal(true);
|
|
232
|
+
throw new Error("Editing storage values is a Pro feature.");
|
|
233
|
+
}
|
|
234
|
+
await setStorageValue(storageKey, raw, mmkvInstances);
|
|
235
|
+
refresh();
|
|
236
|
+
}, [isPro, mmkvInstances, refresh]);
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Flip a boolean key straight from its card.
|
|
240
|
+
*
|
|
241
|
+
* Goes through `handleSaveValue` like every other write, so the Pro gate and
|
|
242
|
+
* the type preservation are the same ones the editor uses — this is only a
|
|
243
|
+
* shortcut past the UI, not past the rules.
|
|
244
|
+
*/
|
|
245
|
+
const handleToggleKey = useCallback(storageKey => {
|
|
246
|
+
void handleSaveValue(storageKey, JSON.stringify(!storageKey.value)).catch(e => {
|
|
247
|
+
Alert.alert("Couldn't save", e instanceof Error ? e.message : String(e));
|
|
248
|
+
});
|
|
249
|
+
}, [handleSaveValue]);
|
|
250
|
+
|
|
251
|
+
/** Permanent, per-key reasons a value can't be edited (never the licence). */
|
|
252
|
+
const editBlockedFor = useCallback(storageKey => storageEditBlockedReason(storageKey, mmkvInstances), [mmkvInstances]);
|
|
253
|
+
|
|
191
254
|
// Memoized export data for copy functionality
|
|
192
255
|
const copyExportData = useMemo(() => {
|
|
193
256
|
const allKeys = allStorageKeys;
|
|
@@ -253,12 +316,34 @@ export function GameUIStorageBrowser({
|
|
|
253
316
|
};
|
|
254
317
|
}, [refreshAsync]);
|
|
255
318
|
|
|
319
|
+
// Auto-refresh when something outside this component knows the underlying
|
|
320
|
+
// storage changed. On device the listeners above cover it; in remote-mirror
|
|
321
|
+
// mode (desktop dashboard) local capture is suppressed and they never fire,
|
|
322
|
+
// so the dashboard signals each incoming device snapshot this way — without
|
|
323
|
+
// it the browse tab is frozen at whatever it read on mount.
|
|
324
|
+
useEffect(() => {
|
|
325
|
+
let timeoutId;
|
|
326
|
+
const unsubscribe = onStorageBrowserRefresh(() => {
|
|
327
|
+
clearTimeout(timeoutId);
|
|
328
|
+
timeoutId = setTimeout(refresh, 100);
|
|
329
|
+
});
|
|
330
|
+
return () => {
|
|
331
|
+
clearTimeout(timeoutId);
|
|
332
|
+
unsubscribe();
|
|
333
|
+
};
|
|
334
|
+
}, [refresh]);
|
|
335
|
+
|
|
256
336
|
// Auto-refresh on MMKV storage events
|
|
257
337
|
useEffect(() => {
|
|
258
338
|
let timeoutId;
|
|
259
339
|
const unsubscribe = addMMKVListener(event => {
|
|
260
|
-
//
|
|
261
|
-
|
|
340
|
+
// Refresh when the changed instance is one we're actually showing.
|
|
341
|
+
// `selectedMMKVInstance` is null by default ("All instances"), so
|
|
342
|
+
// comparing it to event.instanceId directly was never true for the view
|
|
343
|
+
// every user starts on — MMKV values sat stale in the browse list until
|
|
344
|
+
// it remounted, even though the write was recorded in Events.
|
|
345
|
+
const affectsView = selectedMMKVInstance === null || event.instanceId === selectedMMKVInstance;
|
|
346
|
+
if (affectsView) {
|
|
262
347
|
clearTimeout(timeoutId);
|
|
263
348
|
timeoutId = setTimeout(() => {
|
|
264
349
|
refreshMMKV(); // Auto-refresh MMKV when it changes
|
|
@@ -343,6 +428,15 @@ export function GameUIStorageBrowser({
|
|
|
343
428
|
};
|
|
344
429
|
}, [allStorageKeys, activeFilter, activeStorageType]);
|
|
345
430
|
|
|
431
|
+
// Push the status counts to a host toolbar that renders them (desktop).
|
|
432
|
+
useEffect(() => {
|
|
433
|
+
onStatusCountsChange?.({
|
|
434
|
+
validCount: tabStats.validCount,
|
|
435
|
+
missingCount: tabStats.missingCount,
|
|
436
|
+
issuesCount: tabStats.issuesCount
|
|
437
|
+
});
|
|
438
|
+
}, [onStatusCountsChange, tabStats.validCount, tabStats.missingCount, tabStats.issuesCount]);
|
|
439
|
+
|
|
346
440
|
// Sort all keys by priority (issues first)
|
|
347
441
|
const sortedKeys = useMemo(() => {
|
|
348
442
|
// Sort by status priority: errors first, then warnings, then valid
|
|
@@ -464,6 +558,11 @@ export function GameUIStorageBrowser({
|
|
|
464
558
|
setSelectedKeyIds(new Set());
|
|
465
559
|
}, []);
|
|
466
560
|
|
|
561
|
+
/**
|
|
562
|
+
* Serialized size of everything in storage, for the table's footer summary.
|
|
563
|
+
* Keyed on `allStorageKeys` rather than the filtered set: it answers "how much
|
|
564
|
+
* is in here", which a filter should not change.
|
|
565
|
+
*/
|
|
467
566
|
// Calculate health percentage
|
|
468
567
|
const healthPercentage = stats.requiredCount > 0 ? Math.round(stats.presentRequiredCount / stats.requiredCount * 100) : stats.totalCount > 0 ? 100 : 0;
|
|
469
568
|
const healthStatus = healthPercentage >= 90 ? "OPTIMAL" : healthPercentage >= 70 ? "WARNING" : "CRITICAL";
|
|
@@ -501,23 +600,15 @@ export function GameUIStorageBrowser({
|
|
|
501
600
|
})]
|
|
502
601
|
});
|
|
503
602
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
healthPercentage: healthPercentage,
|
|
514
|
-
healthStatus: healthStatus,
|
|
515
|
-
healthColor: healthColor,
|
|
516
|
-
activeFilter: activeFilter,
|
|
517
|
-
onFilterChange: setActiveFilter,
|
|
518
|
-
activeStorageType: activeStorageType,
|
|
519
|
-
onStorageTypeChange: setActiveStorageType
|
|
520
|
-
}), activeStorageType === "mmkv" && mmkvInstances.length === 0 && /*#__PURE__*/_jsxs(View, {
|
|
603
|
+
|
|
604
|
+
/* ---------------------------------------------------------------- *
|
|
605
|
+
* Subtrees the card list composes. Extracted rather than inlined so Pro
|
|
606
|
+
* gating and MMKV
|
|
607
|
+
* instance selection or the empty-state copy.
|
|
608
|
+
* ---------------------------------------------------------------- */
|
|
609
|
+
|
|
610
|
+
const mmkvSection = /*#__PURE__*/_jsxs(_Fragment, {
|
|
611
|
+
children: [activeStorageType === "mmkv" && mmkvInstances.length === 0 && /*#__PURE__*/_jsxs(View, {
|
|
521
612
|
style: styles.emptyMMKVState,
|
|
522
613
|
children: [/*#__PURE__*/_jsx(Text, {
|
|
523
614
|
style: styles.emptyMMKVTitle,
|
|
@@ -601,102 +692,159 @@ export function GameUIStorageBrowser({
|
|
|
601
692
|
}, inst.id);
|
|
602
693
|
})]
|
|
603
694
|
})]
|
|
604
|
-
})
|
|
605
|
-
|
|
695
|
+
})]
|
|
696
|
+
});
|
|
697
|
+
const actionButtonsNode = /*#__PURE__*/_jsx(StorageActionButtons, {
|
|
698
|
+
copyValue: copyExportData,
|
|
699
|
+
mmkvInstances: mmkvInstances.map(inst => ({
|
|
700
|
+
id: inst.id,
|
|
701
|
+
instance: inst.instance
|
|
702
|
+
})),
|
|
703
|
+
activeStorageType: activeStorageType,
|
|
704
|
+
onClearComplete: refresh,
|
|
705
|
+
isSelectMode: isSelectMode,
|
|
706
|
+
onToggleSelectMode: handleToggleSelectMode,
|
|
707
|
+
selectedCount: selectedKeyIds.size
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
/* Selection Action Bar - Only show in select mode with selections */
|
|
711
|
+
const selectionBarNode = isSelectMode ? /*#__PURE__*/_jsx(SelectionActionBar, {
|
|
712
|
+
selectedKeys: selectedKeysInfo,
|
|
713
|
+
mmkvInstances: mmkvInstances.map(inst => ({
|
|
714
|
+
id: inst.id,
|
|
715
|
+
instance: inst.instance
|
|
716
|
+
})),
|
|
717
|
+
onDeleteComplete: handleDeleteComplete,
|
|
718
|
+
onHideKeys: handleHideKeys,
|
|
719
|
+
onSelectAll: handleSelectAll,
|
|
720
|
+
onClearSelection: handleClearSelection,
|
|
721
|
+
totalVisibleKeys: visibleKeys.length
|
|
722
|
+
}) : null;
|
|
723
|
+
const lockedBannerNode = hasLockedKeys ? /*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
724
|
+
style: styles.limitBanner,
|
|
725
|
+
onPress: () => setShowUpgradeModal(true),
|
|
726
|
+
activeOpacity: 0.8,
|
|
727
|
+
children: [/*#__PURE__*/_jsxs(View, {
|
|
728
|
+
style: styles.limitBannerContent,
|
|
606
729
|
children: [/*#__PURE__*/_jsxs(View, {
|
|
607
|
-
style: styles.
|
|
608
|
-
children: [/*#__PURE__*/
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
style: styles.sectionTitle,
|
|
612
|
-
children: [activeFilter === "all" ? "KEYS" : activeFilter === "missing" ? "MISSING KEYS" : "ISSUES TO FIX", activeStorageType !== "all" && ` (${activeStorageType.toUpperCase()})`]
|
|
613
|
-
}), /*#__PURE__*/_jsx(View, {
|
|
614
|
-
style: styles.countBadge,
|
|
615
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
616
|
-
style: styles.countText,
|
|
617
|
-
children: visibleKeys.length
|
|
618
|
-
})
|
|
619
|
-
})]
|
|
620
|
-
}), /*#__PURE__*/_jsx(StorageActionButtons, {
|
|
621
|
-
copyValue: copyExportData,
|
|
622
|
-
mmkvInstances: mmkvInstances.map(inst => ({
|
|
623
|
-
id: inst.id,
|
|
624
|
-
instance: inst.instance
|
|
625
|
-
})),
|
|
626
|
-
activeStorageType: activeStorageType,
|
|
627
|
-
onClearComplete: refresh,
|
|
628
|
-
isSelectMode: isSelectMode,
|
|
629
|
-
onToggleSelectMode: handleToggleSelectMode,
|
|
630
|
-
selectedCount: selectedKeyIds.size
|
|
631
|
-
})]
|
|
632
|
-
}), isSelectMode && /*#__PURE__*/_jsx(SelectionActionBar, {
|
|
633
|
-
selectedKeys: selectedKeysInfo,
|
|
634
|
-
mmkvInstances: mmkvInstances.map(inst => ({
|
|
635
|
-
id: inst.id,
|
|
636
|
-
instance: inst.instance
|
|
637
|
-
})),
|
|
638
|
-
onDeleteComplete: handleDeleteComplete,
|
|
639
|
-
onHideKeys: handleHideKeys,
|
|
640
|
-
onSelectAll: handleSelectAll,
|
|
641
|
-
onClearSelection: handleClearSelection,
|
|
642
|
-
totalVisibleKeys: visibleKeys.length
|
|
643
|
-
}), /*#__PURE__*/_jsx(StorageKeySection, {
|
|
644
|
-
title: "",
|
|
645
|
-
count: -1,
|
|
646
|
-
keys: visibleKeys,
|
|
647
|
-
emptyMessage: "",
|
|
648
|
-
isSelectMode: isSelectMode,
|
|
649
|
-
selectedKeys: selectedKeyIds,
|
|
650
|
-
onSelectionChange: handleSelectionChange,
|
|
651
|
-
eventCountByKey: eventCountByKey,
|
|
652
|
-
onViewHistory: onViewHistory,
|
|
653
|
-
onHideKey: key => handleHideKeys([key]),
|
|
654
|
-
pinnedKeys: pinnedKeys,
|
|
655
|
-
onTogglePin: onTogglePin
|
|
656
|
-
}), hasLockedKeys && /*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
657
|
-
style: styles.limitBanner,
|
|
658
|
-
onPress: () => setShowUpgradeModal(true),
|
|
659
|
-
activeOpacity: 0.8,
|
|
660
|
-
children: [/*#__PURE__*/_jsxs(View, {
|
|
661
|
-
style: styles.limitBannerContent,
|
|
662
|
-
children: [/*#__PURE__*/_jsxs(View, {
|
|
663
|
-
style: styles.limitBannerLeft,
|
|
664
|
-
children: [/*#__PURE__*/_jsx(Zap, {
|
|
665
|
-
size: 16,
|
|
666
|
-
color: "#20C997"
|
|
667
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
668
|
-
style: styles.limitBannerText,
|
|
669
|
-
children: searchQuery ? `${lockedKeyCount} matching ${lockedKeyCount === 1 ? 'key' : 'keys'} in locked storage` : `${lockedKeyCount} keys locked`
|
|
670
|
-
})]
|
|
671
|
-
}), /*#__PURE__*/_jsx(ProBadge, {})]
|
|
730
|
+
style: styles.limitBannerLeft,
|
|
731
|
+
children: [/*#__PURE__*/_jsx(Zap, {
|
|
732
|
+
size: 16,
|
|
733
|
+
color: buoyColors.primary
|
|
672
734
|
}), /*#__PURE__*/_jsx(Text, {
|
|
673
|
-
style: styles.
|
|
674
|
-
children: searchQuery ?
|
|
735
|
+
style: styles.limitBannerText,
|
|
736
|
+
children: searchQuery ? `${lockedKeyCount} matching ${lockedKeyCount === 1 ? 'key' : 'keys'} in locked storage` : `${lockedKeyCount} keys locked`
|
|
675
737
|
})]
|
|
676
|
-
})]
|
|
677
|
-
}) : /*#__PURE__*/_jsxs(View, {
|
|
678
|
-
style: styles.emptyState,
|
|
679
|
-
children: [/*#__PURE__*/_jsx(Database, {
|
|
680
|
-
size: 32,
|
|
681
|
-
color: macOSColors.text.muted
|
|
682
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
683
|
-
style: styles.emptyTitle,
|
|
684
|
-
children: searchQuery ? "No results found" : activeFilter === "all" ? "No valid keys found" : activeFilter === "missing" ? "No missing keys" : "No issues found"
|
|
685
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
686
|
-
style: styles.emptySubtitle,
|
|
687
|
-
children: searchQuery ? `No keys matching "${searchQuery}"` : activeFilter === "all" ? "All keys have issues or are missing" : activeFilter === "missing" ? "All required keys are present" : "All storage keys are correctly configured"
|
|
688
|
-
})]
|
|
738
|
+
}), /*#__PURE__*/_jsx(ProBadge, {})]
|
|
689
739
|
}), /*#__PURE__*/_jsx(Text, {
|
|
690
|
-
style: styles.
|
|
691
|
-
children: "
|
|
692
|
-
})
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
740
|
+
style: styles.limitBannerSubtext,
|
|
741
|
+
children: searchQuery ? "Upgrade to Pro to search your full storage" : "Upgrade to Pro to unlock full storage access"
|
|
742
|
+
})]
|
|
743
|
+
}) : null;
|
|
744
|
+
const emptyStateNode = /*#__PURE__*/_jsxs(View, {
|
|
745
|
+
style: styles.emptyState,
|
|
746
|
+
children: [/*#__PURE__*/_jsx(Database, {
|
|
747
|
+
size: 32,
|
|
748
|
+
color: macOSColors.text.muted
|
|
749
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
750
|
+
style: styles.emptyTitle,
|
|
751
|
+
children: searchQuery ? "No results found" : activeFilter === "all" ? "No valid keys found" : activeFilter === "missing" ? "No missing keys" : "No issues found"
|
|
752
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
753
|
+
style: styles.emptySubtitle,
|
|
754
|
+
children: searchQuery ? `No keys matching "${searchQuery}"` : activeFilter === "all" ? "All keys have issues or are missing" : activeFilter === "missing" ? "All required keys are present" : "All storage keys are correctly configured"
|
|
696
755
|
})]
|
|
697
756
|
});
|
|
757
|
+
const upgradeModalNode = /*#__PURE__*/_jsx(ProUpgradeModal, {
|
|
758
|
+
visible: showUpgradeModal,
|
|
759
|
+
onClose: () => setShowUpgradeModal(false),
|
|
760
|
+
featureName: "Storage Keys"
|
|
761
|
+
});
|
|
762
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
763
|
+
style: styles.root,
|
|
764
|
+
children: [/*#__PURE__*/_jsxs(ScrollView, {
|
|
765
|
+
style: styles.scrollContainer,
|
|
766
|
+
contentContainerStyle: styles.container,
|
|
767
|
+
showsVerticalScrollIndicator: false,
|
|
768
|
+
children: [/*#__PURE__*/_jsx(View, {
|
|
769
|
+
style: styles.backgroundGrid
|
|
770
|
+
}), /*#__PURE__*/_jsx(StorageFilterCards, {
|
|
771
|
+
stats: stats,
|
|
772
|
+
tabStats: tabStats,
|
|
773
|
+
healthPercentage: healthPercentage,
|
|
774
|
+
healthStatus: healthStatus,
|
|
775
|
+
healthColor: healthColor,
|
|
776
|
+
activeFilter: activeFilter,
|
|
777
|
+
onFilterChange: setActiveFilter,
|
|
778
|
+
activeStorageType: activeStorageType,
|
|
779
|
+
onStorageTypeChange: setActiveStorageType
|
|
780
|
+
// Same rule the wide bar follows: when a host toolbar owns the status
|
|
781
|
+
// filter it also renders the counters, so ours would be a second copy.
|
|
782
|
+
,
|
|
783
|
+
showStatus: !isFilterControlled
|
|
784
|
+
}), mmkvSection, filteredKeys.length > 0 ? /*#__PURE__*/_jsxs(View, {
|
|
785
|
+
style: styles.keysSection,
|
|
786
|
+
children: [/*#__PURE__*/_jsxs(View, {
|
|
787
|
+
style: styles.sectionHeader,
|
|
788
|
+
children: [/*#__PURE__*/_jsxs(View, {
|
|
789
|
+
style: styles.sectionHeaderLeft,
|
|
790
|
+
children: [/*#__PURE__*/_jsxs(Text, {
|
|
791
|
+
style: styles.sectionTitle,
|
|
792
|
+
children: [activeFilter === "all" ? "KEYS" : activeFilter === "missing" ? "MISSING KEYS" : "ISSUES TO FIX", activeStorageType !== "all" && ` (${activeStorageType.toUpperCase()})`]
|
|
793
|
+
}), /*#__PURE__*/_jsx(View, {
|
|
794
|
+
style: styles.countBadge,
|
|
795
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
796
|
+
style: styles.countText,
|
|
797
|
+
children: visibleKeys.length
|
|
798
|
+
})
|
|
799
|
+
})]
|
|
800
|
+
}), actionButtonsNode]
|
|
801
|
+
}), selectionBarNode, /*#__PURE__*/_jsx(StorageKeySection, {
|
|
802
|
+
title: "",
|
|
803
|
+
count: -1,
|
|
804
|
+
keys: visibleKeys,
|
|
805
|
+
emptyMessage: "",
|
|
806
|
+
isSelectMode: isSelectMode,
|
|
807
|
+
selectedKeys: selectedKeyIds,
|
|
808
|
+
onSelectionChange: handleSelectionChange,
|
|
809
|
+
eventCountByKey: eventCountByKey,
|
|
810
|
+
onViewHistory: onViewHistory,
|
|
811
|
+
onHideKey: key => handleHideKeys([key]),
|
|
812
|
+
onEditKey: setEditingKey,
|
|
813
|
+
onToggleKey: handleToggleKey,
|
|
814
|
+
editBlockedFor: editBlockedFor,
|
|
815
|
+
pinnedKeys: pinnedKeys,
|
|
816
|
+
onTogglePin: onTogglePin
|
|
817
|
+
}), lockedBannerNode]
|
|
818
|
+
}) : emptyStateNode, /*#__PURE__*/_jsx(Text, {
|
|
819
|
+
style: styles.techFooter,
|
|
820
|
+
children: "ASYNC STORAGE | MMKV | SECURE STORAGE BACKENDS"
|
|
821
|
+
}), upgradeModalNode]
|
|
822
|
+
}), editingKey ? /*#__PURE__*/_jsx(View, {
|
|
823
|
+
style: styles.editorOverlay,
|
|
824
|
+
children: /*#__PURE__*/_jsx(StorageKeyEditorScreen, {
|
|
825
|
+
storageKey: editingKey,
|
|
826
|
+
onSave: raw => handleSaveValue(editingKey, raw),
|
|
827
|
+
onClose: () => setEditingKey(null),
|
|
828
|
+
applySafeAreaInset: applySafeAreaInset
|
|
829
|
+
})
|
|
830
|
+
}) : null]
|
|
831
|
+
});
|
|
698
832
|
}
|
|
699
833
|
const styles = StyleSheet.create({
|
|
834
|
+
/** Measures the space the columns actually get; `flex: 1` so it fills the
|
|
835
|
+
* parent exactly as the bare ScrollView it wraps used to. */
|
|
836
|
+
root: {
|
|
837
|
+
flex: 1,
|
|
838
|
+
minHeight: 0
|
|
839
|
+
},
|
|
840
|
+
/**
|
|
841
|
+
* No background of its own: the tree editor paints an opaque screen, while a
|
|
842
|
+
* scalar key shows only a modal and wants the list visible behind its scrim.
|
|
843
|
+
*/
|
|
844
|
+
editorOverlay: {
|
|
845
|
+
...absoluteFill,
|
|
846
|
+
zIndex: 10
|
|
847
|
+
},
|
|
700
848
|
scrollContainer: {
|
|
701
849
|
flex: 1,
|
|
702
850
|
backgroundColor: gameUIColors.background
|
|
@@ -842,7 +990,7 @@ const styles = StyleSheet.create({
|
|
|
842
990
|
fontSize: 9,
|
|
843
991
|
fontWeight: '700',
|
|
844
992
|
color: macOSColors.text.muted,
|
|
845
|
-
textTransform:
|
|
993
|
+
textTransform: "uppercase",
|
|
846
994
|
letterSpacing: 1
|
|
847
995
|
},
|
|
848
996
|
instanceNavbarScroll: {
|
|
@@ -912,10 +1060,10 @@ const styles = StyleSheet.create({
|
|
|
912
1060
|
},
|
|
913
1061
|
// Free tier limit banner
|
|
914
1062
|
limitBanner: {
|
|
915
|
-
backgroundColor:
|
|
1063
|
+
backgroundColor: buoyColors.card,
|
|
916
1064
|
borderRadius: 10,
|
|
917
1065
|
borderWidth: 1,
|
|
918
|
-
borderColor:
|
|
1066
|
+
borderColor: buoyColors.primary + "40",
|
|
919
1067
|
padding: 12,
|
|
920
1068
|
marginTop: 12
|
|
921
1069
|
},
|
|
@@ -931,12 +1079,12 @@ const styles = StyleSheet.create({
|
|
|
931
1079
|
gap: 8
|
|
932
1080
|
},
|
|
933
1081
|
limitBannerText: {
|
|
934
|
-
color:
|
|
1082
|
+
color: buoyColors.text,
|
|
935
1083
|
fontSize: 13,
|
|
936
1084
|
fontWeight: "600"
|
|
937
1085
|
},
|
|
938
1086
|
limitBannerSubtext: {
|
|
939
|
-
color:
|
|
1087
|
+
color: buoyColors.textMuted,
|
|
940
1088
|
fontSize: 11
|
|
941
1089
|
}
|
|
942
1090
|
});
|
|
@@ -428,7 +428,7 @@ const styles = StyleSheet.create({
|
|
|
428
428
|
marginBottom: 20,
|
|
429
429
|
paddingBottom: 12,
|
|
430
430
|
borderBottomWidth: 1,
|
|
431
|
-
borderBottomColor: "rgba(
|
|
431
|
+
borderBottomColor: "rgba(128, 128, 128, 0.2)"
|
|
432
432
|
},
|
|
433
433
|
headerLeft: {
|
|
434
434
|
gap: 2
|
|
@@ -499,7 +499,7 @@ const styles = StyleSheet.create({
|
|
|
499
499
|
},
|
|
500
500
|
healthBarBg: {
|
|
501
501
|
height: 6,
|
|
502
|
-
backgroundColor: "rgba(
|
|
502
|
+
backgroundColor: "rgba(128, 128, 128, 0.1)",
|
|
503
503
|
borderRadius: 3,
|
|
504
504
|
overflow: "hidden"
|
|
505
505
|
},
|
|
@@ -517,7 +517,7 @@ const styles = StyleSheet.create({
|
|
|
517
517
|
...absoluteFill,
|
|
518
518
|
opacity: 0.1,
|
|
519
519
|
borderWidth: 1,
|
|
520
|
-
borderColor: "rgba(
|
|
520
|
+
borderColor: "rgba(128, 128, 128, 0.2)",
|
|
521
521
|
borderRadius: 3
|
|
522
522
|
},
|
|
523
523
|
// Stats grid
|
|
@@ -634,11 +634,11 @@ const styles = StyleSheet.create({
|
|
|
634
634
|
backendItem: {
|
|
635
635
|
flexDirection: "row",
|
|
636
636
|
alignItems: "center",
|
|
637
|
-
backgroundColor: "rgba(
|
|
637
|
+
backgroundColor: "rgba(128, 128, 128, 0.04)",
|
|
638
638
|
borderRadius: 8,
|
|
639
639
|
padding: 10,
|
|
640
640
|
borderWidth: 1,
|
|
641
|
-
borderColor: "rgba(
|
|
641
|
+
borderColor: "rgba(128, 128, 128, 0.1)"
|
|
642
642
|
},
|
|
643
643
|
backendIcon: {
|
|
644
644
|
width: 24,
|
|
@@ -681,7 +681,7 @@ const styles = StyleSheet.create({
|
|
|
681
681
|
alignItems: "center",
|
|
682
682
|
paddingTop: 12,
|
|
683
683
|
borderTopWidth: 1,
|
|
684
|
-
borderTopColor: "rgba(
|
|
684
|
+
borderTopColor: "rgba(128, 128, 128, 0.2)"
|
|
685
685
|
},
|
|
686
686
|
bottomStats: {
|
|
687
687
|
flex: 1,
|
|
@@ -703,7 +703,7 @@ const styles = StyleSheet.create({
|
|
|
703
703
|
bottomDivider: {
|
|
704
704
|
width: 1,
|
|
705
705
|
height: 20,
|
|
706
|
-
backgroundColor: "rgba(
|
|
706
|
+
backgroundColor: "rgba(128, 128, 128, 0.2)"
|
|
707
707
|
},
|
|
708
708
|
// Empty state
|
|
709
709
|
emptyState: {
|
|
@@ -5,8 +5,7 @@ import { View, Text, StyleSheet, TouchableOpacity, Alert } from "react-native";
|
|
|
5
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 {
|
|
9
|
-
import { secureStoreRegistry } from "../utils/SecureStoreRegistry";
|
|
8
|
+
import { deleteStorageKeys } from "../utils/deleteStorageKeys";
|
|
10
9
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
10
|
export function SelectionActionBar({
|
|
12
11
|
selectedKeys,
|
|
@@ -66,40 +65,7 @@ export function SelectionActionBar({
|
|
|
66
65
|
style: "destructive",
|
|
67
66
|
onPress: async () => {
|
|
68
67
|
try {
|
|
69
|
-
|
|
70
|
-
const asyncKeys = selectedKeys.filter(k => k.storageType === "async");
|
|
71
|
-
const mmkvKeys = selectedKeys.filter(k => k.storageType === "mmkv");
|
|
72
|
-
const secureKeys = selectedKeys.filter(k => k.storageType === "secure");
|
|
73
|
-
|
|
74
|
-
// Delete AsyncStorage keys
|
|
75
|
-
if (asyncKeys.length > 0) {
|
|
76
|
-
await removeMany(asyncKeys.map(k => k.key));
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Delete SecureStore keys (via the registry, which knows each
|
|
80
|
-
// key's keychainService options)
|
|
81
|
-
for (const k of secureKeys) {
|
|
82
|
-
await secureStoreRegistry.deleteValue(k.key);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Delete MMKV keys
|
|
86
|
-
if (mmkvKeys.length > 0) {
|
|
87
|
-
// Group by instance
|
|
88
|
-
const byInstance = {};
|
|
89
|
-
mmkvKeys.forEach(k => {
|
|
90
|
-
const instanceId = k.instanceId || "default";
|
|
91
|
-
if (!byInstance[instanceId]) byInstance[instanceId] = [];
|
|
92
|
-
byInstance[instanceId].push(k.key);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// Delete from each instance
|
|
96
|
-
Object.entries(byInstance).forEach(([instanceId, keys]) => {
|
|
97
|
-
const inst = mmkvInstances.find(i => i.id === instanceId);
|
|
98
|
-
if (inst) {
|
|
99
|
-
keys.forEach(key => inst.instance.delete(key));
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
}
|
|
68
|
+
await deleteStorageKeys(selectedKeys, mmkvInstances);
|
|
103
69
|
onDeleteComplete?.();
|
|
104
70
|
} catch (error) {
|
|
105
71
|
Alert.alert("Error", `Failed to delete keys: ${error}`);
|