@buoy-gg/storage 7.0.30 → 7.0.31
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/storage/components/GameUIStorageBrowser.js +92 -21
- package/lib/commonjs/storage/components/StorageBrowserMode.js +8 -2
- package/lib/commonjs/storage/components/StorageKeyDetailScreen.js +572 -0
- package/lib/commonjs/storage/components/StorageKeyRow.js +21 -2
- package/lib/commonjs/storage/components/StorageKeySection.js +44 -21
- package/lib/commonjs/storage/components/StorageModalWithTabs.js +24 -2
- package/lib/commonjs/storage/components/StorageSaveNotice.js +171 -0
- package/lib/commonjs/storage/components/StorageValueEditModal.js +11 -364
- package/lib/commonjs/storage/hooks/useKeyEditor.js +12 -85
- package/lib/commonjs/storage/hooks/useTreeDraft.js +7 -100
- package/lib/module/storage/components/GameUIStorageBrowser.js +94 -23
- package/lib/module/storage/components/StorageBrowserMode.js +8 -2
- package/lib/module/storage/components/StorageKeyDetailScreen.js +570 -0
- package/lib/module/storage/components/StorageKeyRow.js +20 -3
- package/lib/module/storage/components/StorageKeySection.js +43 -21
- package/lib/module/storage/components/StorageModalWithTabs.js +24 -2
- package/lib/module/storage/components/StorageSaveNotice.js +168 -0
- package/lib/module/storage/components/StorageValueEditModal.js +13 -366
- package/lib/module/storage/hooks/useKeyEditor.js +14 -87
- package/lib/module/storage/hooks/useTreeDraft.js +9 -102
- package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts +13 -2
- package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageBrowserMode.d.ts +7 -2
- package/lib/typescript/storage/components/StorageBrowserMode.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageKeyDetailScreen.d.ts +46 -0
- package/lib/typescript/storage/components/StorageKeyDetailScreen.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageKeyRow.d.ts +9 -0
- package/lib/typescript/storage/components/StorageKeyRow.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageKeySection.d.ts +19 -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/components/StorageSaveNotice.d.ts +6 -0
- package/lib/typescript/storage/components/StorageSaveNotice.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageValueEditModal.d.ts +9 -31
- package/lib/typescript/storage/components/StorageValueEditModal.d.ts.map +1 -1
- package/lib/typescript/storage/hooks/useKeyEditor.d.ts +7 -31
- package/lib/typescript/storage/hooks/useKeyEditor.d.ts.map +1 -1
- package/lib/typescript/storage/hooks/useTreeDraft.d.ts +7 -66
- package/lib/typescript/storage/hooks/useTreeDraft.d.ts.map +1 -1
- package/lib/typescript/storage/sync/storageSyncAdapter.d.ts +1 -1
- package/package.json +5 -5
|
@@ -9,13 +9,14 @@ var _react = require("react");
|
|
|
9
9
|
var _reactNative = require("react-native");
|
|
10
10
|
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
11
11
|
var _StorageKeySection = require("./StorageKeySection");
|
|
12
|
-
var
|
|
12
|
+
var _StorageKeyDetailScreen = require("./StorageKeyDetailScreen");
|
|
13
13
|
var _StorageFilterCards = require("./StorageFilterCards");
|
|
14
14
|
var _useAsyncStorageKeys = require("../hooks/useAsyncStorageKeys");
|
|
15
15
|
var _AsyncStorageListener = require("../utils/AsyncStorageListener");
|
|
16
16
|
var _useMMKVKeys = require("../hooks/useMMKVKeys");
|
|
17
17
|
var _useSecureStoreKeys = require("../hooks/useSecureStoreKeys");
|
|
18
18
|
var _useMMKVInstances = require("../hooks/useMMKVInstances");
|
|
19
|
+
var _StorageSaveNotice = require("./StorageSaveNotice");
|
|
19
20
|
var _StorageActionButtons = require("./StorageActionButtons");
|
|
20
21
|
var _SelectionActionBar = require("./SelectionActionBar");
|
|
21
22
|
var _MMKVListener = require("../utils/MMKVListener");
|
|
@@ -77,7 +78,10 @@ function GameUIStorageBrowser({
|
|
|
77
78
|
onTogglePin,
|
|
78
79
|
storageTypeFilter,
|
|
79
80
|
onStorageTypeFilterChange,
|
|
80
|
-
applySafeAreaInset
|
|
81
|
+
applySafeAreaInset,
|
|
82
|
+
detailKey = null,
|
|
83
|
+
onOpenKey,
|
|
84
|
+
onCloseDetail
|
|
81
85
|
}) {
|
|
82
86
|
const isPro = (0, _license.useIsPro)();
|
|
83
87
|
const keyLimit = (0, _sharedUi.useLimit)("storage.keys");
|
|
@@ -102,16 +106,38 @@ function GameUIStorageBrowser({
|
|
|
102
106
|
const activeStorageType = isStorageTypeControlled ? storageTypeFilter : internalStorageType;
|
|
103
107
|
const setActiveStorageType = isStorageTypeControlled ? onStorageTypeFilterChange : setInternalStorageType;
|
|
104
108
|
const [selectedMMKVInstance, setSelectedMMKVInstance] = (0, _react.useState)(null);
|
|
109
|
+
const scrollRef = (0, _react.useRef)(null);
|
|
110
|
+
const scrollOffsetRef = (0, _react.useRef)(0);
|
|
111
|
+
const rowNodesRef = (0, _react.useRef)(new Map());
|
|
112
|
+
const handleRowRef = (0, _react.useCallback)((id, node) => {
|
|
113
|
+
if (node) rowNodesRef.current.set(id, node);else rowNodesRef.current.delete(id);
|
|
114
|
+
}, []);
|
|
105
115
|
|
|
106
116
|
/**
|
|
107
|
-
*
|
|
117
|
+
* Shown after every successful write, with the same sentence every time.
|
|
118
|
+
*
|
|
119
|
+
* An earlier version looked up which live store owned the key and offered to
|
|
120
|
+
* make that store re-read it. That required knowing what is linked to what,
|
|
121
|
+
* and every route to that knowledge is a guess — key names collide across
|
|
122
|
+
* backends, only zustand stores register at all, and a store can register
|
|
123
|
+
* after the lookup ran. A message that is right most of the time is the
|
|
124
|
+
* problem this tool has, not the fix for it.
|
|
125
|
+
*
|
|
126
|
+
* There is also no general mechanism to find: the app owns its cache, and
|
|
127
|
+
* only the app can invalidate it. So the tool promises the one thing that is
|
|
128
|
+
* true for every key, every backend and every persistence library — this is
|
|
129
|
+
* what loads next launch — and "make it live now" lives in the state tools,
|
|
130
|
+
* where the store you mean is the one you are looking at.
|
|
131
|
+
*/
|
|
132
|
+
/**
|
|
133
|
+
* Bumped on every save rather than flipped to `true`.
|
|
108
134
|
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
135
|
+
* The toast unmounts itself when it fades, and a boolean already `true` would
|
|
136
|
+
* not change on the next save — so a second edit would show nothing. The
|
|
137
|
+
* counter is the toast's `key`, so each save mounts a fresh one and the
|
|
138
|
+
* animation restarts from the beginning.
|
|
113
139
|
*/
|
|
114
|
-
const [
|
|
140
|
+
const [saveToastId, setSaveToastId] = (0, _react.useState)(0);
|
|
115
141
|
|
|
116
142
|
// Selection mode state
|
|
117
143
|
const [isSelectMode, setIsSelectMode] = (0, _react.useState)(false);
|
|
@@ -169,6 +195,22 @@ function GameUIStorageBrowser({
|
|
|
169
195
|
return [...asyncStorageKeys, ...mmkvStorageKeys, ...secureStorageKeys];
|
|
170
196
|
}, [asyncStorageKeys, mmkvStorageKeys, secureStorageKeys]);
|
|
171
197
|
|
|
198
|
+
/**
|
|
199
|
+
* The CURRENT snapshot of the key the detail screen is open on. `detailKey`
|
|
200
|
+
* is only an identity — resolving it fresh each pass is what makes the
|
|
201
|
+
* detail screen live: the app writes, the snapshot refreshes, the screen
|
|
202
|
+
* follows. Falls back to the captured info because the backend hooks
|
|
203
|
+
* transiently EMPTY their lists mid-refresh — treating "missing from this
|
|
204
|
+
* render's list" as "deleted" closed the screen the moment it opened, so
|
|
205
|
+
* absence here is never acted on (deletion closes explicitly, from the
|
|
206
|
+
* delete flow itself).
|
|
207
|
+
*/
|
|
208
|
+
const detailInfo = (0, _react.useMemo)(() => {
|
|
209
|
+
if (!detailKey) return null;
|
|
210
|
+
const id = (0, _StorageKeySection.storageRowId)(detailKey);
|
|
211
|
+
return allStorageKeys.find(k => (0, _StorageKeySection.storageRowId)(k) === id) ?? detailKey;
|
|
212
|
+
}, [detailKey, allStorageKeys]);
|
|
213
|
+
|
|
172
214
|
// Determine loading and error states
|
|
173
215
|
const isLoading = isLoadingAsync || isLoadingMMKV || isLoadingSecure;
|
|
174
216
|
const error = asyncError || mmkvError || secureError;
|
|
@@ -242,6 +284,7 @@ function GameUIStorageBrowser({
|
|
|
242
284
|
}
|
|
243
285
|
await (0, _setStorageValue.setStorageValue)(storageKey, raw, mmkvInstances);
|
|
244
286
|
refresh();
|
|
287
|
+
setSaveToastId(id => id + 1);
|
|
245
288
|
}, [isPro, mmkvInstances, refresh]);
|
|
246
289
|
|
|
247
290
|
/**
|
|
@@ -268,7 +311,7 @@ function GameUIStorageBrowser({
|
|
|
268
311
|
* Pro is checked here rather than on the button because it's an upsell, not
|
|
269
312
|
* a capability: the row shows the action and explains the wall on press.
|
|
270
313
|
*/
|
|
271
|
-
const handleDeleteKey = (0, _react.useCallback)(storageKey => {
|
|
314
|
+
const handleDeleteKey = (0, _react.useCallback)((storageKey, onDeleted) => {
|
|
272
315
|
if (!isPro) {
|
|
273
316
|
setShowUpgradeModal(true);
|
|
274
317
|
return;
|
|
@@ -286,6 +329,9 @@ function GameUIStorageBrowser({
|
|
|
286
329
|
instance: inst.instance
|
|
287
330
|
})));
|
|
288
331
|
refresh();
|
|
332
|
+
// Only after the delete actually landed — a cancelled alert
|
|
333
|
+
// or a failed delete leaves the caller's screen alone.
|
|
334
|
+
onDeleted?.();
|
|
289
335
|
} catch (error) {
|
|
290
336
|
_reactNative.Alert.alert("Error", `Failed to delete key: ${error}`);
|
|
291
337
|
}
|
|
@@ -609,6 +655,31 @@ function GameUIStorageBrowser({
|
|
|
609
655
|
const healthStatus = healthPercentage >= 90 ? "OPTIMAL" : healthPercentage >= 70 ? "WARNING" : "CRITICAL";
|
|
610
656
|
const healthColor = healthPercentage >= 90 ? _sharedUi.gameUIColors.success : healthPercentage >= 70 ? _sharedUi.gameUIColors.warning : _sharedUi.gameUIColors.error;
|
|
611
657
|
|
|
658
|
+
// The detail screen REPLACES the list as the modal's whole body — the host
|
|
659
|
+
// modal's header carries back + the key's name, the React Query
|
|
660
|
+
// interaction (and the Zustand tool's). All hooks above have run; this
|
|
661
|
+
// return only swaps what's painted.
|
|
662
|
+
if (detailInfo) {
|
|
663
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
664
|
+
style: styles.root,
|
|
665
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_StorageKeyDetailScreen.StorageKeyDetailScreen, {
|
|
666
|
+
storageKey: detailInfo,
|
|
667
|
+
editBlockedReason: editBlockedFor(detailInfo),
|
|
668
|
+
eventCount: eventCountByKey?.[detailInfo.key],
|
|
669
|
+
onSave: raw => handleSaveValue(detailInfo, raw),
|
|
670
|
+
onViewHistory: onViewHistory ? () => onViewHistory(detailInfo.key) : undefined,
|
|
671
|
+
onDeleteKey: k => handleDeleteKey(k, () => onCloseDetail?.()),
|
|
672
|
+
onHideKey: k => {
|
|
673
|
+
handleHideKeys([k]);
|
|
674
|
+
onCloseDetail?.();
|
|
675
|
+
},
|
|
676
|
+
isPinned: pinnedKeys?.has(detailInfo.key) ?? false,
|
|
677
|
+
onTogglePin: onTogglePin,
|
|
678
|
+
applySafeAreaInset: applySafeAreaInset
|
|
679
|
+
})
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
|
|
612
683
|
// Loading state
|
|
613
684
|
if (isLoading && allStorageKeys.length === 0) {
|
|
614
685
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
@@ -810,11 +881,18 @@ function GameUIStorageBrowser({
|
|
|
810
881
|
});
|
|
811
882
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
812
883
|
style: styles.root,
|
|
813
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.
|
|
884
|
+
children: [saveToastId > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_StorageSaveNotice.StorageSaveNotice, {
|
|
885
|
+
onDone: () => setSaveToastId(0)
|
|
886
|
+
}, saveToastId) : null, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView
|
|
814
887
|
// Named because `by.type("RCTScrollView")` doesn't match on Fabric
|
|
815
888
|
// (renamed class) — automation scrolls below-the-fold rows by this id.
|
|
816
889
|
, {
|
|
817
890
|
testID: "buoy-storage-scroll",
|
|
891
|
+
ref: scrollRef,
|
|
892
|
+
onScroll: e => {
|
|
893
|
+
scrollOffsetRef.current = e.nativeEvent.contentOffset.y;
|
|
894
|
+
},
|
|
895
|
+
scrollEventThrottle: 32,
|
|
818
896
|
style: styles.scrollContainer,
|
|
819
897
|
contentContainerStyle: styles.container,
|
|
820
898
|
showsVerticalScrollIndicator: false,
|
|
@@ -862,26 +940,19 @@ function GameUIStorageBrowser({
|
|
|
862
940
|
eventCountByKey: eventCountByKey,
|
|
863
941
|
onViewHistory: onViewHistory,
|
|
864
942
|
onHideKey: key => handleHideKeys([key]),
|
|
865
|
-
onEditKey: setEditingKey,
|
|
866
943
|
onToggleKey: handleToggleKey,
|
|
867
944
|
onDeleteKey: handleDeleteKey,
|
|
868
945
|
editBlockedFor: editBlockedFor,
|
|
869
946
|
pinnedKeys: pinnedKeys,
|
|
870
|
-
onTogglePin: onTogglePin
|
|
947
|
+
onTogglePin: onTogglePin,
|
|
948
|
+
onRowRef: handleRowRef,
|
|
949
|
+
onOpenKey: onOpenKey
|
|
871
950
|
}), lockedBannerNode]
|
|
872
951
|
}) : emptyStateNode, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
873
952
|
style: styles.techFooter,
|
|
874
953
|
children: "ASYNC STORAGE | MMKV | SECURE STORAGE BACKENDS"
|
|
875
954
|
}), upgradeModalNode]
|
|
876
|
-
})
|
|
877
|
-
style: styles.editorOverlay,
|
|
878
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_StorageKeyEditorScreen.StorageKeyEditorScreen, {
|
|
879
|
-
storageKey: editingKey,
|
|
880
|
-
onSave: raw => handleSaveValue(editingKey, raw),
|
|
881
|
-
onClose: () => setEditingKey(null),
|
|
882
|
-
applySafeAreaInset: applySafeAreaInset
|
|
883
|
-
})
|
|
884
|
-
}) : null]
|
|
955
|
+
})]
|
|
885
956
|
});
|
|
886
957
|
}
|
|
887
958
|
const styles = _reactNative.StyleSheet.create({
|
|
@@ -29,7 +29,10 @@ function StorageBrowserMode({
|
|
|
29
29
|
onTogglePin,
|
|
30
30
|
applySafeAreaInset,
|
|
31
31
|
storageTypeFilter,
|
|
32
|
-
onStorageTypeFilterChange
|
|
32
|
+
onStorageTypeFilterChange,
|
|
33
|
+
detailKey,
|
|
34
|
+
onOpenKey,
|
|
35
|
+
onCloseDetail
|
|
33
36
|
}) {
|
|
34
37
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_GameUIStorageBrowser.GameUIStorageBrowser, {
|
|
35
38
|
requiredStorageKeys: requiredStorageKeys,
|
|
@@ -50,6 +53,9 @@ function StorageBrowserMode({
|
|
|
50
53
|
onTogglePin: onTogglePin,
|
|
51
54
|
applySafeAreaInset: applySafeAreaInset,
|
|
52
55
|
storageTypeFilter: storageTypeFilter,
|
|
53
|
-
onStorageTypeFilterChange: onStorageTypeFilterChange
|
|
56
|
+
onStorageTypeFilterChange: onStorageTypeFilterChange,
|
|
57
|
+
detailKey: detailKey,
|
|
58
|
+
onOpenKey: onOpenKey,
|
|
59
|
+
onCloseDetail: onCloseDetail
|
|
54
60
|
});
|
|
55
61
|
}
|