react-native-inapp-inspector 1.1.28 → 1.1.31
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 -80
- package/dist/commonjs/components/AnalyticsDetail.js +668 -108
- package/dist/commonjs/components/AppHeaderLogo.js +15 -15
- package/dist/commonjs/components/ConsoleLogCard.js +112 -66
- package/dist/commonjs/components/CopyButton.js +15 -1
- package/dist/commonjs/components/DomainHeader.js +64 -32
- package/dist/commonjs/components/HighlightText.js +7 -1
- package/dist/commonjs/components/Inspector/AnalyticsTab.js +332 -221
- package/dist/commonjs/components/Inspector/ConsoleTab.js +162 -56
- package/dist/commonjs/components/Inspector/InspectorHeader.js +70 -119
- package/dist/commonjs/components/Inspector/LogDetail.js +53 -17
- package/dist/commonjs/components/Inspector/MainScreen.js +4 -0
- package/dist/commonjs/components/Inspector/NetworkDetail.js +24 -24
- package/dist/commonjs/components/Inspector/NetworkTab.js +4 -1
- package/dist/commonjs/components/Inspector/ReduxDetail.js +237 -42
- package/dist/commonjs/components/Inspector/ReduxTab.js +456 -35
- package/dist/commonjs/components/JsonViewer.js +26 -4
- package/dist/commonjs/components/LogCard.js +103 -11
- package/dist/commonjs/components/NetworkIcons.js +19 -10
- 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 +12 -2
- 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/reduxLogger.js +128 -5
- package/dist/commonjs/helpers/index.d.ts +4 -0
- package/dist/commonjs/helpers/index.js +107 -92
- package/dist/commonjs/helpers/toast.d.ts +4 -0
- package/dist/commonjs/helpers/toast.js +20 -0
- package/dist/commonjs/i18n/locales/en.json +49 -3
- package/dist/commonjs/styles/AppColors.d.ts +6 -0
- package/dist/commonjs/styles/AppColors.js +6 -0
- package/dist/commonjs/styles/index.d.ts +82 -15
- package/dist/commonjs/styles/index.js +92 -26
- package/dist/commonjs/types/interfaces.d.ts +7 -0
- package/dist/esm/components/AnalyticsDetail.js +670 -110
- package/dist/esm/components/AppHeaderLogo.js +15 -15
- package/dist/esm/components/ConsoleLogCard.js +114 -68
- package/dist/esm/components/CopyButton.js +15 -1
- package/dist/esm/components/DomainHeader.js +64 -32
- package/dist/esm/components/HighlightText.js +7 -1
- package/dist/esm/components/Inspector/AnalyticsTab.js +334 -223
- package/dist/esm/components/Inspector/ConsoleTab.js +163 -57
- package/dist/esm/components/Inspector/InspectorHeader.js +71 -120
- package/dist/esm/components/Inspector/LogDetail.js +55 -19
- package/dist/esm/components/Inspector/MainScreen.js +4 -0
- package/dist/esm/components/Inspector/NetworkDetail.js +25 -25
- package/dist/esm/components/Inspector/NetworkTab.js +4 -1
- package/dist/esm/components/Inspector/ReduxDetail.js +239 -44
- package/dist/esm/components/Inspector/ReduxTab.js +458 -37
- package/dist/esm/components/JsonViewer.js +26 -4
- package/dist/esm/components/LogCard.js +105 -13
- package/dist/esm/components/NetworkIcons.js +19 -10
- 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 +12 -2
- 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/reduxLogger.js +128 -5
- package/dist/esm/helpers/index.d.ts +4 -0
- package/dist/esm/helpers/index.js +102 -92
- package/dist/esm/helpers/toast.d.ts +4 -0
- package/dist/esm/helpers/toast.js +15 -0
- package/dist/esm/i18n/locales/en.json +49 -3
- package/dist/esm/styles/AppColors.d.ts +6 -0
- package/dist/esm/styles/AppColors.js +6 -0
- package/dist/esm/styles/index.d.ts +82 -15
- package/dist/esm/styles/index.js +92 -26
- package/dist/esm/types/interfaces.d.ts +7 -0
- package/ios/NetworkInspectorModule.m +0 -10
- package/package.json +13 -10
|
@@ -6,11 +6,11 @@ import HighlightText from '../HighlightText';
|
|
|
6
6
|
import JsonViewer from '../JsonViewer';
|
|
7
7
|
import CopyButton from '../CopyButton';
|
|
8
8
|
import SegmentedTabs from '../SegmentedTabs';
|
|
9
|
-
import { ClearIcon, SearchIcon, PrettyIcon, RawIcon, TableIcon, LayersIcon, TerminalIcon, RequestIcon, InfoCircleIcon, DocIcon, } from '../NetworkIcons';
|
|
9
|
+
import { ClearIcon, SearchIcon, PrettyIcon, RawIcon, TableIcon, LayersIcon, TerminalIcon, RequestIcon, InfoCircleIcon, DocIcon, ExternalLinkIcon, } from '../NetworkIcons';
|
|
10
10
|
import { AppColors } from '../../styles/AppColors';
|
|
11
11
|
import { AppFonts } from '../../styles/AppFonts';
|
|
12
12
|
import styles from '../../styles';
|
|
13
|
-
import { getJsonContent, getSize, formatDateTime, parseStackLine, } from '../../helpers';
|
|
13
|
+
import { getJsonContent, getSize, formatDateTime, parseStackLine, openInVSCode, } from '../../helpers';
|
|
14
14
|
const getRelativeTime = (timestamp) => {
|
|
15
15
|
const now = Date.now();
|
|
16
16
|
const diff = now - timestamp;
|
|
@@ -80,27 +80,60 @@ const LogDetail = React.memo(() => {
|
|
|
80
80
|
return null;
|
|
81
81
|
const isDark = AppColors.primaryLight !== AppColors.white;
|
|
82
82
|
const getTypeColors = () => {
|
|
83
|
-
|
|
83
|
+
const type = (selectedLog.type || 'log').toLowerCase();
|
|
84
|
+
const method = (selectedLog.sourceMethod || type).toLowerCase();
|
|
85
|
+
if (type === 'error' || method === 'error') {
|
|
84
86
|
return {
|
|
85
|
-
border: AppColors.
|
|
86
|
-
badgeBg:
|
|
87
|
-
badgeText: AppColors.
|
|
87
|
+
border: AppColors.red500,
|
|
88
|
+
badgeBg: AppColors.red100,
|
|
89
|
+
badgeText: AppColors.redErrorText,
|
|
88
90
|
label: 'ERROR',
|
|
91
|
+
methodBorder: AppColors.errorBorder,
|
|
92
|
+
methodBg: AppColors.red100,
|
|
93
|
+
methodText: AppColors.redErrorText,
|
|
89
94
|
};
|
|
90
95
|
}
|
|
91
|
-
if (
|
|
96
|
+
if (type === 'warn' || method === 'warn') {
|
|
92
97
|
return {
|
|
93
|
-
border: AppColors.
|
|
94
|
-
badgeBg:
|
|
95
|
-
badgeText: AppColors.
|
|
98
|
+
border: AppColors.amber500,
|
|
99
|
+
badgeBg: AppColors.amber100,
|
|
100
|
+
badgeText: AppColors.amber800Warm,
|
|
96
101
|
label: 'WARN',
|
|
102
|
+
methodBorder: AppColors.amber200,
|
|
103
|
+
methodBg: AppColors.amber100,
|
|
104
|
+
methodText: AppColors.amber800Warm,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (type === 'debug' || method === 'debug') {
|
|
108
|
+
return {
|
|
109
|
+
border: AppColors.violet500,
|
|
110
|
+
badgeBg: AppColors.purple100,
|
|
111
|
+
badgeText: AppColors.violet600,
|
|
112
|
+
label: 'DEBUG',
|
|
113
|
+
methodBorder: AppColors.purple200,
|
|
114
|
+
methodBg: AppColors.purple100,
|
|
115
|
+
methodText: AppColors.violet600,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (type === 'info' || method === 'info') {
|
|
119
|
+
return {
|
|
120
|
+
border: AppColors.sky500,
|
|
121
|
+
badgeBg: AppColors.sky100,
|
|
122
|
+
badgeText: AppColors.sky600,
|
|
123
|
+
label: 'INFO',
|
|
124
|
+
methodBorder: AppColors.sky400,
|
|
125
|
+
methodBg: AppColors.sky100,
|
|
126
|
+
methodText: AppColors.sky600,
|
|
97
127
|
};
|
|
98
128
|
}
|
|
99
129
|
return {
|
|
100
|
-
border: AppColors.
|
|
101
|
-
badgeBg:
|
|
102
|
-
badgeText: AppColors.
|
|
103
|
-
label: '
|
|
130
|
+
border: AppColors.indigo500,
|
|
131
|
+
badgeBg: AppColors.indigo50,
|
|
132
|
+
badgeText: AppColors.indigo600Alt,
|
|
133
|
+
label: 'LOG',
|
|
134
|
+
methodBorder: AppColors.indigo400,
|
|
135
|
+
methodBg: AppColors.indigo50,
|
|
136
|
+
methodText: AppColors.indigo600Alt,
|
|
104
137
|
};
|
|
105
138
|
};
|
|
106
139
|
const typeColors = getTypeColors();
|
|
@@ -269,16 +302,18 @@ const LogDetail = React.memo(() => {
|
|
|
269
302
|
: originFrame?.fileName || t('console.consoleLog')}
|
|
270
303
|
</Text>
|
|
271
304
|
|
|
272
|
-
{originFrame?.fileName && (<
|
|
305
|
+
{originFrame?.fileName && (<Pressable onPress={() => openInVSCode(originFrame.rawFilePath ||
|
|
306
|
+
originFrame.fullPath ||
|
|
307
|
+
originFrame.fileName, originFrame.lineNumber, originFrame.columnNumber)} hitSlop={8} style={[
|
|
273
308
|
styles.metaChip,
|
|
274
309
|
{
|
|
275
|
-
backgroundColor: `${AppColors.brandPurple}
|
|
276
|
-
borderColor: `${AppColors.brandPurple}
|
|
310
|
+
backgroundColor: `${AppColors.brandPurple}15`,
|
|
311
|
+
borderColor: `${AppColors.brandPurple}40`,
|
|
277
312
|
paddingHorizontal: 7,
|
|
278
313
|
paddingVertical: 2,
|
|
279
314
|
},
|
|
280
315
|
]}>
|
|
281
|
-
<View style={{ flexDirection: 'row', alignItems: 'center', gap:
|
|
316
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 4, flexShrink: 1 }}>
|
|
282
317
|
<DocIcon color={AppColors.brandPurple} size={10}/>
|
|
283
318
|
<Text style={[
|
|
284
319
|
styles.metaChipText,
|
|
@@ -288,8 +323,9 @@ const LogDetail = React.memo(() => {
|
|
|
288
323
|
{originFrame.lineNumber ? `:${originFrame.lineNumber}` : ''}
|
|
289
324
|
{originFrame.columnNumber ? `:${originFrame.columnNumber}` : ''}
|
|
290
325
|
</Text>
|
|
326
|
+
<ExternalLinkIcon color={AppColors.brandPurple} size={9}/>
|
|
291
327
|
</View>
|
|
292
|
-
</
|
|
328
|
+
</Pressable>)}
|
|
293
329
|
</View>
|
|
294
330
|
|
|
295
331
|
{/* Enhanced metadata metrics row */}
|
|
@@ -18,6 +18,7 @@ import PerformanceTab from './PerformanceTab';
|
|
|
18
18
|
import CrashTab from './CrashTab';
|
|
19
19
|
import CrashDetail from './CrashDetail';
|
|
20
20
|
import SettingsPanel from './SettingsPanel';
|
|
21
|
+
import Toast from '../Toast';
|
|
21
22
|
import styles from '../../styles';
|
|
22
23
|
import { AppColors } from '../../styles/AppColors';
|
|
23
24
|
import NavigationTracker from './NavigationTracker';
|
|
@@ -115,6 +116,9 @@ const MainScreen = () => {
|
|
|
115
116
|
]}>
|
|
116
117
|
<SettingsPanel />
|
|
117
118
|
</View>)}
|
|
119
|
+
|
|
120
|
+
{/* Bottom floating toast notification */}
|
|
121
|
+
<Toast />
|
|
118
122
|
</View>
|
|
119
123
|
</View>
|
|
120
124
|
</ErrorBoundary>)}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { useTranslation } from '../../i18n';
|
|
3
|
-
import { Image, Linking, Pressable, ScrollView, Text, TextInput, View, } from 'react-native';
|
|
3
|
+
import { Alert, Image, Linking, Pressable, ScrollView, Text, TextInput, View, } from 'react-native';
|
|
4
4
|
import { animateNextLayout, useInspector } from './InspectorContext';
|
|
5
5
|
import TouchableScale from '../TouchableScale';
|
|
6
6
|
import SegmentedTabs from '../SegmentedTabs';
|
|
@@ -20,6 +20,26 @@ import { FailIcon, StatusIcon, HeadersIcon, RequestIcon, ResponseIcon, DownloadI
|
|
|
20
20
|
const NetworkDetail = React.memo(() => {
|
|
21
21
|
const { t } = useTranslation();
|
|
22
22
|
const { selected, detailDisplayUrl, apiDetailActiveTab, setApiDetailActiveTab, detailSearch, setDetailSearch, reqExpanded, setReqExpanded, resExpanded, setResExpanded, showReqDiff, setShowReqDiff, showResDiff, setShowResDiff, prevRequestData, prevResponseData, logRouteMapRef, } = useInspector();
|
|
23
|
+
const handleOpenUrl = () => {
|
|
24
|
+
Alert.alert(t('common.openInBrowser') || 'Open in Browser', `${t('common.openInBrowserPrompt') || 'Are you sure you want to open this URL in your external browser?'}\n\n${detailDisplayUrl}`, [
|
|
25
|
+
{ text: t('common.cancel') || 'Cancel', style: 'cancel' },
|
|
26
|
+
{
|
|
27
|
+
text: t('common.open') || 'Open',
|
|
28
|
+
onPress: () => {
|
|
29
|
+
Linking.canOpenURL(detailDisplayUrl)
|
|
30
|
+
.then(supported => {
|
|
31
|
+
if (supported) {
|
|
32
|
+
Linking.openURL(detailDisplayUrl);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
Linking.openURL(detailDisplayUrl).catch(() => { });
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
.catch(() => { });
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
]);
|
|
42
|
+
};
|
|
23
43
|
if (!selected)
|
|
24
44
|
return null;
|
|
25
45
|
return (<View style={{ flex: 1 }}>
|
|
@@ -227,7 +247,7 @@ const NetworkDetail = React.memo(() => {
|
|
|
227
247
|
</View>
|
|
228
248
|
|
|
229
249
|
<View style={styles.detailInfoRight}>
|
|
230
|
-
<TouchableScale style={[styles.iconSquareBtn, { backgroundColor: `${AppColors.sky600}15` }]} onPress={
|
|
250
|
+
<TouchableScale style={[styles.iconSquareBtn, { backgroundColor: `${AppColors.sky600}15` }]} onPress={handleOpenUrl} hitSlop={12}>
|
|
231
251
|
<ExternalLinkIcon color={AppColors.sky600} size={14}/>
|
|
232
252
|
</TouchableScale>
|
|
233
253
|
<CopyButton value={getFetchCommand(selected)} label="fetch()" iconType="fetch"/>
|
|
@@ -245,7 +265,7 @@ const NetworkDetail = React.memo(() => {
|
|
|
245
265
|
padding: 10,
|
|
246
266
|
marginTop: 8,
|
|
247
267
|
gap: 6,
|
|
248
|
-
}} onPress={
|
|
268
|
+
}} onPress={handleOpenUrl}>
|
|
249
269
|
<View style={{
|
|
250
270
|
flexDirection: 'row',
|
|
251
271
|
alignItems: 'center',
|
|
@@ -428,7 +448,7 @@ const NetworkDetail = React.memo(() => {
|
|
|
428
448
|
if (!reqExpanded && !showReqDiff)
|
|
429
449
|
setReqExpanded(true);
|
|
430
450
|
}}/>
|
|
431
|
-
{showReqDiff ? (<DiffViewer oldData={prevRequestData} newData={selected.request} forceOpen={reqExpanded}/>) : reqExpanded ? (<JsonViewer data={selected.request} search={detailSearch}
|
|
451
|
+
{showReqDiff ? (<DiffViewer oldData={prevRequestData} newData={selected.request} forceOpen={reqExpanded}/>) : reqExpanded ? (<JsonViewer data={selected.request} search={detailSearch}/>) : (<View style={styles.codeBlock}>
|
|
432
452
|
<Text style={[
|
|
433
453
|
styles.codeText,
|
|
434
454
|
{ color: AppColors.grayTextWeak },
|
|
@@ -447,26 +467,6 @@ const NetworkDetail = React.memo(() => {
|
|
|
447
467
|
<ClearIcon color={AppColors.grayTextWeak} size={14}/>
|
|
448
468
|
</Pressable>)}
|
|
449
469
|
</View>
|
|
450
|
-
{selected.response != null && (<View style={{
|
|
451
|
-
flexDirection: 'row',
|
|
452
|
-
alignItems: 'center',
|
|
453
|
-
gap: 4,
|
|
454
|
-
paddingHorizontal: 10,
|
|
455
|
-
height: 34,
|
|
456
|
-
borderRadius: 8,
|
|
457
|
-
borderWidth: 1,
|
|
458
|
-
borderColor: `${AppColors.purple}30`,
|
|
459
|
-
backgroundColor: `${AppColors.purple}10`,
|
|
460
|
-
}}>
|
|
461
|
-
<SizeIcon color={AppColors.purple} size={12}/>
|
|
462
|
-
<Text style={{
|
|
463
|
-
fontFamily: AppFonts.interBold,
|
|
464
|
-
fontSize: 10.5,
|
|
465
|
-
color: AppColors.purple,
|
|
466
|
-
}}>
|
|
467
|
-
{getSize(selected.response)}
|
|
468
|
-
</Text>
|
|
469
|
-
</View>)}
|
|
470
470
|
</View>
|
|
471
471
|
|
|
472
472
|
<View style={styles.sectionContainer}>
|
|
@@ -475,7 +475,7 @@ const NetworkDetail = React.memo(() => {
|
|
|
475
475
|
if (!resExpanded && !showResDiff)
|
|
476
476
|
setResExpanded(true);
|
|
477
477
|
}}/>
|
|
478
|
-
{showResDiff ? (<DiffViewer oldData={prevResponseData} newData={selected.response} forceOpen={resExpanded}/>) : resExpanded ? (<JsonViewer data={selected.response} search={detailSearch}
|
|
478
|
+
{showResDiff ? (<DiffViewer oldData={prevResponseData} newData={selected.response} forceOpen={resExpanded}/>) : resExpanded ? (<JsonViewer data={selected.response} search={detailSearch} wrap/>) : (<View style={styles.codeBlock}>
|
|
479
479
|
<Text style={[
|
|
480
480
|
styles.codeText,
|
|
481
481
|
{ color: AppColors.grayTextWeak },
|
|
@@ -67,7 +67,10 @@ const NetworkTab = React.memo(() => {
|
|
|
67
67
|
</AnimatedEntrance>);
|
|
68
68
|
}
|
|
69
69
|
const { log, isLast, color } = item;
|
|
70
|
-
return (<AnimatedEntrance index={index} distance={8} style={
|
|
70
|
+
return (<AnimatedEntrance index={index} distance={8} style={[
|
|
71
|
+
styles.treeNodeRow,
|
|
72
|
+
isLast && styles.treeNodeRowLast,
|
|
73
|
+
]}>
|
|
71
74
|
<View style={styles.treeLines}>
|
|
72
75
|
<View style={[
|
|
73
76
|
styles.modernTreeLine,
|
|
@@ -10,11 +10,55 @@ import TouchableScale from '../TouchableScale';
|
|
|
10
10
|
import HighlightText from '../HighlightText';
|
|
11
11
|
import AnimatedEntrance from '../AnimatedEntrance';
|
|
12
12
|
import { getActionHistory } from '../../customHooks/reduxLogger';
|
|
13
|
-
import { getSize } from '../../helpers';
|
|
13
|
+
import { getSize, openInVSCode, parseStackLine } from '../../helpers';
|
|
14
14
|
import { getCustomStorage } from '../../helpers/settingsStore';
|
|
15
15
|
import { AppColors } from '../../styles/AppColors';
|
|
16
16
|
import { AppFonts } from '../../styles/AppFonts';
|
|
17
|
-
import { SearchIcon, ClearIcon, ClockIcon, TrashIcon, ForwardChevronIcon, LiveStateIcon, TimelineIcon, StorageIcon, MetadataIcon, } from '../NetworkIcons';
|
|
17
|
+
import { SearchIcon, ClearIcon, ClockIcon, LayersIcon, TrashIcon, ForwardChevronIcon, LiveStateIcon, TimelineIcon, StorageIcon, MetadataIcon, BoltIcon, ExternalLinkIcon, } from '../NetworkIcons';
|
|
18
|
+
const getOriginBadge = (originType) => {
|
|
19
|
+
switch (originType) {
|
|
20
|
+
case 'saga':
|
|
21
|
+
return {
|
|
22
|
+
label: 'SAGA',
|
|
23
|
+
icon: '⚡',
|
|
24
|
+
bg: AppColors.purple100,
|
|
25
|
+
text: AppColors.brandPurple,
|
|
26
|
+
border: AppColors.purple200,
|
|
27
|
+
};
|
|
28
|
+
case 'thunk':
|
|
29
|
+
return {
|
|
30
|
+
label: 'THUNK',
|
|
31
|
+
icon: '⚛️',
|
|
32
|
+
bg: AppColors.amber100,
|
|
33
|
+
text: AppColors.amber800Warm,
|
|
34
|
+
border: AppColors.amber200,
|
|
35
|
+
};
|
|
36
|
+
case 'ui':
|
|
37
|
+
return {
|
|
38
|
+
label: 'UI',
|
|
39
|
+
icon: '📱',
|
|
40
|
+
bg: AppColors.sky100,
|
|
41
|
+
text: AppColors.sky600,
|
|
42
|
+
border: AppColors.sky400,
|
|
43
|
+
};
|
|
44
|
+
case 'listener':
|
|
45
|
+
return {
|
|
46
|
+
label: 'LISTENER',
|
|
47
|
+
icon: '👂',
|
|
48
|
+
bg: AppColors.teal100,
|
|
49
|
+
text: AppColors.teal700,
|
|
50
|
+
border: AppColors.teal400,
|
|
51
|
+
};
|
|
52
|
+
default:
|
|
53
|
+
return {
|
|
54
|
+
label: 'DIRECT',
|
|
55
|
+
icon: '⚡',
|
|
56
|
+
bg: AppColors.slate100,
|
|
57
|
+
text: AppColors.slate700,
|
|
58
|
+
border: AppColors.slate200,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
};
|
|
18
62
|
const ReduxDetail = React.memo(() => {
|
|
19
63
|
const { t } = useTranslation();
|
|
20
64
|
const { reduxState, reduxLastActionMap, selectedReduxSlice, selectedReduxAction, setSelectedReduxSlice, setSelectedReduxAction, } = useInspector();
|
|
@@ -136,29 +180,46 @@ const ReduxDetail = React.memo(() => {
|
|
|
136
180
|
: 0;
|
|
137
181
|
const lastAction = reduxLastActionMap[selectedReduxSlice];
|
|
138
182
|
const sliceTabs = [
|
|
139
|
-
{
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
183
|
+
{
|
|
184
|
+
key: 'live',
|
|
185
|
+
label: t('redux.liveState'),
|
|
186
|
+
icon: (isActive) => (<LiveStateIcon color={isActive ? AppColors.white : AppColors.grayText} size={12}/>),
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
key: 'timeline',
|
|
190
|
+
label: `${t('redux.timeline')} (${sliceActions.length})`,
|
|
191
|
+
icon: (isActive) => (<TimelineIcon color={isActive ? AppColors.white : AppColors.grayText} size={12}/>),
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
key: 'persisted',
|
|
195
|
+
label: isPersisted ? t('redux.persisted') : t('redux.storage'),
|
|
196
|
+
icon: (isActive) => (<StorageIcon color={isActive ? AppColors.white : AppColors.grayText} size={12}/>),
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
key: 'metadata',
|
|
200
|
+
label: t('redux.metadata'),
|
|
201
|
+
icon: (isActive) => (<MetadataIcon color={isActive ? AppColors.white : AppColors.grayText} size={12}/>),
|
|
202
|
+
},
|
|
143
203
|
];
|
|
144
204
|
return (<View style={{ flex: 1, backgroundColor: AppColors.grayBackground }}>
|
|
145
205
|
{/* Info Header Bar */}
|
|
146
206
|
<View style={reduxDetailStyles.infoBar}>
|
|
147
207
|
<View style={reduxDetailStyles.infoTopRow}>
|
|
148
208
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flexWrap: 'wrap', flex: 1 }}>
|
|
149
|
-
<View style={[reduxDetailStyles.methodBadge, { backgroundColor: AppColors.
|
|
150
|
-
<Text style={reduxDetailStyles.methodBadgeText}>
|
|
209
|
+
<View style={[reduxDetailStyles.methodBadge, { backgroundColor: AppColors.indigo600Alt }]}>
|
|
210
|
+
<Text style={reduxDetailStyles.methodBadgeText}>{t('redux.slice')}</Text>
|
|
151
211
|
</View>
|
|
152
212
|
<Text style={reduxDetailStyles.sliceNameText}>
|
|
153
213
|
{selectedReduxSlice}
|
|
154
214
|
</Text>
|
|
155
215
|
{isPersisted ? (<View style={reduxDetailStyles.persistedChip}>
|
|
156
|
-
<
|
|
216
|
+
<StorageIcon color={AppColors.emerald700} size={10}/>
|
|
217
|
+
<Text style={reduxDetailStyles.persistedChipText}>{t('redux.persisted').toUpperCase()}</Text>
|
|
157
218
|
</View>) : (<View style={reduxDetailStyles.inMemoryChip}>
|
|
158
|
-
<Text style={reduxDetailStyles.inMemoryChipText}>
|
|
219
|
+
<Text style={reduxDetailStyles.inMemoryChipText}>{t('redux.inMemory').toUpperCase()}</Text>
|
|
159
220
|
</View>)}
|
|
160
221
|
{lastAction?.timestamp && (<View style={reduxDetailStyles.timePill}>
|
|
161
|
-
<ClockIcon color={AppColors.
|
|
222
|
+
<ClockIcon color={AppColors.violet600} size={10}/>
|
|
162
223
|
<Text style={reduxDetailStyles.timeText}>
|
|
163
224
|
{lastAction.timestamp}
|
|
164
225
|
</Text>
|
|
@@ -166,26 +227,29 @@ const ReduxDetail = React.memo(() => {
|
|
|
166
227
|
</View>
|
|
167
228
|
|
|
168
229
|
{/* Copy Button */}
|
|
169
|
-
<CopyButton value={() => (sliceTab === 'live' ? sliceData : persistedData)} label=
|
|
230
|
+
<CopyButton value={() => (sliceTab === 'live' ? sliceData : persistedData)} label={t('redux.sliceJson')}/>
|
|
170
231
|
</View>
|
|
171
232
|
|
|
172
233
|
{/* Sub Stats Row */}
|
|
173
234
|
<View style={reduxDetailStyles.subStatsRow}>
|
|
174
|
-
<View style={reduxDetailStyles.statPill}>
|
|
175
|
-
<
|
|
176
|
-
<Text style={reduxDetailStyles.
|
|
235
|
+
<View style={[reduxDetailStyles.statPill, { backgroundColor: AppColors.indigo50, borderColor: AppColors.indigo400 }]}>
|
|
236
|
+
<LayersIcon color={AppColors.indigo600Alt} size={11}/>
|
|
237
|
+
<Text style={[reduxDetailStyles.statLabel, { color: AppColors.indigo600Alt }]}>{t('redux.keys')}:</Text>
|
|
238
|
+
<Text style={[reduxDetailStyles.statValue, { color: AppColors.indigo600Alt }]}>{fieldsCount}</Text>
|
|
177
239
|
</View>
|
|
178
|
-
<View style={reduxDetailStyles.statPill}>
|
|
179
|
-
<Text style={reduxDetailStyles.statLabel}>
|
|
180
|
-
<Text style={reduxDetailStyles.statValue}>{getSize(sliceData)}</Text>
|
|
240
|
+
<View style={[reduxDetailStyles.statPill, { backgroundColor: AppColors.sky100, borderColor: AppColors.sky400 }]}>
|
|
241
|
+
<Text style={[reduxDetailStyles.statLabel, { color: AppColors.sky600 }]}>{t('redux.size')}:</Text>
|
|
242
|
+
<Text style={[reduxDetailStyles.statValue, { color: AppColors.sky600 }]}>{getSize(sliceData)}</Text>
|
|
181
243
|
</View>
|
|
182
|
-
<View style={reduxDetailStyles.statPill}>
|
|
183
|
-
<
|
|
184
|
-
<Text style={reduxDetailStyles.
|
|
244
|
+
<View style={[reduxDetailStyles.statPill, { backgroundColor: AppColors.purple100, borderColor: AppColors.purple200 }]}>
|
|
245
|
+
<TimelineIcon color={AppColors.violet600} size={11}/>
|
|
246
|
+
<Text style={[reduxDetailStyles.statLabel, { color: AppColors.brandPurple }]}>{t('redux.timeline')}:</Text>
|
|
247
|
+
<Text style={[reduxDetailStyles.statValue, { color: AppColors.brandPurple }]}>{sliceActions.length}</Text>
|
|
185
248
|
</View>
|
|
186
|
-
{lastAction && (<View style={[reduxDetailStyles.statPill, { flex: 1, minWidth: 140 }]}>
|
|
187
|
-
<
|
|
188
|
-
<Text style={reduxDetailStyles.
|
|
249
|
+
{lastAction && (<View style={[reduxDetailStyles.statPill, { backgroundColor: AppColors.amber100, borderColor: AppColors.amber200, flex: 1, minWidth: 140 }]}>
|
|
250
|
+
<BoltIcon color={AppColors.amber800Warm} size={11}/>
|
|
251
|
+
<Text style={[reduxDetailStyles.statLabel, { color: AppColors.amber800Warm }]}>{t('redux.last')}:</Text>
|
|
252
|
+
<Text style={[reduxDetailStyles.statValue, { color: AppColors.amber800Warm }]} numberOfLines={1}>
|
|
189
253
|
{lastAction.type}
|
|
190
254
|
</Text>
|
|
191
255
|
</View>)}
|
|
@@ -212,15 +276,45 @@ const ReduxDetail = React.memo(() => {
|
|
|
212
276
|
</Text>
|
|
213
277
|
</View>) : (sliceActions.map((action, aIdx) => {
|
|
214
278
|
const isExpanded = expandedActionId === action.id;
|
|
279
|
+
const originMeta = getOriginBadge(action.originType);
|
|
280
|
+
const callerBasename = action.callerFile
|
|
281
|
+
? action.callerFile.split('/').pop()
|
|
282
|
+
: null;
|
|
215
283
|
return (<AnimatedEntrance key={action.id} index={aIdx} distance={6}>
|
|
216
284
|
<TouchableScale onPress={() => setExpandedActionId(isExpanded ? null : action.id)} style={reduxDetailStyles.timelineCard}>
|
|
217
285
|
<View style={reduxDetailStyles.timelineHeader}>
|
|
218
|
-
<View style={
|
|
219
|
-
<
|
|
220
|
-
|
|
221
|
-
|
|
286
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flex: 1, minWidth: 0, flexWrap: 'wrap' }}>
|
|
287
|
+
<View style={reduxDetailStyles.actionBadge}>
|
|
288
|
+
<Text style={reduxDetailStyles.actionBadgeText}>
|
|
289
|
+
#{action.id}
|
|
290
|
+
</Text>
|
|
291
|
+
</View>
|
|
292
|
+
{/* Origin Badge */}
|
|
293
|
+
<View style={{
|
|
294
|
+
flexDirection: 'row',
|
|
295
|
+
alignItems: 'center',
|
|
296
|
+
gap: 3,
|
|
297
|
+
backgroundColor: originMeta.bg,
|
|
298
|
+
paddingHorizontal: 6,
|
|
299
|
+
paddingVertical: 2,
|
|
300
|
+
borderRadius: 4,
|
|
301
|
+
borderWidth: 1,
|
|
302
|
+
borderColor: originMeta.border,
|
|
303
|
+
}}>
|
|
304
|
+
<Text style={{ fontSize: 9 }}>{originMeta.icon}</Text>
|
|
305
|
+
<Text style={{
|
|
306
|
+
fontFamily: AppFonts.interBold,
|
|
307
|
+
fontSize: 8.5,
|
|
308
|
+
color: originMeta.text,
|
|
309
|
+
letterSpacing: 0.3,
|
|
310
|
+
}}>
|
|
311
|
+
{originMeta.label}
|
|
312
|
+
</Text>
|
|
313
|
+
</View>
|
|
314
|
+
|
|
315
|
+
<HighlightText text={action.type} search={detailSearch} style={reduxDetailStyles.actionTitle} highlightStyle={reduxDetailStyles.highlight}/>
|
|
222
316
|
</View>
|
|
223
|
-
|
|
317
|
+
|
|
224
318
|
<View style={reduxDetailStyles.timePill}>
|
|
225
319
|
<ClockIcon color={AppColors.purple} size={10}/>
|
|
226
320
|
<Text style={reduxDetailStyles.timeText}>
|
|
@@ -229,22 +323,120 @@ const ReduxDetail = React.memo(() => {
|
|
|
229
323
|
</View>
|
|
230
324
|
</View>
|
|
231
325
|
|
|
232
|
-
{/*
|
|
326
|
+
{/* Trigger caller preview row */}
|
|
327
|
+
{action.callerFile && (<View style={{
|
|
328
|
+
flexDirection: 'row',
|
|
329
|
+
alignItems: 'center',
|
|
330
|
+
justifyContent: 'space-between',
|
|
331
|
+
marginTop: 5,
|
|
332
|
+
marginBottom: 2,
|
|
333
|
+
paddingHorizontal: 7,
|
|
334
|
+
paddingVertical: 3,
|
|
335
|
+
backgroundColor: AppColors.grayBackground,
|
|
336
|
+
borderRadius: 5,
|
|
337
|
+
borderWidth: 1,
|
|
338
|
+
borderColor: AppColors.dividerColor,
|
|
339
|
+
}}>
|
|
340
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 4, flex: 1, minWidth: 0 }}>
|
|
341
|
+
<Text style={{ fontFamily: AppFonts.interMedium, fontSize: 9.5, color: AppColors.grayTextWeak }}>
|
|
342
|
+
{t('redux.triggeredFrom')}
|
|
343
|
+
</Text>
|
|
344
|
+
<Text style={{
|
|
345
|
+
fontFamily: AppFonts.interBold,
|
|
346
|
+
fontSize: 9.5,
|
|
347
|
+
color: AppColors.sky600,
|
|
348
|
+
}} numberOfLines={1}>
|
|
349
|
+
{callerBasename}:{action.callerLine || 1}
|
|
350
|
+
</Text>
|
|
351
|
+
</View>
|
|
352
|
+
|
|
353
|
+
<Pressable onPress={() => openInVSCode(action.callerFile, action.callerLine, action.callerCol)} hitSlop={8} style={{
|
|
354
|
+
flexDirection: 'row',
|
|
355
|
+
alignItems: 'center',
|
|
356
|
+
gap: 3,
|
|
357
|
+
backgroundColor: AppColors.sky100,
|
|
358
|
+
paddingHorizontal: 5,
|
|
359
|
+
paddingVertical: 1.5,
|
|
360
|
+
borderRadius: 3,
|
|
361
|
+
}}>
|
|
362
|
+
<ExternalLinkIcon color={AppColors.sky600} size={9}/>
|
|
363
|
+
<Text style={{ fontFamily: AppFonts.interBold, fontSize: 8.5, color: AppColors.sky600 }}>
|
|
364
|
+
{t('redux.openInEditor')}
|
|
365
|
+
</Text>
|
|
366
|
+
</Pressable>
|
|
367
|
+
</View>)}
|
|
368
|
+
|
|
369
|
+
{/* Expanded Payload, Diff Viewer & Call Stack */}
|
|
233
370
|
{isExpanded ? (<View style={reduxDetailStyles.expandedContent}>
|
|
371
|
+
{/* Call Stack Trace Box */}
|
|
372
|
+
{action.stack ? (<View style={{ marginBottom: 10 }}>
|
|
373
|
+
<Text style={reduxDetailStyles.expandedSectionTitle}>
|
|
374
|
+
{t('redux.callStack')}:
|
|
375
|
+
</Text>
|
|
376
|
+
<View style={{
|
|
377
|
+
backgroundColor: AppColors.grayBackground,
|
|
378
|
+
borderRadius: 8,
|
|
379
|
+
padding: 8,
|
|
380
|
+
borderWidth: 1,
|
|
381
|
+
borderColor: AppColors.dividerColor,
|
|
382
|
+
gap: 4,
|
|
383
|
+
}}>
|
|
384
|
+
{action.stack
|
|
385
|
+
.split('\n')
|
|
386
|
+
.map(l => l.trim())
|
|
387
|
+
.filter(l => l.length > 0 && !l.includes('reduxLogger') && !l.includes('inspectorReduxMiddleware'))
|
|
388
|
+
.slice(0, 8)
|
|
389
|
+
.map((frameLine, fIdx) => {
|
|
390
|
+
const parsed = parseStackLine(frameLine, fIdx === 0);
|
|
391
|
+
const isApp = parsed.frameType === 'app';
|
|
392
|
+
return (<Pressable key={fIdx} onPress={() => {
|
|
393
|
+
if (parsed.fileName !== 'Unknown') {
|
|
394
|
+
openInVSCode(parsed.fullPath || parsed.fileName, parsed.lineNumber, parsed.columnNumber);
|
|
395
|
+
}
|
|
396
|
+
}} style={{
|
|
397
|
+
flexDirection: 'row',
|
|
398
|
+
alignItems: 'center',
|
|
399
|
+
justifyContent: 'space-between',
|
|
400
|
+
paddingVertical: 2.5,
|
|
401
|
+
borderBottomWidth: fIdx < 7 ? 0.5 : 0,
|
|
402
|
+
borderBottomColor: AppColors.dividerColor,
|
|
403
|
+
}}>
|
|
404
|
+
<View style={{ flex: 1, minWidth: 0 }}>
|
|
405
|
+
<Text style={{
|
|
406
|
+
fontFamily: isApp ? AppFonts.interBold : AppFonts.interRegular,
|
|
407
|
+
fontSize: 9.5,
|
|
408
|
+
color: isApp ? AppColors.primaryBlack : AppColors.grayTextWeak,
|
|
409
|
+
}} numberOfLines={1}>
|
|
410
|
+
{parsed.functionName || 'anonymous'}
|
|
411
|
+
</Text>
|
|
412
|
+
<Text style={{
|
|
413
|
+
fontFamily: AppFonts.interRegular,
|
|
414
|
+
fontSize: 8.5,
|
|
415
|
+
color: isApp ? AppColors.sky600 : AppColors.grayTextWeak,
|
|
416
|
+
}} numberOfLines={1}>
|
|
417
|
+
{parsed.fileName}:{parsed.lineNumber || 1}:{parsed.columnNumber || 1}
|
|
418
|
+
</Text>
|
|
419
|
+
</View>
|
|
420
|
+
{isApp && (<ExternalLinkIcon color={AppColors.sky600} size={10}/>)}
|
|
421
|
+
</Pressable>);
|
|
422
|
+
})}
|
|
423
|
+
</View>
|
|
424
|
+
</View>) : null}
|
|
425
|
+
|
|
234
426
|
{action.payload !== undefined && (<View style={{ marginBottom: 10 }}>
|
|
235
427
|
<Text style={reduxDetailStyles.expandedSectionTitle}>
|
|
236
|
-
|
|
428
|
+
{t('redux.actionPayload')}
|
|
237
429
|
</Text>
|
|
238
430
|
<JsonViewer data={action.payload} search={detailSearch} forceOpen={true}/>
|
|
239
431
|
</View>)}
|
|
240
432
|
|
|
241
433
|
<Text style={reduxDetailStyles.expandedSectionTitle}>
|
|
242
|
-
|
|
434
|
+
{t('redux.stateChangesDiff')}
|
|
243
435
|
</Text>
|
|
244
436
|
<DiffViewer oldData={action.prevState?.[selectedReduxSlice] || {}} newData={action.nextState?.[selectedReduxSlice] || {}} forceOpen={true}/>
|
|
245
437
|
</View>) : (<View style={reduxDetailStyles.collapsedPrompt}>
|
|
246
438
|
<Text style={reduxDetailStyles.collapsedPromptText}>
|
|
247
|
-
|
|
439
|
+
{t('redux.tapToInspectAction')}
|
|
248
440
|
</Text>
|
|
249
441
|
<ForwardChevronIcon color={AppColors.grayTextWeak} size={12}/>
|
|
250
442
|
</View>)}
|
|
@@ -327,12 +519,15 @@ const reduxDetailStyles = StyleSheet.create({
|
|
|
327
519
|
color: AppColors.primaryBlack,
|
|
328
520
|
},
|
|
329
521
|
persistedChip: {
|
|
330
|
-
|
|
522
|
+
flexDirection: 'row',
|
|
523
|
+
alignItems: 'center',
|
|
524
|
+
gap: 3.5,
|
|
525
|
+
backgroundColor: '#ECFDF5',
|
|
331
526
|
paddingHorizontal: 6,
|
|
332
527
|
paddingVertical: 2,
|
|
333
528
|
borderRadius: 4,
|
|
334
529
|
borderWidth: 1,
|
|
335
|
-
borderColor:
|
|
530
|
+
borderColor: '#A7F3D0',
|
|
336
531
|
},
|
|
337
532
|
persistedChipText: {
|
|
338
533
|
fontFamily: AppFonts.interBold,
|
|
@@ -340,22 +535,22 @@ const reduxDetailStyles = StyleSheet.create({
|
|
|
340
535
|
color: '#047857',
|
|
341
536
|
},
|
|
342
537
|
inMemoryChip: {
|
|
343
|
-
backgroundColor: '#
|
|
538
|
+
backgroundColor: '#EEF2FF',
|
|
344
539
|
paddingHorizontal: 6,
|
|
345
540
|
paddingVertical: 2,
|
|
346
541
|
borderRadius: 4,
|
|
347
542
|
borderWidth: 1,
|
|
348
|
-
borderColor: '#
|
|
543
|
+
borderColor: '#C7D2FE',
|
|
349
544
|
},
|
|
350
545
|
inMemoryChipText: {
|
|
351
546
|
fontFamily: AppFonts.interBold,
|
|
352
547
|
fontSize: 9,
|
|
353
|
-
color: '#
|
|
548
|
+
color: '#4338CA',
|
|
354
549
|
},
|
|
355
550
|
subStatsRow: {
|
|
356
551
|
flexDirection: 'row',
|
|
357
552
|
alignItems: 'center',
|
|
358
|
-
gap:
|
|
553
|
+
gap: 6,
|
|
359
554
|
flexWrap: 'wrap',
|
|
360
555
|
},
|
|
361
556
|
statPill: {
|
|
@@ -363,20 +558,20 @@ const reduxDetailStyles = StyleSheet.create({
|
|
|
363
558
|
alignItems: 'center',
|
|
364
559
|
gap: 4,
|
|
365
560
|
backgroundColor: AppColors.grayBackground,
|
|
366
|
-
paddingHorizontal:
|
|
367
|
-
paddingVertical:
|
|
561
|
+
paddingHorizontal: 7,
|
|
562
|
+
paddingVertical: 3.5,
|
|
368
563
|
borderRadius: 6,
|
|
369
564
|
borderWidth: 1,
|
|
370
565
|
borderColor: AppColors.dividerColor,
|
|
371
566
|
},
|
|
372
567
|
statLabel: {
|
|
373
|
-
fontFamily: AppFonts.
|
|
374
|
-
fontSize: 10
|
|
568
|
+
fontFamily: AppFonts.interBold,
|
|
569
|
+
fontSize: 10,
|
|
375
570
|
color: AppColors.grayTextWeak,
|
|
376
571
|
},
|
|
377
572
|
statValue: {
|
|
378
573
|
fontFamily: AppFonts.interBold,
|
|
379
|
-
fontSize:
|
|
574
|
+
fontSize: 10.5,
|
|
380
575
|
color: AppColors.primaryBlack,
|
|
381
576
|
},
|
|
382
577
|
searchContainer: {
|