react-native-inapp-inspector 1.1.12 → 1.1.14

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 (64) hide show
  1. package/dist/commonjs/components/AnalyticsDetail.js +64 -21
  2. package/dist/commonjs/components/AnalyticsEventCard.js +100 -13
  3. package/dist/commonjs/components/HighlightText.js +1 -1
  4. package/dist/commonjs/components/JsonViewer.d.ts +3 -1
  5. package/dist/commonjs/components/JsonViewer.js +327 -9
  6. package/dist/commonjs/components/MetaAccordion.d.ts +10 -1
  7. package/dist/commonjs/components/MetaAccordion.js +121 -24
  8. package/dist/commonjs/components/NetworkIcons.d.ts +1 -0
  9. package/dist/commonjs/components/NetworkIcons.js +8 -1
  10. package/dist/commonjs/components/ReduxTreeView.d.ts +12 -3
  11. package/dist/commonjs/components/ReduxTreeView.js +1096 -278
  12. package/dist/commonjs/components/SectionHeader.d.ts +1 -1
  13. package/dist/commonjs/components/SectionHeader.js +7 -1
  14. package/dist/commonjs/constants/version.d.ts +1 -1
  15. package/dist/commonjs/constants/version.js +1 -1
  16. package/dist/commonjs/customHooks/analyticsLogger.d.ts +8 -0
  17. package/dist/commonjs/customHooks/analyticsLogger.js +42 -1
  18. package/dist/commonjs/customHooks/reduxLogger.d.ts +2 -0
  19. package/dist/commonjs/customHooks/reduxLogger.js +2 -0
  20. package/dist/commonjs/helpers/index.d.ts +1 -0
  21. package/dist/commonjs/helpers/index.js +15 -1
  22. package/dist/commonjs/helpers/settingsStore.d.ts +1 -0
  23. package/dist/commonjs/helpers/settingsStore.js +4 -0
  24. package/dist/commonjs/index.d.ts +1 -1
  25. package/dist/commonjs/index.js +347 -444
  26. package/dist/commonjs/styles/index.d.ts +92 -33
  27. package/dist/commonjs/styles/index.js +101 -45
  28. package/dist/commonjs/types/index.d.ts +3 -0
  29. package/dist/esm/components/AnalyticsDetail.js +64 -21
  30. package/dist/esm/components/AnalyticsEventCard.js +100 -13
  31. package/dist/esm/components/HighlightText.js +3 -3
  32. package/dist/esm/components/JsonViewer.d.ts +3 -1
  33. package/dist/esm/components/JsonViewer.js +295 -10
  34. package/dist/esm/components/MetaAccordion.d.ts +10 -1
  35. package/dist/esm/components/MetaAccordion.js +90 -26
  36. package/dist/esm/components/NetworkIcons.d.ts +1 -0
  37. package/dist/esm/components/NetworkIcons.js +6 -0
  38. package/dist/esm/components/ReduxTreeView.d.ts +12 -3
  39. package/dist/esm/components/ReduxTreeView.js +1095 -279
  40. package/dist/esm/components/SectionHeader.d.ts +1 -1
  41. package/dist/esm/components/SectionHeader.js +8 -2
  42. package/dist/esm/constants/version.d.ts +1 -1
  43. package/dist/esm/constants/version.js +1 -1
  44. package/dist/esm/customHooks/analyticsLogger.d.ts +8 -0
  45. package/dist/esm/customHooks/analyticsLogger.js +37 -0
  46. package/dist/esm/customHooks/reduxLogger.d.ts +2 -0
  47. package/dist/esm/customHooks/reduxLogger.js +2 -0
  48. package/dist/esm/helpers/index.d.ts +1 -0
  49. package/dist/esm/helpers/index.js +14 -1
  50. package/dist/esm/helpers/settingsStore.d.ts +1 -0
  51. package/dist/esm/helpers/settingsStore.js +3 -0
  52. package/dist/esm/index.d.ts +1 -1
  53. package/dist/esm/index.js +345 -446
  54. package/dist/esm/styles/index.d.ts +92 -33
  55. package/dist/esm/styles/index.js +101 -45
  56. package/dist/esm/types/index.d.ts +3 -0
  57. package/example/android/app/build.gradle +29 -1
  58. package/example/android/app/src/main/java/com/example/MainApplication.kt +25 -0
  59. package/example/android/build.gradle +31 -4
  60. package/example/android/gradle/wrapper/gradle-wrapper.properties +1 -1
  61. package/example/android/gradle.properties +1 -0
  62. package/example/android/settings.gradle +20 -1
  63. package/example/package-lock.json +1 -1
  64. package/package.json +1 -1
package/dist/esm/index.js CHANGED
@@ -26,7 +26,7 @@ import { formatDateTime, getStatusColor, getNavigationInfo, deduplicateLogs, get
26
26
  // #5 — settings persistence
27
27
  import { loadSettings, saveSettings, setCustomStorage, isPersistentStorageAvailable, clearPersistedSettings, } from './helpers/settingsStore';
28
28
  // Assets
29
- import { EmptyRadarIcon, FailIcon, SearchIcon, ScreenIcon, ClearIcon, SortArrowIcon, FilterIcon, InsightsIcon, GlobeIcon, DownloadIcon, CloseWhite, TrashIcon, WhiteBackNavigation, TerminalIcon, SignalIcon, AnalyticsIcon, SunIcon, MoonIcon, BrandCircleIcon, BrandSquareIcon, HtmlIcon, CssIcon, JsIcon, ClockIcon, EyeIcon, CheckIcon, SettingsIcon, RequestIcon, ResponseIcon, HeadersIcon, StatusIcon, ChevronIcon, WipeIcon, LayersIcon, UserIcon, InfoCircleIcon, WarningTriangleIcon, ErrorCircleIcon, TrendingUpIcon, MotionIcon, } from './components/NetworkIcons';
29
+ import { EmptyRadarIcon, FailIcon, SearchIcon, ScreenIcon, ClearIcon, SortArrowIcon, FilterIcon, InsightsIcon, GlobeIcon, DownloadIcon, CloseWhite, TrashIcon, WhiteBackNavigation, TerminalIcon, SignalIcon, AnalyticsIcon, SunIcon, MoonIcon, BrandCircleIcon, HtmlIcon, CssIcon, JsIcon, ClockIcon, EyeIcon, CheckIcon, SettingsIcon, RequestIcon, ResponseIcon, HeadersIcon, StatusIcon, ChevronIcon, WipeIcon, LayersIcon, UserIcon, InfoCircleIcon, WarningTriangleIcon, ErrorCircleIcon, MotionIcon, } from './components/NetworkIcons';
30
30
  import ErrorBoundary from './components/ErrorBoundary';
31
31
  // Stylesheet
32
32
  import { AppColors } from './styles/AppColors';
@@ -37,8 +37,8 @@ import { setupNetworkLogger, clearNetworkLogs, subscribeNetworkLogs, } from './c
37
37
  // Console
38
38
  import { setupConsoleLogger, clearConsoleLogs, subscribeConsoleLogs, } from './customHooks/consoleLogger';
39
39
  import { IGNORED_LOG_PREFIXES } from './customHooks/logFilters';
40
- import { subscribeAnalyticsEvents, clearAnalyticsEvents, autoSetupAnalyticsLogger, } from './customHooks/analyticsLogger';
41
- import AnalyticsEventCard, { getEventColor, } from './components/AnalyticsEventCard';
40
+ import { subscribeAnalyticsEvents, clearAnalyticsEvents, autoSetupAnalyticsLogger, getCurrentUserProperties, getCurrentUserId, getDefaultEventParameters, getCollectionEnabled, } from './customHooks/analyticsLogger';
41
+ import AnalyticsEventCard from './components/AnalyticsEventCard';
42
42
  import AnalyticsDetail from './components/AnalyticsDetail';
43
43
  import { getWebViewLogs, getWebViewNavHistory, getWebViewHtml, getWebViewCss, getWebViewJs, getWebViewHtmlUrl, clearWebViewData, subscribeWebView, } from './customHooks/webViewLogger';
44
44
  let OriginalWebView = null;
@@ -154,6 +154,7 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
154
154
  const [sectionFilters, setSectionFilters] = useState({});
155
155
  const [collapsedSections, setCollapsedSections] = useState(new Set());
156
156
  const [showNetworkMenu, setShowNetworkMenu] = useState(false);
157
+ const [showHeaderInfo, setShowHeaderInfo] = useState(false);
157
158
  const [showUiMenu, setShowUiMenu] = useState(false);
158
159
  const [sortOrder, setSortOrder] = useState('newest');
159
160
  // #7 — sort order for the Logs (console) tab
@@ -232,10 +233,10 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
232
233
  const [settingsActiveSubTab, setSettingsActiveSubTab] = useState('module');
233
234
  const [insightsActiveSubTab, setInsightsActiveSubTab] = useState('apis');
234
235
  const [tabVisibility, setTabVisibility] = useState({
235
- insights: true,
236
+ insights: false,
236
237
  apis: true,
237
238
  logs: true,
238
- analytics: false,
239
+ analytics: true,
239
240
  webview: false,
240
241
  redux: false,
241
242
  });
@@ -255,10 +256,10 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
255
256
  setModalHeightPercent(90);
256
257
  setModalAnimationType('slide');
257
258
  setTabVisibility({
258
- insights: true,
259
+ insights: false,
259
260
  apis: true,
260
261
  logs: true,
261
- analytics: false,
262
+ analytics: true,
262
263
  webview: false,
263
264
  redux: false,
264
265
  });
@@ -323,10 +324,10 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
323
324
  const dt = saved.defaultTab;
324
325
  const vis = {
325
326
  ...{
326
- insights: true,
327
+ insights: false,
327
328
  apis: true,
328
329
  logs: true,
329
- analytics: false,
330
+ analytics: true,
330
331
  webview: false,
331
332
  redux: false,
332
333
  },
@@ -436,7 +437,6 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
436
437
  });
437
438
  };
438
439
  const switchActiveTab = useCallback((key) => {
439
- animateNextLayout();
440
440
  setActiveTab(key);
441
441
  }, []);
442
442
  const navigateFromDashboard = (key) => {
@@ -490,8 +490,8 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
490
490
  const [selectedEvent, setSelectedEvent] = useState(null);
491
491
  const [analyticsSearch, setAnalyticsSearch] = useState('');
492
492
  const [hideScreenView, setHideScreenView] = useState(true);
493
- const [analyticsSubTab, setAnalyticsSubTab] = useState('ga_events');
494
- const [topEventsExpanded, setTopEventsExpanded] = useState(true);
493
+ const [isAnalyticsLayoutReady, setIsAnalyticsLayoutReady] = useState(false);
494
+ const [analyticsHeaderExpanded, setAnalyticsHeaderExpanded] = useState(false);
495
495
  const [newEventIds, setNewEventIds] = useState(new Set());
496
496
  const prevEventIdsRef = useRef(new Set());
497
497
  const [navState, setNavState] = useState(null);
@@ -687,6 +687,84 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
687
687
  }).start();
688
688
  }
689
689
  }, [newLogIds]);
690
+ const renderAnalyticsHeader = () => {
691
+ const userId = getCurrentUserId();
692
+ const userProperties = getCurrentUserProperties();
693
+ const defaultParams = getDefaultEventParameters();
694
+ const isTrackingEnabled = getCollectionEnabled();
695
+ const hasUserProps = Object.keys(userProperties).length > 0;
696
+ const hasDefaultParams = Object.keys(defaultParams).length > 0;
697
+ const totalEvents = filteredAnalyticsEvents.length;
698
+ return (<View style={styles.analyticsHeaderCard}>
699
+ <View style={[styles.analyticsHeaderTop, !analyticsHeaderExpanded && { marginBottom: 0 }]}>
700
+ <View style={{ flex: 1 }}>
701
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
702
+ <Text style={styles.analyticsHeaderTitle}>Session Info</Text>
703
+ <View style={[
704
+ styles.statusDot,
705
+ { backgroundColor: isTrackingEnabled ? AppColors.greenColor : AppColors.errorColor }
706
+ ]}/>
707
+ <Text style={styles.statusText}>
708
+ {isTrackingEnabled ? 'Active' : 'Paused'}
709
+ </Text>
710
+ </View>
711
+ <Text style={styles.analyticsHeaderSubtitle} numberOfLines={1}>
712
+ {userId ? `User ID: ${userId}` : 'Anonymous Session'}
713
+ </Text>
714
+ </View>
715
+
716
+ <TouchableOpacity style={styles.analyticsHeaderToggle} activeOpacity={0.7} onPress={() => {
717
+ animateNextLayout();
718
+ setAnalyticsHeaderExpanded(!analyticsHeaderExpanded);
719
+ }}>
720
+ <Text style={styles.analyticsHeaderToggleText}>
721
+ {analyticsHeaderExpanded ? 'Collapse' : 'Expand'}
722
+ </Text>
723
+ </TouchableOpacity>
724
+ </View>
725
+
726
+ {analyticsHeaderExpanded && (<>
727
+ {/* Stats Row */}
728
+ <View style={styles.analyticsStatsRow}>
729
+ <View style={styles.analyticsStatBox}>
730
+ <Text style={styles.analyticsStatValue}>{totalEvents}</Text>
731
+ <Text style={styles.analyticsStatLabel}>Events</Text>
732
+ </View>
733
+ <View style={styles.analyticsStatBox}>
734
+ <Text style={styles.analyticsStatValue}>
735
+ {Object.keys(userProperties).length}
736
+ </Text>
737
+ <Text style={styles.analyticsStatLabel}>User Props</Text>
738
+ </View>
739
+ <View style={styles.analyticsStatBox}>
740
+ <Text style={styles.analyticsStatValue}>
741
+ {Object.keys(defaultParams).length}
742
+ </Text>
743
+ <Text style={styles.analyticsStatLabel}>Defaults</Text>
744
+ </View>
745
+ </View>
746
+
747
+ {/* Details Section */}
748
+ {(hasUserProps || hasDefaultParams) && (<View style={styles.analyticsHeaderDetails}>
749
+ {hasUserProps && (<View style={{ marginBottom: 10 }}>
750
+ <Text style={styles.detailsGroupTitle}>Active User Properties</Text>
751
+ {Object.entries(userProperties).map(([k, v]) => (<View key={k} style={styles.detailsRow}>
752
+ <Text style={styles.detailsKey} selectable={true}>{k}</Text>
753
+ <Text style={styles.detailsValue} selectable={true}>{String(v)}</Text>
754
+ </View>))}
755
+ </View>)}
756
+
757
+ {hasDefaultParams && (<View>
758
+ <Text style={styles.detailsGroupTitle}>Default Event Parameters</Text>
759
+ {Object.entries(defaultParams).map(([k, v]) => (<View key={k} style={styles.detailsRow}>
760
+ <Text style={styles.detailsKey} selectable={true}>{k}</Text>
761
+ <Text style={styles.detailsValue} selectable={true}>{String(v)}</Text>
762
+ </View>))}
763
+ </View>)}
764
+ </View>)}
765
+ </>)}
766
+ </View>);
767
+ };
690
768
  // #6 — every time the inspector is opened, land on the chosen default tab.
691
769
  useEffect(() => {
692
770
  if (visible) {
@@ -855,6 +933,11 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
855
933
  unsubscribeRedux();
856
934
  };
857
935
  }, []);
936
+ useEffect(() => {
937
+ if (activeTab !== 'analytics') {
938
+ setIsAnalyticsLayoutReady(false);
939
+ }
940
+ }, [activeTab]);
858
941
  useEffect(() => {
859
942
  setReqExpanded(undefined);
860
943
  setResExpanded(undefined);
@@ -1198,15 +1281,6 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
1198
1281
  analytics: `${searchedLogs.filter(l => l.message.toLowerCase().includes('[analytics error]')).length}/${total}`,
1199
1282
  };
1200
1283
  }, [visibleConsoleLogs, logSearch]);
1201
- const topEventsArray = useMemo(() => {
1202
- const freq = {};
1203
- filteredAnalyticsEvents.forEach(e => {
1204
- if (e.name === 'screen_view')
1205
- return;
1206
- freq[e.name] = (freq[e.name] || 0) + 1;
1207
- });
1208
- return Object.entries(freq).sort((a, b) => b[1] - a[1]);
1209
- }, [filteredAnalyticsEvents]);
1210
1284
  function closeModal() {
1211
1285
  animateNextLayout();
1212
1286
  setVisible(false);
@@ -3645,16 +3719,9 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
3645
3719
  </Animated.View>
3646
3720
  </View>
3647
3721
  {(logs.length > 0 || analyticsEvents.length > 0) && (<Animated.View style={[
3648
- styles.fabBadge,
3649
- hasErrors ? styles.fabBadgeError : styles.fabBadgeNormal,
3650
- { transform: [{ scale: badgeAnim }] },
3651
- ]}>
3652
- <Text style={styles.fabBadgeText}>
3653
- {logs.length + analyticsEvents.length > 99
3654
- ? '99+'
3655
- : logs.length + analyticsEvents.length}
3656
- </Text>
3657
- </Animated.View>)}
3722
+ styles.fabGreenDot,
3723
+ { transform: [{ scale: unreadPulseAnim }] },
3724
+ ]}/>)}
3658
3725
  </TouchableScale>
3659
3726
  </Animated.View>
3660
3727
 
@@ -3711,16 +3778,16 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
3711
3778
  <WhiteBackNavigation />
3712
3779
  </TouchableScale>
3713
3780
 
3714
- {selected == null && selectedEvent == null ? (<View style={{
3781
+ {selected == null && selectedEvent == null ? (<TouchableScale onPress={() => setShowHeaderInfo(prev => !prev)} style={{
3715
3782
  flexDirection: 'row',
3716
3783
  alignItems: 'center',
3717
- gap: 14,
3784
+ gap: 10,
3718
3785
  flex: 1,
3719
3786
  }}>
3720
3787
  <View style={{
3721
- width: 50,
3722
- height: 50,
3723
- borderRadius: 10,
3788
+ width: 38,
3789
+ height: 38,
3790
+ borderRadius: 19,
3724
3791
  backgroundColor: 'rgba(255,255,255,0.13)',
3725
3792
  borderWidth: 1.5,
3726
3793
  borderColor: 'rgba(255,255,255,0.25)',
@@ -3731,17 +3798,13 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
3731
3798
  shadowRadius: 4,
3732
3799
  shadowOffset: { width: 0, height: 2 },
3733
3800
  }}>
3734
- <BrandSquareIcon size={45}/>
3801
+ <BrandCircleIcon size={34}/>
3735
3802
  </View>
3736
3803
  <View style={{ gap: 2, flex: 1 }}>
3737
3804
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
3738
3805
  <Text style={[styles.headerTitle]} numberOfLines={1}>
3739
- {getAppName()}{' '}
3740
- <Text style={{ fontFamily: AppFonts.interRegular, fontSize: 13, color: 'rgba(255,255,255,0.6)' }}>
3741
- (Inspector)
3742
- </Text>
3806
+ {getAppName()}
3743
3807
  </Text>
3744
- {/* #1 — pulsing dot when a newer version is on NPM */}
3745
3808
  {updateAvailable && (<Pressable hitSlop={10} onPress={() => Alert.alert('Update Available', `react-native-inapp-inspector v${latestNpmVersion} is available on NPM (installed: v${LIB_VERSION}).`, [
3746
3809
  { text: 'Later', style: 'cancel' },
3747
3810
  {
@@ -3764,202 +3827,224 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
3764
3827
  }}/>
3765
3828
  </Pressable>)}
3766
3829
  </View>
3767
- <Text style={{
3768
- fontFamily: AppFonts.interRegular,
3769
- fontSize: 10.5,
3770
- color: 'rgba(255, 255, 255, 0.6)',
3771
- marginTop: 1,
3772
- }} selectable={true} numberOfLines={1}>
3773
- {getBundleIdentifier()}
3774
- </Text>
3775
3830
  <View style={{
3776
3831
  flexDirection: 'row',
3777
3832
  alignItems: 'center',
3778
- gap: 6,
3779
- marginTop: 4,
3780
- }}>
3781
- {/* OS chip */}
3782
- <View style={{
3783
- flexDirection: 'row',
3784
- alignItems: 'center',
3785
- borderRadius: 6,
3786
- overflow: 'hidden',
3833
+ alignSelf: 'flex-start',
3834
+ backgroundColor: 'rgba(255,255,255,0.1)',
3835
+ borderRadius: 10,
3836
+ paddingHorizontal: 8,
3837
+ paddingVertical: 3,
3838
+ gap: 5,
3787
3839
  borderWidth: 1,
3788
- borderColor: 'rgba(255,255,255,0.18)',
3789
- }}>
3790
- <View style={{
3791
- paddingHorizontal: 5,
3792
- paddingVertical: 2,
3793
- backgroundColor: 'rgba(255,255,255,0.28)',
3794
- }}>
3795
- <Text style={{
3796
- fontFamily: AppFonts.interBold,
3797
- fontSize: 9,
3798
- color: '#FFFFFF',
3799
- letterSpacing: 0.3,
3800
- }}>
3801
- {Platform.OS === 'ios' ? 'iOS' : 'Android'}
3802
- </Text>
3803
- </View>
3804
- <View style={{
3805
- paddingHorizontal: 5,
3806
- paddingVertical: 2,
3807
- backgroundColor: 'rgba(255,255,255,0.12)',
3808
- }}>
3809
- <Text style={{
3810
- fontFamily: AppFonts.interMedium,
3811
- fontSize: 9.5,
3812
- color: 'rgba(255,255,255,0.92)',
3840
+ borderColor: 'rgba(255,255,255,0.15)',
3813
3841
  }}>
3814
- {String(Platform.Version)}
3815
- </Text>
3816
- </View>
3817
- </View>
3818
-
3819
- {/* npm chip */}
3820
3842
  <View style={{
3821
- flexDirection: 'row',
3822
- alignItems: 'center',
3823
- borderRadius: 6,
3824
- overflow: 'hidden',
3825
- borderWidth: 1,
3826
- borderColor: 'rgba(255,255,255,0.18)',
3843
+ width: 6,
3844
+ height: 6,
3845
+ borderRadius: 3,
3846
+ backgroundColor: '#4ADE80',
3847
+ }}/>
3848
+ <Text style={{
3849
+ fontFamily: AppFonts.interMedium,
3850
+ fontSize: 10,
3851
+ color: 'rgba(255, 255, 255, 0.85)',
3852
+ letterSpacing: 0.2,
3853
+ }} numberOfLines={1}>
3854
+ {getBundleIdentifier()}
3855
+ </Text>
3856
+ <Animated.View style={{
3857
+ transform: [{ rotate: showHeaderInfo ? '180deg' : '0deg' }],
3858
+ marginLeft: 2,
3827
3859
  }}>
3860
+ <ChevronIcon color="rgba(255,255,255,0.6)" size={12}/>
3861
+ </Animated.View>
3862
+ </View>
3863
+ {showHeaderInfo && (<View style={{
3864
+ flexDirection: 'row',
3865
+ alignItems: 'center',
3866
+ gap: 6,
3867
+ marginTop: 4,
3868
+ }}>
3828
3869
  <View style={{
3829
- paddingHorizontal: 5,
3830
- paddingVertical: 2,
3831
- backgroundColor: 'rgba(255,255,255,0.28)',
3832
- }}>
3833
- <Text style={{
3834
- fontFamily: AppFonts.interBold,
3835
- fontSize: 9,
3836
- color: '#FFFFFF',
3837
- letterSpacing: 0.3,
3838
- }}>
3839
- npm
3840
- </Text>
3870
+ flexDirection: 'row',
3871
+ alignItems: 'center',
3872
+ borderRadius: 6,
3873
+ overflow: 'hidden',
3874
+ borderWidth: 1,
3875
+ borderColor: 'rgba(255,255,255,0.18)',
3876
+ }}>
3877
+ <View style={{
3878
+ paddingHorizontal: 5,
3879
+ paddingVertical: 2,
3880
+ backgroundColor: 'rgba(255,255,255,0.28)',
3881
+ }}>
3882
+ <Text style={{
3883
+ fontFamily: AppFonts.interBold,
3884
+ fontSize: 9,
3885
+ color: '#FFFFFF',
3886
+ letterSpacing: 0.3,
3887
+ }}>
3888
+ {Platform.OS === 'ios' ? 'iOS' : 'Android'}
3889
+ </Text>
3890
+ </View>
3891
+ <View style={{
3892
+ paddingHorizontal: 5,
3893
+ paddingVertical: 2,
3894
+ backgroundColor: 'rgba(255,255,255,0.12)',
3895
+ }}>
3896
+ <Text style={{
3897
+ fontFamily: AppFonts.interMedium,
3898
+ fontSize: 9.5,
3899
+ color: 'rgba(255,255,255,0.92)',
3900
+ }}>
3901
+ {String(Platform.Version)}
3902
+ </Text>
3903
+ </View>
3841
3904
  </View>
3842
3905
  <View style={{
3843
- paddingHorizontal: 5,
3844
- paddingVertical: 2,
3845
- backgroundColor: 'rgba(255,255,255,0.12)',
3846
- }}>
3847
- <Text style={{
3848
- fontFamily: AppFonts.interMedium,
3849
- fontSize: 9.5,
3850
- color: 'rgba(255,255,255,0.92)',
3851
- }}>
3852
- v{LIB_VERSION}
3853
- </Text>
3906
+ flexDirection: 'row',
3907
+ alignItems: 'center',
3908
+ borderRadius: 6,
3909
+ overflow: 'hidden',
3910
+ borderWidth: 1,
3911
+ borderColor: 'rgba(255,255,255,0.18)',
3912
+ }}>
3913
+ <View style={{
3914
+ paddingHorizontal: 5,
3915
+ paddingVertical: 2,
3916
+ backgroundColor: 'rgba(255,255,255,0.28)',
3917
+ }}>
3918
+ <Text style={{
3919
+ fontFamily: AppFonts.interBold,
3920
+ fontSize: 9,
3921
+ color: '#FFFFFF',
3922
+ letterSpacing: 0.3,
3923
+ }}>
3924
+ npm
3925
+ </Text>
3926
+ </View>
3927
+ <View style={{
3928
+ paddingHorizontal: 5,
3929
+ paddingVertical: 2,
3930
+ backgroundColor: 'rgba(255,255,255,0.12)',
3931
+ }}>
3932
+ <Text style={{
3933
+ fontFamily: AppFonts.interMedium,
3934
+ fontSize: 9.5,
3935
+ color: 'rgba(255,255,255,0.92)',
3936
+ }}>
3937
+ v{LIB_VERSION}
3938
+ </Text>
3939
+ </View>
3854
3940
  </View>
3855
- </View>
3856
- </View>
3941
+ </View>)}
3857
3942
  </View>
3858
- </View>) : null}
3943
+ </TouchableScale>) : null}
3859
3944
  </View>
3860
3945
 
3861
- <View style={styles.headerCenter}>
3862
- {selected != null ? (<View style={styles.headerDetailCenter}>
3863
- <View style={styles.headerDetailRow}>
3864
- <View style={[
3865
- styles.headerMethodBadge,
3866
- {
3867
- backgroundColor: METHOD_COLORS[selected.method] ??
3868
- AppColors.grayText,
3869
- },
3870
- ]}>
3871
- <Text style={styles.headerMethodText}>
3872
- {selected.method}
3946
+ {(selected != null || selectedEvent != null) && (<View style={styles.headerCenter}>
3947
+ {selected != null ? (<View style={styles.headerDetailCenter}>
3948
+ <View style={styles.headerDetailRow}>
3949
+ <View style={[
3950
+ styles.headerMethodBadge,
3951
+ {
3952
+ backgroundColor: METHOD_COLORS[selected.method] ??
3953
+ AppColors.grayText,
3954
+ },
3955
+ ]}>
3956
+ <Text style={styles.headerMethodText}>
3957
+ {selected.method}
3958
+ </Text>
3959
+ </View>
3960
+ <Text style={styles.headerDetailTitle} numberOfLines={1} ellipsizeMode="middle">
3961
+ {detailTitle}
3873
3962
  </Text>
3874
3963
  </View>
3875
- <Text style={styles.headerDetailTitle} numberOfLines={1} ellipsizeMode="middle">
3876
- {detailTitle}
3877
- </Text>
3878
- </View>
3879
- <View style={styles.headerDetailSubRow}>
3880
- <View style={{
3881
- flexDirection: 'row',
3882
- alignItems: 'center',
3883
- gap: 5,
3884
- paddingHorizontal: 8,
3885
- paddingVertical: 3,
3886
- borderRadius: 20,
3887
- backgroundColor: `${getStatusColor(selected.status)}26`,
3888
- borderWidth: 1,
3889
- borderColor: `${getStatusColor(selected.status)}55`,
3890
- }}>
3964
+ <View style={styles.headerDetailSubRow}>
3965
+ <View style={{
3966
+ flexDirection: 'row',
3967
+ alignItems: 'center',
3968
+ gap: 5,
3969
+ paddingHorizontal: 8,
3970
+ paddingVertical: 3,
3971
+ borderRadius: 20,
3972
+ backgroundColor: `${getStatusColor(selected.status)}26`,
3973
+ borderWidth: 1,
3974
+ borderColor: `${getStatusColor(selected.status)}55`,
3975
+ }}>
3976
+ <View style={[
3977
+ styles.headerStatusDot,
3978
+ {
3979
+ backgroundColor: getStatusColor(selected.status),
3980
+ },
3981
+ ]}/>
3982
+ <Text style={[
3983
+ styles.headerSubTitle,
3984
+ { fontFamily: AppFonts.interBold },
3985
+ ]}>
3986
+ {selected.status === 0
3987
+ ? 'Failed'
3988
+ : selected.status ?? 'Pending'}
3989
+ </Text>
3990
+ </View>
3991
+ <View style={{
3992
+ flexDirection: 'row',
3993
+ alignItems: 'center',
3994
+ gap: 4,
3995
+ paddingHorizontal: 8,
3996
+ paddingVertical: 3,
3997
+ borderRadius: 20,
3998
+ backgroundColor: 'rgba(255,255,255,0.16)',
3999
+ }}>
4000
+ <ClockIcon color="#FFFFFF" size={11}/>
4001
+ <Text style={styles.headerSubTitle}>
4002
+ {selected.duration != null
4003
+ ? `${selected.duration}ms`
4004
+ : '—'}
4005
+ </Text>
4006
+ </View>
4007
+ </View>
4008
+ </View>) : selectedEvent != null ? (<View style={styles.headerDetailCenter}>
4009
+ <View style={styles.headerDetailRow}>
3891
4010
  <View style={[
3892
- styles.headerStatusDot,
3893
- {
3894
- backgroundColor: getStatusColor(selected.status),
3895
- },
3896
- ]}/>
3897
- <Text style={[
3898
- styles.headerSubTitle,
3899
- { fontFamily: AppFonts.interBold },
3900
- ]}>
3901
- {selected.status === 0
3902
- ? 'Failed'
3903
- : selected.status ?? 'Pending'}
4011
+ styles.headerMethodBadge,
4012
+ {
4013
+ backgroundColor: selectedEvent.source === 'firebase'
4014
+ ? 'rgba(224,123,26,0.3)'
4015
+ : 'rgba(124,92,191,0.3)',
4016
+ },
4017
+ ]}>
4018
+ <Text style={styles.headerMethodText}>
4019
+ {selectedEvent.source === 'firebase'
4020
+ ? 'FB'
4021
+ : 'MAN'}
4022
+ </Text>
4023
+ </View>
4024
+ <Text style={styles.headerDetailTitle} numberOfLines={1} ellipsizeMode="middle">
4025
+ {selectedEvent.name}
3904
4026
  </Text>
3905
4027
  </View>
3906
- <View style={{
3907
- flexDirection: 'row',
3908
- alignItems: 'center',
3909
- gap: 4,
3910
- paddingHorizontal: 8,
3911
- paddingVertical: 3,
3912
- borderRadius: 20,
3913
- backgroundColor: 'rgba(255,255,255,0.16)',
3914
- }}>
3915
- <ClockIcon color="#FFFFFF" size={11}/>
4028
+ <View style={styles.headerDetailSubRow}>
4029
+ <View style={[
4030
+ styles.headerStatusDot,
4031
+ {
4032
+ backgroundColor: selectedEvent.source === 'firebase'
4033
+ ? '#E07B1A'
4034
+ : AppColors.purple,
4035
+ },
4036
+ ]}/>
3916
4037
  <Text style={styles.headerSubTitle}>
3917
- {selected.duration != null
3918
- ? `${selected.duration}ms`
3919
- : '—'}
4038
+ {Object.keys(selectedEvent.params).length} param
4039
+ {Object.keys(selectedEvent.params).length !== 1
4040
+ ? 's'
4041
+ : ''}
4042
+ {' · '}
4043
+ {selectedEvent.source}
3920
4044
  </Text>
3921
4045
  </View>
3922
- </View>
3923
- </View>) : selectedEvent != null ? (<View style={styles.headerDetailCenter}>
3924
- <View style={styles.headerDetailRow}>
3925
- <View style={[
3926
- styles.headerMethodBadge,
3927
- {
3928
- backgroundColor: selectedEvent.source === 'firebase'
3929
- ? 'rgba(224,123,26,0.3)'
3930
- : 'rgba(124,92,191,0.3)',
3931
- },
3932
- ]}>
3933
- <Text style={styles.headerMethodText}>
3934
- {selectedEvent.source === 'firebase'
3935
- ? 'FB'
3936
- : 'MAN'}
3937
- </Text>
3938
- </View>
3939
- <Text style={styles.headerDetailTitle} numberOfLines={1} ellipsizeMode="middle">
3940
- {selectedEvent.name}
3941
- </Text>
3942
- </View>
3943
- <View style={styles.headerDetailSubRow}>
3944
- <View style={[
3945
- styles.headerStatusDot,
3946
- {
3947
- backgroundColor: selectedEvent.source === 'firebase'
3948
- ? '#E07B1A'
3949
- : AppColors.purple,
3950
- },
3951
- ]}/>
3952
- <Text style={styles.headerSubTitle}>
3953
- {Object.keys(selectedEvent.params).length} param
3954
- {Object.keys(selectedEvent.params).length !== 1
3955
- ? 's'
3956
- : ''}
3957
- {' · '}
3958
- {selectedEvent.source}
3959
- </Text>
3960
- </View>
3961
- </View>) : null}
3962
- </View>
4046
+ </View>) : null}
4047
+ </View>)}
3963
4048
 
3964
4049
  <View style={[
3965
4050
  styles.headerRight,
@@ -4127,7 +4212,7 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
4127
4212
  {/* ─── Search + Shared Toolbar for Analytics ──────────────────────── */}
4128
4213
  <View style={[
4129
4214
  styles.toolbarRow,
4130
- { marginTop: 12, marginBottom: 4 },
4215
+ { marginTop: 12, marginBottom: 8 },
4131
4216
  ]}>
4132
4217
  <View style={styles.searchContainer}>
4133
4218
  <SearchIcon color={AppColors.grayTextWeak} size={16}/>
@@ -4136,194 +4221,24 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
4136
4221
  <ClearIcon color={AppColors.grayTextWeak} size={14}/>
4137
4222
  </Pressable>)}
4138
4223
  </View>
4139
- {analyticsSubTab === 'ga_events' && (<View style={styles.toolbarRight}>
4140
- <TouchableScale style={{
4141
- flexDirection: 'row',
4142
- alignItems: 'center',
4143
- gap: 4,
4144
- paddingHorizontal: 10,
4145
- paddingVertical: 5,
4146
- borderRadius: 8,
4147
- backgroundColor: 'rgba(255,46,87,0.06)',
4148
- borderWidth: 1,
4149
- borderColor: 'rgba(255,46,87,0.15)',
4150
- }} onPress={handleDelete} hitSlop={6}>
4151
- <WipeIcon color={AppColors.errorColor} size={13}/>
4152
- <Text style={{
4153
- fontFamily: AppFonts.interBold,
4154
- fontSize: 10.5,
4155
- color: AppColors.errorColor,
4156
- }}>
4157
- Clear
4158
- </Text>
4159
- </TouchableScale>
4160
- </View>)}
4161
- </View>
4162
-
4163
- {/* ─── Secondary Tab Bar for Analytics ──────────────────────── */}
4164
- <View style={{
4165
- marginHorizontal: 16,
4166
- marginTop: 4,
4167
- marginBottom: 8,
4168
- backgroundColor: AppColors.grayBackground,
4169
- borderRadius: 8,
4170
- padding: 4,
4171
- flexDirection: 'row',
4172
- borderWidth: 1,
4173
- borderColor: AppColors.grayBorderSecondary,
4174
- }}>
4175
- <Pressable style={[
4176
- {
4177
- flex: 1,
4178
- paddingVertical: 8,
4179
- borderRadius: 6,
4180
- alignItems: 'center',
4181
- },
4182
- analyticsSubTab === 'ga_events' && {
4183
- backgroundColor: AppColors.primaryLight,
4184
- shadowColor: '#000',
4185
- shadowOpacity: 0.1,
4186
- shadowRadius: 3,
4187
- shadowOffset: { width: 0, height: 1 },
4188
- elevation: 2,
4189
- },
4190
- ]} onPress={() => {
4191
- animateNextLayout();
4192
- setAnalyticsSubTab('ga_events');
4193
- }}>
4194
- <View style={{
4195
- flexDirection: 'row',
4196
- alignItems: 'center',
4197
- gap: 6,
4198
- }}>
4199
- {/* #7 */}
4200
- <AnalyticsIcon size={13} color={analyticsSubTab === 'ga_events'
4201
- ? AppColors.purple
4202
- : AppColors.grayTextStrong}/>
4203
- <Text style={[
4204
- {
4205
- fontFamily: AppFonts.interMedium,
4206
- fontSize: 13,
4207
- color: AppColors.grayTextStrong,
4208
- },
4209
- analyticsSubTab === 'ga_events' && {
4210
- fontFamily: AppFonts.interBold,
4211
- color: AppColors.purple,
4212
- },
4213
- ]}>
4214
- GA Events (
4215
- {analyticsSearch
4216
- ? filteredAnalyticsEvents.length
4217
- : analyticsEvents.length}
4218
- )
4219
- </Text>
4220
- </View>
4221
- </Pressable>
4222
- <Pressable style={[
4223
- {
4224
- flex: 1,
4225
- paddingVertical: 8,
4226
- borderRadius: 6,
4227
- alignItems: 'center',
4228
- },
4229
- analyticsSubTab === 'top_events' && {
4230
- backgroundColor: AppColors.primaryLight,
4231
- shadowColor: '#000',
4232
- shadowOpacity: 0.1,
4233
- shadowRadius: 3,
4234
- shadowOffset: { width: 0, height: 1 },
4235
- elevation: 2,
4236
- },
4237
- ]} onPress={() => {
4238
- animateNextLayout();
4239
- setAnalyticsSubTab('top_events');
4240
- }}>
4241
- <View style={{
4242
- flexDirection: 'row',
4243
- alignItems: 'center',
4244
- gap: 6,
4245
- }}>
4246
- {/* #7 */}
4247
- <TrendingUpIcon size={13} color={analyticsSubTab === 'top_events'
4248
- ? AppColors.purple
4249
- : AppColors.grayTextStrong}/>
4250
- <Text style={[
4224
+ <View style={styles.toolbarRight}>
4225
+ <TouchableScale style={[
4226
+ styles.toolbarBtn,
4251
4227
  {
4252
- fontFamily: AppFonts.interMedium,
4253
- fontSize: 13,
4254
- color: AppColors.grayTextStrong,
4255
- },
4256
- analyticsSubTab === 'top_events' && {
4257
- fontFamily: AppFonts.interBold,
4258
- color: AppColors.purple,
4228
+ borderColor: 'rgba(255,46,87,0.22)',
4229
+ backgroundColor: 'rgba(255,46,87,0.06)',
4259
4230
  },
4260
- ]}>
4261
- Top Events ({topEventsArray.length})
4262
- </Text>
4263
- </View>
4264
- </Pressable>
4231
+ ]} onPress={handleDelete} hitSlop={6}>
4232
+ <WipeIcon color={AppColors.errorColor} size={15}/>
4233
+ </TouchableScale>
4234
+ </View>
4265
4235
  </View>
4266
4236
  </View>)}
4267
4237
 
4268
4238
  {isReady ? (activeTab === 'insights' ? (<ScrollView style={styles.insightsContainer} contentContainerStyle={styles.insightsContent} showsVerticalScrollIndicator={false}>
4269
4239
  {renderInsightsDashboard()}
4270
- </ScrollView>) : activeTab === 'analytics' ? (<View style={{ flex: 1 }}>
4271
- {selectedEvent != null ? (<AnalyticsDetail event={selectedEvent}/>) : analyticsSubTab === 'top_events' ? (<FlatList data={topEventsArray} keyExtractor={item => item[0]} contentContainerStyle={[
4272
- styles.listContent,
4273
- { paddingHorizontal: 16, paddingTop: 12 },
4274
- ]} renderItem={({ item: [name, count], index }) => {
4275
- const maxCount = topEventsArray[0]?.[1] || 1;
4276
- const pct = Math.max(6, (count / maxCount) * 100);
4277
- const color = getEventColor(name);
4278
- return (<AnimatedEntrance index={index} distance={8} style={[
4279
- styles.analyticsTopEventsCard,
4280
- { marginBottom: 6, paddingVertical: 10, paddingHorizontal: 12 },
4281
- ]}>
4282
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>
4283
- <Text style={{
4284
- fontFamily: AppFonts.interBold,
4285
- fontSize: 11,
4286
- color: color,
4287
- width: 18,
4288
- textAlign: 'center',
4289
- }}>
4290
- {index + 1}
4291
- </Text>
4292
- <View style={{ flex: 1, gap: 4 }}>
4293
- <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
4294
- <Text style={{
4295
- fontFamily: AppFonts.interMedium,
4296
- fontSize: 12.5,
4297
- color: AppColors.primaryBlack,
4298
- flex: 1,
4299
- }} numberOfLines={1}>
4300
- {name}
4301
- </Text>
4302
- <Text style={{
4303
- fontFamily: AppFonts.interBold,
4304
- fontSize: 12,
4305
- color: color,
4306
- marginLeft: 8,
4307
- }}>
4308
- {count}
4309
- </Text>
4310
- </View>
4311
- <View style={{ height: 3, backgroundColor: AppColors.grayBackground, borderRadius: 2, overflow: 'hidden' }}>
4312
- <LinearGradient colors={[color, `${color}88`]} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={{
4313
- height: '100%',
4314
- borderRadius: 2,
4315
- width: `${pct}%`,
4316
- }}/>
4317
- </View>
4318
- </View>
4319
- </View>
4320
- </AnimatedEntrance>);
4321
- }} ListEmptyComponent={<View style={styles.emptyContainer}>
4322
- <View style={styles.emptyIconWrap}>
4323
- <EmptyRadarIcon color={AppColors.purple} size={32}/>
4324
- </View>
4325
- <Text style={styles.emptyTitle}>No Top Events</Text>
4326
- </View>}/>) : (<FlatList data={filteredAnalyticsEvents} keyExtractor={item => item.id.toString()} renderItem={({ item, index }) => {
4240
+ </ScrollView>) : activeTab === 'analytics' ? (<View style={{ flex: 1 }} onLayout={() => setIsAnalyticsLayoutReady(true)}>
4241
+ {selectedEvent != null ? (<AnalyticsDetail event={selectedEvent}/>) : isAnalyticsLayoutReady ? (<FlatList data={filteredAnalyticsEvents} keyExtractor={(item, index) => item?.id?.toString() ?? index.toString()} ListHeaderComponent={renderAnalyticsHeader} renderItem={({ item, index }) => {
4327
4242
  const prev = filteredAnalyticsEvents[index + 1];
4328
4243
  const next = filteredAnalyticsEvents[index - 1];
4329
4244
  const msSincePrev = prev
@@ -4334,12 +4249,13 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
4334
4249
  ? Math.floor(next.timestamp / 60000)
4335
4250
  : -1;
4336
4251
  const showTimestamp = index === 0 || thisMin !== nextMin;
4337
- return (<AnimatedEntrance index={index} distance={8}>
4338
- <AnalyticsEventCard event={item} onPress={() => {
4252
+ return (<AnalyticsEventCard event={item} onPress={() => {
4339
4253
  animateNextLayout();
4340
4254
  setSelectedEvent(item);
4341
4255
  }} isNew={newEventIds.has(item.id)} searchStr={analyticsSearch} isFirst={index === 0} isLast={index === filteredAnalyticsEvents.length - 1} msSincePrev={msSincePrev} showTimestamp={showTimestamp} computedScreenName={(() => {
4342
- let screenName = item.screenName ||
4256
+ if (!item)
4257
+ return '';
4258
+ const rawScreenName = item.screenName ||
4343
4259
  item.screenClass ||
4344
4260
  item.pageTitle ||
4345
4261
  item.pageLocation ||
@@ -4347,6 +4263,9 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
4347
4263
  item.params?.screen_name ||
4348
4264
  item.params?.firebase_screen_class ||
4349
4265
  item.params?.screen_class;
4266
+ let screenName = typeof rawScreenName === 'string'
4267
+ ? rawScreenName
4268
+ : (rawScreenName ? JSON.stringify(rawScreenName) : '');
4350
4269
  const routeInfo = logRouteMapRef.current.get(item.id + 1000000);
4351
4270
  if (!screenName) {
4352
4271
  if (routeInfo &&
@@ -4356,8 +4275,7 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
4356
4275
  }
4357
4276
  }
4358
4277
  return screenName;
4359
- })()}/>
4360
- </AnimatedEntrance>);
4278
+ })()}/>);
4361
4279
  }} initialNumToRender={20} maxToRenderPerBatch={20} windowSize={5} removeClippedSubviews={false} ListEmptyComponent={<View style={styles.emptyContainer}>
4362
4280
  <View style={styles.emptyIconWrap}>
4363
4281
  <EmptyRadarIcon color={AppColors.purple} size={32}/>
@@ -4377,7 +4295,7 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
4377
4295
  filteredAnalyticsEvents.length === 0 && {
4378
4296
  flexGrow: 1,
4379
4297
  },
4380
- ]} keyboardShouldPersistTaps="handled"/>)}
4298
+ ]} keyboardShouldPersistTaps="handled"/>) : null}
4381
4299
  </View>) : activeTab === 'apis' && selected == null ? (<View style={{ flex: 1 }}>
4382
4300
  <FlatList ref={apisListRef} data={groupedData} keyExtractor={item => item?.id?.toString()} renderItem={renderItem} initialNumToRender={10} maxToRenderPerBatch={10} windowSize={5} removeClippedSubviews={true} ListHeaderComponent={<View style={{ marginTop: 8 }}>
4383
4301
  <View style={styles.toolbarRow}>
@@ -4390,27 +4308,19 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
4390
4308
  </View>
4391
4309
 
4392
4310
  <View style={styles.toolbarRight}>
4393
- <TouchableScale style={{
4394
- flexDirection: 'row',
4395
- alignItems: 'center',
4396
- gap: 4,
4397
- paddingHorizontal: 10,
4398
- paddingVertical: 5,
4399
- borderRadius: 8,
4400
- backgroundColor: 'rgba(255,46,87,0.06)',
4401
- borderWidth: 1,
4402
- borderColor: 'rgba(255,46,87,0.15)',
4403
- }} onPress={handleDelete} hitSlop={6}>
4404
- <WipeIcon color={AppColors.errorColor} size={13}/>
4405
- <Text style={{
4406
- fontFamily: AppFonts.interBold,
4407
- fontSize: 10.5,
4408
- color: AppColors.errorColor,
4409
- }}>
4410
- {selectedLogs.size > 0
4411
- ? `Delete (${selectedLogs.size})`
4412
- : 'Clear'}
4413
- </Text>
4311
+ <TouchableScale style={[
4312
+ styles.toolbarBtn,
4313
+ {
4314
+ borderColor: 'rgba(255,46,87,0.22)',
4315
+ backgroundColor: 'rgba(255,46,87,0.06)',
4316
+ },
4317
+ ]} onPress={handleDelete} hitSlop={6}>
4318
+ <WipeIcon color={AppColors.errorColor} size={15}/>
4319
+ {selectedLogs.size > 0 && (<View style={styles.trashBadge}>
4320
+ <Text style={styles.trashBadgeText}>
4321
+ {selectedLogs.size}
4322
+ </Text>
4323
+ </View>)}
4414
4324
  </TouchableScale>
4415
4325
 
4416
4326
  <TouchableScale style={styles.toolbarBtn} onPress={() => setSortOrder(o => o === 'newest' ? 'oldest' : 'newest')} hitSlop={10}>
@@ -4578,25 +4488,14 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
4578
4488
  <TouchableScale style={styles.toolbarBtn} onPress={() => setLogSortOrder(o => o === 'newest' ? 'oldest' : 'newest')} hitSlop={10}>
4579
4489
  <SortArrowIcon color={AppColors.grayTextStrong} size={18} direction={logSortOrder === 'newest' ? 'down' : 'up'}/>
4580
4490
  </TouchableScale>
4581
- <TouchableScale style={{
4582
- flexDirection: 'row',
4583
- alignItems: 'center',
4584
- gap: 4,
4585
- paddingHorizontal: 10,
4586
- paddingVertical: 5,
4587
- borderRadius: 8,
4588
- backgroundColor: 'rgba(255,46,87,0.06)',
4589
- borderWidth: 1,
4590
- borderColor: 'rgba(255,46,87,0.15)',
4591
- }} onPress={handleDelete} hitSlop={6}>
4592
- <WipeIcon color={AppColors.errorColor} size={13}/>
4593
- <Text style={{
4594
- fontFamily: AppFonts.interBold,
4595
- fontSize: 10.5,
4596
- color: AppColors.errorColor,
4597
- }}>
4598
- Clear
4599
- </Text>
4491
+ <TouchableScale style={[
4492
+ styles.toolbarBtn,
4493
+ {
4494
+ borderColor: 'rgba(255,46,87,0.22)',
4495
+ backgroundColor: 'rgba(255,46,87,0.06)',
4496
+ },
4497
+ ]} onPress={handleDelete} hitSlop={6}>
4498
+ <WipeIcon color={AppColors.errorColor} size={15}/>
4600
4499
  </TouchableScale>
4601
4500
  </View>
4602
4501
  </View>
@@ -4867,7 +4766,7 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
4867
4766
  </ScrollView>
4868
4767
  </View>
4869
4768
 
4870
- <FlatList data={filteredConsoleLogs} keyExtractor={item => item.id.toString()} ListHeaderComponent={(() => {
4769
+ <FlatList data={filteredConsoleLogs} keyExtractor={(item, index) => item?.id?.toString() ?? index.toString()} ListHeaderComponent={(() => {
4871
4770
  const total = visibleConsoleLogs.length;
4872
4771
  const filtered = filteredConsoleLogs.length;
4873
4772
  const isAllSelected = logFilters.has('all') ||
@@ -5930,7 +5829,7 @@ export default NetworkInspectorWrapper;
5930
5829
  // Re-export public APIs
5931
5830
  export { setupNetworkLogger, clearNetworkLogs, subscribeNetworkLogs, addAxiosInterceptors, } from './customHooks/networkLogger';
5932
5831
  export { setupConsoleLogger, clearConsoleLogs, subscribeConsoleLogs, } from './customHooks/consoleLogger';
5933
- export { setupAnalyticsLogger, logAnalyticsEvent, subscribeAnalyticsEvents, clearAnalyticsEvents, } from './customHooks/analyticsLogger';
5832
+ export { setupAnalyticsLogger, logAnalyticsEvent, subscribeAnalyticsEvents, clearAnalyticsEvents, getCurrentUserProperties, getCurrentUserId, getDefaultEventParameters, getCollectionEnabled, } from './customHooks/analyticsLogger';
5934
5833
  export { WebView, getWebViewLogs, getWebViewNavHistory, getWebViewHtml, getWebViewCss, getWebViewJs, getWebViewHtmlUrl, clearWebViewData, subscribeWebView, } from './customHooks/webViewLogger';
5935
5834
  export { default as ErrorBoundary } from './components/ErrorBoundary';
5936
5835
  export { connectReduxStore, inspectorReduxMiddleware, getReduxState, subscribeReduxState, getActionHistory, clearActionHistory, } from './customHooks/reduxLogger';