react-native-inapp-inspector 1.1.27 → 1.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +29 -46
- package/dist/commonjs/components/AppHeaderLogo.js +12 -15
- package/dist/commonjs/components/ConsoleLogCard.js +31 -7
- package/dist/commonjs/components/CopyButton.js +15 -1
- package/dist/commonjs/components/DomainHeader.js +56 -28
- package/dist/commonjs/components/ErrorBoundary.d.ts +1 -1
- package/dist/commonjs/components/ErrorBoundary.js +530 -100
- package/dist/commonjs/components/HighlightText.js +7 -1
- package/dist/commonjs/components/Inspector/AnalyticsTab.js +190 -126
- package/dist/commonjs/components/Inspector/BundleTab.d.ts +1 -0
- package/dist/commonjs/components/Inspector/BundleTab.js +145 -21
- package/dist/commonjs/components/Inspector/CrashTab.js +0 -8
- package/dist/commonjs/components/Inspector/InspectorHeader.js +17 -3
- package/dist/commonjs/components/Inspector/LogDetail.js +8 -5
- package/dist/commonjs/components/Inspector/MainScreen.js +4 -0
- package/dist/commonjs/components/Inspector/NetworkDetail.js +65 -24
- package/dist/commonjs/components/Inspector/PerformanceTab.d.ts +5 -0
- package/dist/commonjs/components/Inspector/PerformanceTab.js +588 -155
- package/dist/commonjs/components/Inspector/SettingsPanel.js +57 -22
- package/dist/commonjs/components/Inspector/TabBar.js +13 -1
- package/dist/commonjs/components/JsonViewer.js +25 -3
- package/dist/commonjs/components/LogCard.js +173 -40
- package/dist/commonjs/components/NetworkIcons.d.ts +112 -102
- package/dist/commonjs/components/Toast.d.ts +3 -0
- package/dist/commonjs/components/Toast.js +152 -0
- package/dist/commonjs/components/TouchableScale.d.ts +1 -0
- package/dist/commonjs/components/TouchableScale.js +3 -3
- package/dist/commonjs/components/TreeNode.js +1 -1
- package/dist/commonjs/constants/index.js +6 -6
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/customHooks/analyticsLogger.d.ts +1 -11
- package/dist/commonjs/customHooks/analyticsLogger.js +8 -1
- package/dist/commonjs/customHooks/bundleAnalyzer.js +121 -124
- package/dist/commonjs/customHooks/networkLogger.d.ts +1 -0
- package/dist/commonjs/customHooks/networkLogger.js +16 -0
- package/dist/commonjs/customHooks/performanceTracker.d.ts +74 -0
- package/dist/commonjs/customHooks/performanceTracker.js +546 -392
- package/dist/commonjs/customHooks/reduxLogger.d.ts +1 -0
- package/dist/commonjs/customHooks/reduxLogger.js +3 -1
- package/dist/commonjs/helpers/index.d.ts +4 -0
- package/dist/commonjs/helpers/index.js +92 -69
- package/dist/commonjs/helpers/toast.d.ts +4 -0
- package/dist/commonjs/helpers/toast.js +20 -0
- package/dist/commonjs/i18n/locales/en.json +229 -5
- package/dist/commonjs/index.d.ts +1 -1
- package/dist/commonjs/index.js +36 -2
- package/dist/commonjs/styles/index.d.ts +63 -15
- package/dist/commonjs/styles/index.js +79 -32
- package/dist/commonjs/types/interfaces.d.ts +3 -0
- package/dist/esm/components/AppHeaderLogo.js +12 -15
- package/dist/esm/components/ConsoleLogCard.js +33 -9
- package/dist/esm/components/CopyButton.js +15 -1
- package/dist/esm/components/DomainHeader.js +56 -28
- package/dist/esm/components/ErrorBoundary.d.ts +1 -1
- package/dist/esm/components/ErrorBoundary.js +498 -101
- package/dist/esm/components/HighlightText.js +7 -1
- package/dist/esm/components/Inspector/AnalyticsTab.js +191 -127
- package/dist/esm/components/Inspector/BundleTab.d.ts +1 -0
- package/dist/esm/components/Inspector/BundleTab.js +145 -23
- package/dist/esm/components/Inspector/CrashTab.js +0 -8
- package/dist/esm/components/Inspector/InspectorHeader.js +18 -4
- package/dist/esm/components/Inspector/LogDetail.js +10 -7
- package/dist/esm/components/Inspector/MainScreen.js +4 -0
- package/dist/esm/components/Inspector/NetworkDetail.js +66 -25
- package/dist/esm/components/Inspector/PerformanceTab.d.ts +5 -0
- package/dist/esm/components/Inspector/PerformanceTab.js +591 -158
- package/dist/esm/components/Inspector/SettingsPanel.js +57 -22
- package/dist/esm/components/Inspector/TabBar.js +13 -1
- package/dist/esm/components/JsonViewer.js +25 -3
- package/dist/esm/components/LogCard.js +175 -42
- package/dist/esm/components/NetworkIcons.d.ts +112 -102
- package/dist/esm/components/Toast.d.ts +3 -0
- package/dist/esm/components/Toast.js +117 -0
- package/dist/esm/components/TouchableScale.d.ts +1 -0
- package/dist/esm/components/TouchableScale.js +3 -3
- package/dist/esm/components/TreeNode.js +1 -1
- package/dist/esm/constants/index.js +6 -6
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/customHooks/analyticsLogger.d.ts +1 -11
- package/dist/esm/customHooks/analyticsLogger.js +6 -0
- package/dist/esm/customHooks/bundleAnalyzer.js +121 -124
- package/dist/esm/customHooks/networkLogger.d.ts +1 -0
- package/dist/esm/customHooks/networkLogger.js +16 -0
- package/dist/esm/customHooks/performanceTracker.d.ts +74 -0
- package/dist/esm/customHooks/performanceTracker.js +531 -391
- package/dist/esm/customHooks/reduxLogger.d.ts +1 -0
- package/dist/esm/customHooks/reduxLogger.js +1 -0
- package/dist/esm/helpers/index.d.ts +4 -0
- package/dist/esm/helpers/index.js +87 -69
- package/dist/esm/helpers/toast.d.ts +4 -0
- package/dist/esm/helpers/toast.js +15 -0
- package/dist/esm/i18n/locales/en.json +229 -5
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +24 -3
- package/dist/esm/styles/index.d.ts +63 -15
- package/dist/esm/styles/index.js +79 -32
- package/dist/esm/types/interfaces.d.ts +3 -0
- package/package.json +4 -1
|
@@ -3,6 +3,12 @@ import { Text } from 'react-native';
|
|
|
3
3
|
// Helpers
|
|
4
4
|
import { escapeRegex, handleOpenExternalLink } from '../helpers';
|
|
5
5
|
const HighlightText = React.memo(function HighlightText({ text, search, style, highlightStyle, detectLinks, numberOfLines, ...rest }) {
|
|
6
|
+
// Optimization: For huge strings (> 20KB) with no active search, bypass all splitting/regex overhead
|
|
7
|
+
if (!text || (text.length > 20000 && !search && !detectLinks)) {
|
|
8
|
+
return (<Text style={style} numberOfLines={numberOfLines} {...rest}>
|
|
9
|
+
{text}
|
|
10
|
+
</Text>);
|
|
11
|
+
}
|
|
6
12
|
// Regex to detect absolute URLs
|
|
7
13
|
const URL_REGEX = /(https?:\/\/[^\s]+)/g;
|
|
8
14
|
const renderNormalOrHighlighted = (subText, keyPrefix) => {
|
|
@@ -17,7 +23,7 @@ const HighlightText = React.memo(function HighlightText({ text, search, style, h
|
|
|
17
23
|
</Text>) : (<Text key={i}>{part}</Text>))}
|
|
18
24
|
</Text>);
|
|
19
25
|
};
|
|
20
|
-
if (detectLinks && text) {
|
|
26
|
+
if (detectLinks && text && text.length < 20000) {
|
|
21
27
|
const parts = text.split(URL_REGEX);
|
|
22
28
|
return (<Text style={style} numberOfLines={numberOfLines} {...rest}>
|
|
23
29
|
{parts.map((part, i) => {
|
|
@@ -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, Rect, 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,
|
|
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 =
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
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
|
-
|
|
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}>
|
|
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 ? '
|
|
202
|
+
{isTrackingEnabled ? t('performance.live') : t('common.pause')}
|
|
176
203
|
</Text>
|
|
177
204
|
</View>
|
|
178
205
|
<Text style={styles.analyticsHeaderSubtitle} numberOfLines={1}>
|
|
179
|
-
{userId ?
|
|
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 ? '
|
|
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:
|
|
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
|
-
|
|
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,143 @@ 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
|
-
{/*
|
|
276
|
-
<View style={{
|
|
277
|
-
|
|
302
|
+
{/* ── Crashlytics-Style Multi-Bar Histogram with Trendline Overlay ── */}
|
|
303
|
+
<View style={{ height: chartHeight, position: 'relative' }}>
|
|
304
|
+
<Svg width="100%" height={chartHeight} viewBox={`0 0 ${svgWidth} ${chartHeight}`}>
|
|
305
|
+
<Defs>
|
|
306
|
+
<SvgLinearGradient id="crashlyticsBarGrad" x1="0" y1="0" x2="0" y2="1">
|
|
307
|
+
<Stop offset="0%" stopColor="#8B5CF6" stopOpacity="0.9"/>
|
|
308
|
+
<Stop offset="100%" stopColor="#6366F1" stopOpacity="0.6"/>
|
|
309
|
+
</SvgLinearGradient>
|
|
310
|
+
<SvgLinearGradient id="crashlyticsAreaGrad" x1="0" y1="0" x2="0" y2="1">
|
|
311
|
+
<Stop offset="0%" stopColor="#8B5CF6" stopOpacity="0.25"/>
|
|
312
|
+
<Stop offset="80%" stopColor="#8B5CF6" stopOpacity="0.04"/>
|
|
313
|
+
<Stop offset="100%" stopColor="#8B5CF6" stopOpacity="0.0"/>
|
|
314
|
+
</SvgLinearGradient>
|
|
315
|
+
</Defs>
|
|
316
|
+
|
|
317
|
+
{/* Subtle Grid Guidelines */}
|
|
318
|
+
<Line x1="6" y1={chartHeight - 6} x2={svgWidth - 6} y2={chartHeight - 6} stroke="#E2E8F0" strokeWidth="1"/>
|
|
319
|
+
<Line x1="6" y1={chartHeight / 2} x2={svgWidth - 6} y2={chartHeight / 2} stroke="#F1F5F9" strokeWidth="1" strokeDasharray="4,4"/>
|
|
320
|
+
|
|
321
|
+
{/* Glowing Area Underlay */}
|
|
322
|
+
<Path d={histogramData.areaPath} fill="url(#crashlyticsAreaGrad)"/>
|
|
323
|
+
|
|
324
|
+
{/* Vertical Crashlytics Histogram Bars */}
|
|
325
|
+
{histogramData.buckets.map((b, idx) => {
|
|
326
|
+
const isSelected = selectedBucketIdx === idx;
|
|
327
|
+
const barWidth = 10;
|
|
328
|
+
const barX = b.x - barWidth / 2;
|
|
329
|
+
const barHeight = Math.max(3, chartHeight - 8 - b.y);
|
|
330
|
+
const barY = chartHeight - 8 - barHeight;
|
|
331
|
+
return (<G key={`bar-${b.index}`}>
|
|
332
|
+
{/* Background slot */}
|
|
333
|
+
<Rect x={barX} y={6} width={barWidth} height={chartHeight - 14} rx={3} fill={isSelected ? '#EDE9FE' : '#F8FAFC'}/>
|
|
334
|
+
|
|
335
|
+
{/* Active Event Bar */}
|
|
336
|
+
{b.total > 0 && (<Rect x={barX} y={barY} width={barWidth} height={barHeight} rx={3} fill="url(#crashlyticsBarGrad)"/>)}
|
|
337
|
+
|
|
338
|
+
{/* Selected Cursor Line */}
|
|
339
|
+
{isSelected && (<Line x1={b.x} y1={4} x2={b.x} y2={chartHeight - 6} stroke="#8B5CF6" strokeWidth="1.5" strokeDasharray="2,2"/>)}
|
|
340
|
+
</G>);
|
|
341
|
+
})}
|
|
342
|
+
|
|
343
|
+
{/* Crashlytics Apex Trendline */}
|
|
344
|
+
<Path d={histogramData.linePath} fill="none" stroke="#8B5CF6" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
345
|
+
|
|
346
|
+
{/* Pulsing Dot on Current Active Time Bucket */}
|
|
347
|
+
{histogramData.buckets.length > 0 && (<G>
|
|
348
|
+
<Circle cx={histogramData.buckets[histogramData.buckets.length - 1].x} cy={histogramData.buckets[histogramData.buckets.length - 1].y} r="5.5" fill="#8B5CF6" opacity="0.25"/>
|
|
349
|
+
<Circle cx={histogramData.buckets[histogramData.buckets.length - 1].x} cy={histogramData.buckets[histogramData.buckets.length - 1].y} r="3" fill="#7C3AED"/>
|
|
350
|
+
</G>)}
|
|
351
|
+
</Svg>
|
|
352
|
+
|
|
353
|
+
{/* Invisible Touch Columns for Interactive Bucket Selection */}
|
|
354
|
+
<View style={{
|
|
355
|
+
position: 'absolute',
|
|
356
|
+
top: 0,
|
|
357
|
+
left: 0,
|
|
358
|
+
right: 0,
|
|
359
|
+
bottom: 0,
|
|
278
360
|
flexDirection: 'row',
|
|
279
|
-
alignItems: 'flex-end',
|
|
280
|
-
justifyContent: 'space-between',
|
|
281
|
-
paddingHorizontal: 2,
|
|
282
|
-
paddingBottom: 2,
|
|
283
|
-
borderBottomWidth: 1,
|
|
284
|
-
borderBottomColor: AppColors.dividerColor,
|
|
285
361
|
}}>
|
|
286
|
-
|
|
287
|
-
|
|
362
|
+
{histogramData.buckets.map((b, idx) => (<TouchableOpacity key={b.index} style={{ flex: 1, height: '100%' }} activeOpacity={0.8} onPress={() => setSelectedBucketIdx(selectedBucketIdx === idx ? null : idx)}/>))}
|
|
363
|
+
</View>
|
|
364
|
+
</View>
|
|
365
|
+
|
|
366
|
+
{/* Selected Bucket Quick Breakdown Overlay */}
|
|
367
|
+
{selectedBucket && (<View style={{
|
|
368
|
+
flexDirection: 'row',
|
|
288
369
|
alignItems: 'center',
|
|
289
|
-
justifyContent: '
|
|
290
|
-
|
|
291
|
-
|
|
370
|
+
justifyContent: 'space-between',
|
|
371
|
+
backgroundColor: AppColors.white,
|
|
372
|
+
borderRadius: 6,
|
|
373
|
+
paddingHorizontal: 8,
|
|
374
|
+
paddingVertical: 4,
|
|
375
|
+
marginTop: 4,
|
|
376
|
+
borderWidth: 1,
|
|
377
|
+
borderColor: AppColors.brandPurple,
|
|
292
378
|
}}>
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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`,
|
|
379
|
+
<Text style={{
|
|
380
|
+
fontFamily: AppFonts.interBold,
|
|
381
|
+
fontSize: 10,
|
|
382
|
+
color: AppColors.primaryBlack,
|
|
313
383
|
}}>
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
</>) : (<View style={{
|
|
332
|
-
height: 2.5,
|
|
333
|
-
backgroundColor: `${AppColors.grayBorderSecondary}80`,
|
|
334
|
-
}}/>)}
|
|
335
|
-
</View>
|
|
336
|
-
</View>))}
|
|
337
|
-
</View>
|
|
384
|
+
{t('analytics.selectedBucket', {
|
|
385
|
+
time: selectedBucket.ageLabel,
|
|
386
|
+
count: selectedBucket.total,
|
|
387
|
+
})}
|
|
388
|
+
</Text>
|
|
389
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
390
|
+
{selectedBucket.ecommerce > 0 && (<Text style={{ fontSize: 9, fontFamily: AppFonts.interBold, color: AppColors.amber700 }}>
|
|
391
|
+
Ecom: {selectedBucket.ecommerce}
|
|
392
|
+
</Text>)}
|
|
393
|
+
{selectedBucket.page_view > 0 && (<Text style={{ fontSize: 9, fontFamily: AppFonts.interBold, color: AppColors.sky500 }}>
|
|
394
|
+
Screen: {selectedBucket.page_view}
|
|
395
|
+
</Text>)}
|
|
396
|
+
{selectedBucket.custom > 0 && (<Text style={{ fontSize: 9, fontFamily: AppFonts.interBold, color: AppColors.brandPurple }}>
|
|
397
|
+
Custom: {selectedBucket.custom}
|
|
398
|
+
</Text>)}
|
|
399
|
+
</View>
|
|
400
|
+
</View>)}
|
|
338
401
|
|
|
339
402
|
{/* Timeline Axis Labels */}
|
|
340
403
|
<View style={{
|
|
341
404
|
flexDirection: 'row',
|
|
342
405
|
justifyContent: 'space-between',
|
|
343
406
|
marginTop: 4,
|
|
344
|
-
paddingHorizontal:
|
|
407
|
+
paddingHorizontal: 4,
|
|
345
408
|
}}>
|
|
346
409
|
<Text style={{
|
|
347
410
|
fontFamily: AppFonts.interMedium,
|
|
348
|
-
fontSize: 8,
|
|
411
|
+
fontSize: 8.5,
|
|
349
412
|
color: AppColors.grayTextWeak,
|
|
350
413
|
}}>
|
|
351
|
-
|
|
414
|
+
{t('analytics.time30mAgo')}
|
|
352
415
|
</Text>
|
|
353
416
|
<Text style={{
|
|
354
417
|
fontFamily: AppFonts.interMedium,
|
|
355
|
-
fontSize: 8,
|
|
418
|
+
fontSize: 8.5,
|
|
356
419
|
color: AppColors.grayTextWeak,
|
|
357
420
|
}}>
|
|
358
|
-
|
|
421
|
+
{t('analytics.time20mAgo')}
|
|
359
422
|
</Text>
|
|
360
423
|
<Text style={{
|
|
361
424
|
fontFamily: AppFonts.interMedium,
|
|
362
|
-
fontSize: 8,
|
|
425
|
+
fontSize: 8.5,
|
|
363
426
|
color: AppColors.grayTextWeak,
|
|
364
427
|
}}>
|
|
365
|
-
|
|
428
|
+
{t('analytics.time10mAgo')}
|
|
366
429
|
</Text>
|
|
367
430
|
<Text style={{
|
|
368
431
|
fontFamily: AppFonts.interBold,
|
|
369
|
-
fontSize: 8,
|
|
432
|
+
fontSize: 8.5,
|
|
370
433
|
color: AppColors.brandPurple,
|
|
371
434
|
}}>
|
|
372
|
-
|
|
435
|
+
{t('analytics.timeNow')}
|
|
373
436
|
</Text>
|
|
374
437
|
</View>
|
|
375
438
|
</View>
|
|
@@ -648,26 +711,26 @@ const AnalyticsHeader = React.memo(() => {
|
|
|
648
711
|
<View style={styles.analyticsStatsRow}>
|
|
649
712
|
<View style={styles.analyticsStatBox}>
|
|
650
713
|
<Text style={styles.analyticsStatValue}>{totalEvents}</Text>
|
|
651
|
-
<Text style={styles.analyticsStatLabel}>
|
|
714
|
+
<Text style={styles.analyticsStatLabel}>{t('analytics.eventCount', { count: totalEvents })}</Text>
|
|
652
715
|
</View>
|
|
653
716
|
<View style={styles.analyticsStatBox}>
|
|
654
717
|
<Text style={styles.analyticsStatValue}>
|
|
655
718
|
{Object.keys(userProperties).length}
|
|
656
719
|
</Text>
|
|
657
|
-
<Text style={styles.analyticsStatLabel}>
|
|
720
|
+
<Text style={styles.analyticsStatLabel}>{t('analytics.props')}</Text>
|
|
658
721
|
</View>
|
|
659
722
|
<View style={styles.analyticsStatBox}>
|
|
660
723
|
<Text style={styles.analyticsStatValue}>
|
|
661
724
|
{Object.keys(defaultParams).length}
|
|
662
725
|
</Text>
|
|
663
|
-
<Text style={styles.analyticsStatLabel}>
|
|
726
|
+
<Text style={styles.analyticsStatLabel}>{t('analytics.defaultParameters')}</Text>
|
|
664
727
|
</View>
|
|
665
728
|
</View>
|
|
666
729
|
|
|
667
730
|
{/* Details Section */}
|
|
668
731
|
{(hasUserProps || hasDefaultParams) && (<View style={styles.analyticsHeaderDetails}>
|
|
669
732
|
{hasUserProps && (<View style={{ marginBottom: 10 }}>
|
|
670
|
-
<Text style={styles.detailsGroupTitle}>
|
|
733
|
+
<Text style={styles.detailsGroupTitle}>{t('analytics.userProperties')}</Text>
|
|
671
734
|
{Object.entries(userProperties).map(([k, v]) => (<View key={k} style={styles.detailsRow}>
|
|
672
735
|
<Text style={styles.detailsKey} selectable={true}>{k}</Text>
|
|
673
736
|
<Text style={styles.detailsValue} selectable={true}>{String(v)}</Text>
|
|
@@ -675,7 +738,7 @@ const AnalyticsHeader = React.memo(() => {
|
|
|
675
738
|
</View>)}
|
|
676
739
|
|
|
677
740
|
{hasDefaultParams && (<View>
|
|
678
|
-
<Text style={styles.detailsGroupTitle}>
|
|
741
|
+
<Text style={styles.detailsGroupTitle}>{t('analytics.defaultParameters')}</Text>
|
|
679
742
|
{Object.entries(defaultParams).map(([k, v]) => (<View key={k} style={styles.detailsRow}>
|
|
680
743
|
<Text style={styles.detailsKey} selectable={true}>{k}</Text>
|
|
681
744
|
<Text style={styles.detailsValue} selectable={true}>{String(v)}</Text>
|
|
@@ -686,6 +749,7 @@ const AnalyticsHeader = React.memo(() => {
|
|
|
686
749
|
</View>);
|
|
687
750
|
});
|
|
688
751
|
const AnalyticsTab = React.memo(() => {
|
|
752
|
+
const { t } = useTranslation();
|
|
689
753
|
const { filteredAnalyticsEvents, analyticsSearch, setAnalyticsSearch, handleDelete, selectedEvent, setSelectedEvent, newEventIds, logRouteMapRef, isAnalyticsLayoutReady, setIsAnalyticsLayoutReady, isAnalyticsPaused, setIsAnalyticsPaused, isAnalyticsFilterApplied, } = useInspector();
|
|
690
754
|
const [isFilterModalOpen, setIsFilterModalOpen] = useState(false);
|
|
691
755
|
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;
|