react-native-inapp-inspector 1.1.26 → 1.1.28

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 (67) hide show
  1. package/android/build.gradle +1 -0
  2. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +34 -0
  3. package/dist/commonjs/components/ErrorBoundary.d.ts +1 -1
  4. package/dist/commonjs/components/ErrorBoundary.js +530 -100
  5. package/dist/commonjs/components/Inspector/AnalyticsTab.js +176 -126
  6. package/dist/commonjs/components/Inspector/BundleTab.d.ts +1 -0
  7. package/dist/commonjs/components/Inspector/BundleTab.js +145 -21
  8. package/dist/commonjs/components/Inspector/CrashTab.js +0 -8
  9. package/dist/commonjs/components/Inspector/NetworkDetail.js +41 -0
  10. package/dist/commonjs/components/Inspector/PerformanceTab.d.ts +5 -0
  11. package/dist/commonjs/components/Inspector/PerformanceTab.js +588 -155
  12. package/dist/commonjs/components/Inspector/SettingsPanel.js +57 -22
  13. package/dist/commonjs/components/Inspector/TabBar.js +13 -1
  14. package/dist/commonjs/components/LogCard.js +75 -34
  15. package/dist/commonjs/components/NetworkIcons.d.ts +112 -102
  16. package/dist/commonjs/constants/version.d.ts +1 -1
  17. package/dist/commonjs/constants/version.js +1 -1
  18. package/dist/commonjs/customHooks/analyticsLogger.d.ts +1 -11
  19. package/dist/commonjs/customHooks/analyticsLogger.js +8 -1
  20. package/dist/commonjs/customHooks/bundleAnalyzer.js +121 -124
  21. package/dist/commonjs/customHooks/networkLogger.d.ts +1 -0
  22. package/dist/commonjs/customHooks/networkLogger.js +16 -0
  23. package/dist/commonjs/customHooks/performanceTracker.d.ts +74 -0
  24. package/dist/commonjs/customHooks/performanceTracker.js +546 -392
  25. package/dist/commonjs/customHooks/reduxLogger.d.ts +1 -0
  26. package/dist/commonjs/customHooks/reduxLogger.js +3 -1
  27. package/dist/commonjs/helpers/index.js +27 -3
  28. package/dist/commonjs/i18n/locales/en.json +225 -4
  29. package/dist/commonjs/index.d.ts +1 -1
  30. package/dist/commonjs/index.js +36 -2
  31. package/dist/commonjs/styles/index.d.ts +3 -1
  32. package/dist/commonjs/styles/index.js +8 -6
  33. package/dist/commonjs/types/interfaces.d.ts +3 -0
  34. package/dist/esm/components/ErrorBoundary.d.ts +1 -1
  35. package/dist/esm/components/ErrorBoundary.js +498 -101
  36. package/dist/esm/components/Inspector/AnalyticsTab.js +177 -127
  37. package/dist/esm/components/Inspector/BundleTab.d.ts +1 -0
  38. package/dist/esm/components/Inspector/BundleTab.js +145 -23
  39. package/dist/esm/components/Inspector/CrashTab.js +0 -8
  40. package/dist/esm/components/Inspector/NetworkDetail.js +41 -0
  41. package/dist/esm/components/Inspector/PerformanceTab.d.ts +5 -0
  42. package/dist/esm/components/Inspector/PerformanceTab.js +591 -158
  43. package/dist/esm/components/Inspector/SettingsPanel.js +57 -22
  44. package/dist/esm/components/Inspector/TabBar.js +13 -1
  45. package/dist/esm/components/LogCard.js +75 -34
  46. package/dist/esm/components/NetworkIcons.d.ts +112 -102
  47. package/dist/esm/constants/version.d.ts +1 -1
  48. package/dist/esm/constants/version.js +1 -1
  49. package/dist/esm/customHooks/analyticsLogger.d.ts +1 -11
  50. package/dist/esm/customHooks/analyticsLogger.js +6 -0
  51. package/dist/esm/customHooks/bundleAnalyzer.js +121 -124
  52. package/dist/esm/customHooks/networkLogger.d.ts +1 -0
  53. package/dist/esm/customHooks/networkLogger.js +16 -0
  54. package/dist/esm/customHooks/performanceTracker.d.ts +74 -0
  55. package/dist/esm/customHooks/performanceTracker.js +531 -391
  56. package/dist/esm/customHooks/reduxLogger.d.ts +1 -0
  57. package/dist/esm/customHooks/reduxLogger.js +1 -0
  58. package/dist/esm/helpers/index.js +27 -3
  59. package/dist/esm/i18n/locales/en.json +225 -4
  60. package/dist/esm/index.d.ts +1 -1
  61. package/dist/esm/index.js +24 -3
  62. package/dist/esm/styles/index.d.ts +3 -1
  63. package/dist/esm/styles/index.js +8 -6
  64. package/dist/esm/types/interfaces.d.ts +3 -0
  65. package/ios/NetworkInspectorModule.h +2 -0
  66. package/ios/NetworkInspectorModule.m +10 -0
  67. package/package.json +1 -1
@@ -8,11 +8,14 @@ import EndOfListFooter from '../EndOfListFooter';
8
8
  import styles from '../../styles';
9
9
  import { AppColors } from '../../styles/AppColors';
10
10
  import { AppFonts } from '../../styles/AppFonts';
11
+ import { useTranslation } from '../../i18n';
12
+ import Svg, { Path, Line, Circle, G, Defs, LinearGradient as SvgLinearGradient, Stop, } from 'react-native-svg';
11
13
  import TouchableScale from '../TouchableScale';
12
14
  import { getCurrentUserId, getCurrentUserProperties, getDefaultEventParameters, getCollectionEnabled, } from '../../customHooks/analyticsLogger';
13
15
  import { getEventCategory, } from '../../helpers';
14
- import { SearchIcon, ClearIcon, TrashIcon, EmptyRadarIcon, HeaderPauseIcon, FilterIcon, CartIcon, GlobeIcon, BoltIcon, SparkleIcon, TargetGoalIcon, PinIcon, MoneyIcon, } from '../NetworkIcons';
16
+ import { SearchIcon, ClearIcon, TrashIcon, EmptyRadarIcon, HeaderPauseIcon, FilterIcon, CartIcon, GlobeIcon, BoltIcon, SparkleIcon, PinIcon, MoneyIcon, } from '../NetworkIcons';
15
17
  const AnalyticsHeader = React.memo(() => {
18
+ const { t } = useTranslation();
16
19
  const { analyticsEvents, filteredAnalyticsEvents, analyticsFilters, setAnalyticsFilters, analyticsHeaderExpanded, setAnalyticsHeaderExpanded, } = useInspector();
17
20
  const userId = getCurrentUserId();
18
21
  const userProperties = getCurrentUserProperties();
@@ -97,20 +100,31 @@ const AnalyticsHeader = React.memo(() => {
97
100
  }
98
101
  });
99
102
  };
100
- // 12-Bucket GA4 Realtime Stacked Histogram Graph (-30m / 30s to NOW)
103
+ // 12-Bucket GA4 Realtime Stacked Histogram & Spline Wave Graph (-30m / 30s to NOW)
101
104
  const BUCKET_COUNT = 12;
102
- const chartHeight = 56;
103
- const histogramBars = useMemo(() => {
104
- const buckets = Array.from({ length: BUCKET_COUNT }, () => ({
105
- ecommerce: 0,
106
- page_view: 0,
107
- system: 0,
108
- custom: 0,
109
- total: 0,
110
- }));
105
+ const chartHeight = 64;
106
+ const svgWidth = 320;
107
+ const [selectedBucketIdx, setSelectedBucketIdx] = useState(null);
108
+ const histogramData = useMemo(() => {
109
+ const buckets = [];
111
110
  const now = Date.now();
112
111
  const windowMs = 60000; // 60s window
113
112
  const bucketDuration = windowMs / BUCKET_COUNT;
113
+ for (let i = 0; i < BUCKET_COUNT; i++) {
114
+ const bucketAgeSec = Math.round(((BUCKET_COUNT - 1 - i) * bucketDuration) / 1000);
115
+ buckets.push({
116
+ index: i,
117
+ ecommerce: 0,
118
+ page_view: 0,
119
+ system: 0,
120
+ custom: 0,
121
+ total: 0,
122
+ x: (i / (BUCKET_COUNT - 1)) * (svgWidth - 24) + 12,
123
+ y: chartHeight - 12,
124
+ isLatest: i === BUCKET_COUNT - 1,
125
+ ageLabel: i === BUCKET_COUNT - 1 ? t('analytics.timeNow') : `-${bucketAgeSec}s`,
126
+ });
127
+ }
114
128
  for (const e of filteredAnalyticsEvents) {
115
129
  const age = now - e.timestamp;
116
130
  if (age >= 0 && age < windowMs) {
@@ -131,30 +145,43 @@ const AnalyticsHeader = React.memo(() => {
131
145
  }
132
146
  }
133
147
  const maxVal = Math.max(...buckets.map(b => b.total), 4);
134
- return buckets.map((b, idx) => {
135
- const totalH = Math.max((b.total / maxVal) * (chartHeight - 14), b.total > 0 ? 8 : 2.5);
136
- const ecomH = b.total > 0 ? (b.ecommerce / b.total) * totalH : 0;
137
- const screenH = b.total > 0 ? (b.page_view / b.total) * totalH : 0;
138
- const sysH = b.total > 0 ? (b.system / b.total) * totalH : 0;
139
- const custH = b.total > 0 ? (b.custom / b.total) * totalH : totalH;
140
- return {
141
- index: idx,
142
- total: b.total,
143
- totalHeight: totalH,
144
- ecomHeight: ecomH,
145
- screenHeight: screenH,
146
- sysHeight: sysH,
147
- custHeight: custH,
148
- isLatest: idx === BUCKET_COUNT - 1,
149
- };
148
+ buckets.forEach(b => {
149
+ const normalizedH = (b.total / maxVal) * (chartHeight - 22);
150
+ b.y = chartHeight - 10 - normalizedH;
150
151
  });
151
- }, [filteredAnalyticsEvents, chartHeight]);
152
+ // Generate Cubic Bézier Spline paths
153
+ let linePath = `M ${buckets[0].x.toFixed(1)} ${buckets[0].y.toFixed(1)}`;
154
+ for (let i = 0; i < buckets.length - 1; i++) {
155
+ const p0 = buckets[Math.max(0, i - 1)];
156
+ const p1 = buckets[i];
157
+ const p2 = buckets[i + 1];
158
+ const p3 = buckets[Math.min(buckets.length - 1, i + 2)];
159
+ const cp1x = p1.x + (p2.x - p0.x) / 6;
160
+ const cp1y = p1.y + (p2.y - p0.y) / 6;
161
+ const cp2x = p2.x - (p3.x - p1.x) / 6;
162
+ const cp2y = p2.y - (p3.y - p1.y) / 6;
163
+ linePath += ` C ${cp1x.toFixed(1)} ${cp1y.toFixed(1)}, ${cp2x.toFixed(1)} ${cp2y.toFixed(1)}, ${p2.x.toFixed(1)} ${p2.y.toFixed(1)}`;
164
+ }
165
+ const lastB = buckets[buckets.length - 1];
166
+ const firstB = buckets[0];
167
+ const areaPath = `${linePath} L ${lastB.x.toFixed(1)} ${chartHeight - 4} L ${firstB.x.toFixed(1)} ${chartHeight - 4} Z`;
168
+ const peakVal = Math.max(...buckets.map(b => b.total), 0);
169
+ const eventRate = Math.round(buckets.reduce((a, b) => a + b.total, 0));
170
+ return {
171
+ buckets,
172
+ linePath,
173
+ areaPath,
174
+ peakVal,
175
+ eventRate,
176
+ };
177
+ }, [filteredAnalyticsEvents, chartHeight, svgWidth, t]);
178
+ const selectedBucket = selectedBucketIdx != null ? histogramData.buckets[selectedBucketIdx] : null;
152
179
  return (<View style={styles.analyticsHeaderCard}>
153
180
  {/* ── Top Header Row (GA4 Style) ─────────────────────────────────────── */}
154
181
  <View style={styles.analyticsHeaderTop}>
155
182
  <View style={{ flex: 1 }}>
156
183
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
157
- <Text style={styles.analyticsHeaderTitle}>Users in last 30 minutes</Text>
184
+ <Text style={styles.analyticsHeaderTitle}>{t('analytics.realtimeStream')}</Text>
158
185
  <View style={[
159
186
  styles.statusDot,
160
187
  {
@@ -172,11 +199,11 @@ const AnalyticsHeader = React.memo(() => {
172
199
  fontFamily: AppFonts.interBold,
173
200
  },
174
201
  ]}>
175
- {isTrackingEnabled ? 'Realtime' : 'Paused'}
202
+ {isTrackingEnabled ? t('performance.live') : t('common.pause')}
176
203
  </Text>
177
204
  </View>
178
205
  <Text style={styles.analyticsHeaderSubtitle} numberOfLines={1}>
179
- {userId ? `User: ${userId}` : 'Anonymous Session'}
206
+ {userId ? `${t('analytics.userId')}: ${userId}` : t('analytics.emptySubtitle')}
180
207
  </Text>
181
208
  </View>
182
209
 
@@ -185,7 +212,7 @@ const AnalyticsHeader = React.memo(() => {
185
212
  setAnalyticsHeaderExpanded(!analyticsHeaderExpanded);
186
213
  }}>
187
214
  <Text style={styles.analyticsHeaderToggleText}>
188
- {analyticsHeaderExpanded ? 'Hide Info' : 'Session Info'}
215
+ {analyticsHeaderExpanded ? t('common.close') : t('analytics.eventDetails')}
189
216
  </Text>
190
217
  </TouchableOpacity>
191
218
  </View>
@@ -205,7 +232,7 @@ const AnalyticsHeader = React.memo(() => {
205
232
  flexDirection: 'row',
206
233
  justifyContent: 'space-between',
207
234
  alignItems: 'flex-start',
208
- marginBottom: 8,
235
+ marginBottom: 6,
209
236
  }}>
210
237
  <View>
211
238
  <View style={{ flexDirection: 'row', alignItems: 'baseline', gap: 6 }}>
@@ -222,13 +249,33 @@ const AnalyticsHeader = React.memo(() => {
222
249
  fontSize: 10.5,
223
250
  color: AppColors.grayTextWeak,
224
251
  }}>
225
- events in window
252
+ {t('analytics.eventsInWindow')}
226
253
  </Text>
227
254
  </View>
228
255
  </View>
229
256
 
230
- {/* Realtime Badges */}
257
+ {/* Realtime Rate Badges */}
231
258
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 5 }}>
259
+ <View style={{
260
+ flexDirection: 'row',
261
+ alignItems: 'center',
262
+ gap: 3,
263
+ backgroundColor: `${AppColors.purple}14`,
264
+ paddingHorizontal: 6,
265
+ paddingVertical: 2.5,
266
+ borderRadius: 4,
267
+ borderWidth: 1,
268
+ borderColor: `${AppColors.purple}2E`,
269
+ }}>
270
+ <Text style={{
271
+ fontFamily: AppFonts.interBold,
272
+ fontSize: 9.5,
273
+ color: AppColors.purple,
274
+ }}>
275
+ {t('analytics.eventVelocity', { rate: histogramData.eventRate })}
276
+ </Text>
277
+ </View>
278
+
232
279
  {categoryStats.totalRevenue > 0 && (<View style={{
233
280
  flexDirection: 'row',
234
281
  alignItems: 'center',
@@ -249,127 +296,129 @@ const AnalyticsHeader = React.memo(() => {
249
296
  ${categoryStats.totalRevenue.toFixed(2)}
250
297
  </Text>
251
298
  </View>)}
252
- <View style={{
253
- flexDirection: 'row',
254
- alignItems: 'center',
255
- gap: 4,
256
- backgroundColor: `${AppColors.brandPurple}14`,
257
- paddingHorizontal: 6,
258
- paddingVertical: 2.5,
259
- borderRadius: 4,
260
- borderWidth: 1,
261
- borderColor: `${AppColors.brandPurple}2E`,
262
- }}>
263
- <TargetGoalIcon color={AppColors.brandPurple} size={10}/>
264
- <Text style={{
265
- fontFamily: AppFonts.interBold,
266
- fontSize: 9.5,
267
- color: AppColors.brandPurple,
268
- }}>
269
- Live Telemetry
270
- </Text>
271
- </View>
272
299
  </View>
273
300
  </View>
274
301
 
275
- {/* GA4 Stacked Realtime Histogram Bar Graph */}
276
- <View style={{
277
- height: chartHeight,
302
+ {/* ── High Precision SVG Spline Area Graph ── */}
303
+ <View style={{ height: chartHeight, position: 'relative' }}>
304
+ <Svg width="100%" height={chartHeight} viewBox={`0 0 ${svgWidth} ${chartHeight}`}>
305
+ <Defs>
306
+ <SvgLinearGradient id="telemetryAreaGrad" x1="0" y1="0" x2="0" y2="1">
307
+ <Stop offset="0%" stopColor={AppColors.brandPurple} stopOpacity="0.4"/>
308
+ <Stop offset="80%" stopColor={AppColors.brandPurple} stopOpacity="0.05"/>
309
+ <Stop offset="100%" stopColor={AppColors.brandPurple} stopOpacity="0.0"/>
310
+ </SvgLinearGradient>
311
+ </Defs>
312
+
313
+ {/* Baseline & Grid lines */}
314
+ <Line x1="8" y1={chartHeight - 6} x2={svgWidth - 8} y2={chartHeight - 6} stroke={AppColors.dividerColor} strokeWidth="1"/>
315
+ <Line x1="8" y1={chartHeight / 2} x2={svgWidth - 8} y2={chartHeight / 2} stroke={AppColors.dividerColor} strokeWidth="0.8" strokeDasharray="3,3"/>
316
+
317
+ {/* Glowing Area Fill */}
318
+ <Path d={histogramData.areaPath} fill="url(#telemetryAreaGrad)"/>
319
+
320
+ {/* Smooth Spline Wave Stroke */}
321
+ <Path d={histogramData.linePath} fill="none" stroke={AppColors.brandPurple} strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/>
322
+
323
+ {/* Data Points & Active Bucket Indicators */}
324
+ {histogramData.buckets.map((b, idx) => {
325
+ const isSelected = selectedBucketIdx === idx;
326
+ return (<G key={b.index}>
327
+ {isSelected && (<Line x1={b.x} y1={4} x2={b.x} y2={chartHeight - 6} stroke={AppColors.brandPurple} strokeWidth="1.2" strokeDasharray="2,2"/>)}
328
+
329
+ {b.total > 0 && (<Circle cx={b.x} cy={b.y} r={isSelected ? 4.5 : 2.8} fill={isSelected ? AppColors.brandPurple : AppColors.white} stroke={AppColors.brandPurple} strokeWidth={isSelected ? 2 : 1.5}/>)}
330
+
331
+ {b.isLatest && (<G>
332
+ <Circle cx={b.x} cy={b.y} r="6.5" fill={AppColors.brandPurple} opacity="0.22"/>
333
+ <Circle cx={b.x} cy={b.y} r="3" fill={AppColors.brandPurple}/>
334
+ </G>)}
335
+ </G>);
336
+ })}
337
+ </Svg>
338
+
339
+ {/* Invisible Touch Columns for Interactive Bucket Selection */}
340
+ <View style={{
341
+ position: 'absolute',
342
+ top: 0,
343
+ left: 0,
344
+ right: 0,
345
+ bottom: 0,
278
346
  flexDirection: 'row',
279
- alignItems: 'flex-end',
280
- justifyContent: 'space-between',
281
- paddingHorizontal: 2,
282
- paddingBottom: 2,
283
- borderBottomWidth: 1,
284
- borderBottomColor: AppColors.dividerColor,
285
347
  }}>
286
- {histogramBars.map(bar => (<View key={bar.index} style={{
287
- flex: 1,
348
+ {histogramData.buckets.map((b, idx) => (<TouchableOpacity key={b.index} style={{ flex: 1, height: '100%' }} activeOpacity={0.8} onPress={() => setSelectedBucketIdx(selectedBucketIdx === idx ? null : idx)}/>))}
349
+ </View>
350
+ </View>
351
+
352
+ {/* Selected Bucket Quick Breakdown Overlay */}
353
+ {selectedBucket && (<View style={{
354
+ flexDirection: 'row',
288
355
  alignItems: 'center',
289
- justifyContent: 'flex-end',
290
- height: '100%',
291
- marginHorizontal: 1.5,
356
+ justifyContent: 'space-between',
357
+ backgroundColor: AppColors.white,
358
+ borderRadius: 6,
359
+ paddingHorizontal: 8,
360
+ paddingVertical: 4,
361
+ marginTop: 4,
362
+ borderWidth: 1,
363
+ borderColor: AppColors.brandPurple,
292
364
  }}>
293
- {bar.total > 0 && (<Text style={{
294
- fontFamily: AppFonts.interBold,
295
- fontSize: 7.5,
296
- color: bar.isLatest
297
- ? AppColors.brandPurple
298
- : AppColors.grayTextWeak,
299
- marginBottom: 2,
300
- }}>
301
- {bar.total}
302
- </Text>)}
303
- {/* Stacked Category Segments Column */}
304
- <View style={{
305
- width: '100%',
306
- maxWidth: 15,
307
- height: bar.totalHeight,
308
- borderRadius: 3,
309
- overflow: 'hidden',
310
- backgroundColor: bar.total > 0
311
- ? `${AppColors.grayBorderSecondary}40`
312
- : `${AppColors.grayBorderSecondary}40`,
365
+ <Text style={{
366
+ fontFamily: AppFonts.interBold,
367
+ fontSize: 10,
368
+ color: AppColors.primaryBlack,
313
369
  }}>
314
- {bar.total > 0 ? (<>
315
- {bar.ecomHeight > 0 && (<View style={{
316
- height: bar.ecomHeight,
317
- backgroundColor: AppColors.amber500,
318
- }}/>)}
319
- {bar.screenHeight > 0 && (<View style={{
320
- height: bar.screenHeight,
321
- backgroundColor: AppColors.sky500,
322
- }}/>)}
323
- {bar.sysHeight > 0 && (<View style={{
324
- height: bar.sysHeight,
325
- backgroundColor: AppColors.purple,
326
- }}/>)}
327
- {bar.custHeight > 0 && (<View style={{
328
- height: bar.custHeight,
329
- backgroundColor: AppColors.brandPurple,
330
- }}/>)}
331
- </>) : (<View style={{
332
- height: 2.5,
333
- backgroundColor: `${AppColors.grayBorderSecondary}80`,
334
- }}/>)}
335
- </View>
336
- </View>))}
337
- </View>
370
+ {t('analytics.selectedBucket', {
371
+ time: selectedBucket.ageLabel,
372
+ count: selectedBucket.total,
373
+ })}
374
+ </Text>
375
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
376
+ {selectedBucket.ecommerce > 0 && (<Text style={{ fontSize: 9, fontFamily: AppFonts.interBold, color: AppColors.amber700 }}>
377
+ Ecom: {selectedBucket.ecommerce}
378
+ </Text>)}
379
+ {selectedBucket.page_view > 0 && (<Text style={{ fontSize: 9, fontFamily: AppFonts.interBold, color: AppColors.sky500 }}>
380
+ Screen: {selectedBucket.page_view}
381
+ </Text>)}
382
+ {selectedBucket.custom > 0 && (<Text style={{ fontSize: 9, fontFamily: AppFonts.interBold, color: AppColors.brandPurple }}>
383
+ Custom: {selectedBucket.custom}
384
+ </Text>)}
385
+ </View>
386
+ </View>)}
338
387
 
339
388
  {/* Timeline Axis Labels */}
340
389
  <View style={{
341
390
  flexDirection: 'row',
342
391
  justifyContent: 'space-between',
343
392
  marginTop: 4,
344
- paddingHorizontal: 2,
393
+ paddingHorizontal: 4,
345
394
  }}>
346
395
  <Text style={{
347
396
  fontFamily: AppFonts.interMedium,
348
- fontSize: 8,
397
+ fontSize: 8.5,
349
398
  color: AppColors.grayTextWeak,
350
399
  }}>
351
- -30 min
400
+ {t('analytics.time30mAgo')}
352
401
  </Text>
353
402
  <Text style={{
354
403
  fontFamily: AppFonts.interMedium,
355
- fontSize: 8,
404
+ fontSize: 8.5,
356
405
  color: AppColors.grayTextWeak,
357
406
  }}>
358
- -20 min
407
+ {t('analytics.time20mAgo')}
359
408
  </Text>
360
409
  <Text style={{
361
410
  fontFamily: AppFonts.interMedium,
362
- fontSize: 8,
411
+ fontSize: 8.5,
363
412
  color: AppColors.grayTextWeak,
364
413
  }}>
365
- -10 min
414
+ {t('analytics.time10mAgo')}
366
415
  </Text>
367
416
  <Text style={{
368
417
  fontFamily: AppFonts.interBold,
369
- fontSize: 8,
418
+ fontSize: 8.5,
370
419
  color: AppColors.brandPurple,
371
420
  }}>
372
- NOW
421
+ {t('analytics.timeNow')}
373
422
  </Text>
374
423
  </View>
375
424
  </View>
@@ -648,26 +697,26 @@ const AnalyticsHeader = React.memo(() => {
648
697
  <View style={styles.analyticsStatsRow}>
649
698
  <View style={styles.analyticsStatBox}>
650
699
  <Text style={styles.analyticsStatValue}>{totalEvents}</Text>
651
- <Text style={styles.analyticsStatLabel}>Events</Text>
700
+ <Text style={styles.analyticsStatLabel}>{t('analytics.eventCount', { count: totalEvents })}</Text>
652
701
  </View>
653
702
  <View style={styles.analyticsStatBox}>
654
703
  <Text style={styles.analyticsStatValue}>
655
704
  {Object.keys(userProperties).length}
656
705
  </Text>
657
- <Text style={styles.analyticsStatLabel}>User Props</Text>
706
+ <Text style={styles.analyticsStatLabel}>{t('analytics.props')}</Text>
658
707
  </View>
659
708
  <View style={styles.analyticsStatBox}>
660
709
  <Text style={styles.analyticsStatValue}>
661
710
  {Object.keys(defaultParams).length}
662
711
  </Text>
663
- <Text style={styles.analyticsStatLabel}>Defaults</Text>
712
+ <Text style={styles.analyticsStatLabel}>{t('analytics.defaultParameters')}</Text>
664
713
  </View>
665
714
  </View>
666
715
 
667
716
  {/* Details Section */}
668
717
  {(hasUserProps || hasDefaultParams) && (<View style={styles.analyticsHeaderDetails}>
669
718
  {hasUserProps && (<View style={{ marginBottom: 10 }}>
670
- <Text style={styles.detailsGroupTitle}>Active User Properties</Text>
719
+ <Text style={styles.detailsGroupTitle}>{t('analytics.userProperties')}</Text>
671
720
  {Object.entries(userProperties).map(([k, v]) => (<View key={k} style={styles.detailsRow}>
672
721
  <Text style={styles.detailsKey} selectable={true}>{k}</Text>
673
722
  <Text style={styles.detailsValue} selectable={true}>{String(v)}</Text>
@@ -675,7 +724,7 @@ const AnalyticsHeader = React.memo(() => {
675
724
  </View>)}
676
725
 
677
726
  {hasDefaultParams && (<View>
678
- <Text style={styles.detailsGroupTitle}>Default Event Parameters</Text>
727
+ <Text style={styles.detailsGroupTitle}>{t('analytics.defaultParameters')}</Text>
679
728
  {Object.entries(defaultParams).map(([k, v]) => (<View key={k} style={styles.detailsRow}>
680
729
  <Text style={styles.detailsKey} selectable={true}>{k}</Text>
681
730
  <Text style={styles.detailsValue} selectable={true}>{String(v)}</Text>
@@ -686,6 +735,7 @@ const AnalyticsHeader = React.memo(() => {
686
735
  </View>);
687
736
  });
688
737
  const AnalyticsTab = React.memo(() => {
738
+ const { t } = useTranslation();
689
739
  const { filteredAnalyticsEvents, analyticsSearch, setAnalyticsSearch, handleDelete, selectedEvent, setSelectedEvent, newEventIds, logRouteMapRef, isAnalyticsLayoutReady, setIsAnalyticsLayoutReady, isAnalyticsPaused, setIsAnalyticsPaused, isAnalyticsFilterApplied, } = useInspector();
690
740
  const [isFilterModalOpen, setIsFilterModalOpen] = useState(false);
691
741
  const keyExtractor = useCallback((item, index) => item?.id?.toString() ?? index.toString(), []);
@@ -43,5 +43,6 @@ export interface TreeNode {
43
43
  fileCount: number;
44
44
  children: TreeNode[];
45
45
  }
46
+ export declare const downloadBundleFile: (file: BundleFileItem, scriptURL?: string) => Promise<void>;
46
47
  declare const BundleTab: React.MemoExoticComponent<() => React.JSX.Element>;
47
48
  export default BundleTab;