react-native-inapp-inspector 1.1.13 → 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.
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, 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;
@@ -233,10 +233,10 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
233
233
  const [settingsActiveSubTab, setSettingsActiveSubTab] = useState('module');
234
234
  const [insightsActiveSubTab, setInsightsActiveSubTab] = useState('apis');
235
235
  const [tabVisibility, setTabVisibility] = useState({
236
- insights: true,
236
+ insights: false,
237
237
  apis: true,
238
238
  logs: true,
239
- analytics: false,
239
+ analytics: true,
240
240
  webview: false,
241
241
  redux: false,
242
242
  });
@@ -256,10 +256,10 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
256
256
  setModalHeightPercent(90);
257
257
  setModalAnimationType('slide');
258
258
  setTabVisibility({
259
- insights: true,
259
+ insights: false,
260
260
  apis: true,
261
261
  logs: true,
262
- analytics: false,
262
+ analytics: true,
263
263
  webview: false,
264
264
  redux: false,
265
265
  });
@@ -324,10 +324,10 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
324
324
  const dt = saved.defaultTab;
325
325
  const vis = {
326
326
  ...{
327
- insights: true,
327
+ insights: false,
328
328
  apis: true,
329
329
  logs: true,
330
- analytics: false,
330
+ analytics: true,
331
331
  webview: false,
332
332
  redux: false,
333
333
  },
@@ -437,7 +437,6 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
437
437
  });
438
438
  };
439
439
  const switchActiveTab = useCallback((key) => {
440
- animateNextLayout();
441
440
  setActiveTab(key);
442
441
  }, []);
443
442
  const navigateFromDashboard = (key) => {
@@ -491,8 +490,8 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
491
490
  const [selectedEvent, setSelectedEvent] = useState(null);
492
491
  const [analyticsSearch, setAnalyticsSearch] = useState('');
493
492
  const [hideScreenView, setHideScreenView] = useState(true);
494
- const [analyticsSubTab, setAnalyticsSubTab] = useState('ga_events');
495
- const [topEventsExpanded, setTopEventsExpanded] = useState(true);
493
+ const [isAnalyticsLayoutReady, setIsAnalyticsLayoutReady] = useState(false);
494
+ const [analyticsHeaderExpanded, setAnalyticsHeaderExpanded] = useState(false);
496
495
  const [newEventIds, setNewEventIds] = useState(new Set());
497
496
  const prevEventIdsRef = useRef(new Set());
498
497
  const [navState, setNavState] = useState(null);
@@ -688,6 +687,84 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
688
687
  }).start();
689
688
  }
690
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
+ };
691
768
  // #6 — every time the inspector is opened, land on the chosen default tab.
692
769
  useEffect(() => {
693
770
  if (visible) {
@@ -856,6 +933,11 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
856
933
  unsubscribeRedux();
857
934
  };
858
935
  }, []);
936
+ useEffect(() => {
937
+ if (activeTab !== 'analytics') {
938
+ setIsAnalyticsLayoutReady(false);
939
+ }
940
+ }, [activeTab]);
859
941
  useEffect(() => {
860
942
  setReqExpanded(undefined);
861
943
  setResExpanded(undefined);
@@ -1199,15 +1281,6 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
1199
1281
  analytics: `${searchedLogs.filter(l => l.message.toLowerCase().includes('[analytics error]')).length}/${total}`,
1200
1282
  };
1201
1283
  }, [visibleConsoleLogs, logSearch]);
1202
- const topEventsArray = useMemo(() => {
1203
- const freq = {};
1204
- filteredAnalyticsEvents.forEach(e => {
1205
- if (e.name === 'screen_view')
1206
- return;
1207
- freq[e.name] = (freq[e.name] || 0) + 1;
1208
- });
1209
- return Object.entries(freq).sort((a, b) => b[1] - a[1]);
1210
- }, [filteredAnalyticsEvents]);
1211
1284
  function closeModal() {
1212
1285
  animateNextLayout();
1213
1286
  setVisible(false);
@@ -3646,16 +3719,9 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
3646
3719
  </Animated.View>
3647
3720
  </View>
3648
3721
  {(logs.length > 0 || analyticsEvents.length > 0) && (<Animated.View style={[
3649
- styles.fabBadge,
3650
- hasErrors ? styles.fabBadgeError : styles.fabBadgeNormal,
3651
- { transform: [{ scale: badgeAnim }] },
3652
- ]}>
3653
- <Text style={styles.fabBadgeText}>
3654
- {logs.length + analyticsEvents.length > 99
3655
- ? '99+'
3656
- : logs.length + analyticsEvents.length}
3657
- </Text>
3658
- </Animated.View>)}
3722
+ styles.fabGreenDot,
3723
+ { transform: [{ scale: unreadPulseAnim }] },
3724
+ ]}/>)}
3659
3725
  </TouchableScale>
3660
3726
  </Animated.View>
3661
3727
 
@@ -3877,108 +3943,108 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
3877
3943
  </TouchableScale>) : null}
3878
3944
  </View>
3879
3945
 
3880
- <View style={styles.headerCenter}>
3881
- {selected != null ? (<View style={styles.headerDetailCenter}>
3882
- <View style={styles.headerDetailRow}>
3883
- <View style={[
3884
- styles.headerMethodBadge,
3885
- {
3886
- backgroundColor: METHOD_COLORS[selected.method] ??
3887
- AppColors.grayText,
3888
- },
3889
- ]}>
3890
- <Text style={styles.headerMethodText}>
3891
- {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}
3892
3962
  </Text>
3893
3963
  </View>
3894
- <Text style={styles.headerDetailTitle} numberOfLines={1} ellipsizeMode="middle">
3895
- {detailTitle}
3896
- </Text>
3897
- </View>
3898
- <View style={styles.headerDetailSubRow}>
3899
- <View style={{
3900
- flexDirection: 'row',
3901
- alignItems: 'center',
3902
- gap: 5,
3903
- paddingHorizontal: 8,
3904
- paddingVertical: 3,
3905
- borderRadius: 20,
3906
- backgroundColor: `${getStatusColor(selected.status)}26`,
3907
- borderWidth: 1,
3908
- borderColor: `${getStatusColor(selected.status)}55`,
3909
- }}>
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}>
3910
4010
  <View style={[
3911
- styles.headerStatusDot,
3912
- {
3913
- backgroundColor: getStatusColor(selected.status),
3914
- },
3915
- ]}/>
3916
- <Text style={[
3917
- styles.headerSubTitle,
3918
- { fontFamily: AppFonts.interBold },
3919
- ]}>
3920
- {selected.status === 0
3921
- ? 'Failed'
3922
- : 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}
3923
4026
  </Text>
3924
4027
  </View>
3925
- <View style={{
3926
- flexDirection: 'row',
3927
- alignItems: 'center',
3928
- gap: 4,
3929
- paddingHorizontal: 8,
3930
- paddingVertical: 3,
3931
- borderRadius: 20,
3932
- backgroundColor: 'rgba(255,255,255,0.16)',
3933
- }}>
3934
- <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
+ ]}/>
3935
4037
  <Text style={styles.headerSubTitle}>
3936
- {selected.duration != null
3937
- ? `${selected.duration}ms`
3938
- : ''}
4038
+ {Object.keys(selectedEvent.params).length} param
4039
+ {Object.keys(selectedEvent.params).length !== 1
4040
+ ? 's'
4041
+ : ''}
4042
+ {' · '}
4043
+ {selectedEvent.source}
3939
4044
  </Text>
3940
4045
  </View>
3941
- </View>
3942
- </View>) : selectedEvent != null ? (<View style={styles.headerDetailCenter}>
3943
- <View style={styles.headerDetailRow}>
3944
- <View style={[
3945
- styles.headerMethodBadge,
3946
- {
3947
- backgroundColor: selectedEvent.source === 'firebase'
3948
- ? 'rgba(224,123,26,0.3)'
3949
- : 'rgba(124,92,191,0.3)',
3950
- },
3951
- ]}>
3952
- <Text style={styles.headerMethodText}>
3953
- {selectedEvent.source === 'firebase'
3954
- ? 'FB'
3955
- : 'MAN'}
3956
- </Text>
3957
- </View>
3958
- <Text style={styles.headerDetailTitle} numberOfLines={1} ellipsizeMode="middle">
3959
- {selectedEvent.name}
3960
- </Text>
3961
- </View>
3962
- <View style={styles.headerDetailSubRow}>
3963
- <View style={[
3964
- styles.headerStatusDot,
3965
- {
3966
- backgroundColor: selectedEvent.source === 'firebase'
3967
- ? '#E07B1A'
3968
- : AppColors.purple,
3969
- },
3970
- ]}/>
3971
- <Text style={styles.headerSubTitle}>
3972
- {Object.keys(selectedEvent.params).length} param
3973
- {Object.keys(selectedEvent.params).length !== 1
3974
- ? 's'
3975
- : ''}
3976
- {' · '}
3977
- {selectedEvent.source}
3978
- </Text>
3979
- </View>
3980
- </View>) : null}
3981
- </View>
4046
+ </View>) : null}
4047
+ </View>)}
3982
4048
 
3983
4049
  <View style={[
3984
4050
  styles.headerRight,
@@ -4146,7 +4212,7 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
4146
4212
  {/* ─── Search + Shared Toolbar for Analytics ──────────────────────── */}
4147
4213
  <View style={[
4148
4214
  styles.toolbarRow,
4149
- { marginTop: 12, marginBottom: 4 },
4215
+ { marginTop: 12, marginBottom: 8 },
4150
4216
  ]}>
4151
4217
  <View style={styles.searchContainer}>
4152
4218
  <SearchIcon color={AppColors.grayTextWeak} size={16}/>
@@ -4155,194 +4221,24 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
4155
4221
  <ClearIcon color={AppColors.grayTextWeak} size={14}/>
4156
4222
  </Pressable>)}
4157
4223
  </View>
4158
- {analyticsSubTab === 'ga_events' && (<View style={styles.toolbarRight}>
4159
- <TouchableScale style={{
4160
- flexDirection: 'row',
4161
- alignItems: 'center',
4162
- gap: 4,
4163
- paddingHorizontal: 10,
4164
- paddingVertical: 5,
4165
- borderRadius: 8,
4166
- backgroundColor: 'rgba(255,46,87,0.06)',
4167
- borderWidth: 1,
4168
- borderColor: 'rgba(255,46,87,0.15)',
4169
- }} onPress={handleDelete} hitSlop={6}>
4170
- <WipeIcon color={AppColors.errorColor} size={13}/>
4171
- <Text style={{
4172
- fontFamily: AppFonts.interBold,
4173
- fontSize: 10.5,
4174
- color: AppColors.errorColor,
4175
- }}>
4176
- Clear
4177
- </Text>
4178
- </TouchableScale>
4179
- </View>)}
4180
- </View>
4181
-
4182
- {/* ─── Secondary Tab Bar for Analytics ──────────────────────── */}
4183
- <View style={{
4184
- marginHorizontal: 16,
4185
- marginTop: 4,
4186
- marginBottom: 8,
4187
- backgroundColor: AppColors.grayBackground,
4188
- borderRadius: 8,
4189
- padding: 4,
4190
- flexDirection: 'row',
4191
- borderWidth: 1,
4192
- borderColor: AppColors.grayBorderSecondary,
4193
- }}>
4194
- <Pressable style={[
4224
+ <View style={styles.toolbarRight}>
4225
+ <TouchableScale style={[
4226
+ styles.toolbarBtn,
4195
4227
  {
4196
- flex: 1,
4197
- paddingVertical: 8,
4198
- borderRadius: 6,
4199
- alignItems: 'center',
4200
- },
4201
- analyticsSubTab === 'ga_events' && {
4202
- backgroundColor: AppColors.primaryLight,
4203
- shadowColor: '#000',
4204
- shadowOpacity: 0.1,
4205
- shadowRadius: 3,
4206
- shadowOffset: { width: 0, height: 1 },
4207
- elevation: 2,
4208
- },
4209
- ]} onPress={() => {
4210
- animateNextLayout();
4211
- setAnalyticsSubTab('ga_events');
4212
- }}>
4213
- <View style={{
4214
- flexDirection: 'row',
4215
- alignItems: 'center',
4216
- gap: 6,
4217
- }}>
4218
- {/* #7 */}
4219
- <AnalyticsIcon size={13} color={analyticsSubTab === 'ga_events'
4220
- ? AppColors.purple
4221
- : AppColors.grayTextStrong}/>
4222
- <Text style={[
4223
- {
4224
- fontFamily: AppFonts.interMedium,
4225
- fontSize: 13,
4226
- color: AppColors.grayTextStrong,
4227
- },
4228
- analyticsSubTab === 'ga_events' && {
4229
- fontFamily: AppFonts.interBold,
4230
- color: AppColors.purple,
4231
- },
4232
- ]}>
4233
- GA Events (
4234
- {analyticsSearch
4235
- ? filteredAnalyticsEvents.length
4236
- : analyticsEvents.length}
4237
- )
4238
- </Text>
4239
- </View>
4240
- </Pressable>
4241
- <Pressable style={[
4242
- {
4243
- flex: 1,
4244
- paddingVertical: 8,
4245
- borderRadius: 6,
4246
- alignItems: 'center',
4247
- },
4248
- analyticsSubTab === 'top_events' && {
4249
- backgroundColor: AppColors.primaryLight,
4250
- shadowColor: '#000',
4251
- shadowOpacity: 0.1,
4252
- shadowRadius: 3,
4253
- shadowOffset: { width: 0, height: 1 },
4254
- elevation: 2,
4255
- },
4256
- ]} onPress={() => {
4257
- animateNextLayout();
4258
- setAnalyticsSubTab('top_events');
4259
- }}>
4260
- <View style={{
4261
- flexDirection: 'row',
4262
- alignItems: 'center',
4263
- gap: 6,
4264
- }}>
4265
- {/* #7 */}
4266
- <TrendingUpIcon size={13} color={analyticsSubTab === 'top_events'
4267
- ? AppColors.purple
4268
- : AppColors.grayTextStrong}/>
4269
- <Text style={[
4270
- {
4271
- fontFamily: AppFonts.interMedium,
4272
- fontSize: 13,
4273
- color: AppColors.grayTextStrong,
4274
- },
4275
- analyticsSubTab === 'top_events' && {
4276
- fontFamily: AppFonts.interBold,
4277
- color: AppColors.purple,
4228
+ borderColor: 'rgba(255,46,87,0.22)',
4229
+ backgroundColor: 'rgba(255,46,87,0.06)',
4278
4230
  },
4279
- ]}>
4280
- Top Events ({topEventsArray.length})
4281
- </Text>
4282
- </View>
4283
- </Pressable>
4231
+ ]} onPress={handleDelete} hitSlop={6}>
4232
+ <WipeIcon color={AppColors.errorColor} size={15}/>
4233
+ </TouchableScale>
4234
+ </View>
4284
4235
  </View>
4285
4236
  </View>)}
4286
4237
 
4287
4238
  {isReady ? (activeTab === 'insights' ? (<ScrollView style={styles.insightsContainer} contentContainerStyle={styles.insightsContent} showsVerticalScrollIndicator={false}>
4288
4239
  {renderInsightsDashboard()}
4289
- </ScrollView>) : activeTab === 'analytics' ? (<View style={{ flex: 1 }}>
4290
- {selectedEvent != null ? (<AnalyticsDetail event={selectedEvent}/>) : analyticsSubTab === 'top_events' ? (<FlatList data={topEventsArray} keyExtractor={item => item[0]} contentContainerStyle={[
4291
- styles.listContent,
4292
- { paddingHorizontal: 16, paddingTop: 12 },
4293
- ]} renderItem={({ item: [name, count], index }) => {
4294
- const maxCount = topEventsArray[0]?.[1] || 1;
4295
- const pct = Math.max(6, (count / maxCount) * 100);
4296
- const color = getEventColor(name);
4297
- return (<AnimatedEntrance index={index} distance={8} style={[
4298
- styles.analyticsTopEventsCard,
4299
- { marginBottom: 6, paddingVertical: 10, paddingHorizontal: 12 },
4300
- ]}>
4301
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>
4302
- <Text style={{
4303
- fontFamily: AppFonts.interBold,
4304
- fontSize: 11,
4305
- color: color,
4306
- width: 18,
4307
- textAlign: 'center',
4308
- }}>
4309
- {index + 1}
4310
- </Text>
4311
- <View style={{ flex: 1, gap: 4 }}>
4312
- <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
4313
- <Text style={{
4314
- fontFamily: AppFonts.interMedium,
4315
- fontSize: 12.5,
4316
- color: AppColors.primaryBlack,
4317
- flex: 1,
4318
- }} numberOfLines={1}>
4319
- {name}
4320
- </Text>
4321
- <Text style={{
4322
- fontFamily: AppFonts.interBold,
4323
- fontSize: 12,
4324
- color: color,
4325
- marginLeft: 8,
4326
- }}>
4327
- {count}
4328
- </Text>
4329
- </View>
4330
- <View style={{ height: 3, backgroundColor: AppColors.grayBackground, borderRadius: 2, overflow: 'hidden' }}>
4331
- <LinearGradient colors={[color, `${color}88`]} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={{
4332
- height: '100%',
4333
- borderRadius: 2,
4334
- width: `${pct}%`,
4335
- }}/>
4336
- </View>
4337
- </View>
4338
- </View>
4339
- </AnimatedEntrance>);
4340
- }} ListEmptyComponent={<View style={styles.emptyContainer}>
4341
- <View style={styles.emptyIconWrap}>
4342
- <EmptyRadarIcon color={AppColors.purple} size={32}/>
4343
- </View>
4344
- <Text style={styles.emptyTitle}>No Top Events</Text>
4345
- </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 }) => {
4346
4242
  const prev = filteredAnalyticsEvents[index + 1];
4347
4243
  const next = filteredAnalyticsEvents[index - 1];
4348
4244
  const msSincePrev = prev
@@ -4353,12 +4249,13 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
4353
4249
  ? Math.floor(next.timestamp / 60000)
4354
4250
  : -1;
4355
4251
  const showTimestamp = index === 0 || thisMin !== nextMin;
4356
- return (<AnimatedEntrance index={index} distance={8}>
4357
- <AnalyticsEventCard event={item} onPress={() => {
4252
+ return (<AnalyticsEventCard event={item} onPress={() => {
4358
4253
  animateNextLayout();
4359
4254
  setSelectedEvent(item);
4360
4255
  }} isNew={newEventIds.has(item.id)} searchStr={analyticsSearch} isFirst={index === 0} isLast={index === filteredAnalyticsEvents.length - 1} msSincePrev={msSincePrev} showTimestamp={showTimestamp} computedScreenName={(() => {
4361
- let screenName = item.screenName ||
4256
+ if (!item)
4257
+ return '';
4258
+ const rawScreenName = item.screenName ||
4362
4259
  item.screenClass ||
4363
4260
  item.pageTitle ||
4364
4261
  item.pageLocation ||
@@ -4366,6 +4263,9 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
4366
4263
  item.params?.screen_name ||
4367
4264
  item.params?.firebase_screen_class ||
4368
4265
  item.params?.screen_class;
4266
+ let screenName = typeof rawScreenName === 'string'
4267
+ ? rawScreenName
4268
+ : (rawScreenName ? JSON.stringify(rawScreenName) : '');
4369
4269
  const routeInfo = logRouteMapRef.current.get(item.id + 1000000);
4370
4270
  if (!screenName) {
4371
4271
  if (routeInfo &&
@@ -4375,8 +4275,7 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
4375
4275
  }
4376
4276
  }
4377
4277
  return screenName;
4378
- })()}/>
4379
- </AnimatedEntrance>);
4278
+ })()}/>);
4380
4279
  }} initialNumToRender={20} maxToRenderPerBatch={20} windowSize={5} removeClippedSubviews={false} ListEmptyComponent={<View style={styles.emptyContainer}>
4381
4280
  <View style={styles.emptyIconWrap}>
4382
4281
  <EmptyRadarIcon color={AppColors.purple} size={32}/>
@@ -4396,7 +4295,7 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
4396
4295
  filteredAnalyticsEvents.length === 0 && {
4397
4296
  flexGrow: 1,
4398
4297
  },
4399
- ]} keyboardShouldPersistTaps="handled"/>)}
4298
+ ]} keyboardShouldPersistTaps="handled"/>) : null}
4400
4299
  </View>) : activeTab === 'apis' && selected == null ? (<View style={{ flex: 1 }}>
4401
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 }}>
4402
4301
  <View style={styles.toolbarRow}>
@@ -4409,27 +4308,19 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
4409
4308
  </View>
4410
4309
 
4411
4310
  <View style={styles.toolbarRight}>
4412
- <TouchableScale style={{
4413
- flexDirection: 'row',
4414
- alignItems: 'center',
4415
- gap: 4,
4416
- paddingHorizontal: 10,
4417
- paddingVertical: 5,
4418
- borderRadius: 8,
4419
- backgroundColor: 'rgba(255,46,87,0.06)',
4420
- borderWidth: 1,
4421
- borderColor: 'rgba(255,46,87,0.15)',
4422
- }} onPress={handleDelete} hitSlop={6}>
4423
- <WipeIcon color={AppColors.errorColor} size={13}/>
4424
- <Text style={{
4425
- fontFamily: AppFonts.interBold,
4426
- fontSize: 10.5,
4427
- color: AppColors.errorColor,
4428
- }}>
4429
- {selectedLogs.size > 0
4430
- ? `Delete (${selectedLogs.size})`
4431
- : 'Clear'}
4432
- </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>)}
4433
4324
  </TouchableScale>
4434
4325
 
4435
4326
  <TouchableScale style={styles.toolbarBtn} onPress={() => setSortOrder(o => o === 'newest' ? 'oldest' : 'newest')} hitSlop={10}>
@@ -4597,25 +4488,14 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
4597
4488
  <TouchableScale style={styles.toolbarBtn} onPress={() => setLogSortOrder(o => o === 'newest' ? 'oldest' : 'newest')} hitSlop={10}>
4598
4489
  <SortArrowIcon color={AppColors.grayTextStrong} size={18} direction={logSortOrder === 'newest' ? 'down' : 'up'}/>
4599
4490
  </TouchableScale>
4600
- <TouchableScale style={{
4601
- flexDirection: 'row',
4602
- alignItems: 'center',
4603
- gap: 4,
4604
- paddingHorizontal: 10,
4605
- paddingVertical: 5,
4606
- borderRadius: 8,
4607
- backgroundColor: 'rgba(255,46,87,0.06)',
4608
- borderWidth: 1,
4609
- borderColor: 'rgba(255,46,87,0.15)',
4610
- }} onPress={handleDelete} hitSlop={6}>
4611
- <WipeIcon color={AppColors.errorColor} size={13}/>
4612
- <Text style={{
4613
- fontFamily: AppFonts.interBold,
4614
- fontSize: 10.5,
4615
- color: AppColors.errorColor,
4616
- }}>
4617
- Clear
4618
- </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}/>
4619
4499
  </TouchableScale>
4620
4500
  </View>
4621
4501
  </View>
@@ -4886,7 +4766,7 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
4886
4766
  </ScrollView>
4887
4767
  </View>
4888
4768
 
4889
- <FlatList data={filteredConsoleLogs} keyExtractor={item => item.id.toString()} ListHeaderComponent={(() => {
4769
+ <FlatList data={filteredConsoleLogs} keyExtractor={(item, index) => item?.id?.toString() ?? index.toString()} ListHeaderComponent={(() => {
4890
4770
  const total = visibleConsoleLogs.length;
4891
4771
  const filtered = filteredConsoleLogs.length;
4892
4772
  const isAllSelected = logFilters.has('all') ||
@@ -5949,7 +5829,7 @@ export default NetworkInspectorWrapper;
5949
5829
  // Re-export public APIs
5950
5830
  export { setupNetworkLogger, clearNetworkLogs, subscribeNetworkLogs, addAxiosInterceptors, } from './customHooks/networkLogger';
5951
5831
  export { setupConsoleLogger, clearConsoleLogs, subscribeConsoleLogs, } from './customHooks/consoleLogger';
5952
- export { setupAnalyticsLogger, logAnalyticsEvent, subscribeAnalyticsEvents, clearAnalyticsEvents, } from './customHooks/analyticsLogger';
5832
+ export { setupAnalyticsLogger, logAnalyticsEvent, subscribeAnalyticsEvents, clearAnalyticsEvents, getCurrentUserProperties, getCurrentUserId, getDefaultEventParameters, getCollectionEnabled, } from './customHooks/analyticsLogger';
5953
5833
  export { WebView, getWebViewLogs, getWebViewNavHistory, getWebViewHtml, getWebViewCss, getWebViewJs, getWebViewHtmlUrl, clearWebViewData, subscribeWebView, } from './customHooks/webViewLogger';
5954
5834
  export { default as ErrorBoundary } from './components/ErrorBoundary';
5955
5835
  export { connectReduxStore, inspectorReduxMiddleware, getReduxState, subscribeReduxState, getActionHistory, clearActionHistory, } from './customHooks/reduxLogger';