react-native-inapp-inspector 1.1.15 → 1.1.17
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 +150 -130
- package/dist/commonjs/components/AnimatedEntrance.js +3 -0
- package/dist/commonjs/components/AppHeaderLogo.d.ts +8 -0
- package/dist/commonjs/components/AppHeaderLogo.js +85 -0
- package/dist/commonjs/components/CopyButton.d.ts +1 -1
- package/dist/commonjs/components/CopyButton.js +4 -4
- package/dist/commonjs/components/EndOfListFooter.js +2 -3
- package/dist/commonjs/components/Inspector/AnalyticsTab.d.ts +1 -1
- package/dist/commonjs/components/Inspector/AnalyticsTab.js +7 -8
- package/dist/commonjs/components/Inspector/BundleTab.d.ts +41 -0
- package/dist/commonjs/components/Inspector/BundleTab.js +2309 -0
- package/dist/commonjs/components/Inspector/ConsoleTab.d.ts +1 -1
- package/dist/commonjs/components/Inspector/ConsoleTab.js +9 -9
- package/dist/commonjs/components/Inspector/InspectorHeader.d.ts +1 -1
- package/dist/commonjs/components/Inspector/InspectorHeader.js +187 -54
- package/dist/commonjs/components/Inspector/LogDetail.d.ts +1 -1
- package/dist/commonjs/components/Inspector/LogDetail.js +2 -2
- package/dist/commonjs/components/Inspector/MainScreen.js +86 -7
- package/dist/commonjs/components/Inspector/NetworkDetail.d.ts +1 -1
- package/dist/commonjs/components/Inspector/NetworkDetail.js +2 -2
- package/dist/commonjs/components/Inspector/NetworkTab.d.ts +1 -1
- package/dist/commonjs/components/Inspector/NetworkTab.js +10 -9
- package/dist/commonjs/components/Inspector/ReduxDetail.d.ts +3 -0
- package/dist/commonjs/components/Inspector/ReduxDetail.js +576 -0
- package/dist/commonjs/components/Inspector/ReduxTab.d.ts +1 -1
- package/dist/commonjs/components/Inspector/ReduxTab.js +435 -51
- package/dist/commonjs/components/Inspector/SettingsPanel.js +8 -2
- package/dist/commonjs/components/Inspector/TabBar.d.ts +1 -1
- package/dist/commonjs/components/Inspector/TabBar.js +11 -8
- package/dist/commonjs/components/JsonViewer.d.ts +2 -2
- package/dist/commonjs/components/JsonViewer.js +93 -32
- package/dist/commonjs/components/LogCard.js +3 -4
- package/dist/commonjs/components/NetworkIcons.d.ts +8 -0
- package/dist/commonjs/components/NetworkIcons.js +54 -1
- package/dist/commonjs/components/ReduxTreeView.d.ts +2 -2
- package/dist/commonjs/components/ReduxTreeView.js +2 -3
- package/dist/commonjs/components/SegmentedTabs.d.ts +1 -1
- package/dist/commonjs/components/SegmentedTabs.js +2 -2
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/customHooks/consoleLogger.js +37 -9
- package/dist/commonjs/customHooks/reduxLogger.js +4 -2
- package/dist/commonjs/helpers/index.js +6 -2
- package/dist/commonjs/index.js +15 -1
- package/dist/commonjs/styles/AppColors.js +163 -163
- package/dist/commonjs/styles/index.d.ts +14 -0
- package/dist/commonjs/styles/index.js +14 -0
- package/dist/commonjs/types/enums.d.ts +2 -2
- package/dist/commonjs/types/interfaces.d.ts +11 -0
- package/dist/esm/components/AnimatedEntrance.js +4 -1
- package/dist/esm/components/AppHeaderLogo.d.ts +8 -0
- package/dist/esm/components/AppHeaderLogo.js +45 -0
- package/dist/esm/components/CopyButton.d.ts +1 -1
- package/dist/esm/components/CopyButton.js +5 -5
- package/dist/esm/components/EndOfListFooter.js +2 -2
- package/dist/esm/components/Inspector/AnalyticsTab.d.ts +1 -1
- package/dist/esm/components/Inspector/AnalyticsTab.js +7 -8
- package/dist/esm/components/Inspector/BundleTab.d.ts +41 -0
- package/dist/esm/components/Inspector/BundleTab.js +2271 -0
- package/dist/esm/components/Inspector/ConsoleTab.d.ts +1 -1
- package/dist/esm/components/Inspector/ConsoleTab.js +9 -9
- package/dist/esm/components/Inspector/InspectorHeader.d.ts +1 -1
- package/dist/esm/components/Inspector/InspectorHeader.js +153 -53
- package/dist/esm/components/Inspector/LogDetail.d.ts +1 -1
- package/dist/esm/components/Inspector/LogDetail.js +2 -2
- package/dist/esm/components/Inspector/MainScreen.js +53 -7
- package/dist/esm/components/Inspector/NetworkDetail.d.ts +1 -1
- package/dist/esm/components/Inspector/NetworkDetail.js +2 -2
- package/dist/esm/components/Inspector/NetworkTab.d.ts +1 -1
- package/dist/esm/components/Inspector/NetworkTab.js +10 -9
- package/dist/esm/components/Inspector/ReduxDetail.d.ts +3 -0
- package/dist/esm/components/Inspector/ReduxDetail.js +538 -0
- package/dist/esm/components/Inspector/ReduxTab.d.ts +1 -1
- package/dist/esm/components/Inspector/ReduxTab.js +404 -53
- package/dist/esm/components/Inspector/SettingsPanel.js +9 -3
- package/dist/esm/components/Inspector/TabBar.d.ts +1 -1
- package/dist/esm/components/Inspector/TabBar.js +12 -9
- package/dist/esm/components/JsonViewer.d.ts +2 -2
- package/dist/esm/components/JsonViewer.js +94 -33
- package/dist/esm/components/LogCard.js +3 -4
- package/dist/esm/components/NetworkIcons.d.ts +8 -0
- package/dist/esm/components/NetworkIcons.js +47 -1
- package/dist/esm/components/ReduxTreeView.d.ts +2 -2
- package/dist/esm/components/ReduxTreeView.js +2 -2
- package/dist/esm/components/SegmentedTabs.d.ts +1 -1
- package/dist/esm/components/SegmentedTabs.js +2 -2
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/customHooks/consoleLogger.js +37 -9
- package/dist/esm/customHooks/reduxLogger.js +4 -2
- package/dist/esm/helpers/index.js +6 -2
- package/dist/esm/index.js +15 -1
- package/dist/esm/styles/AppColors.js +163 -163
- package/dist/esm/styles/index.d.ts +14 -0
- package/dist/esm/styles/index.js +14 -0
- package/dist/esm/types/enums.d.ts +2 -2
- package/dist/esm/types/interfaces.d.ts +11 -0
- package/example/App.tsx +62 -2
- package/example/README.md +33 -77
- package/example/assets/app_icon.png +0 -0
- package/example/ios/Podfile.lock +3 -3
- package/example/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json +18 -9
- package/example/ios/example/Images.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
- package/example/package-lock.json +824 -10
- package/example/package.json +6 -2
- package/package.json +4 -2
|
@@ -11,14 +11,14 @@ import styles from '../../styles';
|
|
|
11
11
|
import { AppColors } from '../../styles/AppColors';
|
|
12
12
|
import { AppFonts } from '../../styles/AppFonts';
|
|
13
13
|
import { SearchIcon, ClearIcon, SortArrowIcon, TrashIcon, LayersIcon, UserIcon, InfoCircleIcon, WarningTriangleIcon, ErrorCircleIcon, AnalyticsIcon, HeaderPauseIcon, } from '../NetworkIcons';
|
|
14
|
-
const ConsoleTab = () => {
|
|
14
|
+
const ConsoleTab = React.memo(() => {
|
|
15
15
|
const { t } = useTranslation();
|
|
16
16
|
const { logSearch, setLogSearch, logSortOrder, setLogSortOrder, handleDelete, logFilters, setLogFilters, logCounts, filteredConsoleLogs, visibleConsoleLogs, isConsolePaused, setIsConsolePaused, } = useInspector();
|
|
17
17
|
const renderItem = useCallback(({ item, index }) => (<AnimatedEntrance index={index} distance={8}>
|
|
18
18
|
<ConsoleLogCard item={item} searchStr={logSearch}/>
|
|
19
19
|
</AnimatedEntrance>), [logSearch]);
|
|
20
20
|
const keyExtractor = useCallback((item, index) => item?.id?.toString() ?? index.toString(), []);
|
|
21
|
-
const listHeader = useMemo(() =>
|
|
21
|
+
const listHeader = useMemo(() => {
|
|
22
22
|
const total = visibleConsoleLogs.length;
|
|
23
23
|
const filtered = filteredConsoleLogs.length;
|
|
24
24
|
const isAllSelected = logFilters.has('all') ||
|
|
@@ -28,8 +28,8 @@ const ConsoleTab = () => {
|
|
|
28
28
|
styles.resultCount,
|
|
29
29
|
{ marginBottom: 4, marginTop: 12 },
|
|
30
30
|
]}>
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
Showing ({filtered}/{total}) logs showing
|
|
32
|
+
</Text>);
|
|
33
33
|
}
|
|
34
34
|
else {
|
|
35
35
|
const activeFilterNames = Array.from(logFilters)
|
|
@@ -46,11 +46,11 @@ const ConsoleTab = () => {
|
|
|
46
46
|
styles.resultCount,
|
|
47
47
|
{ marginBottom: 4, marginTop: 12 },
|
|
48
48
|
]}>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
Filtering with {activeFilterNames.join(', ')} (
|
|
50
|
+
{filtered}/{total}) logs is showing
|
|
51
|
+
</Text>);
|
|
52
52
|
}
|
|
53
|
-
}
|
|
53
|
+
}, [visibleConsoleLogs.length, filteredConsoleLogs.length, logFilters]);
|
|
54
54
|
return (<View style={{ flex: 1 }}>
|
|
55
55
|
<View style={{
|
|
56
56
|
backgroundColor: AppColors.white,
|
|
@@ -368,5 +368,5 @@ const ConsoleTab = () => {
|
|
|
368
368
|
filteredConsoleLogs.length === 0 && { flexGrow: 1 },
|
|
369
369
|
]} keyboardShouldPersistTaps="handled"/>
|
|
370
370
|
</View>);
|
|
371
|
-
};
|
|
371
|
+
});
|
|
372
372
|
export default ConsoleTab;
|
|
@@ -1,21 +1,58 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
2
|
import { Alert, Animated, Linking, Platform, Pressable, Text, View, } from 'react-native';
|
|
3
3
|
import LinearGradient from 'react-native-linear-gradient';
|
|
4
4
|
import { useInspector } from './InspectorContext';
|
|
5
5
|
import TouchableScale from '../TouchableScale';
|
|
6
|
-
import
|
|
6
|
+
import AppHeaderLogo from '../AppHeaderLogo';
|
|
7
7
|
import styles from '../../styles';
|
|
8
8
|
import { AppColors } from '../../styles/AppColors';
|
|
9
9
|
import { AppFonts } from '../../styles/AppFonts';
|
|
10
10
|
import { METHOD_COLORS } from '../../constants';
|
|
11
11
|
import { LIB_VERSION } from '../../constants';
|
|
12
|
-
import { getStatusColor } from '../../helpers';
|
|
13
|
-
import { getAppName } from '../../helpers';
|
|
14
|
-
import { getBundleIdentifier } from '../../helpers';
|
|
15
|
-
import { formatTime } from '../../helpers';
|
|
12
|
+
import { getStatusColor, getAppName, getBundleIdentifier, formatTime, getSize } from '../../helpers';
|
|
16
13
|
import { WhiteBackNavigation, TrashIcon, SettingsIcon, CloseWhite, ChevronIcon, ClockIcon, } from '../NetworkIcons';
|
|
17
|
-
const InspectorHeader = () => {
|
|
18
|
-
const { modalHeightPercent, selected, setSelected, selectedEvent, setSelectedEvent, selectedLog, setSelectedLog, showHeaderInfo, setShowHeaderInfo, updateAvailable, latestNpmVersion, clearAnim, activePulseAnim, unreadPulseAnim, runClearAllWithAnimation, setSettingsPage, closeModal, detailTitle, } = useInspector();
|
|
14
|
+
const InspectorHeader = React.memo(() => {
|
|
15
|
+
const { modalHeightPercent, appIcon, selected, setSelected, selectedEvent, setSelectedEvent, selectedLog, setSelectedLog, selectedReduxSlice, setSelectedReduxSlice, selectedReduxAction, setSelectedReduxAction, reduxState, reduxLastActionMap, showHeaderInfo, setShowHeaderInfo, updateAvailable, latestNpmVersion, clearAnim, activePulseAnim, unreadPulseAnim, runClearAllWithAnimation, setSettingsPage, closeModal, detailTitle, activeTab, environment, } = useInspector();
|
|
16
|
+
const envConfig = useMemo(() => {
|
|
17
|
+
const rawEnv = (environment || (__DEV__ ? 'DEV' : 'PROD')).trim();
|
|
18
|
+
const clean = rawEnv.toUpperCase();
|
|
19
|
+
if (clean === 'DEV' || clean.includes('DEV') || clean === 'LOCAL') {
|
|
20
|
+
return {
|
|
21
|
+
label: rawEnv,
|
|
22
|
+
bg: 'rgba(16, 185, 129, 0.25)', // emerald
|
|
23
|
+
border: 'rgba(110, 231, 183, 0.55)',
|
|
24
|
+
text: '#A7F3D0',
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (clean === 'UAT' || clean === 'QA' || clean === 'TEST') {
|
|
28
|
+
return {
|
|
29
|
+
label: rawEnv,
|
|
30
|
+
bg: 'rgba(245, 158, 11, 0.28)', // amber
|
|
31
|
+
border: 'rgba(252, 211, 77, 0.6)',
|
|
32
|
+
text: '#FDE68A',
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (clean === 'PREPROD' || clean === 'STAGE' || clean === 'STAGING') {
|
|
36
|
+
return {
|
|
37
|
+
label: rawEnv,
|
|
38
|
+
bg: 'rgba(139, 92, 246, 0.28)', // purple
|
|
39
|
+
border: 'rgba(196, 181, 253, 0.6)',
|
|
40
|
+
text: '#DDD6FE',
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// PROD / Live
|
|
44
|
+
return {
|
|
45
|
+
label: rawEnv,
|
|
46
|
+
bg: 'rgba(244, 63, 94, 0.25)', // rose
|
|
47
|
+
border: 'rgba(253, 164, 175, 0.55)',
|
|
48
|
+
text: '#FECDD3',
|
|
49
|
+
};
|
|
50
|
+
}, [environment]);
|
|
51
|
+
const isDetailView = (activeTab === 'apis' && selected != null) ||
|
|
52
|
+
(activeTab === 'analytics' && selectedEvent != null) ||
|
|
53
|
+
(activeTab === 'logs' && selectedLog != null) ||
|
|
54
|
+
(activeTab === 'redux' && (selectedReduxSlice != null || selectedReduxAction != null));
|
|
55
|
+
const isAnySelected = isDetailView;
|
|
19
56
|
const headerTopPadding = Platform.OS === 'ios' && modalHeightPercent >= 95 ? 44 : 0;
|
|
20
57
|
return (<LinearGradient colors={[AppColors.purple, AppColors.brandPurple]} style={styles.headerGradient}>
|
|
21
58
|
<View style={{ paddingTop: headerTopPadding, width: '100%' }}>
|
|
@@ -26,11 +63,7 @@ const InspectorHeader = () => {
|
|
|
26
63
|
flexDirection: 'row',
|
|
27
64
|
alignItems: 'center',
|
|
28
65
|
gap: 16,
|
|
29
|
-
flex:
|
|
30
|
-
selectedEvent == null &&
|
|
31
|
-
selectedLog == null
|
|
32
|
-
? 5
|
|
33
|
-
: 1,
|
|
66
|
+
flex: !isDetailView ? 5 : 1,
|
|
34
67
|
},
|
|
35
68
|
]}>
|
|
36
69
|
<TouchableScale onPress={() => {
|
|
@@ -38,6 +71,8 @@ const InspectorHeader = () => {
|
|
|
38
71
|
setSelected(null);
|
|
39
72
|
setSelectedEvent(null);
|
|
40
73
|
setSelectedLog(null);
|
|
74
|
+
setSelectedReduxSlice(null);
|
|
75
|
+
setSelectedReduxAction(null);
|
|
41
76
|
});
|
|
42
77
|
}} hitSlop={15} style={[
|
|
43
78
|
{
|
|
@@ -50,9 +85,7 @@ const InspectorHeader = () => {
|
|
|
50
85
|
borderWidth: 1,
|
|
51
86
|
borderColor: `${AppColors.white}4D`,
|
|
52
87
|
},
|
|
53
|
-
|
|
54
|
-
selectedEvent == null &&
|
|
55
|
-
selectedLog == null && { display: 'none' },
|
|
88
|
+
!isAnySelected && { display: 'none' },
|
|
56
89
|
]}>
|
|
57
90
|
{/* Soft outer glow to fake a blurred circle */}
|
|
58
91
|
<View style={{
|
|
@@ -65,36 +98,32 @@ const InspectorHeader = () => {
|
|
|
65
98
|
<WhiteBackNavigation />
|
|
66
99
|
</TouchableScale>
|
|
67
100
|
|
|
68
|
-
{
|
|
69
|
-
selectedEvent == null &&
|
|
70
|
-
selectedLog == null ? (<TouchableScale onPress={() => setShowHeaderInfo(prev => !prev)} style={{
|
|
101
|
+
{!isAnySelected ? (<TouchableScale onPress={() => setShowHeaderInfo(prev => !prev)} style={{
|
|
71
102
|
flexDirection: 'row',
|
|
72
103
|
alignItems: 'flex-start',
|
|
73
104
|
gap: 10,
|
|
74
105
|
flex: 1,
|
|
75
106
|
}}>
|
|
76
|
-
<
|
|
77
|
-
width: 46,
|
|
78
|
-
height: 46,
|
|
79
|
-
borderRadius: 12,
|
|
80
|
-
alignItems: 'center',
|
|
81
|
-
justifyContent: 'center',
|
|
82
|
-
backgroundColor: `${AppColors.white}21`,
|
|
83
|
-
borderWidth: 1.5,
|
|
84
|
-
borderColor: `${AppColors.white}40`,
|
|
85
|
-
shadowColor: AppColors.black,
|
|
86
|
-
shadowOpacity: 0.15,
|
|
87
|
-
shadowRadius: 4,
|
|
88
|
-
shadowOffset: { width: 0, height: 2 },
|
|
89
|
-
flexShrink: 0,
|
|
90
|
-
}}>
|
|
91
|
-
<BrandSquareIcon size={42}/>
|
|
92
|
-
</View>
|
|
107
|
+
<AppHeaderLogo size={46} customIcon={appIcon}/>
|
|
93
108
|
<View style={{ gap: 2, flex: 1 }}>
|
|
94
|
-
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
109
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
|
|
95
110
|
<Text style={[styles.headerTitle]} numberOfLines={1}>
|
|
96
111
|
{getAppName()}
|
|
97
112
|
</Text>
|
|
113
|
+
<View style={[
|
|
114
|
+
styles.envBadge,
|
|
115
|
+
{
|
|
116
|
+
backgroundColor: envConfig.bg,
|
|
117
|
+
borderColor: envConfig.border,
|
|
118
|
+
},
|
|
119
|
+
]}>
|
|
120
|
+
<Text style={[
|
|
121
|
+
styles.envBadgeText,
|
|
122
|
+
{ color: envConfig.text },
|
|
123
|
+
]}>
|
|
124
|
+
{envConfig.label}
|
|
125
|
+
</Text>
|
|
126
|
+
</View>
|
|
98
127
|
{updateAvailable && (<Pressable hitSlop={10} onPress={() => Alert.alert('Update Available', `react-native-inapp-inspector v${latestNpmVersion} is available on NPM (installed: v${LIB_VERSION}).`, [
|
|
99
128
|
{ text: 'Later', style: 'cancel' },
|
|
100
129
|
{
|
|
@@ -233,10 +262,8 @@ const InspectorHeader = () => {
|
|
|
233
262
|
</TouchableScale>) : null}
|
|
234
263
|
</View>
|
|
235
264
|
|
|
236
|
-
{
|
|
237
|
-
|
|
238
|
-
selectedLog != null) && (<View style={styles.headerCenter}>
|
|
239
|
-
{selected != null ? (<View style={styles.headerDetailCenter}>
|
|
265
|
+
{isDetailView && (<View style={styles.headerCenter}>
|
|
266
|
+
{activeTab === 'apis' && selected != null ? (<View style={styles.headerDetailCenter}>
|
|
240
267
|
<View style={styles.headerDetailRow}>
|
|
241
268
|
<View style={[
|
|
242
269
|
styles.headerMethodBadge,
|
|
@@ -297,7 +324,7 @@ const InspectorHeader = () => {
|
|
|
297
324
|
</Text>
|
|
298
325
|
</View>
|
|
299
326
|
</View>
|
|
300
|
-
</View>) : selectedEvent != null ? (<View style={styles.headerDetailCenter}>
|
|
327
|
+
</View>) : activeTab === 'analytics' && selectedEvent != null ? (<View style={styles.headerDetailCenter}>
|
|
301
328
|
<View style={styles.headerDetailRow}>
|
|
302
329
|
<View style={[
|
|
303
330
|
styles.headerMethodBadge,
|
|
@@ -335,7 +362,7 @@ const InspectorHeader = () => {
|
|
|
335
362
|
{selectedEvent.source}
|
|
336
363
|
</Text>
|
|
337
364
|
</View>
|
|
338
|
-
</View>) : selectedLog != null ? (<View style={styles.headerDetailCenter}>
|
|
365
|
+
</View>) : activeTab === 'logs' && selectedLog != null ? (<View style={styles.headerDetailCenter}>
|
|
339
366
|
<View style={styles.headerDetailRow}>
|
|
340
367
|
<View style={[
|
|
341
368
|
styles.headerMethodBadge,
|
|
@@ -368,27 +395,100 @@ const InspectorHeader = () => {
|
|
|
368
395
|
borderRadius: 20,
|
|
369
396
|
backgroundColor: `${AppColors.white}29`,
|
|
370
397
|
}}>
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
398
|
+
<ClockIcon color={AppColors.white} size={11}/>
|
|
399
|
+
<Text style={styles.headerSubTitle}>
|
|
400
|
+
{formatTime(selectedLog.timestamp)}
|
|
401
|
+
</Text>
|
|
402
|
+
</View>
|
|
403
|
+
</View>
|
|
404
|
+
</View>) : activeTab === 'redux' && selectedReduxSlice != null ? ((() => {
|
|
405
|
+
const sliceData = reduxState?.[selectedReduxSlice];
|
|
406
|
+
const keyCount = sliceData && typeof sliceData === 'object'
|
|
407
|
+
? Object.keys(sliceData).length
|
|
408
|
+
: typeof sliceData !== 'undefined'
|
|
409
|
+
? 1
|
|
410
|
+
: 0;
|
|
411
|
+
const sliceSize = getSize(sliceData);
|
|
412
|
+
const lastAction = reduxLastActionMap[selectedReduxSlice];
|
|
413
|
+
return (<View style={styles.headerDetailCenter}>
|
|
414
|
+
<View style={styles.headerDetailRow}>
|
|
415
|
+
<View style={[
|
|
416
|
+
styles.headerMethodBadge,
|
|
417
|
+
{
|
|
418
|
+
backgroundColor: `${AppColors.purple}4D`,
|
|
419
|
+
},
|
|
420
|
+
]}>
|
|
421
|
+
<Text style={styles.headerMethodText}>
|
|
422
|
+
SLICE
|
|
423
|
+
</Text>
|
|
424
|
+
</View>
|
|
425
|
+
<Text style={styles.headerDetailTitle} numberOfLines={1} ellipsizeMode="middle">
|
|
426
|
+
{selectedReduxSlice}
|
|
427
|
+
</Text>
|
|
428
|
+
</View>
|
|
429
|
+
<View style={styles.headerDetailSubRow}>
|
|
430
|
+
<View style={[
|
|
431
|
+
styles.headerStatusDot,
|
|
432
|
+
{ backgroundColor: AppColors.liveGreen },
|
|
433
|
+
]}/>
|
|
434
|
+
<Text style={styles.headerSubTitle}>
|
|
435
|
+
Live
|
|
436
|
+
</Text>
|
|
437
|
+
<Text style={[styles.headerSubTitle, { opacity: 0.6 }]}>•</Text>
|
|
438
|
+
<Text style={styles.headerSubTitle}>
|
|
439
|
+
{keyCount} keys
|
|
440
|
+
</Text>
|
|
441
|
+
<Text style={[styles.headerSubTitle, { opacity: 0.6 }]}>•</Text>
|
|
442
|
+
<Text style={styles.headerSubTitle}>
|
|
443
|
+
{sliceSize}
|
|
444
|
+
</Text>
|
|
445
|
+
{lastAction?.timestamp && (<>
|
|
446
|
+
<Text style={[styles.headerSubTitle, { opacity: 0.6 }]}>•</Text>
|
|
447
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 3 }}>
|
|
448
|
+
<ClockIcon color={AppColors.white} size={10}/>
|
|
449
|
+
<Text style={styles.headerSubTitle}>
|
|
450
|
+
{lastAction.timestamp}
|
|
451
|
+
</Text>
|
|
452
|
+
</View>
|
|
453
|
+
</>)}
|
|
454
|
+
</View>
|
|
455
|
+
</View>);
|
|
456
|
+
})()) : activeTab === 'redux' && selectedReduxAction != null ? (<View style={styles.headerDetailCenter}>
|
|
457
|
+
<View style={styles.headerDetailRow}>
|
|
458
|
+
<View style={[
|
|
459
|
+
styles.headerMethodBadge,
|
|
460
|
+
{
|
|
461
|
+
backgroundColor: `${AppColors.brandPurple}4D`,
|
|
462
|
+
},
|
|
463
|
+
]}>
|
|
464
|
+
<Text style={styles.headerMethodText}>
|
|
465
|
+
ACTION
|
|
374
466
|
</Text>
|
|
375
467
|
</View>
|
|
468
|
+
<Text style={styles.headerDetailTitle} numberOfLines={1} ellipsizeMode="middle">
|
|
469
|
+
{selectedReduxAction.type}
|
|
470
|
+
</Text>
|
|
471
|
+
</View>
|
|
472
|
+
<View style={styles.headerDetailSubRow}>
|
|
473
|
+
<View style={[
|
|
474
|
+
styles.headerStatusDot,
|
|
475
|
+
{ backgroundColor: AppColors.purple },
|
|
476
|
+
]}/>
|
|
477
|
+
<Text style={styles.headerSubTitle}>
|
|
478
|
+
{selectedReduxAction.timestamp || 'Dispatched'}
|
|
479
|
+
</Text>
|
|
376
480
|
</View>
|
|
377
481
|
</View>) : null}
|
|
378
482
|
</View>)}
|
|
379
483
|
|
|
380
484
|
<View style={[
|
|
381
485
|
styles.headerRight,
|
|
382
|
-
|
|
383
|
-
selectedEvent == null &&
|
|
384
|
-
selectedLog == null && {
|
|
486
|
+
!isAnySelected && {
|
|
385
487
|
flexShrink: 0,
|
|
386
488
|
minWidth: 116,
|
|
387
489
|
},
|
|
388
490
|
]}>
|
|
389
|
-
{
|
|
390
|
-
selectedEvent == null &&
|
|
391
|
-
selectedLog == null && (<TouchableScale onPress={() => {
|
|
491
|
+
{!isAnySelected && (<TouchableScale onPress={() => {
|
|
392
492
|
Alert.alert('Clear Everything', 'This clears all tabs — APIs, Logs, Analytics and Redux timeline. Continue?', [
|
|
393
493
|
{ text: 'Cancel', style: 'cancel' },
|
|
394
494
|
{
|
|
@@ -443,5 +543,5 @@ const InspectorHeader = () => {
|
|
|
443
543
|
</View>
|
|
444
544
|
</View>
|
|
445
545
|
</LinearGradient>);
|
|
446
|
-
};
|
|
546
|
+
});
|
|
447
547
|
export default InspectorHeader;
|
|
@@ -26,7 +26,7 @@ const getRelativeTime = (timestamp) => {
|
|
|
26
26
|
return `${hours}h ago`;
|
|
27
27
|
return `${days}d ago`;
|
|
28
28
|
};
|
|
29
|
-
const LogDetail = () => {
|
|
29
|
+
const LogDetail = React.memo(() => {
|
|
30
30
|
const { t } = useTranslation();
|
|
31
31
|
const { selectedLog } = useInspector();
|
|
32
32
|
const [activeTab, setActiveTab] = useState('output');
|
|
@@ -909,7 +909,7 @@ const LogDetail = () => {
|
|
|
909
909
|
</View>)}
|
|
910
910
|
</ScrollView>
|
|
911
911
|
</View>);
|
|
912
|
-
};
|
|
912
|
+
});
|
|
913
913
|
const detailStyles = StyleSheet.create({
|
|
914
914
|
metaDot: {
|
|
915
915
|
fontSize: 10,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
2
|
import { ActivityIndicator, Modal, Pressable, StatusBar, StyleSheet, Text, View, } from 'react-native';
|
|
3
3
|
import { useInspector } from './InspectorContext';
|
|
4
4
|
import ErrorBoundary from '../ErrorBoundary';
|
|
@@ -10,13 +10,31 @@ import NetworkDetail from './NetworkDetail';
|
|
|
10
10
|
import LogDetail from './LogDetail';
|
|
11
11
|
import ConsoleTab from './ConsoleTab';
|
|
12
12
|
import AnalyticsTab from './AnalyticsTab';
|
|
13
|
+
import AnalyticsDetail from '../AnalyticsDetail';
|
|
13
14
|
import ReduxTab from './ReduxTab';
|
|
15
|
+
import ReduxDetail from './ReduxDetail';
|
|
16
|
+
import BundleTab from './BundleTab';
|
|
14
17
|
import SettingsPanel from './SettingsPanel';
|
|
15
18
|
import styles from '../../styles';
|
|
16
19
|
import { AppColors } from '../../styles/AppColors';
|
|
17
20
|
import NavigationTracker from './NavigationTracker';
|
|
18
21
|
const MainScreen = () => {
|
|
19
|
-
const { visible, modalAnimationType, closeModal, modalHeightPercent, selected, selectedEvent, selectedLog, settingsPage, activeTab, isReady, isEnabled, hasNavigationContext, setNavState, } = useInspector();
|
|
22
|
+
const { visible, modalAnimationType, closeModal, modalHeightPercent, selected, selectedEvent, selectedLog, selectedReduxSlice, selectedReduxAction, settingsPage, activeTab, isReady, isEnabled, hasNavigationContext, setNavState, } = useInspector();
|
|
23
|
+
// Lazy-mount each tab on first visit and cache it in memory for 0ms instantaneous switching
|
|
24
|
+
const [mountedTabs, setMountedTabs] = useState({
|
|
25
|
+
[activeTab]: true,
|
|
26
|
+
});
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
setMountedTabs(prev => {
|
|
29
|
+
if (prev[activeTab])
|
|
30
|
+
return prev;
|
|
31
|
+
return { ...prev, [activeTab]: true };
|
|
32
|
+
});
|
|
33
|
+
}, [activeTab]);
|
|
34
|
+
const isDetailActive = (activeTab === 'apis' && selected != null) ||
|
|
35
|
+
(activeTab === 'analytics' && selectedEvent != null) ||
|
|
36
|
+
(activeTab === 'logs' && selectedLog != null) ||
|
|
37
|
+
(activeTab === 'redux' && (selectedReduxSlice != null || selectedReduxAction != null));
|
|
20
38
|
return (<>
|
|
21
39
|
{isEnabled && <FabLauncher />}
|
|
22
40
|
<Modal visible={visible} animationType={modalAnimationType} transparent>
|
|
@@ -32,12 +50,40 @@ const MainScreen = () => {
|
|
|
32
50
|
<InspectorHeader />
|
|
33
51
|
|
|
34
52
|
{/* ─── Horizontal Scrollable Tab Bar inside Content ─── */}
|
|
35
|
-
{
|
|
36
|
-
selectedEvent == null &&
|
|
37
|
-
selectedLog == null &&
|
|
38
|
-
settingsPage === null ? (<TabBar />) : null}
|
|
53
|
+
{!isDetailActive && settingsPage === null ? (<TabBar />) : null}
|
|
39
54
|
|
|
40
|
-
{isReady ? (
|
|
55
|
+
{isReady ? (isDetailActive ? (activeTab === 'apis' && selected != null ? (<NetworkDetail />) : activeTab === 'analytics' && selectedEvent != null ? (<AnalyticsDetail event={selectedEvent}/>) : activeTab === 'logs' && selectedLog != null ? (<LogDetail />) : activeTab === 'redux' ? (<ReduxDetail />) : null) : (<>
|
|
56
|
+
<View style={{
|
|
57
|
+
flex: 1,
|
|
58
|
+
display: activeTab === 'apis' ? 'flex' : 'none',
|
|
59
|
+
}}>
|
|
60
|
+
{mountedTabs.apis && <NetworkTab />}
|
|
61
|
+
</View>
|
|
62
|
+
<View style={{
|
|
63
|
+
flex: 1,
|
|
64
|
+
display: activeTab === 'logs' ? 'flex' : 'none',
|
|
65
|
+
}}>
|
|
66
|
+
{mountedTabs.logs && <ConsoleTab />}
|
|
67
|
+
</View>
|
|
68
|
+
<View style={{
|
|
69
|
+
flex: 1,
|
|
70
|
+
display: activeTab === 'analytics' ? 'flex' : 'none',
|
|
71
|
+
}}>
|
|
72
|
+
{mountedTabs.analytics && <AnalyticsTab />}
|
|
73
|
+
</View>
|
|
74
|
+
<View style={{
|
|
75
|
+
flex: 1,
|
|
76
|
+
display: activeTab === 'redux' ? 'flex' : 'none',
|
|
77
|
+
}}>
|
|
78
|
+
{mountedTabs.redux && <ReduxTab />}
|
|
79
|
+
</View>
|
|
80
|
+
<View style={{
|
|
81
|
+
flex: 1,
|
|
82
|
+
display: activeTab === 'bundle' ? 'flex' : 'none',
|
|
83
|
+
}}>
|
|
84
|
+
{mountedTabs.bundle && <BundleTab />}
|
|
85
|
+
</View>
|
|
86
|
+
</>)) : (<View style={styles.empty}>
|
|
41
87
|
<ActivityIndicator size="large" color={AppColors.purple}/>
|
|
42
88
|
<Text style={[styles.emptySub, { marginTop: 12 }]}>
|
|
43
89
|
Loading logs...
|
|
@@ -18,7 +18,7 @@ import { AppFonts } from '../../styles/AppFonts';
|
|
|
18
18
|
import { METHOD_COLORS } from '../../constants';
|
|
19
19
|
import { getStatusColor, getSize, formatDateTime, getFetchCommand, getCurlCommand, } from '../../helpers';
|
|
20
20
|
import { FailIcon, GlobeIcon, StatusIcon, HeadersIcon, RequestIcon, ResponseIcon, DownloadIcon, ClearIcon, SizeIcon, } from '../NetworkIcons';
|
|
21
|
-
const NetworkDetail = () => {
|
|
21
|
+
const NetworkDetail = React.memo(() => {
|
|
22
22
|
const { t } = useTranslation();
|
|
23
23
|
const { selected, detailDisplayUrl, apiDetailActiveTab, setApiDetailActiveTab, detailSearch, setDetailSearch, reqExpanded, setReqExpanded, resExpanded, setResExpanded, showReqDiff, setShowReqDiff, showResDiff, setShowResDiff, prevRequestData, prevResponseData, logRouteMapRef, } = useInspector();
|
|
24
24
|
if (!selected)
|
|
@@ -363,5 +363,5 @@ const NetworkDetail = () => {
|
|
|
363
363
|
</>)}
|
|
364
364
|
</ScrollView>
|
|
365
365
|
</View>);
|
|
366
|
-
};
|
|
366
|
+
});
|
|
367
367
|
export default NetworkDetail;
|
|
@@ -21,7 +21,7 @@ const STATUS_META = {
|
|
|
21
21
|
'5xx': { color: AppColors.errorColor, Icon: CircleXIcon },
|
|
22
22
|
Failed: { color: AppColors.errorColor, Icon: CircleXIcon },
|
|
23
23
|
};
|
|
24
|
-
const FilterChip = ({ label, color, Icon, badge, active, onPress, }) => {
|
|
24
|
+
const FilterChip = React.memo(({ label, color, Icon, badge, active, onPress, }) => {
|
|
25
25
|
const iconColor = active ? color : AppColors.grayTextWeak;
|
|
26
26
|
return (<TouchableScale style={styles.statusFilterWrap} onPress={onPress} hitSlop={10}>
|
|
27
27
|
<View style={[
|
|
@@ -43,19 +43,20 @@ const FilterChip = ({ label, color, Icon, badge, active, onPress, }) => {
|
|
|
43
43
|
{badge != null ? (<View style={[
|
|
44
44
|
styles.methodBadgeMini,
|
|
45
45
|
{ backgroundColor: badge },
|
|
46
|
-
]}/>) : (Icon && (<
|
|
47
|
-
|
|
48
|
-
</View>))}
|
|
49
|
-
<Text style={[
|
|
46
|
+
]}/>) : (Icon && (<Icon color={iconColor} size={13}/>))}
|
|
47
|
+
<Text numberOfLines={1} style={[
|
|
50
48
|
styles.statusFilterText,
|
|
51
|
-
active && {
|
|
49
|
+
active && {
|
|
50
|
+
color,
|
|
51
|
+
fontFamily: AppFonts.interBold,
|
|
52
|
+
},
|
|
52
53
|
]}>
|
|
53
54
|
{label}
|
|
54
55
|
</Text>
|
|
55
56
|
</View>
|
|
56
57
|
</TouchableScale>);
|
|
57
|
-
};
|
|
58
|
-
const NetworkTab = () => {
|
|
58
|
+
});
|
|
59
|
+
const NetworkTab = React.memo(() => {
|
|
59
60
|
const { groupedData, search, setSearch, handleDelete, selectedLogs, sortOrder, setSortOrder, statusFilters, setStatusFilters, methodFilters, setMethodFilters, availableMethods, filteredLogs, logs, toggleSectionFilter, toggleSectionCollapse, minStart, totalRange, newLogIds, toggleSelect, setSelected, isNetworkPaused, setIsNetworkPaused, } = useInspector();
|
|
60
61
|
const filtersAccordion = useAccordion(false, 300, 260);
|
|
61
62
|
const apisListRef = useRef(null);
|
|
@@ -239,5 +240,5 @@ const NetworkTab = () => {
|
|
|
239
240
|
</View>
|
|
240
241
|
</TouchableScale>
|
|
241
242
|
</View>);
|
|
242
|
-
};
|
|
243
|
+
});
|
|
243
244
|
export default NetworkTab;
|