react-native-inapp-inspector 1.1.35 → 2.0.1
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/README.md +123 -101
- package/android/build.gradle +13 -1
- package/android/src/main/java/com/inappinspector/NetworkInspectorModule.kt +890 -0
- package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.kt +17 -0
- package/dist/commonjs/components/ConsoleLogCard.js +5 -5
- package/dist/commonjs/components/CopyButton.js +3 -0
- package/dist/commonjs/components/EndOfListFooter.js +1 -1
- package/dist/commonjs/components/ErrorBoundary.js +3 -0
- package/dist/commonjs/components/Inspector/AnalyticsTab.js +25 -73
- package/dist/commonjs/components/Inspector/BundleTab.js +671 -189
- package/dist/commonjs/components/Inspector/ConsoleTab.js +110 -99
- package/dist/commonjs/components/Inspector/CrashTab.js +99 -91
- package/dist/commonjs/components/Inspector/InspectorHeader.js +107 -20
- package/dist/commonjs/components/Inspector/MainScreen.js +111 -68
- package/dist/commonjs/components/Inspector/NetworkTab.js +151 -30
- package/dist/commonjs/components/Inspector/PerformanceTab.js +96 -3
- package/dist/commonjs/components/Inspector/ReduxTab.js +18 -10
- package/dist/commonjs/components/Inspector/SettingsPanel.js +1218 -1238
- package/dist/commonjs/components/Inspector/TabBar.js +4 -2
- package/dist/commonjs/components/JsonViewer.js +9 -4
- package/dist/commonjs/components/LogCard.js +31 -1
- package/dist/commonjs/components/LogSyntaxHighlighter.d.ts +16 -0
- package/dist/commonjs/components/LogSyntaxHighlighter.js +130 -0
- package/dist/commonjs/components/NetworkIcons.d.ts +3 -0
- package/dist/commonjs/components/NetworkIcons.js +15 -1
- package/dist/commonjs/components/Slider.d.ts +13 -0
- package/dist/commonjs/components/Slider.js +261 -0
- package/dist/commonjs/components/TouchableScale.d.ts +1 -0
- package/dist/commonjs/components/TouchableScale.js +30 -32
- package/dist/commonjs/constants/index.d.ts +1 -2
- package/dist/commonjs/constants/index.js +4 -0
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/customHooks/analyticsLogger.d.ts +4 -0
- package/dist/commonjs/customHooks/analyticsLogger.js +8 -6
- package/dist/commonjs/customHooks/crashHandler.js +8 -1
- package/dist/commonjs/helpers/index.d.ts +1 -0
- package/dist/commonjs/helpers/index.js +25 -1
- package/dist/commonjs/helpers/searchQueryParser.d.ts +6 -0
- package/dist/commonjs/helpers/searchQueryParser.js +236 -0
- package/dist/commonjs/helpers/settingsStore.d.ts +9 -0
- package/dist/commonjs/helpers/settingsStore.js +59 -3
- package/dist/commonjs/index.d.ts +1 -0
- package/dist/commonjs/index.js +220 -43
- package/dist/commonjs/native/NativeInspector.d.ts +125 -0
- package/dist/commonjs/native/NativeInspector.js +313 -0
- package/dist/commonjs/native/NativeNetworkInspector.d.ts +22 -0
- package/dist/commonjs/native/NativeNetworkInspector.js +4 -0
- package/dist/commonjs/styles/index.js +41 -41
- package/dist/commonjs/types/enums.d.ts +1 -1
- package/dist/commonjs/types/interfaces.d.ts +9 -0
- package/dist/esm/components/ConsoleLogCard.js +5 -5
- package/dist/esm/components/CopyButton.js +3 -0
- package/dist/esm/components/EndOfListFooter.js +2 -2
- package/dist/esm/components/ErrorBoundary.js +3 -0
- package/dist/esm/components/Inspector/AnalyticsTab.js +27 -75
- package/dist/esm/components/Inspector/BundleTab.js +671 -189
- package/dist/esm/components/Inspector/ConsoleTab.js +111 -100
- package/dist/esm/components/Inspector/CrashTab.js +99 -91
- package/dist/esm/components/Inspector/InspectorHeader.js +108 -21
- package/dist/esm/components/Inspector/MainScreen.js +113 -70
- package/dist/esm/components/Inspector/NetworkTab.js +152 -31
- package/dist/esm/components/Inspector/PerformanceTab.js +96 -3
- package/dist/esm/components/Inspector/ReduxTab.js +18 -10
- package/dist/esm/components/Inspector/SettingsPanel.js +1222 -1242
- package/dist/esm/components/Inspector/TabBar.js +4 -2
- package/dist/esm/components/JsonViewer.js +9 -4
- package/dist/esm/components/LogCard.js +32 -2
- package/dist/esm/components/LogSyntaxHighlighter.d.ts +16 -0
- package/dist/esm/components/LogSyntaxHighlighter.js +123 -0
- package/dist/esm/components/NetworkIcons.d.ts +3 -0
- package/dist/esm/components/NetworkIcons.js +11 -0
- package/dist/esm/components/Slider.d.ts +13 -0
- package/dist/esm/components/Slider.js +222 -0
- package/dist/esm/components/TouchableScale.d.ts +1 -0
- package/dist/esm/components/TouchableScale.js +31 -33
- package/dist/esm/constants/index.d.ts +1 -2
- package/dist/esm/constants/index.js +4 -0
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/customHooks/analyticsLogger.d.ts +4 -0
- package/dist/esm/customHooks/analyticsLogger.js +6 -5
- package/dist/esm/customHooks/crashHandler.js +8 -1
- package/dist/esm/helpers/index.d.ts +1 -0
- package/dist/esm/helpers/index.js +11 -1
- package/dist/esm/helpers/searchQueryParser.d.ts +6 -0
- package/dist/esm/helpers/searchQueryParser.js +233 -0
- package/dist/esm/helpers/settingsStore.d.ts +9 -0
- package/dist/esm/helpers/settingsStore.js +58 -3
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +207 -44
- package/dist/esm/native/NativeInspector.d.ts +125 -0
- package/dist/esm/native/NativeInspector.js +289 -0
- package/dist/esm/native/NativeNetworkInspector.d.ts +22 -0
- package/dist/esm/native/NativeNetworkInspector.js +2 -0
- package/dist/esm/styles/index.js +41 -41
- package/dist/esm/types/enums.d.ts +1 -1
- package/dist/esm/types/interfaces.d.ts +9 -0
- package/ios/NetworkInspectorModule.h +6 -0
- package/ios/NetworkInspectorModule.m +760 -5
- package/package.json +13 -2
- package/react-native-inapp-inspector.podspec +7 -2
- package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +0 -97
- package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.java +0 -28
|
@@ -55,39 +55,70 @@ const NetworkIcons_1 = require("../NetworkIcons");
|
|
|
55
55
|
const STATUS_META = {
|
|
56
56
|
ALL: { color: AppColors_1.AppColors.grayText, Icon: NetworkIcons_1.LayersIcon },
|
|
57
57
|
'2xx': { color: AppColors_1.AppColors.greenColor, Icon: NetworkIcons_1.CircleCheckIcon },
|
|
58
|
+
'200': { color: AppColors_1.AppColors.greenColor, Icon: NetworkIcons_1.CircleCheckIcon },
|
|
58
59
|
'3xx': { color: AppColors_1.AppColors.warningIconGold, Icon: NetworkIcons_1.CircleAlertIcon },
|
|
60
|
+
'300': { color: AppColors_1.AppColors.warningIconGold, Icon: NetworkIcons_1.CircleAlertIcon },
|
|
59
61
|
'4xx': { color: AppColors_1.AppColors.darkOrange, Icon: NetworkIcons_1.CircleAlertIcon },
|
|
62
|
+
'400': { color: AppColors_1.AppColors.darkOrange, Icon: NetworkIcons_1.CircleAlertIcon },
|
|
63
|
+
'404': { color: AppColors_1.AppColors.darkOrange, Icon: NetworkIcons_1.CircleAlertIcon },
|
|
60
64
|
'5xx': { color: AppColors_1.AppColors.errorColor, Icon: NetworkIcons_1.CircleXIcon },
|
|
65
|
+
'500': { color: AppColors_1.AppColors.errorColor, Icon: NetworkIcons_1.CircleXIcon },
|
|
61
66
|
Failed: { color: AppColors_1.AppColors.errorColor, Icon: NetworkIcons_1.CircleXIcon },
|
|
62
67
|
};
|
|
68
|
+
const getStatusMeta = (filter) => {
|
|
69
|
+
if (STATUS_META[filter])
|
|
70
|
+
return STATUS_META[filter];
|
|
71
|
+
const num = parseInt(filter, 10);
|
|
72
|
+
if (!isNaN(num)) {
|
|
73
|
+
if (num >= 200 && num < 300)
|
|
74
|
+
return { color: AppColors_1.AppColors.greenColor, Icon: NetworkIcons_1.CircleCheckIcon };
|
|
75
|
+
if (num >= 300 && num < 400)
|
|
76
|
+
return { color: AppColors_1.AppColors.warningIconGold, Icon: NetworkIcons_1.CircleAlertIcon };
|
|
77
|
+
if (num >= 400 && num < 500)
|
|
78
|
+
return { color: AppColors_1.AppColors.darkOrange, Icon: NetworkIcons_1.CircleAlertIcon };
|
|
79
|
+
if (num >= 500)
|
|
80
|
+
return { color: AppColors_1.AppColors.errorColor, Icon: NetworkIcons_1.CircleXIcon };
|
|
81
|
+
}
|
|
82
|
+
if (filter.startsWith('2'))
|
|
83
|
+
return { color: AppColors_1.AppColors.greenColor, Icon: NetworkIcons_1.CircleCheckIcon };
|
|
84
|
+
if (filter.startsWith('3'))
|
|
85
|
+
return { color: AppColors_1.AppColors.warningIconGold, Icon: NetworkIcons_1.CircleAlertIcon };
|
|
86
|
+
if (filter.startsWith('4'))
|
|
87
|
+
return { color: AppColors_1.AppColors.darkOrange, Icon: NetworkIcons_1.CircleAlertIcon };
|
|
88
|
+
if (filter.startsWith('5'))
|
|
89
|
+
return { color: AppColors_1.AppColors.errorColor, Icon: NetworkIcons_1.CircleXIcon };
|
|
90
|
+
return { color: AppColors_1.AppColors.grayText, Icon: NetworkIcons_1.LayersIcon };
|
|
91
|
+
};
|
|
63
92
|
const FilterChip = react_1.default.memo(({ label, color, Icon, badge, active, onPress, }) => {
|
|
64
93
|
const iconColor = active ? color : AppColors_1.AppColors.grayTextWeak;
|
|
65
94
|
return (<TouchableScale_1.default style={styles_1.default.statusFilterWrap} onPress={onPress} hitSlop={10}>
|
|
66
95
|
<react_native_1.View style={[
|
|
67
96
|
styles_1.default.statusFilterChip,
|
|
97
|
+
{
|
|
98
|
+
paddingHorizontal: 9,
|
|
99
|
+
paddingVertical: 5,
|
|
100
|
+
borderRadius: 7,
|
|
101
|
+
borderWidth: 1,
|
|
102
|
+
borderColor: active ? color : `${AppColors_1.AppColors.grayBorderSecondary}`,
|
|
103
|
+
backgroundColor: active ? `${color}14` : AppColors_1.AppColors.grayBackground,
|
|
104
|
+
},
|
|
68
105
|
active && {
|
|
69
|
-
|
|
70
|
-
|
|
106
|
+
shadowColor: color,
|
|
107
|
+
shadowOffset: { width: 0, height: 1 },
|
|
108
|
+
shadowOpacity: 0.2,
|
|
109
|
+
shadowRadius: 2,
|
|
110
|
+
elevation: 1.5,
|
|
71
111
|
},
|
|
72
112
|
]}>
|
|
73
|
-
<react_native_1.View style={[
|
|
74
|
-
styles_1.default.filterCheckbox,
|
|
75
|
-
active && [
|
|
76
|
-
styles_1.default.filterCheckboxActive,
|
|
77
|
-
{ backgroundColor: color },
|
|
78
|
-
],
|
|
79
|
-
]}>
|
|
80
|
-
{active && (<NetworkIcons_1.CheckIcon color={AppColors_1.AppColors.white} size={10}/>)}
|
|
81
|
-
</react_native_1.View>
|
|
82
113
|
{badge != null ? (<react_native_1.View style={[
|
|
83
114
|
styles_1.default.methodBadgeMini,
|
|
84
115
|
{ backgroundColor: badge },
|
|
85
116
|
]}/>) : (Icon && (<Icon color={iconColor} size={13}/>))}
|
|
86
117
|
<react_native_1.Text numberOfLines={1} style={[
|
|
87
118
|
styles_1.default.statusFilterText,
|
|
88
|
-
|
|
89
|
-
color,
|
|
90
|
-
fontFamily: AppFonts_1.AppFonts.interBold,
|
|
119
|
+
{
|
|
120
|
+
color: active ? color : AppColors_1.AppColors.grayText,
|
|
121
|
+
fontFamily: active ? AppFonts_1.AppFonts.interBold : AppFonts_1.AppFonts.interMedium,
|
|
91
122
|
},
|
|
92
123
|
]}>
|
|
93
124
|
{label}
|
|
@@ -100,6 +131,19 @@ const NetworkTab = react_1.default.memo(() => {
|
|
|
100
131
|
const { t } = (0, i18n_1.useTranslation)();
|
|
101
132
|
const filtersAccordion = (0, useAccordion_1.default)(false, 300, 260);
|
|
102
133
|
const apisListRef = (0, react_1.useRef)(null);
|
|
134
|
+
const [isSearchFocused, setIsSearchFocused] = react_1.default.useState(false);
|
|
135
|
+
const QUICK_API_QUERY_TAGS = (0, react_1.useMemo)(() => [
|
|
136
|
+
{ label: '⚠️ Failed', query: 'is:error' },
|
|
137
|
+
{ label: '🐢 Slow >1s', query: 'slow:>1s' },
|
|
138
|
+
{ label: 'POST', query: 'm:POST' },
|
|
139
|
+
{ label: 'GET', query: 'm:GET' },
|
|
140
|
+
{ label: 'GraphQL', query: 'is:graphql' },
|
|
141
|
+
{ label: 'Axios', query: 'client:axios' },
|
|
142
|
+
{ label: '200 OK', query: 'status:200' },
|
|
143
|
+
{ label: '404', query: 'status:404' },
|
|
144
|
+
{ label: '500', query: 'status:500' },
|
|
145
|
+
{ label: '🔒 HTTPS', query: 'is:https' },
|
|
146
|
+
], []);
|
|
103
147
|
const networkMetrics = (0, react_1.useMemo)(() => {
|
|
104
148
|
if (!logs || logs.length === 0)
|
|
105
149
|
return null;
|
|
@@ -172,7 +216,7 @@ const NetworkTab = react_1.default.memo(() => {
|
|
|
172
216
|
setSelected,
|
|
173
217
|
]);
|
|
174
218
|
return (<react_native_1.View style={{ flex: 1 }}>
|
|
175
|
-
<react_native_1.FlatList ref={apisListRef} data={groupedData} keyExtractor={item => item?.id?.toString()} renderItem={renderItem} initialNumToRender={
|
|
219
|
+
<react_native_1.FlatList ref={apisListRef} data={groupedData} keyExtractor={item => item?.id?.toString()} renderItem={renderItem} initialNumToRender={12} maxToRenderPerBatch={8} windowSize={5} removeClippedSubviews={true} renderToHardwareTextureAndroid={true} ListHeaderComponent={<react_native_1.View style={{ marginTop: 6 }}>
|
|
176
220
|
{/* Network Health & Telemetry Strip */}
|
|
177
221
|
{networkMetrics != null && networkMetrics.totalCount > 0 && (<react_native_1.View style={{
|
|
178
222
|
flexDirection: 'row',
|
|
@@ -234,12 +278,38 @@ const NetworkTab = react_1.default.memo(() => {
|
|
|
234
278
|
</react_native_1.View>)}
|
|
235
279
|
|
|
236
280
|
<react_native_1.View style={styles_1.default.toolbarRow}>
|
|
237
|
-
<react_native_1.View style={
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
281
|
+
<react_native_1.View style={[
|
|
282
|
+
styles_1.default.searchContainer,
|
|
283
|
+
isSearchFocused && {
|
|
284
|
+
borderColor: AppColors_1.AppColors.purple,
|
|
285
|
+
borderWidth: 1.5,
|
|
286
|
+
},
|
|
287
|
+
]}>
|
|
288
|
+
<NetworkIcons_1.SearchIcon color={isSearchFocused ? AppColors_1.AppColors.purple : AppColors_1.AppColors.grayTextWeak} size={16}/>
|
|
289
|
+
<react_native_1.TextInput placeholder="Search url, body, is:error, slow:>1s..." placeholderTextColor={AppColors_1.AppColors.grayTextWeak} value={search} onChangeText={setSearch} onFocus={() => setIsSearchFocused(true)} onBlur={() => setIsSearchFocused(false)} style={styles_1.default.searchInput} autoCorrect={false} autoCapitalize="none"/>
|
|
290
|
+
{search.length > 0 && (<react_native_1.View style={{
|
|
291
|
+
flexDirection: 'row',
|
|
292
|
+
alignItems: 'center',
|
|
293
|
+
gap: 4,
|
|
294
|
+
}}>
|
|
295
|
+
<react_native_1.View style={{
|
|
296
|
+
backgroundColor: `${AppColors_1.AppColors.purple}20`,
|
|
297
|
+
borderRadius: 10,
|
|
298
|
+
paddingHorizontal: 6,
|
|
299
|
+
paddingVertical: 2,
|
|
300
|
+
}}>
|
|
301
|
+
<react_native_1.Text style={{
|
|
302
|
+
color: AppColors_1.AppColors.purple,
|
|
303
|
+
fontSize: 10,
|
|
304
|
+
fontFamily: AppFonts_1.AppFonts.interBold,
|
|
305
|
+
}}>
|
|
306
|
+
{filteredLogs.length}
|
|
307
|
+
</react_native_1.Text>
|
|
308
|
+
</react_native_1.View>
|
|
309
|
+
<react_native_1.Pressable onPress={() => setSearch('')} hitSlop={10} style={styles_1.default.clearBtn}>
|
|
310
|
+
<NetworkIcons_1.ClearIcon color={AppColors_1.AppColors.grayTextWeak} size={14}/>
|
|
311
|
+
</react_native_1.Pressable>
|
|
312
|
+
</react_native_1.View>)}
|
|
243
313
|
</react_native_1.View>
|
|
244
314
|
|
|
245
315
|
<react_native_1.View style={styles_1.default.toolbarRight}>
|
|
@@ -286,6 +356,49 @@ const NetworkTab = react_1.default.memo(() => {
|
|
|
286
356
|
</react_native_1.View>
|
|
287
357
|
</react_native_1.View>
|
|
288
358
|
|
|
359
|
+
{/* Advanced Search Quick Query Suggestions Bar */}
|
|
360
|
+
{(isSearchFocused || search.length > 0) && (<react_native_1.ScrollView horizontal showsHorizontalScrollIndicator={false} style={{ marginBottom: 6, maxHeight: 30 }} contentContainerStyle={{
|
|
361
|
+
paddingHorizontal: 12,
|
|
362
|
+
flexDirection: 'row',
|
|
363
|
+
alignItems: 'center',
|
|
364
|
+
gap: 6,
|
|
365
|
+
}}>
|
|
366
|
+
{QUICK_API_QUERY_TAGS.map(item => {
|
|
367
|
+
const isSelected = search.includes(item.query);
|
|
368
|
+
return (<TouchableScale_1.default key={item.query} onPress={() => {
|
|
369
|
+
if (isSelected) {
|
|
370
|
+
setSearch(prev => prev.replace(item.query, '').trim());
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
setSearch(prev => prev ? `${prev} ${item.query}`.trim() : item.query);
|
|
374
|
+
}
|
|
375
|
+
}}>
|
|
376
|
+
<react_native_1.View style={{
|
|
377
|
+
paddingHorizontal: 8,
|
|
378
|
+
paddingVertical: 3.5,
|
|
379
|
+
borderRadius: 6,
|
|
380
|
+
backgroundColor: isSelected
|
|
381
|
+
? AppColors_1.AppColors.purple
|
|
382
|
+
: `${AppColors_1.AppColors.purple}14`,
|
|
383
|
+
borderWidth: 1,
|
|
384
|
+
borderColor: isSelected
|
|
385
|
+
? AppColors_1.AppColors.purple
|
|
386
|
+
: `${AppColors_1.AppColors.purple}30`,
|
|
387
|
+
}}>
|
|
388
|
+
<react_native_1.Text style={{
|
|
389
|
+
fontFamily: AppFonts_1.AppFonts.interBold,
|
|
390
|
+
fontSize: 10,
|
|
391
|
+
color: isSelected
|
|
392
|
+
? AppColors_1.AppColors.white
|
|
393
|
+
: AppColors_1.AppColors.purple,
|
|
394
|
+
}}>
|
|
395
|
+
{item.label}
|
|
396
|
+
</react_native_1.Text>
|
|
397
|
+
</react_native_1.View>
|
|
398
|
+
</TouchableScale_1.default>);
|
|
399
|
+
})}
|
|
400
|
+
</react_native_1.ScrollView>)}
|
|
401
|
+
|
|
289
402
|
<react_native_1.Animated.View style={[
|
|
290
403
|
filtersAccordion.bodyStyle,
|
|
291
404
|
{ overflow: 'hidden' },
|
|
@@ -300,10 +413,7 @@ const NetworkTab = react_1.default.memo(() => {
|
|
|
300
413
|
const active = isAll
|
|
301
414
|
? statusFilters.size === 0
|
|
302
415
|
: statusFilters.has(filter);
|
|
303
|
-
const meta =
|
|
304
|
-
color: AppColors_1.AppColors.grayText,
|
|
305
|
-
Icon: NetworkIcons_1.LayersIcon,
|
|
306
|
-
};
|
|
416
|
+
const meta = getStatusMeta(filter);
|
|
307
417
|
return (<FilterChip key={filter} label={filter} color={meta.color} Icon={meta.Icon} active={active} onPress={() => {
|
|
308
418
|
if (isAll) {
|
|
309
419
|
setStatusFilters(new Set());
|
|
@@ -367,11 +477,22 @@ const NetworkTab = react_1.default.memo(() => {
|
|
|
367
477
|
]} keyboardShouldPersistTaps="handled"/>
|
|
368
478
|
{/* #2 — always-visible scroll-to-top, bottom right */}
|
|
369
479
|
<TouchableScale_1.default onPress={() => {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
480
|
+
try {
|
|
481
|
+
apisListRef.current?.scrollToOffset({
|
|
482
|
+
offset: 0,
|
|
483
|
+
animated: true,
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
catch {
|
|
487
|
+
try {
|
|
488
|
+
apisListRef.current?.scrollToIndex({
|
|
489
|
+
index: 0,
|
|
490
|
+
animated: true,
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
catch { }
|
|
494
|
+
}
|
|
495
|
+
}} hitSlop={12} style={styles_1.default.scrollTopBtn}>
|
|
375
496
|
<react_native_1.View style={{ transform: [{ rotate: '180deg' }] }}>
|
|
376
497
|
<NetworkIcons_1.ChevronIcon color={AppColors_1.AppColors.white} size={18}/>
|
|
377
498
|
</react_native_1.View>
|
|
@@ -49,6 +49,7 @@ const i18n_1 = require("../../i18n");
|
|
|
49
49
|
const helpers_1 = require("../../helpers");
|
|
50
50
|
const NetworkIcons_1 = require("../NetworkIcons");
|
|
51
51
|
const performanceTracker_1 = require("../../customHooks/performanceTracker");
|
|
52
|
+
const NativeInspector_1 = require("../../native/NativeInspector");
|
|
52
53
|
const PerformanceTab = react_1.default.memo(() => {
|
|
53
54
|
const { t } = (0, i18n_1.useTranslation)();
|
|
54
55
|
// ─── Real-Time FPS Tracking ────────────────────────────────────────────────
|
|
@@ -87,6 +88,30 @@ const PerformanceTab = react_1.default.memo(() => {
|
|
|
87
88
|
// ─── Live Frame Measurement Loop (1-Second Batch Interval) ────────────────
|
|
88
89
|
const lastFrameTimeRef = (0, react_1.useRef)(Date.now());
|
|
89
90
|
const rafIdRef = (0, react_1.useRef)(null);
|
|
91
|
+
const [systemMetrics, setSystemMetrics] = (0, react_1.useState)(null);
|
|
92
|
+
(0, react_1.useEffect)(() => {
|
|
93
|
+
let isMounted = true;
|
|
94
|
+
const fetchMetrics = async () => {
|
|
95
|
+
const metrics = await (0, NativeInspector_1.getNativeSystemMetrics)();
|
|
96
|
+
if (isMounted && metrics) {
|
|
97
|
+
setSystemMetrics(metrics);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
fetchMetrics();
|
|
101
|
+
const interval = setInterval(fetchMetrics, 2000);
|
|
102
|
+
return () => {
|
|
103
|
+
isMounted = false;
|
|
104
|
+
clearInterval(interval);
|
|
105
|
+
};
|
|
106
|
+
}, []);
|
|
107
|
+
(0, react_1.useEffect)(() => {
|
|
108
|
+
if ((0, NativeInspector_1.isNativeModuleAvailable)()) {
|
|
109
|
+
(0, NativeInspector_1.startNativeFpsMonitoring)();
|
|
110
|
+
return () => {
|
|
111
|
+
(0, NativeInspector_1.stopNativeFpsMonitoring)();
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}, []);
|
|
90
115
|
(0, react_1.useEffect)(() => {
|
|
91
116
|
if (!isRecording)
|
|
92
117
|
return;
|
|
@@ -94,7 +119,7 @@ const PerformanceTab = react_1.default.memo(() => {
|
|
|
94
119
|
let frameCount = 0;
|
|
95
120
|
let maxLagInSecond = 0;
|
|
96
121
|
let lastSecond = Date.now();
|
|
97
|
-
const measureFrame = () => {
|
|
122
|
+
const measureFrame = async () => {
|
|
98
123
|
if (!isMounted)
|
|
99
124
|
return;
|
|
100
125
|
const now = Date.now();
|
|
@@ -109,7 +134,16 @@ const PerformanceTab = react_1.default.memo(() => {
|
|
|
109
134
|
// Update React state ONCE every 1 second (1000ms) to prevent UI shaking/re-rendering
|
|
110
135
|
if (now - lastSecond >= 1000) {
|
|
111
136
|
const elapsed = now - lastSecond;
|
|
112
|
-
|
|
137
|
+
let measuredFps = Math.min(120, Math.max(0, Math.round((frameCount * 1000) / elapsed)));
|
|
138
|
+
if ((0, NativeInspector_1.isNativeModuleAvailable)()) {
|
|
139
|
+
try {
|
|
140
|
+
const nativeMetrics = await (0, NativeInspector_1.getNativeFpsMetrics)();
|
|
141
|
+
if (nativeMetrics && nativeMetrics.fps > 0) {
|
|
142
|
+
measuredFps = Math.round(nativeMetrics.fps);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch { }
|
|
146
|
+
}
|
|
113
147
|
setCurrentFps(measuredFps);
|
|
114
148
|
setMinFps(prev => Math.min(prev, measuredFps));
|
|
115
149
|
setMaxFps(prev => Math.max(prev, measuredFps));
|
|
@@ -503,7 +537,7 @@ const PerformanceTab = react_1.default.memo(() => {
|
|
|
503
537
|
|
|
504
538
|
{/* Real-Time SVG Smooth Area & Trend Wave Graph */}
|
|
505
539
|
<react_native_1.View style={perfStyles.svgGraphContainer}>
|
|
506
|
-
<react_native_svg_1.default width="100%" height={svgGraphHeight} viewBox={`0 0 ${svgGraphWidth} ${svgGraphHeight}`}>
|
|
540
|
+
<react_native_svg_1.default width="100%" height={svgGraphHeight} viewBox={`0 0 ${svgGraphWidth} ${svgGraphHeight}`} preserveAspectRatio="none">
|
|
507
541
|
<react_native_svg_1.Defs>
|
|
508
542
|
<react_native_svg_1.LinearGradient id="fpsAreaGradient" x1="0" y1="0" x2="0" y2="1">
|
|
509
543
|
<react_native_svg_1.Stop offset="0%" stopColor={avgFps >= 55
|
|
@@ -566,6 +600,63 @@ const PerformanceTab = react_1.default.memo(() => {
|
|
|
566
600
|
</react_native_1.View>
|
|
567
601
|
</react_native_1.View>
|
|
568
602
|
|
|
603
|
+
{/* ── 1.5 NATIVE HARDWARE HEALTH & SYSTEM TELEMETRY CARD ── */}
|
|
604
|
+
<react_native_1.View style={perfStyles.budgetCard}>
|
|
605
|
+
<react_native_1.View style={perfStyles.budgetHeader}>
|
|
606
|
+
<react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
607
|
+
<NetworkIcons_1.BoltIcon color={AppColors_1.AppColors.brandPurple} size={15}/>
|
|
608
|
+
<react_native_1.View>
|
|
609
|
+
<react_native_1.Text style={perfStyles.budgetTitle}>Native Hardware & System Health</react_native_1.Text>
|
|
610
|
+
<react_native_1.Text style={perfStyles.heroSubTitle}>Live kernel metrics, resident RAM, and thermal state</react_native_1.Text>
|
|
611
|
+
</react_native_1.View>
|
|
612
|
+
</react_native_1.View>
|
|
613
|
+
{systemMetrics && (<react_native_1.View style={{
|
|
614
|
+
paddingHorizontal: 7,
|
|
615
|
+
paddingVertical: 2.5,
|
|
616
|
+
borderRadius: 5,
|
|
617
|
+
backgroundColor: systemMetrics.thermalState === 'nominal'
|
|
618
|
+
? `${AppColors_1.AppColors.emerald600}18`
|
|
619
|
+
: systemMetrics.thermalState === 'fair'
|
|
620
|
+
? `${AppColors_1.AppColors.amber800Warm}18`
|
|
621
|
+
: `${AppColors_1.AppColors.errorColor}18`,
|
|
622
|
+
}}>
|
|
623
|
+
<react_native_1.Text style={{
|
|
624
|
+
fontFamily: AppFonts_1.AppFonts.interBold,
|
|
625
|
+
fontSize: 9.5,
|
|
626
|
+
color: systemMetrics.thermalState === 'nominal'
|
|
627
|
+
? AppColors_1.AppColors.emerald600
|
|
628
|
+
: systemMetrics.thermalState === 'fair'
|
|
629
|
+
? AppColors_1.AppColors.amber800Warm
|
|
630
|
+
: AppColors_1.AppColors.errorColor,
|
|
631
|
+
textTransform: 'uppercase',
|
|
632
|
+
}}>
|
|
633
|
+
{systemMetrics.thermalState} THERMAL
|
|
634
|
+
</react_native_1.Text>
|
|
635
|
+
</react_native_1.View>)}
|
|
636
|
+
</react_native_1.View>
|
|
637
|
+
|
|
638
|
+
<react_native_1.View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8, marginTop: 10 }}>
|
|
639
|
+
<react_native_1.View style={{ flex: 1, minWidth: 100, backgroundColor: AppColors_1.AppColors.grayBackground, padding: 8, borderRadius: 8, borderWidth: 1, borderColor: AppColors_1.AppColors.dividerColor }}>
|
|
640
|
+
<react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interRegular, fontSize: 9.5, color: AppColors_1.AppColors.grayTextWeak }}>Resident RAM</react_native_1.Text>
|
|
641
|
+
<react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interBold, fontSize: 13, color: AppColors_1.AppColors.brandPurple, marginTop: 2 }}>
|
|
642
|
+
{systemMetrics ? `${systemMetrics.residentRamMb.toFixed(1)} MB` : '18.4 MB'}
|
|
643
|
+
</react_native_1.Text>
|
|
644
|
+
</react_native_1.View>
|
|
645
|
+
<react_native_1.View style={{ flex: 1, minWidth: 100, backgroundColor: AppColors_1.AppColors.grayBackground, padding: 8, borderRadius: 8, borderWidth: 1, borderColor: AppColors_1.AppColors.dividerColor }}>
|
|
646
|
+
<react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interRegular, fontSize: 9.5, color: AppColors_1.AppColors.grayTextWeak }}>Total Physical RAM</react_native_1.Text>
|
|
647
|
+
<react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interBold, fontSize: 13, color: AppColors_1.AppColors.primaryBlack, marginTop: 2 }}>
|
|
648
|
+
{systemMetrics ? `${Math.round(systemMetrics.totalPhysicalRamMb)} MB` : '3840 MB'}
|
|
649
|
+
</react_native_1.Text>
|
|
650
|
+
</react_native_1.View>
|
|
651
|
+
<react_native_1.View style={{ flex: 1, minWidth: 100, backgroundColor: AppColors_1.AppColors.grayBackground, padding: 8, borderRadius: 8, borderWidth: 1, borderColor: AppColors_1.AppColors.dividerColor }}>
|
|
652
|
+
<react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interRegular, fontSize: 9.5, color: AppColors_1.AppColors.grayTextWeak }}>Active CPU Cores</react_native_1.Text>
|
|
653
|
+
<react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interBold, fontSize: 13, color: AppColors_1.AppColors.skyBlue, marginTop: 2 }}>
|
|
654
|
+
{systemMetrics ? `${systemMetrics.activeCpuCores} Cores` : '6 Cores'}
|
|
655
|
+
</react_native_1.Text>
|
|
656
|
+
</react_native_1.View>
|
|
657
|
+
</react_native_1.View>
|
|
658
|
+
</react_native_1.View>
|
|
659
|
+
|
|
569
660
|
{/* ── 2. FRAME LATENCY DISTRIBUTION HISTOGRAM CARD ── */}
|
|
570
661
|
<react_native_1.View style={perfStyles.budgetCard}>
|
|
571
662
|
<react_native_1.View style={perfStyles.budgetHeader}>
|
|
@@ -1107,6 +1198,8 @@ const perfStyles = react_native_1.StyleSheet.create({
|
|
|
1107
1198
|
height: 64,
|
|
1108
1199
|
marginTop: 6,
|
|
1109
1200
|
position: 'relative',
|
|
1201
|
+
overflow: 'hidden',
|
|
1202
|
+
borderRadius: 8,
|
|
1110
1203
|
},
|
|
1111
1204
|
svgTouchOverlay: {
|
|
1112
1205
|
position: 'absolute',
|
|
@@ -214,10 +214,21 @@ const ReduxTab = react_1.default.memo(() => {
|
|
|
214
214
|
// Filter & Sort slices: by default, the most recently updated item is at the FIRST position
|
|
215
215
|
const filteredSlices = (0, react_1.useMemo)(() => {
|
|
216
216
|
let list = sliceItems;
|
|
217
|
-
if (reduxSearch.trim().length > 0) {
|
|
218
|
-
const
|
|
219
|
-
list = list.filter(item =>
|
|
220
|
-
|
|
217
|
+
if (reduxSearch && reduxSearch.trim().length > 0) {
|
|
218
|
+
const queryTokens = reduxSearch.trim().toLowerCase().split(/\s+/).filter(Boolean);
|
|
219
|
+
list = list.filter(item => {
|
|
220
|
+
const sliceVal = reduxState ? reduxState[item.name] : null;
|
|
221
|
+
const searchTarget = [
|
|
222
|
+
item.name || '',
|
|
223
|
+
item.lastAction?.type || '',
|
|
224
|
+
item.lastAction?.payloadPreview || '',
|
|
225
|
+
item.lastAction?.originType || '',
|
|
226
|
+
item.status || '',
|
|
227
|
+
item.statusMessage || '',
|
|
228
|
+
sliceVal ? (typeof sliceVal === 'string' ? sliceVal : JSON.stringify(sliceVal)) : '',
|
|
229
|
+
].join(' ').toLowerCase();
|
|
230
|
+
return queryTokens.every(tok => searchTarget.includes(tok));
|
|
231
|
+
});
|
|
221
232
|
}
|
|
222
233
|
return [...list].sort((a, b) => {
|
|
223
234
|
if (sortMode === 'latest') {
|
|
@@ -227,12 +238,9 @@ const ReduxTab = react_1.default.memo(() => {
|
|
|
227
238
|
}
|
|
228
239
|
return a.name.localeCompare(b.name);
|
|
229
240
|
}
|
|
230
|
-
|
|
231
|
-
// Alphabetical A-Z
|
|
232
|
-
return a.name.localeCompare(b.name);
|
|
233
|
-
}
|
|
241
|
+
return a.name.localeCompare(b.name);
|
|
234
242
|
});
|
|
235
|
-
}, [sliceItems, reduxSearch, sortMode]);
|
|
243
|
+
}, [sliceItems, reduxSearch, sortMode, reduxState]);
|
|
236
244
|
const lastGlobalAction = (0, react_1.useMemo)(() => {
|
|
237
245
|
const actions = Object.values(reduxLastActionMap);
|
|
238
246
|
if (actions.length === 0)
|
|
@@ -476,7 +484,7 @@ const ReduxTab = react_1.default.memo(() => {
|
|
|
476
484
|
</react_native_1.View>
|
|
477
485
|
|
|
478
486
|
{/* ─── FlatList of Slices ─── */}
|
|
479
|
-
<react_native_1.FlatList data={filteredSlices} keyExtractor={item => item.id} renderItem={renderSliceItem} initialNumToRender={
|
|
487
|
+
<react_native_1.FlatList data={filteredSlices} keyExtractor={item => item.id} renderItem={renderSliceItem} initialNumToRender={12} maxToRenderPerBatch={8} windowSize={5} removeClippedSubviews={true} renderToHardwareTextureAndroid={true} ListEmptyComponent={<EmptyState_1.default isSearch={reduxSearch.length > 0}/>} ListFooterComponent={filteredSlices.length > 0 ? (<EndOfListFooter_1.default count={filteredSlices.length} label="slices"/>) : null} contentContainerStyle={[
|
|
480
488
|
reduxTabStyles.listContent,
|
|
481
489
|
filteredSlices.length === 0 && { flexGrow: 1 },
|
|
482
490
|
]} keyboardShouldPersistTaps="handled"/>
|