@buoy-gg/storage 7.0.27 → 7.0.29
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 +51 -1
- package/lib/commonjs/storage/components/SelectionActionBar.js +10 -0
- package/lib/commonjs/storage/components/StorageActionButtons.js +16 -0
- package/lib/commonjs/storage/components/StorageEventActionButton.js +1 -0
- package/lib/commonjs/storage/components/StorageEventCard.js +5 -2
- package/lib/commonjs/storage/components/StorageEventFilterView.js +18 -0
- package/lib/commonjs/storage/components/StorageFilterCards.js +9 -0
- package/lib/commonjs/storage/components/StorageKeyEditorScreen.js +26 -5
- package/lib/commonjs/storage/components/StorageKeyRow.js +51 -2
- package/lib/commonjs/storage/components/StorageKeySection.js +23 -7
- package/lib/commonjs/storage/components/StorageModalWithTabs.js +26 -7
- package/lib/commonjs/storage/components/StorageScalarKeyEditor.js +16 -1
- package/lib/commonjs/storage/components/StorageTypeFilterBar.js +2 -0
- package/lib/commonjs/storage/components/StorageValueEditModal.js +18 -2
- package/lib/commonjs/storage/hooks/useTreeDraft.js +12 -1
- package/lib/commonjs/storage/utils/deleteStorageKeys.js +34 -5
- package/lib/module/storage/components/GameUIStorageBrowser.js +51 -1
- package/lib/module/storage/components/SelectionActionBar.js +10 -0
- package/lib/module/storage/components/StorageActionButtons.js +16 -0
- package/lib/module/storage/components/StorageEventActionButton.js +1 -0
- package/lib/module/storage/components/StorageEventCard.js +5 -2
- package/lib/module/storage/components/StorageEventFilterView.js +18 -0
- package/lib/module/storage/components/StorageFilterCards.js +9 -0
- package/lib/module/storage/components/StorageKeyEditorScreen.js +27 -6
- package/lib/module/storage/components/StorageKeyRow.js +52 -3
- package/lib/module/storage/components/StorageKeySection.js +23 -7
- package/lib/module/storage/components/StorageModalWithTabs.js +26 -7
- package/lib/module/storage/components/StorageScalarKeyEditor.js +16 -1
- package/lib/module/storage/components/StorageTypeFilterBar.js +2 -0
- package/lib/module/storage/components/StorageValueEditModal.js +18 -2
- package/lib/module/storage/hooks/useTreeDraft.js +12 -1
- package/lib/module/storage/utils/deleteStorageKeys.js +34 -5
- 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/StorageActionButtons.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageEventActionButton.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageEventCard.d.ts +5 -0
- package/lib/typescript/storage/components/StorageEventCard.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageEventFilterView.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageFilterCards.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageKeyEditorScreen.d.ts.map +1 -1
- 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 +3 -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/StorageScalarKeyEditor.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageTypeFilterBar.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageValueEditModal.d.ts.map +1 -1
- package/lib/typescript/storage/hooks/useTreeDraft.d.ts +9 -1
- package/lib/typescript/storage/hooks/useTreeDraft.d.ts.map +1 -1
- package/lib/typescript/storage/utils/deleteStorageKeys.d.ts +15 -5
- package/lib/typescript/storage/utils/deleteStorageKeys.d.ts.map +1 -1
- package/package.json +5 -5
|
@@ -20,6 +20,7 @@ var _StorageActionButtons = require("./StorageActionButtons");
|
|
|
20
20
|
var _SelectionActionBar = require("./SelectionActionBar");
|
|
21
21
|
var _MMKVListener = require("../utils/MMKVListener");
|
|
22
22
|
var _storageBrowserRefresh = require("../utils/storageBrowserRefresh");
|
|
23
|
+
var _deleteStorageKeys = require("../utils/deleteStorageKeys");
|
|
23
24
|
var _setStorageValue = require("../utils/setStorageValue");
|
|
24
25
|
var _license = require("@buoy-gg/license");
|
|
25
26
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
@@ -256,6 +257,42 @@ function GameUIStorageBrowser({
|
|
|
256
257
|
});
|
|
257
258
|
}, [handleSaveValue]);
|
|
258
259
|
|
|
260
|
+
/**
|
|
261
|
+
* Delete ONE key, from its own card.
|
|
262
|
+
*
|
|
263
|
+
* Routed through `deleteStorageKeys` exactly like the selection bar, so the
|
|
264
|
+
* per-backend fan-out keeps having a single implementation — the last time
|
|
265
|
+
* that fan-out was wrong (an MMKV method renamed upstream) it was wrong in
|
|
266
|
+
* silence, and a second copy is how one backend quietly stops being deleted.
|
|
267
|
+
*
|
|
268
|
+
* Pro is checked here rather than on the button because it's an upsell, not
|
|
269
|
+
* a capability: the row shows the action and explains the wall on press.
|
|
270
|
+
*/
|
|
271
|
+
const handleDeleteKey = (0, _react.useCallback)(storageKey => {
|
|
272
|
+
if (!isPro) {
|
|
273
|
+
setShowUpgradeModal(true);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
_reactNative.Alert.alert("Delete key", `Delete "${storageKey.key}"? This cannot be undone.`, [{
|
|
277
|
+
text: "Cancel",
|
|
278
|
+
style: "cancel"
|
|
279
|
+
}, {
|
|
280
|
+
text: "Delete",
|
|
281
|
+
style: "destructive",
|
|
282
|
+
onPress: async () => {
|
|
283
|
+
try {
|
|
284
|
+
await (0, _deleteStorageKeys.deleteStorageKeys)([storageKey], mmkvInstances.map(inst => ({
|
|
285
|
+
id: inst.id,
|
|
286
|
+
instance: inst.instance
|
|
287
|
+
})));
|
|
288
|
+
refresh();
|
|
289
|
+
} catch (error) {
|
|
290
|
+
_reactNative.Alert.alert("Error", `Failed to delete key: ${error}`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}]);
|
|
294
|
+
}, [isPro, mmkvInstances, refresh]);
|
|
295
|
+
|
|
259
296
|
/** Permanent, per-key reasons a value can't be edited (never the licence). */
|
|
260
297
|
const editBlockedFor = (0, _react.useCallback)(storageKey => (0, _setStorageValue.storageEditBlockedReason)(storageKey, mmkvInstances), [mmkvInstances]);
|
|
261
298
|
|
|
@@ -644,6 +681,9 @@ function GameUIStorageBrowser({
|
|
|
644
681
|
contentContainerStyle: styles.instanceNavbarScroll,
|
|
645
682
|
children: [(isPro || mmkvInstances.length === 1) && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
646
683
|
onPress: () => setSelectedMMKVInstance(null),
|
|
684
|
+
testID: "buoy-storage-instance-all",
|
|
685
|
+
accessibilityRole: "button",
|
|
686
|
+
accessibilityLabel: "All MMKV instances",
|
|
647
687
|
style: [styles.instanceNavbarButton, selectedMMKVInstance === null && styles.instanceNavbarButtonActive],
|
|
648
688
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.HardDrive, {
|
|
649
689
|
size: 12,
|
|
@@ -661,6 +701,9 @@ function GameUIStorageBrowser({
|
|
|
661
701
|
}), mmkvInstances.map((inst, index) => {
|
|
662
702
|
const isLocked = index >= mmkvInstanceLimit;
|
|
663
703
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
704
|
+
testID: `buoy-storage-instance-${inst.id}`,
|
|
705
|
+
accessibilityRole: "button",
|
|
706
|
+
accessibilityLabel: `MMKV instance ${inst.id}`,
|
|
664
707
|
onPress: () => {
|
|
665
708
|
if (isLocked) {
|
|
666
709
|
setShowUpgradeModal(true);
|
|
@@ -728,6 +771,8 @@ function GameUIStorageBrowser({
|
|
|
728
771
|
style: styles.limitBanner,
|
|
729
772
|
onPress: () => setShowUpgradeModal(true),
|
|
730
773
|
activeOpacity: 0.8,
|
|
774
|
+
testID: "buoy-storage-locked-banner",
|
|
775
|
+
accessibilityRole: "button",
|
|
731
776
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
732
777
|
style: styles.limitBannerContent,
|
|
733
778
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
@@ -765,7 +810,11 @@ function GameUIStorageBrowser({
|
|
|
765
810
|
});
|
|
766
811
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
767
812
|
style: styles.root,
|
|
768
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView
|
|
813
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView
|
|
814
|
+
// Named because `by.type("RCTScrollView")` doesn't match on Fabric
|
|
815
|
+
// (renamed class) — automation scrolls below-the-fold rows by this id.
|
|
816
|
+
, {
|
|
817
|
+
testID: "buoy-storage-scroll",
|
|
769
818
|
style: styles.scrollContainer,
|
|
770
819
|
contentContainerStyle: styles.container,
|
|
771
820
|
showsVerticalScrollIndicator: false,
|
|
@@ -815,6 +864,7 @@ function GameUIStorageBrowser({
|
|
|
815
864
|
onHideKey: key => handleHideKeys([key]),
|
|
816
865
|
onEditKey: setEditingKey,
|
|
817
866
|
onToggleKey: handleToggleKey,
|
|
867
|
+
onDeleteKey: handleDeleteKey,
|
|
818
868
|
editBlockedFor: editBlockedFor,
|
|
819
869
|
pinnedKeys: pinnedKeys,
|
|
820
870
|
onTogglePin: onTogglePin
|
|
@@ -86,6 +86,9 @@ function SelectionActionBar({
|
|
|
86
86
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
87
87
|
style: styles.selectAllButton,
|
|
88
88
|
onPress: allSelected ? onClearSelection : onSelectAll,
|
|
89
|
+
testID: "buoy-storage-select-all",
|
|
90
|
+
accessibilityLabel: allSelected ? "Deselect all keys" : "Select all keys",
|
|
91
|
+
accessibilityRole: "button",
|
|
89
92
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CheckSquare, {
|
|
90
93
|
size: 14,
|
|
91
94
|
color: allSelected ? _sharedUi.night.color.accent : _sharedUi.night.color.textSecondary
|
|
@@ -105,6 +108,7 @@ function SelectionActionBar({
|
|
|
105
108
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CopyButton, {
|
|
106
109
|
value: getCopyData(),
|
|
107
110
|
size: 14,
|
|
111
|
+
testID: "buoy-storage-selection-copy",
|
|
108
112
|
buttonStyle: styles.actionButton,
|
|
109
113
|
colors: {
|
|
110
114
|
idle: _sharedUi.night.color.accent,
|
|
@@ -114,6 +118,9 @@ function SelectionActionBar({
|
|
|
114
118
|
}), onHideKeys && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
115
119
|
style: styles.actionButton,
|
|
116
120
|
onPress: handleHideSelected,
|
|
121
|
+
testID: "buoy-storage-selection-hide",
|
|
122
|
+
accessibilityLabel: "Hide selected keys",
|
|
123
|
+
accessibilityRole: "button",
|
|
117
124
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.EyeOff, {
|
|
118
125
|
size: 14,
|
|
119
126
|
color: _sharedUi.night.color.warning
|
|
@@ -121,6 +128,9 @@ function SelectionActionBar({
|
|
|
121
128
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
122
129
|
style: styles.actionButton,
|
|
123
130
|
onPress: handleDeleteSelected,
|
|
131
|
+
testID: "buoy-storage-selection-delete",
|
|
132
|
+
accessibilityLabel: "Delete selected keys",
|
|
133
|
+
accessibilityRole: "button",
|
|
124
134
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Trash2, {
|
|
125
135
|
size: 14,
|
|
126
136
|
color: _sharedUi.night.color.danger
|
|
@@ -133,6 +133,9 @@ function StorageActionButtons({
|
|
|
133
133
|
style: styles.container,
|
|
134
134
|
children: [onToggleSelectMode && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
135
135
|
onPress: onToggleSelectMode,
|
|
136
|
+
testID: "buoy-storage-select-mode",
|
|
137
|
+
accessibilityLabel: isSelectMode ? "Exit selection mode" : "Select keys",
|
|
138
|
+
accessibilityRole: "button",
|
|
136
139
|
style: [styles.actionButton, isSelectMode && styles.actionButtonActive],
|
|
137
140
|
children: isSelectMode ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
138
141
|
style: styles.selectModeActiveContainer,
|
|
@@ -150,6 +153,7 @@ function StorageActionButtons({
|
|
|
150
153
|
}), !isSelectMode && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CopyButton, {
|
|
151
154
|
value: copyValue,
|
|
152
155
|
size: 16,
|
|
156
|
+
testID: "buoy-storage-export-copy",
|
|
153
157
|
buttonStyle: styles.actionButton,
|
|
154
158
|
colors: {
|
|
155
159
|
idle: _sharedUi.night.color.textSecondary,
|
|
@@ -158,6 +162,9 @@ function StorageActionButtons({
|
|
|
158
162
|
}
|
|
159
163
|
}), !isSelectMode && showClearAll && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
160
164
|
onPress: handleClearAll,
|
|
165
|
+
testID: "buoy-storage-clear-all",
|
|
166
|
+
accessibilityLabel: "Clear all storage",
|
|
167
|
+
accessibilityRole: "button",
|
|
161
168
|
style: styles.actionButton,
|
|
162
169
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Trash2, {
|
|
163
170
|
size: 16,
|
|
@@ -165,6 +172,9 @@ function StorageActionButtons({
|
|
|
165
172
|
})
|
|
166
173
|
}), !isSelectMode && showClearAsync && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
167
174
|
onPress: handleClearAsyncStorage,
|
|
175
|
+
testID: "buoy-storage-clear-async",
|
|
176
|
+
accessibilityLabel: "Clear AsyncStorage",
|
|
177
|
+
accessibilityRole: "button",
|
|
168
178
|
style: styles.actionButton,
|
|
169
179
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Trash2, {
|
|
170
180
|
size: 16,
|
|
@@ -172,6 +182,9 @@ function StorageActionButtons({
|
|
|
172
182
|
})
|
|
173
183
|
}), !isSelectMode && showClearAllMMKV && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
174
184
|
onPress: handleClearAllMMKV,
|
|
185
|
+
testID: "buoy-storage-clear-mmkv-all",
|
|
186
|
+
accessibilityLabel: "Clear all MMKV instances",
|
|
187
|
+
accessibilityRole: "button",
|
|
175
188
|
style: styles.actionButton,
|
|
176
189
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Trash2, {
|
|
177
190
|
size: 16,
|
|
@@ -179,6 +192,9 @@ function StorageActionButtons({
|
|
|
179
192
|
})
|
|
180
193
|
}), !isSelectMode && showClearMMKVIndividual && mmkvInstances.length === 1 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
181
194
|
onPress: () => handleClearMMKV(mmkvInstances[0].id, mmkvInstances[0].id),
|
|
195
|
+
testID: "buoy-storage-clear-mmkv",
|
|
196
|
+
accessibilityLabel: "Clear MMKV instance",
|
|
197
|
+
accessibilityRole: "button",
|
|
182
198
|
style: styles.actionButton,
|
|
183
199
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Trash2, {
|
|
184
200
|
size: 16,
|
|
@@ -82,7 +82,8 @@ const StorageEventCard = exports.StorageEventCard = /*#__PURE__*/(0, _react.memo
|
|
|
82
82
|
data,
|
|
83
83
|
onPress,
|
|
84
84
|
isSelected = false,
|
|
85
|
-
showChevron = true
|
|
85
|
+
showChevron = true,
|
|
86
|
+
testID
|
|
86
87
|
}) {
|
|
87
88
|
const actionColor = getActionColor(data.lastAction);
|
|
88
89
|
const actionLabel = (0, _storageActionHelpers.translateStorageAction)(data.lastAction);
|
|
@@ -109,6 +110,8 @@ const StorageEventCard = exports.StorageEventCard = /*#__PURE__*/(0, _react.memo
|
|
|
109
110
|
// Interaction
|
|
110
111
|
,
|
|
111
112
|
isSelected: isSelected,
|
|
112
|
-
onPress: onPress
|
|
113
|
+
onPress: onPress,
|
|
114
|
+
testID: testID,
|
|
115
|
+
accessibilityLabel: `${actionLabel} ${data.key}`
|
|
113
116
|
});
|
|
114
117
|
});
|
|
@@ -53,6 +53,12 @@ function StorageEventFilterView({
|
|
|
53
53
|
borderColor: _sharedUi.macOSColors.semantic.warning + '40'
|
|
54
54
|
}],
|
|
55
55
|
onPress: () => onToggleStorageType('async'),
|
|
56
|
+
testID: "buoy-storage-backend-async",
|
|
57
|
+
accessibilityRole: "button",
|
|
58
|
+
accessibilityLabel: "Toggle AsyncStorage capture",
|
|
59
|
+
accessibilityState: {
|
|
60
|
+
selected: enabledStorageTypes.has('async')
|
|
61
|
+
},
|
|
56
62
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Database, {
|
|
57
63
|
size: 16,
|
|
58
64
|
color: enabledStorageTypes.has('async') ? _sharedUi.macOSColors.semantic.warning : _sharedUi.night.color.textTertiary
|
|
@@ -67,6 +73,12 @@ function StorageEventFilterView({
|
|
|
67
73
|
borderColor: _sharedUi.macOSColors.semantic.info + '40'
|
|
68
74
|
}],
|
|
69
75
|
onPress: () => onToggleStorageType('mmkv'),
|
|
76
|
+
testID: "buoy-storage-backend-mmkv",
|
|
77
|
+
accessibilityRole: "button",
|
|
78
|
+
accessibilityLabel: "Toggle MMKV capture",
|
|
79
|
+
accessibilityState: {
|
|
80
|
+
selected: enabledStorageTypes.has('mmkv')
|
|
81
|
+
},
|
|
70
82
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.HardDrive, {
|
|
71
83
|
size: 16,
|
|
72
84
|
color: enabledStorageTypes.has('mmkv') ? _sharedUi.macOSColors.semantic.info : _sharedUi.night.color.textTertiary
|
|
@@ -81,6 +93,12 @@ function StorageEventFilterView({
|
|
|
81
93
|
borderColor: _sharedUi.macOSColors.semantic.success + '40'
|
|
82
94
|
}],
|
|
83
95
|
onPress: () => onToggleStorageType('secure'),
|
|
96
|
+
testID: "buoy-storage-backend-secure",
|
|
97
|
+
accessibilityRole: "button",
|
|
98
|
+
accessibilityLabel: "Toggle Secure Storage capture",
|
|
99
|
+
accessibilityState: {
|
|
100
|
+
selected: enabledStorageTypes.has('secure')
|
|
101
|
+
},
|
|
84
102
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Shield, {
|
|
85
103
|
size: 16,
|
|
86
104
|
color: enabledStorageTypes.has('secure') ? _sharedUi.macOSColors.semantic.success : _sharedUi.night.color.textTertiary
|
|
@@ -48,6 +48,9 @@ function StorageFilterCards({
|
|
|
48
48
|
}]],
|
|
49
49
|
onPress: () => onFilterChange?.("all"),
|
|
50
50
|
activeOpacity: 0.8,
|
|
51
|
+
testID: "buoy-storage-status-all",
|
|
52
|
+
accessibilityRole: "button",
|
|
53
|
+
accessibilityLabel: "Valid keys",
|
|
51
54
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
52
55
|
style: [styles.filterValue, {
|
|
53
56
|
color: _sharedUi.night.color.text
|
|
@@ -65,6 +68,9 @@ function StorageFilterCards({
|
|
|
65
68
|
}]],
|
|
66
69
|
onPress: () => onFilterChange?.("missing"),
|
|
67
70
|
activeOpacity: 0.8,
|
|
71
|
+
testID: "buoy-storage-status-missing",
|
|
72
|
+
accessibilityRole: "button",
|
|
73
|
+
accessibilityLabel: "Missing keys",
|
|
68
74
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
69
75
|
style: [styles.filterValue, {
|
|
70
76
|
color: displayStats.missingCount > 0 ? _sharedUi.macOSColors.semantic.error : _sharedUi.night.color.textTertiary
|
|
@@ -82,6 +88,9 @@ function StorageFilterCards({
|
|
|
82
88
|
}]],
|
|
83
89
|
onPress: () => onFilterChange?.("issues"),
|
|
84
90
|
activeOpacity: 0.8,
|
|
91
|
+
testID: "buoy-storage-status-issues",
|
|
92
|
+
accessibilityRole: "button",
|
|
93
|
+
accessibilityLabel: "Issues keys",
|
|
85
94
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
86
95
|
style: [styles.filterValue, {
|
|
87
96
|
color: displayStats.issuesCount > 0 ? _sharedUi.macOSColors.semantic.warning : _sharedUi.night.color.textTertiary
|
|
@@ -92,7 +92,7 @@ function StorageTreeEditor({
|
|
|
92
92
|
} = editor;
|
|
93
93
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
94
94
|
style: styles.screen,
|
|
95
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(EditorHeader, {
|
|
95
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolBackground, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(EditorHeader, {
|
|
96
96
|
storageKey: storageKey,
|
|
97
97
|
state: draft.isSaving ? "Saving…" : draft.isDirty ? "Draft · unsaved" : "In sync with device",
|
|
98
98
|
dirty: draft.isDirty,
|
|
@@ -111,6 +111,7 @@ function StorageTreeEditor({
|
|
|
111
111
|
children: summarize(draft.value)
|
|
112
112
|
})]
|
|
113
113
|
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.ScrollView, {
|
|
114
|
+
testID: "buoy-storage-editor-scroll",
|
|
114
115
|
style: styles.treeScroll,
|
|
115
116
|
contentContainerStyle: styles.treeContent,
|
|
116
117
|
keyboardShouldPersistTaps: "handled",
|
|
@@ -128,7 +129,11 @@ function StorageTreeEditor({
|
|
|
128
129
|
,
|
|
129
130
|
showTypeFilter: false,
|
|
130
131
|
onSelectionChange: editor.setSelection,
|
|
131
|
-
selectPath: draft.selectPath
|
|
132
|
+
selectPath: draft.selectPath
|
|
133
|
+
// Its own handle root: the card that opened this editor is still
|
|
134
|
+
// mounted underneath, rendering the same value in its preview tree.
|
|
135
|
+
,
|
|
136
|
+
testIDPrefix: "buoy-storage-editor-row",
|
|
132
137
|
onRequestInlineEdit: sel => editor.openValueEditor(sel.path)
|
|
133
138
|
})
|
|
134
139
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
@@ -156,6 +161,8 @@ function StorageTreeEditor({
|
|
|
156
161
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
157
162
|
onPress: draft.discard,
|
|
158
163
|
disabled: draft.isSaving,
|
|
164
|
+
testID: "buoy-storage-editor-discard",
|
|
165
|
+
accessibilityRole: "button",
|
|
159
166
|
accessibilityLabel: "Discard changes",
|
|
160
167
|
style: styles.discardButton,
|
|
161
168
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
@@ -165,6 +172,8 @@ function StorageTreeEditor({
|
|
|
165
172
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
166
173
|
onPress: editor.saveAndClose,
|
|
167
174
|
disabled: draft.isSaving,
|
|
175
|
+
testID: "buoy-storage-editor-save",
|
|
176
|
+
accessibilityRole: "button",
|
|
168
177
|
accessibilityLabel: "Save changes",
|
|
169
178
|
style: [styles.saveButton, draft.isSaving && styles.saveDisabled],
|
|
170
179
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
@@ -200,6 +209,8 @@ function EditorHeader({
|
|
|
200
209
|
style: styles.header,
|
|
201
210
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
202
211
|
onPress: onClose,
|
|
212
|
+
testID: "buoy-storage-editor-close",
|
|
213
|
+
accessibilityRole: "button",
|
|
203
214
|
accessibilityLabel: "Close editor",
|
|
204
215
|
style: styles.headerButton,
|
|
205
216
|
hitSlop: {
|
|
@@ -214,7 +225,13 @@ function EditorHeader({
|
|
|
214
225
|
})
|
|
215
226
|
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
216
227
|
style: styles.headerTitle,
|
|
217
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text
|
|
228
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text
|
|
229
|
+
// Which key this editor is pointed at. Worth a handle of its own:
|
|
230
|
+
// the editor is reached by expanding a card in a list of hundreds,
|
|
231
|
+
// and opening it on the wrong one looks exactly like opening it on
|
|
232
|
+
// the right one until a write lands somewhere unexpected.
|
|
233
|
+
, {
|
|
234
|
+
testID: "buoy-storage-editor-key",
|
|
218
235
|
style: styles.headerKey,
|
|
219
236
|
numberOfLines: 1,
|
|
220
237
|
children: storageKey.key
|
|
@@ -224,6 +241,8 @@ function EditorHeader({
|
|
|
224
241
|
})]
|
|
225
242
|
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
226
243
|
onPress: onRaw,
|
|
244
|
+
testID: "buoy-storage-editor-raw",
|
|
245
|
+
accessibilityRole: "button",
|
|
227
246
|
accessibilityLabel: "Edit raw JSON",
|
|
228
247
|
style: styles.headerButton,
|
|
229
248
|
hitSlop: {
|
|
@@ -269,8 +288,10 @@ function storageTypeColor(storageType) {
|
|
|
269
288
|
const styles = _reactNative.StyleSheet.create({
|
|
270
289
|
screen: {
|
|
271
290
|
flex: 1,
|
|
272
|
-
//
|
|
273
|
-
|
|
291
|
+
// Opaque on purpose: transparency here showed the whole key list through
|
|
292
|
+
// the editor, not just the sheet's star field. The screen paints its own
|
|
293
|
+
// ToolBackground instead — see the mount site above.
|
|
294
|
+
backgroundColor: _sharedUi.night.color.bg
|
|
274
295
|
},
|
|
275
296
|
header: {
|
|
276
297
|
flexDirection: "row",
|
|
@@ -92,6 +92,7 @@ const StorageKeyRow = exports.StorageKeyRow = /*#__PURE__*/(0, _react.memo)(func
|
|
|
92
92
|
onTogglePin,
|
|
93
93
|
onEditKey,
|
|
94
94
|
onToggleKey,
|
|
95
|
+
onDeleteKey,
|
|
95
96
|
editBlockedReason
|
|
96
97
|
}) {
|
|
97
98
|
const canEdit = onEditKey != null && editBlockedReason == null;
|
|
@@ -141,6 +142,9 @@ const StorageKeyRow = exports.StorageKeyRow = /*#__PURE__*/(0, _react.memo)(func
|
|
|
141
142
|
children: String(eventCount)
|
|
142
143
|
}), eventCount > 1 && onViewHistory && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
143
144
|
onPress: onViewHistory,
|
|
145
|
+
testID: "buoy-storage-key-history",
|
|
146
|
+
accessibilityLabel: `View history for ${storageKey.key}`,
|
|
147
|
+
accessibilityRole: "button",
|
|
144
148
|
style: styles.viewHistoryButton,
|
|
145
149
|
hitSlop: {
|
|
146
150
|
top: 6,
|
|
@@ -220,7 +224,14 @@ const StorageKeyRow = exports.StorageKeyRow = /*#__PURE__*/(0, _react.memo)(func
|
|
|
220
224
|
}) : null, onTogglePin || onHideKey || canEdit ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
221
225
|
style: styles.actionRow,
|
|
222
226
|
children: [canEdit ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
223
|
-
style: styles.editButton
|
|
227
|
+
style: styles.editButton
|
|
228
|
+
// One handle for both jobs on purpose: this is the card's single
|
|
229
|
+
// "change it" affordance, and which of the two it performs is the
|
|
230
|
+
// key's type, not a different control.
|
|
231
|
+
,
|
|
232
|
+
testID: "buoy-storage-key-edit",
|
|
233
|
+
accessibilityLabel: isBoolean && onToggleKey ? `Toggle ${storageKey.key}` : `Edit ${storageKey.key}`,
|
|
234
|
+
accessibilityRole: "button",
|
|
224
235
|
onPress: () => isBoolean && onToggleKey ? onToggleKey(storageKey) : onEditKey?.(storageKey),
|
|
225
236
|
hitSlop: {
|
|
226
237
|
top: 6,
|
|
@@ -237,6 +248,9 @@ const StorageKeyRow = exports.StorageKeyRow = /*#__PURE__*/(0, _react.memo)(func
|
|
|
237
248
|
})]
|
|
238
249
|
}) : null, onTogglePin ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
239
250
|
style: [styles.actionChip, isPinned && styles.actionChipActive],
|
|
251
|
+
testID: "buoy-storage-key-pin",
|
|
252
|
+
accessibilityLabel: isPinned ? `Unpin ${storageKey.key}` : `Pin ${storageKey.key} to top`,
|
|
253
|
+
accessibilityRole: "button",
|
|
240
254
|
onPress: () => onTogglePin(storageKey.key),
|
|
241
255
|
hitSlop: {
|
|
242
256
|
top: 6,
|
|
@@ -256,6 +270,9 @@ const StorageKeyRow = exports.StorageKeyRow = /*#__PURE__*/(0, _react.memo)(func
|
|
|
256
270
|
})]
|
|
257
271
|
}) : null, onHideKey ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
258
272
|
style: styles.actionChip,
|
|
273
|
+
testID: "buoy-storage-key-hide",
|
|
274
|
+
accessibilityLabel: `Hide ${storageKey.key} from list`,
|
|
275
|
+
accessibilityRole: "button",
|
|
259
276
|
onPress: () => onHideKey(storageKey),
|
|
260
277
|
hitSlop: {
|
|
261
278
|
top: 6,
|
|
@@ -272,6 +289,27 @@ const StorageKeyRow = exports.StorageKeyRow = /*#__PURE__*/(0, _react.memo)(func
|
|
|
272
289
|
}],
|
|
273
290
|
children: "Hide from list"
|
|
274
291
|
})]
|
|
292
|
+
}) : null, canEdit && onDeleteKey ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
293
|
+
style: [styles.actionChip, styles.dangerChip],
|
|
294
|
+
testID: "buoy-storage-key-delete",
|
|
295
|
+
accessibilityLabel: `Delete key ${storageKey.key}`,
|
|
296
|
+
accessibilityRole: "button",
|
|
297
|
+
onPress: () => onDeleteKey(storageKey),
|
|
298
|
+
hitSlop: {
|
|
299
|
+
top: 6,
|
|
300
|
+
bottom: 6,
|
|
301
|
+
left: 6,
|
|
302
|
+
right: 6
|
|
303
|
+
},
|
|
304
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Trash2, {
|
|
305
|
+
size: 12,
|
|
306
|
+
color: _sharedUi.night.color.danger
|
|
307
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
308
|
+
style: [styles.actionChipText, {
|
|
309
|
+
color: _sharedUi.night.color.danger
|
|
310
|
+
}],
|
|
311
|
+
children: "Delete key"
|
|
312
|
+
})]
|
|
275
313
|
}) : null]
|
|
276
314
|
}) : null]
|
|
277
315
|
});
|
|
@@ -284,6 +322,12 @@ const StorageKeyRow = exports.StorageKeyRow = /*#__PURE__*/(0, _react.memo)(func
|
|
|
284
322
|
// Create checkbox element for select mode
|
|
285
323
|
const selectionCheckbox = isSelectMode ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
286
324
|
onPress: handleCheckboxPress,
|
|
325
|
+
testID: `buoy-storage-checkbox-${storageKey.storageType}-${storageKey.key}`,
|
|
326
|
+
accessibilityLabel: `Select ${storageKey.key}`,
|
|
327
|
+
accessibilityRole: "checkbox",
|
|
328
|
+
accessibilityState: {
|
|
329
|
+
checked: isSelected
|
|
330
|
+
},
|
|
287
331
|
style: styles.checkboxContainer,
|
|
288
332
|
hitSlop: {
|
|
289
333
|
top: 8,
|
|
@@ -329,7 +373,9 @@ const StorageKeyRow = exports.StorageKeyRow = /*#__PURE__*/(0, _react.memo)(func
|
|
|
329
373
|
})]
|
|
330
374
|
}),
|
|
331
375
|
showChevron: !isSelectMode,
|
|
332
|
-
onPress: isSelectMode ? handleCheckboxPress : onPress ? () => onPress(storageKey) : undefined
|
|
376
|
+
onPress: isSelectMode ? handleCheckboxPress : onPress ? () => onPress(storageKey) : undefined,
|
|
377
|
+
testID: `buoy-storage-row-${storageKey.storageType}-${storageKey.key}`,
|
|
378
|
+
accessibilityLabel: `${storageKey.storageType} ${storageKey.key}`
|
|
333
379
|
}), eventCount != null && eventCount > 1 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
334
380
|
style: [styles.absCountBadge, {
|
|
335
381
|
backgroundColor: _sharedUi.macOSColors.semantic.warning + "22",
|
|
@@ -455,6 +501,9 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
455
501
|
borderWidth: 1,
|
|
456
502
|
borderColor: _sharedUi.night.color.border
|
|
457
503
|
},
|
|
504
|
+
dangerChip: {
|
|
505
|
+
borderColor: _sharedUi.night.color.danger + "55"
|
|
506
|
+
},
|
|
458
507
|
actionChipActive: {
|
|
459
508
|
backgroundColor: _sharedUi.night.color.accentSoft,
|
|
460
509
|
borderColor: _sharedUi.night.color.accentBorderStrong
|
|
@@ -33,17 +33,32 @@ function StorageKeySection({
|
|
|
33
33
|
onTogglePin,
|
|
34
34
|
onEditKey,
|
|
35
35
|
onToggleKey,
|
|
36
|
+
onDeleteKey,
|
|
36
37
|
editBlockedFor
|
|
37
38
|
}) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Identifies a ROW, not a key name.
|
|
41
|
+
*
|
|
42
|
+
* The same name routinely exists in more than one place — an app that keeps
|
|
43
|
+
* `auth.refreshToken` in both MMKV and the keychain has two rows called
|
|
44
|
+
* that, and they are different values in different stores.
|
|
45
|
+
*/
|
|
44
46
|
const getKeyIdentifier = (0, _react.useCallback)(storageKey => {
|
|
45
47
|
return storageKey.instanceId ? `${storageKey.storageType}-${storageKey.instanceId}-${storageKey.key}` : `${storageKey.storageType}-${storageKey.key}`;
|
|
46
48
|
}, []);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Which ROW is expanded — by identifier, not by key name.
|
|
52
|
+
*
|
|
53
|
+
* Keyed by name, tapping one of a pair of same-named rows opened BOTH of
|
|
54
|
+
* them: two cards, two value trees, and two Edit buttons, one of which
|
|
55
|
+
* writes to a store you weren't looking at.
|
|
56
|
+
*/
|
|
57
|
+
const [expandedKey, setExpandedKey] = (0, _react.useState)(null);
|
|
58
|
+
const handleKeyPress = (0, _react.useCallback)(storageKey => {
|
|
59
|
+
const id = getKeyIdentifier(storageKey);
|
|
60
|
+
setExpandedKey(prev => prev === id ? null : id);
|
|
61
|
+
}, [getKeyIdentifier]);
|
|
47
62
|
if (keys.length === 0 && title === "Required Keys") {
|
|
48
63
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
49
64
|
style: styles.sectionContainer,
|
|
@@ -80,7 +95,7 @@ function StorageKeySection({
|
|
|
80
95
|
const uniqueKey = getKeyIdentifier(storageKey);
|
|
81
96
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_StorageKeyRow.StorageKeyRow, {
|
|
82
97
|
storageKey: storageKey,
|
|
83
|
-
isExpanded: !isSelectMode && expandedKey ===
|
|
98
|
+
isExpanded: !isSelectMode && expandedKey === uniqueKey,
|
|
84
99
|
onPress: handleKeyPress,
|
|
85
100
|
isSelectMode: isSelectMode,
|
|
86
101
|
isSelected: selectedKeys.has(uniqueKey),
|
|
@@ -90,6 +105,7 @@ function StorageKeySection({
|
|
|
90
105
|
onHideKey: onHideKey,
|
|
91
106
|
onEditKey: onEditKey,
|
|
92
107
|
onToggleKey: onToggleKey,
|
|
108
|
+
onDeleteKey: onDeleteKey,
|
|
93
109
|
editBlockedReason: editBlockedFor?.(storageKey),
|
|
94
110
|
isPinned: pinnedKeys?.has(storageKey.key),
|
|
95
111
|
onTogglePin: onTogglePin
|