@buoy-gg/storage 2.1.5 → 2.1.8

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.
@@ -3,13 +3,12 @@
3
3
  import { useState, useCallback, useEffect, useRef, useMemo } from "react";
4
4
  import { Text, View, TouchableOpacity, StyleSheet, FlatList, Alert } from "react-native";
5
5
  import AsyncStorage from "@react-native-async-storage/async-storage";
6
- import { JsModal, ModalHeader, TabSelector, parseValue, devToolsStorageKeys, macOSColors, Database, Trash2, Filter, Search, SearchBar, PowerToggleButton } from "@buoy-gg/shared-ui";
6
+ import { JsModal, ModalHeader, TabSelector, parseValue, devToolsStorageKeys, macOSColors, Database, Filter, Search, SearchBar, PowerToggleButton, ToolbarCopyButton, ToolbarClearButton, truncatePayload } from "@buoy-gg/shared-ui";
7
7
  import { StorageBrowserMode } from "./StorageBrowserMode";
8
8
  import { clearAllAppStorage, clearAllStorageIncludingDevTools } from "../utils/clearAllStorage";
9
9
  import { ProFeatureBanner } from "@buoy-gg/shared-ui";
10
10
  import { useStorageEvents } from "../hooks/useStorageEvents";
11
11
  import { StorageEventDetailContent, StorageEventDetailFooter } from "./StorageEventDetailContent";
12
- import { StorageFilterViewV2 } from "./StorageFilterViewV2";
13
12
  import { StorageEventFilterView } from "./StorageEventFilterView";
14
13
  import { StorageEventCard } from "./StorageEventCard";
15
14
  // Note: StorageEvent type is now imported from useStorageEvents hook
@@ -32,12 +31,8 @@ export function StorageModalWithTabs({
32
31
  const [activeTab, setActiveTab] = useState("browser");
33
32
 
34
33
  // Storage Browser state
35
- const [showStorageFilters, setShowStorageFilters] = useState(false);
36
- const [storageIgnoredPatterns, setStorageIgnoredPatterns] = useState(new Set(["@react_buoy"]) // Auto-hide dev tool keys by default
37
- );
38
34
  const [searchQuery, setSearchQuery] = useState("");
39
35
  const [isSearchActive, setIsSearchActive] = useState(false);
40
- const hasLoadedStorageFilters = useRef(false);
41
36
 
42
37
  // Local state to control subscription (true = subscribed, false = unsubscribed)
43
38
  const [isListeningEnabled, setIsListeningEnabled] = useState(true);
@@ -53,6 +48,8 @@ export function StorageModalWithTabs({
53
48
  enabled: isListeningEnabled
54
49
  });
55
50
  const [selectedConversationKey, setSelectedConversationKey] = useState(null);
51
+ const [selectedHistoryKey, setSelectedHistoryKey] = useState(null);
52
+ const [selectedHistoryEventIndex, setSelectedHistoryEventIndex] = useState(null);
56
53
  const [selectedEventIndex, setSelectedEventIndex] = useState(0);
57
54
  const [showFilters, setShowFilters] = useState(false);
58
55
  const [ignoredPatterns, setIgnoredPatterns] = useState(new Set(["@react_buoy", "@RNAsyncStorage", "redux-persist", "persist:"]) // Auto-hide dev tool keys by default
@@ -185,10 +182,15 @@ export function StorageModalWithTabs({
185
182
  const handleClearEvents = useCallback(() => {
186
183
  clearStorageEvents();
187
184
  setSelectedConversationKey(null);
188
- }, [clearStorageEvents]);
189
- const handleConversationPress = useCallback(conversation => {
190
- setSelectedConversationKey(conversation.key);
191
185
  setSelectedEventIndex(0);
186
+ }, [clearStorageEvents]);
187
+ const handleViewHistoryFromBrowser = useCallback(key => {
188
+ setSelectedHistoryKey(key);
189
+ setSelectedHistoryEventIndex(null);
190
+ }, []);
191
+ const handleBackFromHistory = useCallback(() => {
192
+ setSelectedHistoryKey(null);
193
+ setSelectedHistoryEventIndex(null);
192
194
  }, []);
193
195
  const handleTogglePattern = useCallback(pattern => {
194
196
  setIgnoredPatterns(prev => {
@@ -221,22 +223,26 @@ export function StorageModalWithTabs({
221
223
 
222
224
  // Storage Browser handlers
223
225
  const storageDataRef = useRef([]);
224
- const handleToggleStorageFilters = useCallback(() => {
225
- setShowStorageFilters(!showStorageFilters);
226
- }, [showStorageFilters]);
227
- const handleToggleStoragePattern = useCallback(pattern => {
228
- setStorageIgnoredPatterns(prev => {
229
- const next = new Set(prev);
230
- if (next.has(pattern)) {
231
- next.delete(pattern);
232
- } else {
233
- next.add(pattern);
234
- }
235
- return next;
236
- });
237
- }, []);
238
- const handleAddStoragePattern = useCallback(pattern => {
239
- setStorageIgnoredPatterns(prev => new Set([...prev, pattern]));
226
+ const getStorageSnapshot = useCallback(() => {
227
+ const keys = storageDataRef.current;
228
+ return {
229
+ timestamp: new Date().toISOString(),
230
+ totalKeys: keys.length,
231
+ asyncStorage: keys.filter(k => k.storageType === 'async').reduce((acc, k) => {
232
+ acc[k.key] = truncatePayload(k.value);
233
+ return acc;
234
+ }, {}),
235
+ mmkv: keys.filter(k => k.storageType === 'mmkv').reduce((acc, k) => {
236
+ const id = k.instanceId || 'default';
237
+ if (!acc[id]) acc[id] = {};
238
+ acc[id][k.key] = truncatePayload(k.value);
239
+ return acc;
240
+ }, {}),
241
+ secure: keys.filter(k => k.storageType === 'secure').reduce((acc, k) => {
242
+ acc[k.key] = truncatePayload(k.value);
243
+ return acc;
244
+ }, {})
245
+ };
240
246
  }, []);
241
247
  const handlePurgeStorage = useCallback(async () => {
242
248
  Alert.alert("Clear Storage", "Choose what to clear:", [{
@@ -353,67 +359,154 @@ export function StorageModalWithTabs({
353
359
  return conversations.find(c => c.key === selectedConversationKey) || null;
354
360
  }, [selectedConversationKey, conversations]);
355
361
 
356
- // For free users, limit visible conversations
357
- const visibleConversations = useMemo(() => {
358
- if (isPro) return conversations;
359
- return conversations.slice(0, FREE_TIER_EVENT_LIMIT);
360
- }, [conversations, isPro]);
361
- const lockedConversationCount = useMemo(() => {
362
+ // Event count per key for storage browser badges
363
+ const eventCountByKey = useMemo(() => {
364
+ const counts = {};
365
+ conversations.forEach(conv => {
366
+ counts[conv.key] = conv.totalOperations;
367
+ });
368
+ return counts;
369
+ }, [conversations]);
370
+
371
+ // Selected history conversation (from browser tab "view history")
372
+ const selectedHistoryConversation = useMemo(() => {
373
+ if (!selectedHistoryKey) return null;
374
+ return conversations.find(c => c.key === selectedHistoryKey) || null;
375
+ }, [selectedHistoryKey, conversations]);
376
+
377
+ // Filtered raw events for the events tab (chronological stream, newest-first)
378
+ const filteredEvents = useMemo(() => {
379
+ return events.filter(event => {
380
+ if (!event.data?.key) return false;
381
+ if (!enabledStorageTypes.has(event.storageType)) return false;
382
+ const key = event.data.key;
383
+ return !Array.from(ignoredPatterns).some(p => key.includes(p));
384
+ });
385
+ }, [events, ignoredPatterns, enabledStorageTypes]);
386
+ const getEventsSnapshot = useCallback(() => {
387
+ return filteredEvents.map(event => ({
388
+ key: event.data?.key,
389
+ action: event.action,
390
+ value: truncatePayload(event.data?.value),
391
+ storageType: event.storageType,
392
+ timestamp: event.timestamp.toISOString()
393
+ }));
394
+ }, [filteredEvents]);
395
+ const visibleEvents = useMemo(() => {
396
+ if (isPro) return filteredEvents;
397
+ return filteredEvents.slice(0, FREE_TIER_EVENT_LIMIT);
398
+ }, [filteredEvents, isPro]);
399
+ const lockedEventCount = useMemo(() => {
362
400
  if (isPro) return 0;
363
- return Math.max(0, conversations.length - FREE_TIER_EVENT_LIMIT);
364
- }, [conversations.length, isPro]);
401
+ return Math.max(0, filteredEvents.length - FREE_TIER_EVENT_LIMIT);
402
+ }, [filteredEvents.length, isPro]);
365
403
 
366
404
  // FlatList optimization constants
367
405
  const END_REACHED_THRESHOLD = 0.8;
368
406
 
369
- // Stable keyExtractor for FlatList
370
- const keyExtractor = useCallback(item => {
371
- return item.key;
407
+ // keyExtractor for raw event FlatList
408
+ const eventKeyExtractor = useCallback((item, index) => {
409
+ return `${item.timestamp.getTime()}-${item.data?.key || index}`;
372
410
  }, []);
373
411
 
374
- // Removed getItemType as it's FlatList-specific
375
-
376
- // Create stable ref for event handler
377
- const selectConversationRef = useRef(undefined);
378
- selectConversationRef.current = handleConversationPress;
379
-
380
- // Stable renderItem with ref pattern
381
- const renderConversationItem = useCallback(({
412
+ // When a raw event card is pressed, open that key's conversation detail at the matching event
413
+ const handleRawEventPress = useCallback(item => {
414
+ const conv = conversations.find(c => c.key === item.data?.key);
415
+ if (!conv) return;
416
+ const sortedEvents = [...conv.events].sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
417
+ const eventIndex = sortedEvents.findIndex(e => e.timestamp.getTime() === item.timestamp.getTime());
418
+ setSelectedConversationKey(conv.key);
419
+ setSelectedEventIndex(eventIndex >= 0 ? eventIndex : 0);
420
+ }, [conversations]);
421
+
422
+ // Render a single raw event card
423
+ const renderEventItem = useCallback(({
382
424
  item
383
425
  }) => {
384
426
  const cardData = {
385
- key: item.key,
386
- lastAction: item.lastEvent.action,
387
- totalOperations: item.totalOperations,
388
- lastEventTimestamp: item.lastEvent.timestamp,
389
- storageTypes: item.storageTypes,
390
- valueType: item.valueType
427
+ key: item.data?.key || "",
428
+ lastAction: item.action,
429
+ totalOperations: 1,
430
+ lastEventTimestamp: item.timestamp,
431
+ storageTypes: new Set([item.storageType]),
432
+ valueType: getValueType(item.data?.value)
391
433
  };
392
434
  return /*#__PURE__*/_jsx(StorageEventCard, {
393
435
  data: cardData,
394
- onPress: () => selectConversationRef.current?.(item)
436
+ onPress: () => handleRawEventPress(item)
395
437
  });
396
- }, []);
438
+ }, [handleRawEventPress]);
397
439
  if (!visible) return null;
398
440
  const persistenceKey = enableSharedModalDimensions ? devToolsStorageKeys.modal.root() : devToolsStorageKeys.storage.modal();
399
441
  const renderContent = () => {
442
+ // Filters overlay — applies to both tabs
443
+ if (showFilters) {
444
+ return /*#__PURE__*/_jsx(StorageEventFilterView, {
445
+ ignoredPatterns: ignoredPatterns,
446
+ onTogglePattern: handleTogglePattern,
447
+ onAddPattern: handleAddPattern,
448
+ availableKeys: allEventKeys,
449
+ enabledStorageTypes: enabledStorageTypes,
450
+ onToggleStorageType: handleToggleStorageType
451
+ });
452
+ }
400
453
  if (activeTab === "browser") {
401
- if (showStorageFilters) {
402
- return /*#__PURE__*/_jsx(StorageFilterViewV2, {
403
- ignoredPatterns: storageIgnoredPatterns,
404
- onTogglePattern: handleToggleStoragePattern,
405
- onAddPattern: handleAddStoragePattern,
406
- availableKeys: allStorageKeys
454
+ // History flow for a specific key (from "view history" in browser tab)
455
+ if (selectedHistoryKey && selectedHistoryConversation) {
456
+ // Level 2: detail view for a specific event
457
+ if (selectedHistoryEventIndex !== null) {
458
+ return /*#__PURE__*/_jsx(View, {
459
+ style: styles.contentWrapper,
460
+ children: /*#__PURE__*/_jsx(StorageEventDetailContent, {
461
+ conversation: selectedHistoryConversation,
462
+ selectedEventIndex: selectedHistoryEventIndex,
463
+ onEventIndexChange: setSelectedHistoryEventIndex,
464
+ disableInternalFooter: true
465
+ })
466
+ });
467
+ }
468
+
469
+ // Level 1: event list for this key (newest-first)
470
+ const ascSortedEvents = [...selectedHistoryConversation.events].sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
471
+ const newestFirstEvents = [...ascSortedEvents].reverse();
472
+ return /*#__PURE__*/_jsx(FlatList, {
473
+ data: newestFirstEvents,
474
+ keyExtractor: (item, index) => `${item.timestamp.getTime()}-${index}`,
475
+ renderItem: ({
476
+ item
477
+ }) => {
478
+ const ascIdx = ascSortedEvents.findIndex(e => e.timestamp.getTime() === item.timestamp.getTime());
479
+ const cardData = {
480
+ key: item.data?.key || "",
481
+ lastAction: item.action,
482
+ totalOperations: 1,
483
+ lastEventTimestamp: item.timestamp,
484
+ storageTypes: new Set([item.storageType]),
485
+ valueType: getValueType(item.data?.value)
486
+ };
487
+ return /*#__PURE__*/_jsx(StorageEventCard, {
488
+ data: cardData,
489
+ onPress: () => setSelectedHistoryEventIndex(ascIdx >= 0 ? ascIdx : 0)
490
+ });
491
+ },
492
+ contentContainerStyle: styles.listContent,
493
+ ItemSeparatorComponent: () => /*#__PURE__*/_jsx(View, {
494
+ style: styles.separator
495
+ }),
496
+ initialNumToRender: 20,
497
+ scrollEnabled: false
407
498
  });
408
499
  }
409
500
  return /*#__PURE__*/_jsx(StorageBrowserMode, {
410
501
  requiredStorageKeys: requiredStorageKeys,
411
- showFilters: showStorageFilters,
412
- ignoredPatterns: storageIgnoredPatterns,
413
- onTogglePattern: handleToggleStoragePattern,
414
- onAddPattern: handleAddStoragePattern,
502
+ ignoredPatterns: ignoredPatterns,
503
+ onTogglePattern: handleTogglePattern,
504
+ onAddPattern: handleAddPattern,
415
505
  searchQuery: searchQuery,
416
- storageDataRef: storageDataRef
506
+ storageDataRef: storageDataRef,
507
+ eventCountByKey: eventCountByKey,
508
+ onViewHistory: handleViewHistoryFromBrowser,
509
+ enabledStorageTypes: enabledStorageTypes
417
510
  });
418
511
  }
419
512
 
@@ -429,17 +522,7 @@ export function StorageModalWithTabs({
429
522
  })
430
523
  });
431
524
  }
432
- if (showFilters) {
433
- return /*#__PURE__*/_jsx(StorageEventFilterView, {
434
- ignoredPatterns: ignoredPatterns,
435
- onTogglePattern: handleTogglePattern,
436
- onAddPattern: handleAddPattern,
437
- availableKeys: allEventKeys,
438
- enabledStorageTypes: enabledStorageTypes,
439
- onToggleStorageType: handleToggleStorageType
440
- });
441
- }
442
- if (conversations.length === 0) {
525
+ if (filteredEvents.length === 0) {
443
526
  return /*#__PURE__*/_jsxs(View, {
444
527
  style: styles.emptyState,
445
528
  children: [/*#__PURE__*/_jsx(Database, {
@@ -455,18 +538,15 @@ export function StorageModalWithTabs({
455
538
  });
456
539
  }
457
540
  return /*#__PURE__*/_jsx(FlatList, {
458
- data: visibleConversations,
459
- renderItem: renderConversationItem,
460
- keyExtractor: keyExtractor,
541
+ data: visibleEvents,
542
+ renderItem: renderEventItem,
543
+ keyExtractor: eventKeyExtractor,
461
544
  onEndReachedThreshold: END_REACHED_THRESHOLD,
462
545
  contentContainerStyle: styles.listContent,
463
- ItemSeparatorComponent: () => /*#__PURE__*/_jsx(View, {
464
- style: styles.separator
465
- }),
466
- ListFooterComponent: lockedConversationCount > 0 ? /*#__PURE__*/_jsx(View, {
546
+ ListFooterComponent: lockedEventCount > 0 ? /*#__PURE__*/_jsx(View, {
467
547
  style: styles.lockedBannerContainer,
468
548
  children: /*#__PURE__*/_jsx(ProFeatureBanner, {
469
- featureName: `${lockedConversationCount} event${lockedConversationCount > 1 ? 's' : ''} locked`,
549
+ featureName: `${lockedEventCount} event${lockedEventCount > 1 ? 's' : ''} locked`,
470
550
  description: "Upgrade to Pro to unlock full event history"
471
551
  })
472
552
  }) : null,
@@ -480,10 +560,14 @@ export function StorageModalWithTabs({
480
560
  conversation: selectedConversation,
481
561
  selectedEventIndex: selectedEventIndex,
482
562
  onEventIndexChange: setSelectedEventIndex
563
+ }) : selectedHistoryConversation && selectedHistoryEventIndex !== null ? /*#__PURE__*/_jsx(StorageEventDetailFooter, {
564
+ conversation: selectedHistoryConversation,
565
+ selectedEventIndex: selectedHistoryEventIndex,
566
+ onEventIndexChange: setSelectedHistoryEventIndex
483
567
  }) : null;
484
568
 
485
569
  // Determine the appropriate back handler based on current view state
486
- const currentBackHandler = showStorageFilters ? () => setShowStorageFilters(false) : showFilters ? () => setShowFilters(false) : selectedConversation ? () => {
570
+ const currentBackHandler = showFilters ? () => setShowFilters(false) : selectedHistoryEventIndex !== null ? () => setSelectedHistoryEventIndex(null) : selectedHistoryKey ? handleBackFromHistory : selectedConversation ? () => {
487
571
  setSelectedConversationKey(null);
488
572
  setSelectedEventIndex(0);
489
573
  } : onBack;
@@ -495,17 +579,18 @@ export function StorageModalWithTabs({
495
579
  persistenceKey: persistenceKey,
496
580
  header: {
497
581
  showToggleButton: true,
498
- customContent: showStorageFilters ? /*#__PURE__*/_jsxs(ModalHeader, {
582
+ customContent: showFilters ? /*#__PURE__*/_jsxs(ModalHeader, {
499
583
  children: [/*#__PURE__*/_jsx(ModalHeader.Navigation, {
500
- onBack: () => setShowStorageFilters(false)
584
+ onBack: () => setShowFilters(false)
501
585
  }), /*#__PURE__*/_jsx(ModalHeader.Content, {
502
586
  title: "Filters"
503
587
  })]
504
- }) : showFilters ? /*#__PURE__*/_jsxs(ModalHeader, {
588
+ }) : selectedHistoryKey ? /*#__PURE__*/_jsxs(ModalHeader, {
505
589
  children: [/*#__PURE__*/_jsx(ModalHeader.Navigation, {
506
- onBack: () => setShowFilters(false)
590
+ onBack: selectedHistoryEventIndex !== null ? () => setSelectedHistoryEventIndex(null) : handleBackFromHistory
507
591
  }), /*#__PURE__*/_jsx(ModalHeader.Content, {
508
- title: "Event Filters"
592
+ title: `${selectedHistoryKey} History`,
593
+ centered: true
509
594
  })]
510
595
  }) : selectedConversation ? /*#__PURE__*/_jsxs(ModalHeader, {
511
596
  children: [/*#__PURE__*/_jsx(ModalHeader.Navigation, {
@@ -540,13 +625,26 @@ export function StorageModalWithTabs({
540
625
  label: "Storage"
541
626
  }, {
542
627
  key: "events",
543
- label: `Events${conversations.length > 0 && activeTab !== "events" ? ` (${conversations.length})` : ""}`
628
+ label: `Events${filteredEvents.length > 0 ? ` (${filteredEvents.length})` : ""}`
544
629
  }],
545
630
  activeTab: activeTab,
546
- onTabChange: tab => setActiveTab(tab)
631
+ onTabChange: tab => {
632
+ setActiveTab(tab);
633
+ setSelectedHistoryKey(null);
634
+ setSelectedHistoryEventIndex(null);
635
+ setSelectedConversationKey(null);
636
+ setSelectedEventIndex(0);
637
+ }
547
638
  })
548
639
  }), /*#__PURE__*/_jsxs(ModalHeader.Actions, {
549
- children: [activeTab === "browser" && !isSearchActive && /*#__PURE__*/_jsxs(_Fragment, {
640
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
641
+ onPress: handleToggleFilters,
642
+ style: [styles.iconButton, (ignoredPatterns.size > 0 || enabledStorageTypes.size < 3) && styles.activeFilterButton],
643
+ children: /*#__PURE__*/_jsx(Filter, {
644
+ size: 14,
645
+ color: ignoredPatterns.size > 0 || enabledStorageTypes.size < 3 ? macOSColors.semantic.debug : macOSColors.text.secondary
646
+ })
647
+ }), activeTab === "browser" && !isSearchActive && /*#__PURE__*/_jsxs(_Fragment, {
550
648
  children: [/*#__PURE__*/_jsx(TouchableOpacity, {
551
649
  onPress: () => setIsSearchActive(true),
552
650
  style: styles.iconButton,
@@ -554,33 +652,23 @@ export function StorageModalWithTabs({
554
652
  size: 14,
555
653
  color: macOSColors.text.secondary
556
654
  })
557
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
558
- onPress: handleToggleStorageFilters,
559
- style: [styles.iconButton, storageIgnoredPatterns.size > 0 && styles.activeFilterButton],
560
- children: /*#__PURE__*/_jsx(Filter, {
561
- size: 14,
562
- color: storageIgnoredPatterns.size > 0 ? macOSColors.semantic.debug : macOSColors.text.secondary
563
- })
655
+ }), /*#__PURE__*/_jsx(ToolbarCopyButton, {
656
+ value: getStorageSnapshot,
657
+ disabled: storageDataRef.current.length === 0,
658
+ buttonStyle: styles.iconButton
564
659
  })]
565
660
  }), activeTab === "events" && /*#__PURE__*/_jsxs(_Fragment, {
566
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
567
- onPress: handleToggleFilters,
568
- style: [styles.iconButton, ignoredPatterns.size > 0 && styles.activeFilterButton],
569
- children: /*#__PURE__*/_jsx(Filter, {
570
- size: 14,
571
- color: ignoredPatterns.size > 0 ? macOSColors.semantic.debug : macOSColors.text.secondary
572
- })
661
+ children: [/*#__PURE__*/_jsx(ToolbarCopyButton, {
662
+ value: getEventsSnapshot,
663
+ disabled: filteredEvents.length === 0,
664
+ buttonStyle: styles.iconButton
573
665
  }), /*#__PURE__*/_jsx(PowerToggleButton, {
574
666
  isEnabled: isListening,
575
667
  onToggle: handleToggleListening,
576
668
  accessibilityLabel: "Toggle storage event monitoring"
577
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
669
+ }), /*#__PURE__*/_jsx(ToolbarClearButton, {
578
670
  onPress: handleClearEvents,
579
- style: styles.iconButton,
580
- children: /*#__PURE__*/_jsx(Trash2, {
581
- size: 14,
582
- color: macOSColors.semantic.error
583
- })
671
+ buttonStyle: styles.iconButton
584
672
  })]
585
673
  })]
586
674
  })]
@@ -617,7 +705,7 @@ const styles = StyleSheet.create({
617
705
  height: 8
618
706
  },
619
707
  listContent: {
620
- paddingVertical: 16
708
+ paddingVertical: 6
621
709
  },
622
710
  lockedBannerContainer: {
623
711
  marginTop: 8,
@@ -10,7 +10,10 @@ interface GameUIStorageBrowserProps {
10
10
  onAddPattern?: (pattern: string) => void;
11
11
  searchQuery?: string;
12
12
  storageDataRef?: MutableRefObject<any[]>;
13
+ eventCountByKey?: Record<string, number>;
14
+ onViewHistory?: (key: string) => void;
15
+ enabledStorageTypes?: Set<'async' | 'mmkv' | 'secure'>;
13
16
  }
14
- export declare function GameUIStorageBrowser({ requiredStorageKeys, showFilters, ignoredPatterns, onTogglePattern, onAddPattern, searchQuery, storageDataRef, }: GameUIStorageBrowserProps): import("react").JSX.Element;
17
+ export declare function GameUIStorageBrowser({ requiredStorageKeys, showFilters, ignoredPatterns, onTogglePattern, onAddPattern, searchQuery, storageDataRef, eventCountByKey, onViewHistory, enabledStorageTypes, }: GameUIStorageBrowserProps): import("react").JSX.Element;
15
18
  export {};
16
19
  //# sourceMappingURL=GameUIStorageBrowser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"GameUIStorageBrowser.d.ts","sourceRoot":"","sources":["../../../../src/storage/components/GameUIStorageBrowser.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,gBAAgB,EACjB,MAAM,OAAO,CAAC;AASf,OAAO,EAAkB,kBAAkB,EAAmB,MAAM,UAAU,CAAC;AAgD/E,uCAAuC;AACvC,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,UAAU,yBAAyB;IACjC,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC3C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,gBAAgB,CAAC,GAAG,EAAE,CAAC,CAAC;CAC1C;AAED,wBAAgB,oBAAoB,CAAC,EACnC,mBAAwB,EACxB,WAAmB,EACnB,eAA0C,EAC1C,eAAe,EACf,YAAY,EACZ,WAAgB,EAChB,cAAc,GACf,EAAE,yBAAyB,+BA6wB3B"}
1
+ {"version":3,"file":"GameUIStorageBrowser.d.ts","sourceRoot":"","sources":["../../../../src/storage/components/GameUIStorageBrowser.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,gBAAgB,EACjB,MAAM,OAAO,CAAC;AASf,OAAO,EAAkB,kBAAkB,EAAmB,MAAM,UAAU,CAAC;AAgD/E,uCAAuC;AACvC,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,UAAU,yBAAyB;IACjC,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC3C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,gBAAgB,CAAC,GAAG,EAAE,CAAC,CAAC;IACzC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,mBAAmB,CAAC,EAAE,GAAG,CAAC,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC,CAAC;CACxD;AAED,wBAAgB,oBAAoB,CAAC,EACnC,mBAAwB,EACxB,WAAmB,EACnB,eAA0C,EAC1C,eAAe,EACf,YAAY,EACZ,WAAgB,EAChB,cAAc,EACd,eAAe,EACf,aAAa,EACb,mBAAmB,GACpB,EAAE,yBAAyB,+BAqxB3B"}
@@ -8,11 +8,14 @@ interface StorageBrowserModeProps {
8
8
  onAddPattern?: (pattern: string) => void;
9
9
  searchQuery?: string;
10
10
  storageDataRef?: MutableRefObject<any[]>;
11
+ eventCountByKey?: Record<string, number>;
12
+ onViewHistory?: (key: string) => void;
13
+ enabledStorageTypes?: Set<'async' | 'mmkv' | 'secure'>;
11
14
  }
12
15
  /**
13
16
  * Storage browser mode component
14
17
  * Displays storage keys with game UI styled interface
15
18
  */
16
- export declare function StorageBrowserMode({ requiredStorageKeys, showFilters, ignoredPatterns, onTogglePattern, onAddPattern, searchQuery, storageDataRef, }: StorageBrowserModeProps): import("react").JSX.Element;
19
+ export declare function StorageBrowserMode({ requiredStorageKeys, showFilters, ignoredPatterns, onTogglePattern, onAddPattern, searchQuery, storageDataRef, eventCountByKey, onViewHistory, enabledStorageTypes, }: StorageBrowserModeProps): import("react").JSX.Element;
17
20
  export {};
18
21
  //# sourceMappingURL=StorageBrowserMode.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"StorageBrowserMode.d.ts","sourceRoot":"","sources":["../../../../src/storage/components/StorageBrowserMode.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAE9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAEzC,UAAU,uBAAuB;IAC/B,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC3C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,gBAAgB,CAAC,GAAG,EAAE,CAAC,CAAC;CAC1C;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,mBAAwB,EACxB,WAAmB,EACnB,eAA2B,EAC3B,eAAe,EACf,YAAY,EACZ,WAAgB,EAChB,cAAc,GACf,EAAE,uBAAuB,+BAYzB"}
1
+ {"version":3,"file":"StorageBrowserMode.d.ts","sourceRoot":"","sources":["../../../../src/storage/components/StorageBrowserMode.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAE9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAEzC,UAAU,uBAAuB;IAC/B,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC3C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,gBAAgB,CAAC,GAAG,EAAE,CAAC,CAAC;IACzC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,mBAAmB,CAAC,EAAE,GAAG,CAAC,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC,CAAC;CACxD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,mBAAwB,EACxB,WAAmB,EACnB,eAA2B,EAC3B,eAAe,EACf,YAAY,EACZ,WAAgB,EAChB,cAAc,EACd,eAAe,EACf,aAAa,EACb,mBAAmB,GACpB,EAAE,uBAAuB,+BAezB"}
@@ -9,7 +9,11 @@ interface StorageKeyRowProps {
9
9
  isSelected?: boolean;
10
10
  /** Callback when selection changes */
11
11
  onSelectionChange?: (storageKey: StorageKeyInfo, selected: boolean) => void;
12
+ /** Number of recorded events for this key */
13
+ eventCount?: number;
14
+ /** Called when "view history" is pressed */
15
+ onViewHistory?: () => void;
12
16
  }
13
- export declare function StorageKeyRow({ storageKey, isExpanded, onPress, isSelectMode, isSelected, onSelectionChange, }: StorageKeyRowProps): import("react").JSX.Element;
17
+ export declare function StorageKeyRow({ storageKey, isExpanded, onPress, isSelectMode, isSelected, onSelectionChange, eventCount, onViewHistory, }: StorageKeyRowProps): import("react").JSX.Element;
14
18
  export {};
15
19
  //# sourceMappingURL=StorageKeyRow.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"StorageKeyRow.d.ts","sourceRoot":"","sources":["../../../../src/storage/components/StorageKeyRow.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAyB1C,UAAU,kBAAkB;IAC1B,UAAU,EAAE,cAAc,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,cAAc,KAAK,IAAI,CAAC;IAC/C,uCAAuC;IACvC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,oCAAoC;IACpC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,sCAAsC;IACtC,iBAAiB,CAAC,EAAE,CAAC,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;CAC7E;AA6CD,wBAAgB,aAAa,CAAC,EAC5B,UAAU,EACV,UAAU,EACV,OAAO,EACP,YAAoB,EACpB,UAAkB,EAClB,iBAAiB,GAClB,EAAE,kBAAkB,+BAgMpB"}
1
+ {"version":3,"file":"StorageKeyRow.d.ts","sourceRoot":"","sources":["../../../../src/storage/components/StorageKeyRow.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAyB1C,UAAU,kBAAkB;IAC1B,UAAU,EAAE,cAAc,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,cAAc,KAAK,IAAI,CAAC;IAC/C,uCAAuC;IACvC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,oCAAoC;IACpC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,sCAAsC;IACtC,iBAAiB,CAAC,EAAE,CAAC,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5E,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;CAC5B;AA6CD,wBAAgB,aAAa,CAAC,EAC5B,UAAU,EACV,UAAU,EACV,OAAO,EACP,YAAoB,EACpB,UAAkB,EAClB,iBAAiB,EACjB,UAAU,EACV,aAAa,GACd,EAAE,kBAAkB,+BAiKpB"}
@@ -11,6 +11,10 @@ interface StorageKeySectionProps {
11
11
  selectedKeys?: Set<string>;
12
12
  /** Callback when selection changes */
13
13
  onSelectionChange?: (storageKey: StorageKeyInfo, selected: boolean) => void;
14
+ /** Event counts per key name */
15
+ eventCountByKey?: Record<string, number>;
16
+ /** Called when "view history" is pressed for a key */
17
+ onViewHistory?: (key: string) => void;
14
18
  }
15
19
  /**
16
20
  * Storage key section component following composition principles [[rule3]]
@@ -20,6 +24,6 @@ interface StorageKeySectionProps {
20
24
  * - Prefer Composition over Configuration: Reuses patterns from EnvVarSection
21
25
  * - Extract Reusable Logic: Shares expansion state management pattern
22
26
  */
23
- export declare function StorageKeySection({ title, count, keys, emptyMessage, headerColor, isSelectMode, selectedKeys, onSelectionChange, }: StorageKeySectionProps): import("react").JSX.Element | null;
27
+ export declare function StorageKeySection({ title, count, keys, emptyMessage, headerColor, isSelectMode, selectedKeys, onSelectionChange, eventCountByKey, onViewHistory, }: StorageKeySectionProps): import("react").JSX.Element | null;
24
28
  export {};
25
29
  //# sourceMappingURL=StorageKeySection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"StorageKeySection.d.ts","sourceRoot":"","sources":["../../../../src/storage/components/StorageKeySection.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAI1C,UAAU,sBAAsB;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,mEAAmE;IACnE,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,sCAAsC;IACtC,iBAAiB,CAAC,EAAE,CAAC,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;CAC7E;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,KAAK,EACL,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,YAAoB,EACpB,YAAwB,EACxB,iBAAiB,GAClB,EAAE,sBAAsB,sCA4DxB"}
1
+ {"version":3,"file":"StorageKeySection.d.ts","sourceRoot":"","sources":["../../../../src/storage/components/StorageKeySection.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAI1C,UAAU,sBAAsB;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,mEAAmE;IACnE,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,sCAAsC;IACtC,iBAAiB,CAAC,EAAE,CAAC,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5E,gCAAgC;IAChC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,sDAAsD;IACtD,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,KAAK,EACL,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,YAAoB,EACpB,YAAwB,EACxB,iBAAiB,EACjB,eAAe,EACf,aAAa,GACd,EAAE,sBAAsB,sCA8DxB"}
@@ -1 +1 @@
1
- {"version":3,"file":"StorageModalWithTabs.d.ts","sourceRoot":"","sources":["../../../../src/storage/components/StorageModalWithTabs.tsx"],"names":[],"mappings":"AAyBA,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAuB9C,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC,UAAU,yBAAyB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,KAAK,IAAI,CAAC;IACvC,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAC5C;AAqBD,wBAAgB,oBAAoB,CAAC,EACnC,OAAO,EACP,OAAO,EACP,MAAM,EACN,UAAU,EACV,2BAAmC,EACnC,mBAAwB,GACzB,EAAE,yBAAyB,sCAmsB3B"}
1
+ {"version":3,"file":"StorageModalWithTabs.d.ts","sourceRoot":"","sources":["../../../../src/storage/components/StorageModalWithTabs.tsx"],"names":[],"mappings":"AA2BA,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAsB9C,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC,UAAU,yBAAyB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,KAAK,IAAI,CAAC;IACvC,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAC5C;AAqBD,wBAAgB,oBAAoB,CAAC,EACnC,OAAO,EACP,OAAO,EACP,MAAM,EACN,UAAU,EACV,2BAAmC,EACnC,mBAAwB,GACzB,EAAE,yBAAyB,sCAmyB3B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buoy-gg/storage",
3
- "version": "2.1.5",
3
+ "version": "2.1.8",
4
4
  "description": "storage package",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -26,11 +26,11 @@
26
26
  ],
27
27
  "sideEffects": false,
28
28
  "dependencies": {
29
- "@buoy-gg/floating-tools-core": "2.1.5",
30
- "@buoy-gg/shared-ui": "2.1.5"
29
+ "@buoy-gg/floating-tools-core": "2.1.8",
30
+ "@buoy-gg/shared-ui": "2.1.8"
31
31
  },
32
32
  "peerDependencies": {
33
- "@buoy-gg/license": "2.1.5",
33
+ "@buoy-gg/license": "2.1.8",
34
34
  "@react-native-async-storage/async-storage": ">=1.0.0",
35
35
  "react": "*",
36
36
  "react-native": "*"
@@ -40,7 +40,7 @@
40
40
  "@types/react": "^19.1.0",
41
41
  "@types/react-native": "^0.73.0",
42
42
  "typescript": "~5.8.3",
43
- "@buoy-gg/license": "2.1.5"
43
+ "@buoy-gg/license": "2.1.8"
44
44
  },
45
45
  "react-native-builder-bob": {
46
46
  "source": "src",