@buoy-gg/storage 7.0.30 → 7.0.32

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.
Files changed (41) hide show
  1. package/lib/commonjs/storage/components/GameUIStorageBrowser.js +92 -21
  2. package/lib/commonjs/storage/components/StorageBrowserMode.js +8 -2
  3. package/lib/commonjs/storage/components/StorageKeyDetailScreen.js +572 -0
  4. package/lib/commonjs/storage/components/StorageKeyRow.js +21 -2
  5. package/lib/commonjs/storage/components/StorageKeySection.js +44 -21
  6. package/lib/commonjs/storage/components/StorageModalWithTabs.js +25 -2
  7. package/lib/commonjs/storage/components/StorageSaveNotice.js +171 -0
  8. package/lib/commonjs/storage/components/StorageValueEditModal.js +11 -364
  9. package/lib/commonjs/storage/hooks/useKeyEditor.js +12 -85
  10. package/lib/commonjs/storage/hooks/useTreeDraft.js +7 -100
  11. package/lib/module/storage/components/GameUIStorageBrowser.js +94 -23
  12. package/lib/module/storage/components/StorageBrowserMode.js +8 -2
  13. package/lib/module/storage/components/StorageKeyDetailScreen.js +570 -0
  14. package/lib/module/storage/components/StorageKeyRow.js +20 -3
  15. package/lib/module/storage/components/StorageKeySection.js +43 -21
  16. package/lib/module/storage/components/StorageModalWithTabs.js +26 -3
  17. package/lib/module/storage/components/StorageSaveNotice.js +168 -0
  18. package/lib/module/storage/components/StorageValueEditModal.js +13 -366
  19. package/lib/module/storage/hooks/useKeyEditor.js +14 -87
  20. package/lib/module/storage/hooks/useTreeDraft.js +9 -102
  21. package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts +13 -2
  22. package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts.map +1 -1
  23. package/lib/typescript/storage/components/StorageBrowserMode.d.ts +7 -2
  24. package/lib/typescript/storage/components/StorageBrowserMode.d.ts.map +1 -1
  25. package/lib/typescript/storage/components/StorageKeyDetailScreen.d.ts +46 -0
  26. package/lib/typescript/storage/components/StorageKeyDetailScreen.d.ts.map +1 -0
  27. package/lib/typescript/storage/components/StorageKeyRow.d.ts +9 -0
  28. package/lib/typescript/storage/components/StorageKeyRow.d.ts.map +1 -1
  29. package/lib/typescript/storage/components/StorageKeySection.d.ts +19 -1
  30. package/lib/typescript/storage/components/StorageKeySection.d.ts.map +1 -1
  31. package/lib/typescript/storage/components/StorageModalWithTabs.d.ts.map +1 -1
  32. package/lib/typescript/storage/components/StorageSaveNotice.d.ts +6 -0
  33. package/lib/typescript/storage/components/StorageSaveNotice.d.ts.map +1 -0
  34. package/lib/typescript/storage/components/StorageValueEditModal.d.ts +9 -31
  35. package/lib/typescript/storage/components/StorageValueEditModal.d.ts.map +1 -1
  36. package/lib/typescript/storage/hooks/useKeyEditor.d.ts +7 -31
  37. package/lib/typescript/storage/hooks/useKeyEditor.d.ts.map +1 -1
  38. package/lib/typescript/storage/hooks/useTreeDraft.d.ts +7 -66
  39. package/lib/typescript/storage/hooks/useTreeDraft.d.ts.map +1 -1
  40. package/lib/typescript/storage/sync/storageSyncAdapter.d.ts +1 -1
  41. 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 _StorageKeyEditorScreen = require("./StorageKeyEditorScreen");
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
- * The key the full-screen editor is open on, if any.
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
- * Held here rather than in the row because the editor covers the whole
110
- * browser, and because the write it eventually performs is `handleSaveValue`
111
- * — which needs the MMKV instance registry and the refresh fan-out that live
112
- * at this level.
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 [editingKey, setEditingKey] = (0, _react.useState)(null);
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.jsxs)(_reactNative.ScrollView
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
- }), editingKey ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
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
  }