react-native-inapp-inspector 1.1.34 → 2.0.0
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 +904 -0
- package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.kt +17 -0
- package/dist/commonjs/components/ConsoleLogCard.js +57 -56
- package/dist/commonjs/components/CopyButton.js +10 -1
- package/dist/commonjs/components/ErrorBoundary.js +3 -0
- package/dist/commonjs/components/Inspector/AnalyticsTab.js +7 -3
- package/dist/commonjs/components/Inspector/BundleTab.js +20 -20
- package/dist/commonjs/components/Inspector/ConsoleTab.js +1 -1
- package/dist/commonjs/components/Inspector/CrashDetail.js +16 -12
- package/dist/commonjs/components/Inspector/CrashTab.js +97 -90
- package/dist/commonjs/components/Inspector/InspectorHeader.js +28 -24
- package/dist/commonjs/components/Inspector/MainScreen.js +20 -103
- package/dist/commonjs/components/Inspector/NetworkTab.js +143 -10
- package/dist/commonjs/components/Inspector/PerformanceTab.js +96 -3
- package/dist/commonjs/components/Inspector/ReduxDetail.js +24 -19
- package/dist/commonjs/components/Inspector/ReduxTab.js +30 -18
- package/dist/commonjs/components/Inspector/SettingsPanel.js +1578 -754
- package/dist/commonjs/components/Inspector/TabBar.js +6 -3
- package/dist/commonjs/components/LogCard.js +27 -9
- package/dist/commonjs/components/MetaAccordion.js +42 -0
- package/dist/commonjs/components/NetworkIcons.d.ts +10 -0
- package/dist/commonjs/components/NetworkIcons.js +53 -1
- package/dist/commonjs/components/Toast.js +3 -3
- package/dist/commonjs/components/TouchableScale.d.ts +5 -2
- 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 +6 -0
- package/dist/commonjs/customHooks/analyticsLogger.js +16 -5
- package/dist/commonjs/customHooks/bundleAnalyzer.d.ts +2 -0
- package/dist/commonjs/customHooks/bundleAnalyzer.js +26 -17
- package/dist/commonjs/customHooks/consoleLogger.d.ts +2 -0
- package/dist/commonjs/customHooks/consoleLogger.js +10 -1
- package/dist/commonjs/customHooks/crashHandler.d.ts +2 -0
- package/dist/commonjs/customHooks/crashHandler.js +17 -1
- package/dist/commonjs/customHooks/networkLogger.d.ts +2 -0
- package/dist/commonjs/customHooks/networkLogger.js +12 -1
- package/dist/commonjs/customHooks/performanceTracker.d.ts +3 -1
- package/dist/commonjs/customHooks/performanceTracker.js +18 -4
- package/dist/commonjs/customHooks/reduxLogger.d.ts +2 -0
- package/dist/commonjs/customHooks/reduxLogger.js +10 -1
- package/dist/commonjs/helpers/index.js +10 -1
- package/dist/commonjs/helpers/settingsStore.d.ts +9 -0
- package/dist/commonjs/helpers/settingsStore.js +59 -3
- package/dist/commonjs/i18n/locales/en.json +96 -28
- package/dist/commonjs/index.d.ts +1 -0
- package/dist/commonjs/index.js +307 -73
- 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/AppColors.d.ts +28 -0
- package/dist/commonjs/styles/AppColors.js +30 -0
- package/dist/commonjs/styles/index.js +6 -6
- package/dist/commonjs/types/enums.d.ts +1 -1
- package/dist/commonjs/types/interfaces.d.ts +9 -0
- package/dist/esm/components/ConsoleLogCard.js +58 -57
- package/dist/esm/components/CopyButton.js +10 -1
- package/dist/esm/components/ErrorBoundary.js +3 -0
- package/dist/esm/components/Inspector/AnalyticsTab.js +7 -3
- package/dist/esm/components/Inspector/BundleTab.js +20 -20
- package/dist/esm/components/Inspector/ConsoleTab.js +1 -1
- package/dist/esm/components/Inspector/CrashDetail.js +16 -12
- package/dist/esm/components/Inspector/CrashTab.js +97 -90
- package/dist/esm/components/Inspector/InspectorHeader.js +29 -25
- package/dist/esm/components/Inspector/MainScreen.js +21 -71
- package/dist/esm/components/Inspector/NetworkTab.js +145 -12
- package/dist/esm/components/Inspector/PerformanceTab.js +96 -3
- package/dist/esm/components/Inspector/ReduxDetail.js +24 -19
- package/dist/esm/components/Inspector/ReduxTab.js +30 -18
- package/dist/esm/components/Inspector/SettingsPanel.js +1582 -758
- package/dist/esm/components/Inspector/TabBar.js +7 -4
- package/dist/esm/components/LogCard.js +27 -9
- package/dist/esm/components/MetaAccordion.js +42 -0
- package/dist/esm/components/NetworkIcons.d.ts +10 -0
- package/dist/esm/components/NetworkIcons.js +42 -0
- package/dist/esm/components/Toast.js +3 -3
- package/dist/esm/components/TouchableScale.d.ts +5 -2
- 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 +6 -0
- package/dist/esm/customHooks/analyticsLogger.js +12 -4
- package/dist/esm/customHooks/bundleAnalyzer.d.ts +2 -0
- package/dist/esm/customHooks/bundleAnalyzer.js +23 -16
- package/dist/esm/customHooks/consoleLogger.d.ts +2 -0
- package/dist/esm/customHooks/consoleLogger.js +7 -0
- package/dist/esm/customHooks/crashHandler.d.ts +2 -0
- package/dist/esm/customHooks/crashHandler.js +14 -0
- package/dist/esm/customHooks/networkLogger.d.ts +2 -0
- package/dist/esm/customHooks/networkLogger.js +9 -0
- package/dist/esm/customHooks/performanceTracker.d.ts +3 -1
- package/dist/esm/customHooks/performanceTracker.js +15 -3
- package/dist/esm/customHooks/reduxLogger.d.ts +2 -0
- package/dist/esm/customHooks/reduxLogger.js +7 -0
- package/dist/esm/helpers/index.js +10 -1
- package/dist/esm/helpers/settingsStore.d.ts +9 -0
- package/dist/esm/helpers/settingsStore.js +58 -3
- package/dist/esm/i18n/locales/en.json +96 -28
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +279 -59
- 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/AppColors.d.ts +28 -0
- package/dist/esm/styles/AppColors.js +30 -0
- package/dist/esm/styles/index.js +6 -6
- 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 +720 -5
- package/package.json +15 -3
- 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
|
@@ -6,7 +6,7 @@ import { AppFonts } from '../styles/AppFonts';
|
|
|
6
6
|
import { formatTime, getJsonContent, getJsonPreviewText, parseStackLine, openInVSCode, } from '../helpers';
|
|
7
7
|
import HighlightText from './HighlightText';
|
|
8
8
|
import CopyButton from './CopyButton';
|
|
9
|
-
import { ChevronIcon, ExternalLinkIcon } from './NetworkIcons';
|
|
9
|
+
import { ChevronIcon, ExternalLinkIcon, FlaskIcon, ZapIcon, GlobeIcon, DiceIcon, AtomIcon, BarChartIcon, KeyIcon, SmartphoneIcon, AlertTriangleIcon, BugIcon, TagIcon, } from './NetworkIcons';
|
|
10
10
|
import { useInspector } from './Inspector/InspectorContext';
|
|
11
11
|
const getLogMessageWithBadges = (message, searchStr, textStyle, highlightStyle, numberOfLines) => {
|
|
12
12
|
if (!message)
|
|
@@ -20,44 +20,45 @@ const getLogMessageWithBadges = (message, searchStr, textStyle, highlightStyle,
|
|
|
20
20
|
const getTagDecorator = (tag) => {
|
|
21
21
|
const clean = tag.replace(/[\[\]]/g, '').trim().toUpperCase();
|
|
22
22
|
if (clean === 'AXIOS') {
|
|
23
|
-
return { color: AppColors.emerald600,
|
|
23
|
+
return { color: AppColors.emerald600, Icon: ZapIcon, label: 'AXIOS' };
|
|
24
24
|
}
|
|
25
25
|
if (clean === 'API' || clean === 'FETCH' || clean === 'HTTP' || clean === 'NETWORK') {
|
|
26
|
-
return { color: AppColors.sky600,
|
|
26
|
+
return { color: AppColors.sky600, Icon: GlobeIcon, label: clean };
|
|
27
27
|
}
|
|
28
28
|
if (clean === 'SAMPLE' || clean === 'BATCH') {
|
|
29
|
-
return { color: AppColors.indigo600Alt,
|
|
29
|
+
return { color: AppColors.indigo600Alt, Icon: DiceIcon, label: clean };
|
|
30
30
|
}
|
|
31
31
|
if (clean === 'REDUX' || clean === 'STORE' || clean === 'STATE') {
|
|
32
|
-
return { color: AppColors.violet600,
|
|
32
|
+
return { color: AppColors.violet600, Icon: AtomIcon, label: clean };
|
|
33
33
|
}
|
|
34
34
|
if (clean === 'ANALYTICS' || clean === 'FIREBASE' || clean === 'GA4' || clean === 'GA') {
|
|
35
|
-
return { color: AppColors.teal600,
|
|
35
|
+
return { color: AppColors.teal600, Icon: BarChartIcon, label: clean };
|
|
36
36
|
}
|
|
37
37
|
if (clean === 'AUTH' || clean === 'TOKEN' || clean === 'SESSION') {
|
|
38
|
-
return { color: AppColors.amber600,
|
|
38
|
+
return { color: AppColors.amber600, Icon: KeyIcon, label: clean };
|
|
39
39
|
}
|
|
40
40
|
if (clean === 'APP') {
|
|
41
|
-
return { color: AppColors.indigo600Alt,
|
|
41
|
+
return { color: AppColors.indigo600Alt, Icon: SmartphoneIcon, label: 'APP' };
|
|
42
42
|
}
|
|
43
43
|
if (clean === 'TEST') {
|
|
44
|
-
return { color: AppColors.emerald500,
|
|
44
|
+
return { color: AppColors.emerald500, Icon: FlaskIcon, label: 'TEST' };
|
|
45
45
|
}
|
|
46
46
|
if (clean === 'WARN' || clean === 'WARNING') {
|
|
47
|
-
return { color: AppColors.amber600,
|
|
47
|
+
return { color: AppColors.amber600, Icon: AlertTriangleIcon, label: 'WARN' };
|
|
48
48
|
}
|
|
49
49
|
if (clean === 'ERROR' || clean === 'CRASH' || clean === 'BUG') {
|
|
50
|
-
return { color: AppColors.red600,
|
|
50
|
+
return { color: AppColors.red600, Icon: BugIcon, label: 'ERROR' };
|
|
51
51
|
}
|
|
52
52
|
if (clean === 'PERF' || clean === 'RENDER') {
|
|
53
|
-
return { color: AppColors.violet500,
|
|
53
|
+
return { color: AppColors.violet500, Icon: ZapIcon, label: clean };
|
|
54
54
|
}
|
|
55
|
-
return { color: AppColors.slate600,
|
|
55
|
+
return { color: AppColors.slate600, Icon: TagIcon, label: clean };
|
|
56
56
|
};
|
|
57
57
|
return (<View style={{ flexDirection: 'column', gap: 6 }}>
|
|
58
58
|
<View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 5 }}>
|
|
59
59
|
{tags.map((tag, i) => {
|
|
60
60
|
const dec = getTagDecorator(tag);
|
|
61
|
+
const IconComp = dec.Icon;
|
|
61
62
|
return (<View key={i} style={[
|
|
62
63
|
styles.prefixTag,
|
|
63
64
|
{
|
|
@@ -65,7 +66,7 @@ const getLogMessageWithBadges = (message, searchStr, textStyle, highlightStyle,
|
|
|
65
66
|
borderColor: `${dec.color}35`,
|
|
66
67
|
},
|
|
67
68
|
]}>
|
|
68
|
-
<
|
|
69
|
+
<IconComp color={dec.color} size={10}/>
|
|
69
70
|
<Text style={[styles.prefixTagText, { color: dec.color }]}>
|
|
70
71
|
{dec.label}
|
|
71
72
|
</Text>
|
|
@@ -93,67 +94,67 @@ export const ConsoleLogCard = React.memo(function ConsoleLogCard({ item, searchS
|
|
|
93
94
|
const method = (item.sourceMethod || type).toLowerCase();
|
|
94
95
|
if (isAnalyticsError || type === 'error' || method === 'error') {
|
|
95
96
|
return {
|
|
96
|
-
border:
|
|
97
|
-
badgeBg:
|
|
98
|
-
badgeText:
|
|
97
|
+
border: AppColors.errorColor,
|
|
98
|
+
badgeBg: AppColors.errorCardBg,
|
|
99
|
+
badgeText: AppColors.rose600,
|
|
99
100
|
label: 'ERROR',
|
|
100
|
-
cardBg:
|
|
101
|
-
methodBorder:
|
|
102
|
-
methodBg:
|
|
103
|
-
methodText:
|
|
104
|
-
textColor:
|
|
101
|
+
cardBg: AppColors.errorCardBg,
|
|
102
|
+
methodBorder: AppColors.errorBorder,
|
|
103
|
+
methodBg: AppColors.errorCardBg,
|
|
104
|
+
methodText: AppColors.rose600,
|
|
105
|
+
textColor: AppColors.redErrorText,
|
|
105
106
|
};
|
|
106
107
|
}
|
|
107
108
|
if (type === 'warn' || method === 'warn') {
|
|
108
109
|
return {
|
|
109
|
-
border:
|
|
110
|
-
badgeBg:
|
|
111
|
-
badgeText:
|
|
110
|
+
border: AppColors.amber600,
|
|
111
|
+
badgeBg: AppColors.amberBg,
|
|
112
|
+
badgeText: AppColors.amber700,
|
|
112
113
|
label: 'WARN',
|
|
113
|
-
cardBg:
|
|
114
|
-
methodBorder:
|
|
115
|
-
methodBg:
|
|
116
|
-
methodText:
|
|
117
|
-
textColor:
|
|
114
|
+
cardBg: AppColors.amberWarmBg,
|
|
115
|
+
methodBorder: AppColors.amberWarmBorder,
|
|
116
|
+
methodBg: AppColors.amberBg,
|
|
117
|
+
methodText: AppColors.amber700,
|
|
118
|
+
textColor: AppColors.amberWarmText,
|
|
118
119
|
};
|
|
119
120
|
}
|
|
120
121
|
if (type === 'debug' || method === 'debug') {
|
|
121
122
|
return {
|
|
122
|
-
border:
|
|
123
|
-
badgeBg:
|
|
124
|
-
badgeText:
|
|
123
|
+
border: AppColors.purple500,
|
|
124
|
+
badgeBg: AppColors.violetSoftBg,
|
|
125
|
+
badgeText: AppColors.violetSoftText,
|
|
125
126
|
label: 'DEBUG',
|
|
126
|
-
cardBg:
|
|
127
|
-
methodBorder:
|
|
128
|
-
methodBg:
|
|
129
|
-
methodText:
|
|
130
|
-
textColor:
|
|
127
|
+
cardBg: AppColors.purpleShade50,
|
|
128
|
+
methodBorder: AppColors.violetSoftBorder,
|
|
129
|
+
methodBg: AppColors.violetSoftBg,
|
|
130
|
+
methodText: AppColors.violetSoftText,
|
|
131
|
+
textColor: AppColors.purpleText,
|
|
131
132
|
};
|
|
132
133
|
}
|
|
133
134
|
if (type === 'info' || method === 'info') {
|
|
134
135
|
return {
|
|
135
|
-
border:
|
|
136
|
-
badgeBg:
|
|
137
|
-
badgeText:
|
|
136
|
+
border: AppColors.sky500,
|
|
137
|
+
badgeBg: AppColors.skySoftBg,
|
|
138
|
+
badgeText: AppColors.skySoftText,
|
|
138
139
|
label: 'INFO',
|
|
139
|
-
cardBg:
|
|
140
|
-
methodBorder:
|
|
141
|
-
methodBg:
|
|
142
|
-
methodText:
|
|
143
|
-
textColor:
|
|
140
|
+
cardBg: AppColors.blueBg,
|
|
141
|
+
methodBorder: AppColors.skySoftBorder,
|
|
142
|
+
methodBg: AppColors.skySoftBg,
|
|
143
|
+
methodText: AppColors.skySoftText,
|
|
144
|
+
textColor: AppColors.blue800,
|
|
144
145
|
};
|
|
145
146
|
}
|
|
146
147
|
// Default / Standard console.log
|
|
147
148
|
return {
|
|
148
|
-
border:
|
|
149
|
-
badgeBg:
|
|
150
|
-
badgeText:
|
|
149
|
+
border: AppColors.indigo500,
|
|
150
|
+
badgeBg: AppColors.indigo50,
|
|
151
|
+
badgeText: AppColors.indigo600Alt,
|
|
151
152
|
label: 'LOG',
|
|
152
|
-
cardBg:
|
|
153
|
-
methodBorder:
|
|
154
|
-
methodBg:
|
|
155
|
-
methodText:
|
|
156
|
-
textColor:
|
|
153
|
+
cardBg: AppColors.primaryLight,
|
|
154
|
+
methodBorder: AppColors.dividerColor,
|
|
155
|
+
methodBg: AppColors.indigo50,
|
|
156
|
+
methodText: AppColors.indigo600Alt,
|
|
157
|
+
textColor: AppColors.primaryBlack,
|
|
157
158
|
};
|
|
158
159
|
};
|
|
159
160
|
const colors = getLogColors();
|
|
@@ -198,11 +199,11 @@ export const ConsoleLogCard = React.memo(function ConsoleLogCard({ item, searchS
|
|
|
198
199
|
{jsonContent && (<View style={[
|
|
199
200
|
styles.metaChip,
|
|
200
201
|
{
|
|
201
|
-
backgroundColor:
|
|
202
|
-
borderColor:
|
|
202
|
+
backgroundColor: AppColors.mintGreenBg,
|
|
203
|
+
borderColor: AppColors.mintGreenBorder,
|
|
203
204
|
},
|
|
204
205
|
]}>
|
|
205
|
-
<Text style={[styles.metaChipText, { color:
|
|
206
|
+
<Text style={[styles.metaChipText, { color: AppColors.mintGreenText, fontFamily: AppFonts.interBold }]}>
|
|
206
207
|
{Array.isArray(jsonContent.data)
|
|
207
208
|
? `Array[${jsonContent.data.length}]`
|
|
208
209
|
: `Object{${Object.keys(jsonContent.data).length}}`}
|
|
@@ -4,6 +4,7 @@ import { Share } from 'react-native';
|
|
|
4
4
|
import TouchableScale from './TouchableScale';
|
|
5
5
|
// Helpers
|
|
6
6
|
import { copyToClipboard } from '../helpers';
|
|
7
|
+
import { triggerNativeHaptic } from '../native/NativeInspector';
|
|
7
8
|
// Assets
|
|
8
9
|
import { TerminalIcon, FetchIcon, CopyIcon, CheckIcon } from './NetworkIcons';
|
|
9
10
|
// Stylesheet
|
|
@@ -12,6 +13,7 @@ import styles from '../styles';
|
|
|
12
13
|
const CopyButton = React.memo(({ value, label, iconType = 'copy' }) => {
|
|
13
14
|
const [copied, setCopied] = useState(false);
|
|
14
15
|
const handlePress = useCallback(() => {
|
|
16
|
+
triggerNativeHaptic('success');
|
|
15
17
|
const resolvedValue = typeof value === 'function' ? value() : value;
|
|
16
18
|
copyToClipboard(resolvedValue, label);
|
|
17
19
|
setCopied(true);
|
|
@@ -19,6 +21,7 @@ const CopyButton = React.memo(({ value, label, iconType = 'copy' }) => {
|
|
|
19
21
|
}, [value, label]);
|
|
20
22
|
const handleLongPress = useCallback(() => {
|
|
21
23
|
try {
|
|
24
|
+
triggerNativeHaptic('medium');
|
|
22
25
|
const resolvedValue = typeof value === 'function' ? value() : value;
|
|
23
26
|
const textToShare = typeof resolvedValue === 'string'
|
|
24
27
|
? resolvedValue
|
|
@@ -39,7 +42,13 @@ const CopyButton = React.memo(({ value, label, iconType = 'copy' }) => {
|
|
|
39
42
|
: iconType === 'fetch'
|
|
40
43
|
? FetchIcon
|
|
41
44
|
: CopyIcon;
|
|
42
|
-
return (<TouchableScale
|
|
45
|
+
return (<TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel={label
|
|
46
|
+
? copied
|
|
47
|
+
? `${label} copied`
|
|
48
|
+
: `Copy ${label}`
|
|
49
|
+
: copied
|
|
50
|
+
? 'Copied to clipboard'
|
|
51
|
+
: 'Copy to clipboard'} accessibilityHint="Double tap to copy, long press to share" onPress={handlePress} onLongPress={handleLongPress} hitSlop={12} style={containerStyle}>
|
|
43
52
|
{copied ? (<CheckIcon color={AppColors.greenColor} size={14}/>) : (<IconComponent color={AppColors.grayTextWeak} size={14}/>)}
|
|
44
53
|
</TouchableScale>);
|
|
45
54
|
});
|
|
@@ -7,6 +7,7 @@ import { t } from '../i18n';
|
|
|
7
7
|
import { WarningTriangleIcon, CopyIcon, RefreshCcwIcon, ClearIcon, CircleAlertIcon, } from './NetworkIcons';
|
|
8
8
|
import { handleInterceptedCrash } from '../customHooks/crashHandler';
|
|
9
9
|
import { CrashType } from '../types/enums';
|
|
10
|
+
import { showNativeFloatingButton, setNativeFloatingButtonBadge, } from '../native/NativeInspector';
|
|
10
11
|
function parseStackTrace(stack) {
|
|
11
12
|
if (!stack)
|
|
12
13
|
return { rootFrame: null, frames: [] };
|
|
@@ -109,6 +110,8 @@ ${error.stack || 'No stack trace available'}
|
|
|
109
110
|
try {
|
|
110
111
|
const stack = error?.stack || errorInfo?.componentStack || '';
|
|
111
112
|
handleInterceptedCrash(error, stack, false, CrashType.Render, errorInfo?.componentStack);
|
|
113
|
+
showNativeFloatingButton();
|
|
114
|
+
setNativeFloatingButtonBadge(true);
|
|
112
115
|
}
|
|
113
116
|
catch { }
|
|
114
117
|
}
|
|
@@ -21,6 +21,10 @@ const AnalyticsHeader = React.memo(() => {
|
|
|
21
21
|
const userProperties = getCurrentUserProperties();
|
|
22
22
|
const defaultParams = getDefaultEventParameters();
|
|
23
23
|
const isTrackingEnabled = getCollectionEnabled();
|
|
24
|
+
// If there are no analytics events in the session, hide the graph, category breakdown, and realtime card
|
|
25
|
+
if (analyticsEvents.length === 0) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
24
28
|
const hasUserProps = Object.keys(userProperties).length > 0;
|
|
25
29
|
const hasDefaultParams = Object.keys(defaultParams).length > 0;
|
|
26
30
|
const totalEvents = filteredAnalyticsEvents.length;
|
|
@@ -590,8 +594,8 @@ const AnalyticsHeader = React.memo(() => {
|
|
|
590
594
|
</View>
|
|
591
595
|
|
|
592
596
|
{/* ── Crashlytics-Style Multi-Bar Histogram with Trendline Overlay ── */}
|
|
593
|
-
<View style={{ height: chartHeight, position: 'relative' }}>
|
|
594
|
-
<Svg width="100%" height={chartHeight} viewBox={`0 0 ${svgWidth} ${chartHeight}`}>
|
|
597
|
+
<View style={{ height: chartHeight, position: 'relative', overflow: 'hidden', borderRadius: 8 }}>
|
|
598
|
+
<Svg width="100%" height={chartHeight} viewBox={`0 0 ${svgWidth} ${chartHeight}`} preserveAspectRatio="none">
|
|
595
599
|
<Defs>
|
|
596
600
|
<SvgLinearGradient id="crashlyticsBarGrad" x1="0" y1="0" x2="0" y2="1">
|
|
597
601
|
<Stop offset="0%" stopColor="#8B5CF6" stopOpacity="0.9"/>
|
|
@@ -955,7 +959,7 @@ const AnalyticsTab = React.memo(() => {
|
|
|
955
959
|
<AnalyticsFilterModal visible={isFilterModalOpen} onClose={() => setIsFilterModalOpen(false)}/>
|
|
956
960
|
|
|
957
961
|
<View style={{ flex: 1 }} onLayout={() => setIsAnalyticsLayoutReady(true)}>
|
|
958
|
-
{selectedEvent != null ? (<AnalyticsDetail event={selectedEvent}/>) : isAnalyticsLayoutReady ? (<FlatList data={filteredAnalyticsEvents} keyExtractor={keyExtractor} ListHeaderComponent={AnalyticsHeader} renderItem={renderItem} initialNumToRender={
|
|
962
|
+
{selectedEvent != null ? (<AnalyticsDetail event={selectedEvent}/>) : isAnalyticsLayoutReady ? (<FlatList data={filteredAnalyticsEvents} keyExtractor={keyExtractor} ListHeaderComponent={AnalyticsHeader} renderItem={renderItem} initialNumToRender={12} maxToRenderPerBatch={8} windowSize={5} removeClippedSubviews={true} renderToHardwareTextureAndroid={true} ListEmptyComponent={<View style={styles.emptyContainer}>
|
|
959
963
|
<View style={styles.emptyIconWrap}>
|
|
960
964
|
<EmptyRadarIcon color={AppColors.purple} size={32}/>
|
|
961
965
|
</View>
|
|
@@ -379,7 +379,7 @@ const BundleTreeNodeView = ({ node, level, search, totalBundleKb, expandedFolder
|
|
|
379
379
|
const indentLeft = level * 16 + 10;
|
|
380
380
|
if (node.isFolder) {
|
|
381
381
|
return (<View style={bundleStyles.treeFolderBlock}>
|
|
382
|
-
<TouchableScale onPress={() => toggleFolder(node.fullPath)} style={[
|
|
382
|
+
<TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel={`Folder ${node.name}, ${node.fileCount} ${node.fileCount === 1 ? 'file' : 'files'}, ${node.sizeKb >= 1024 ? `${(node.sizeKb / 1024).toFixed(2)} MB` : `${node.sizeKb} KB`}`} accessibilityState={{ expanded: isExpanded }} accessibilityHint="Double tap to expand or collapse folder" onPress={() => toggleFolder(node.fullPath)} style={[
|
|
383
383
|
bundleStyles.treeFolderRow,
|
|
384
384
|
{ paddingLeft: indentLeft },
|
|
385
385
|
]}>
|
|
@@ -453,7 +453,7 @@ const BundleTreeNodeView = ({ node, level, search, totalBundleKb, expandedFolder
|
|
|
453
453
|
</View>
|
|
454
454
|
|
|
455
455
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 5 }}>
|
|
456
|
-
{onDownload && (<TouchableScale onPress={() => onDownload(file)} style={bundleStyles.treeActionBtn} hitSlop={8}>
|
|
456
|
+
{onDownload && (<TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel={`Download source code for ${file.name}`} accessibilityHint="Fetches module source code from Metro dev server" onPress={() => onDownload(file)} style={bundleStyles.treeActionBtn} hitSlop={8}>
|
|
457
457
|
<DownloadIcon size={13} color={AppColors.sky500}/>
|
|
458
458
|
</TouchableScale>)}
|
|
459
459
|
|
|
@@ -746,7 +746,7 @@ const BundleTab = React.memo(() => {
|
|
|
746
746
|
<ScrollView horizontal showsHorizontalScrollIndicator={false} style={{ flex: 1 }} contentContainerStyle={bundleStyles.subTabsScroll}>
|
|
747
747
|
{subTabs.map(tab => {
|
|
748
748
|
const isActive = activeSubTab === tab.key;
|
|
749
|
-
return (<TouchableScale key={tab.key} onPress={() => setActiveSubTab(tab.key)} style={[
|
|
749
|
+
return (<TouchableScale key={tab.key} accessible={true} accessibilityRole="tab" accessibilityLabel={`${tab.label} tab`} accessibilityState={{ selected: isActive }} accessibilityHint={`Switches to ${tab.label} tab`} onPress={() => setActiveSubTab(tab.key)} style={[
|
|
750
750
|
bundleStyles.subTabPill,
|
|
751
751
|
isActive && bundleStyles.subTabPillActive,
|
|
752
752
|
]}>
|
|
@@ -760,7 +760,7 @@ const BundleTab = React.memo(() => {
|
|
|
760
760
|
</TouchableScale>);
|
|
761
761
|
})}
|
|
762
762
|
</ScrollView>
|
|
763
|
-
<TouchableScale onPress={refreshAnalysis} style={bundleStyles.reloadButton} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}>
|
|
763
|
+
<TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel={isAnalyzing ? "Analyzing Metro bundle" : "Reload bundle analysis"} accessibilityHint="Re-probes Metro dev server and refreshes bundle statistics" onPress={refreshAnalysis} style={bundleStyles.reloadButton} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}>
|
|
764
764
|
{isAnalyzing ? (<ActivityIndicator size="small" color={AppColors.brandPurple}/>) : (<RefreshCcwIcon color={AppColors.brandPurple} size={14}/>)}
|
|
765
765
|
</TouchableScale>
|
|
766
766
|
</View>
|
|
@@ -782,7 +782,7 @@ const BundleTab = React.memo(() => {
|
|
|
782
782
|
<Text style={bundleStyles.heroSubtitle}>
|
|
783
783
|
{t('bundle.heroSubtitle')}
|
|
784
784
|
</Text>
|
|
785
|
-
<Pressable style={{
|
|
785
|
+
<Pressable accessible={true} accessibilityRole="link" accessibilityLabel={`Live Metro bundle URL: ${analysis.scriptURL}`} accessibilityHint="Opens Metro bundle script in browser" style={{
|
|
786
786
|
backgroundColor: `${AppColors.skyBlue}10`,
|
|
787
787
|
borderColor: `${AppColors.skyBlue}30`,
|
|
788
788
|
borderWidth: 1,
|
|
@@ -816,7 +816,7 @@ const BundleTab = React.memo(() => {
|
|
|
816
816
|
{analysis.scriptURL.length} chars
|
|
817
817
|
</Text>
|
|
818
818
|
</View>
|
|
819
|
-
<Text selectable={true} style={{
|
|
819
|
+
<Text selectable={true} numberOfLines={1} ellipsizeMode="tail" style={{
|
|
820
820
|
fontFamily: AppFonts.interMedium,
|
|
821
821
|
fontSize: 11,
|
|
822
822
|
color: AppColors.skyBlue,
|
|
@@ -992,7 +992,7 @@ const BundleTab = React.memo(() => {
|
|
|
992
992
|
<CopyButton value={() => summary} label={t('bundle.categoriesJson')}/>
|
|
993
993
|
</View>
|
|
994
994
|
|
|
995
|
-
{summary.categories.map((cat, cIdx) => (<Pressable key={cIdx} style={({ pressed }) => [
|
|
995
|
+
{summary.categories.map((cat, cIdx) => (<Pressable key={cIdx} accessible={true} accessibilityRole="button" accessibilityLabel={`${cat.title}, ${cat.sizeKb >= 1024 ? `${cat.sizeMb} MB` : `${cat.sizeKb} KB`}, ${cat.pct}% of bundle`} accessibilityHint={`Double tap to view ${cat.title} details`} style={({ pressed }) => [
|
|
996
996
|
bundleStyles.catRowCard,
|
|
997
997
|
pressed && { opacity: 0.85, transform: [{ scale: 0.99 }] },
|
|
998
998
|
]} onPress={() => setActiveSubTab(cat.targetTab)}>
|
|
@@ -1250,7 +1250,7 @@ const BundleTab = React.memo(() => {
|
|
|
1250
1250
|
{ key: 'androidApk', label: 'Universal APK (.apk)', badge: `${analysis?.production.androidApk.totalInstallMb || 364.0} MB` },
|
|
1251
1251
|
].map(tab => {
|
|
1252
1252
|
const isSelected = prodPlatform === tab.key;
|
|
1253
|
-
return (<TouchableScale key={tab.key} onPress={() => setProdPlatform(tab.key)} style={[
|
|
1253
|
+
return (<TouchableScale key={tab.key} accessible={true} accessibilityRole="tab" accessibilityLabel={`${tab.label}, ${tab.badge}`} accessibilityState={{ selected: isSelected }} accessibilityHint={`Switches production estimate to ${tab.label}`} onPress={() => setProdPlatform(tab.key)} style={[
|
|
1254
1254
|
bundleStyles.prodPlatformBtn,
|
|
1255
1255
|
isSelected && bundleStyles.prodPlatformBtnActive,
|
|
1256
1256
|
]}>
|
|
@@ -1500,8 +1500,8 @@ const BundleTab = React.memo(() => {
|
|
|
1500
1500
|
<View style={bundleStyles.filterContainer}>
|
|
1501
1501
|
<View style={bundleStyles.searchRow}>
|
|
1502
1502
|
<SearchIcon color={AppColors.grayTextWeak} size={15}/>
|
|
1503
|
-
<TextInput placeholder={t('bundle.searchFilesPlaceholder')} placeholderTextColor={AppColors.grayTextWeak} value={search} onChangeText={setSearch} style={bundleStyles.searchInput} autoCorrect={false} autoCapitalize="none"/>
|
|
1504
|
-
{search.length > 0 && (<Pressable onPress={() => setSearch('')} hitSlop={10}>
|
|
1503
|
+
<TextInput accessible={true} accessibilityRole="search" accessibilityLabel="Search files" accessibilityHint="Type a filename, path, or extension to filter" placeholder={t('bundle.searchFilesPlaceholder')} placeholderTextColor={AppColors.grayTextWeak} value={search} onChangeText={setSearch} style={bundleStyles.searchInput} autoCorrect={false} autoCapitalize="none"/>
|
|
1504
|
+
{search.length > 0 && (<Pressable accessible={true} accessibilityRole="button" accessibilityLabel="Clear search input" onPress={() => setSearch('')} hitSlop={10}>
|
|
1505
1505
|
<ClearIcon color={AppColors.grayTextWeak} size={14}/>
|
|
1506
1506
|
</Pressable>)}
|
|
1507
1507
|
<CopyButton value={() => filteredFiles} label={t('bundle.filteredFilesJson')}/>
|
|
@@ -1518,7 +1518,7 @@ const BundleTab = React.memo(() => {
|
|
|
1518
1518
|
: tab.key === 'CONSUMED'
|
|
1519
1519
|
? bundleFiles.filter(f => f.isConsumed !== false).length
|
|
1520
1520
|
: bundleFiles.filter(f => f.category === tab.key).length;
|
|
1521
|
-
return (<TouchableScale key={tab.key} onPress={() => setActiveCategory(tab.key)} style={[
|
|
1521
|
+
return (<TouchableScale key={tab.key} accessible={true} accessibilityRole="button" accessibilityLabel={`Filter by ${t(tab.labelKey)}, ${count} items`} accessibilityState={{ selected: isActive }} accessibilityHint={`Filters files list to ${t(tab.labelKey)}`} onPress={() => setActiveCategory(tab.key)} style={[
|
|
1522
1522
|
bundleStyles.catPill,
|
|
1523
1523
|
isActive && bundleStyles.catPillActive,
|
|
1524
1524
|
tab.key === 'UNUSED' && isActive && { backgroundColor: AppColors.red500, borderColor: AppColors.red600 },
|
|
@@ -1542,7 +1542,7 @@ const BundleTab = React.memo(() => {
|
|
|
1542
1542
|
{/* View Mode Toggle Header Bar */}
|
|
1543
1543
|
<View style={bundleStyles.treeHeaderRow}>
|
|
1544
1544
|
<View style={bundleStyles.viewModeToggleGroup}>
|
|
1545
|
-
<TouchableScale onPress={() => setFilesViewMode('tree')} style={[
|
|
1545
|
+
<TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel="Tree folder view" accessibilityState={{ selected: filesViewMode === 'tree' }} onPress={() => setFilesViewMode('tree')} style={[
|
|
1546
1546
|
bundleStyles.viewModeBtn,
|
|
1547
1547
|
filesViewMode === 'tree' && bundleStyles.viewModeBtnActive,
|
|
1548
1548
|
]}>
|
|
@@ -1553,7 +1553,7 @@ const BundleTab = React.memo(() => {
|
|
|
1553
1553
|
{t('bundle.treeView')}
|
|
1554
1554
|
</Text>
|
|
1555
1555
|
</TouchableScale>
|
|
1556
|
-
<TouchableScale onPress={() => setFilesViewMode('list')} style={[
|
|
1556
|
+
<TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel="Flat list view" accessibilityState={{ selected: filesViewMode === 'list' }} onPress={() => setFilesViewMode('list')} style={[
|
|
1557
1557
|
bundleStyles.viewModeBtn,
|
|
1558
1558
|
filesViewMode === 'list' && bundleStyles.viewModeBtnActive,
|
|
1559
1559
|
]}>
|
|
@@ -1567,11 +1567,11 @@ const BundleTab = React.memo(() => {
|
|
|
1567
1567
|
</View>
|
|
1568
1568
|
|
|
1569
1569
|
{filesViewMode === 'tree' ? (<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
|
|
1570
|
-
<TouchableScale onPress={expandAllFolders}>
|
|
1570
|
+
<TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel="Expand all folders" onPress={expandAllFolders}>
|
|
1571
1571
|
<Text style={bundleStyles.treeActionLink}>{t('bundle.expand')}</Text>
|
|
1572
1572
|
</TouchableScale>
|
|
1573
1573
|
<Text style={{ color: AppColors.grayTextWeak, fontSize: 11 }}>•</Text>
|
|
1574
|
-
<TouchableScale onPress={collapseAllFolders}>
|
|
1574
|
+
<TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel="Collapse all folders" onPress={collapseAllFolders}>
|
|
1575
1575
|
<Text style={bundleStyles.treeActionLink}>{t('bundle.collapse')}</Text>
|
|
1576
1576
|
</TouchableScale>
|
|
1577
1577
|
</View>) : (<Text style={bundleStyles.listHeaderCount}>
|
|
@@ -1695,8 +1695,8 @@ const BundleTab = React.memo(() => {
|
|
|
1695
1695
|
<View style={bundleStyles.filterContainer}>
|
|
1696
1696
|
<View style={bundleStyles.searchRow}>
|
|
1697
1697
|
<SearchIcon color={AppColors.grayTextWeak} size={15}/>
|
|
1698
|
-
<TextInput placeholder={t('bundle.searchPackagesPlaceholder')} placeholderTextColor={AppColors.grayTextWeak} value={search} onChangeText={setSearch} style={bundleStyles.searchInput} autoCorrect={false} autoCapitalize="none"/>
|
|
1699
|
-
{search.length > 0 && (<Pressable onPress={() => setSearch('')} hitSlop={10}>
|
|
1698
|
+
<TextInput accessible={true} accessibilityRole="search" accessibilityLabel="Search packages" accessibilityHint="Type a package name or description to filter" placeholder={t('bundle.searchPackagesPlaceholder')} placeholderTextColor={AppColors.grayTextWeak} value={search} onChangeText={setSearch} style={bundleStyles.searchInput} autoCorrect={false} autoCapitalize="none"/>
|
|
1699
|
+
{search.length > 0 && (<Pressable accessible={true} accessibilityRole="button" accessibilityLabel="Clear search input" onPress={() => setSearch('')} hitSlop={10}>
|
|
1700
1700
|
<ClearIcon color={AppColors.grayTextWeak} size={14}/>
|
|
1701
1701
|
</Pressable>)}
|
|
1702
1702
|
<CopyButton value={() => filteredPackages} label={t('bundle.dependenciesJson')}/>
|
|
@@ -1711,7 +1711,7 @@ const BundleTab = React.memo(() => {
|
|
|
1711
1711
|
const cleanInstalledVersion = pkg.version ? pkg.version.replace(/^\^/, '') : '';
|
|
1712
1712
|
const hasUpdate = !!pkg.latestVersion && !!cleanInstalledVersion &&
|
|
1713
1713
|
pkg.latestVersion !== cleanInstalledVersion;
|
|
1714
|
-
return (<View key={pkg.id} style={[bundleStyles.fileCard, pkg.isDeprecated && { borderColor: AppColors.errorBorder, backgroundColor:
|
|
1714
|
+
return (<View key={pkg.id} style={[bundleStyles.fileCard, pkg.isDeprecated && { borderColor: AppColors.errorBorder, backgroundColor: AppColors.errorCardBg }]}>
|
|
1715
1715
|
{/* Header Row: #s.no + Logo + Package Name + Version Pill + Update/Deprecated Badges + Size & Copy */}
|
|
1716
1716
|
<View style={bundleStyles.fileCardTop}>
|
|
1717
1717
|
<View style={bundleStyles.sNoBadge}>
|
|
@@ -1811,7 +1811,7 @@ const BundleTab = React.memo(() => {
|
|
|
1811
1811
|
<Text style={bundleStyles.fileMetaText}>
|
|
1812
1812
|
{t('bundle.minified', { size: pkg.sizeKb })}
|
|
1813
1813
|
</Text>
|
|
1814
|
-
<TouchableScale onPress={() => {
|
|
1814
|
+
<TouchableScale accessible={true} accessibilityRole="link" accessibilityLabel={`Open ${pkg.name} on NPM`} accessibilityHint="Opens NPM package page in browser" onPress={() => {
|
|
1815
1815
|
const url = pkg.npmUrl || `https://www.npmjs.com/package/${pkg.name}`;
|
|
1816
1816
|
Linking.openURL(url).catch(() => { });
|
|
1817
1817
|
}} style={bundleStyles.npmLinkBtn}>
|
|
@@ -1890,7 +1890,7 @@ const BundleTab = React.memo(() => {
|
|
|
1890
1890
|
{t('bundle.fileSizeKb', { size: file.sizeKb })}
|
|
1891
1891
|
</Text>
|
|
1892
1892
|
</View>
|
|
1893
|
-
<TouchableScale onPress={() => downloadBundleFile(file, analysis?.scriptURL)} style={bundleStyles.downloadFileBtn} hitSlop={8}>
|
|
1893
|
+
<TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel={`Download ${file.name}`} accessibilityHint="Downloads media asset file" onPress={() => downloadBundleFile(file, analysis?.scriptURL)} style={bundleStyles.downloadFileBtn} hitSlop={8}>
|
|
1894
1894
|
<DownloadIcon size={14} color={AppColors.sky500}/>
|
|
1895
1895
|
</TouchableScale>
|
|
1896
1896
|
<CopyButton value={() => file} label={t('bundle.mediaItemJson')}/>
|
|
@@ -469,7 +469,7 @@ const ConsoleTab = React.memo(() => {
|
|
|
469
469
|
</ScrollView>
|
|
470
470
|
</View>
|
|
471
471
|
|
|
472
|
-
<FlatList data={filteredConsoleLogs} keyExtractor={keyExtractor} ListHeaderComponent={listHeader} renderItem={renderItem} initialNumToRender={
|
|
472
|
+
<FlatList data={filteredConsoleLogs} keyExtractor={keyExtractor} ListHeaderComponent={listHeader} renderItem={renderItem} initialNumToRender={12} maxToRenderPerBatch={8} windowSize={5} removeClippedSubviews={true} renderToHardwareTextureAndroid={true} ListEmptyComponent={<EmptyState isSearch={logSearch.length > 0 || logFilters.size > 0}/>} ListFooterComponent={filteredConsoleLogs.length > 0 ? (<EndOfListFooter count={filteredConsoleLogs.length} label="logs"/>) : null} contentContainerStyle={[
|
|
473
473
|
styles.listContent,
|
|
474
474
|
filteredConsoleLogs.length === 0 && { flexGrow: 1 },
|
|
475
475
|
]} keyboardShouldPersistTaps="handled"/>
|
|
@@ -97,23 +97,27 @@ const CrashDetail = React.memo(() => {
|
|
|
97
97
|
localStyles.pillBadge,
|
|
98
98
|
{
|
|
99
99
|
backgroundColor: selectedCrash.isFatal
|
|
100
|
-
?
|
|
101
|
-
:
|
|
102
|
-
borderColor: selectedCrash.isFatal
|
|
100
|
+
? AppColors.errorCardBg
|
|
101
|
+
: AppColors.amberBg,
|
|
102
|
+
borderColor: selectedCrash.isFatal
|
|
103
|
+
? AppColors.errorBorder
|
|
104
|
+
: AppColors.amberWarmBorder,
|
|
103
105
|
},
|
|
104
106
|
]}>
|
|
105
107
|
<View style={[
|
|
106
108
|
localStyles.statusDot,
|
|
107
109
|
{
|
|
108
110
|
backgroundColor: selectedCrash.isFatal
|
|
109
|
-
?
|
|
110
|
-
:
|
|
111
|
+
? AppColors.red600
|
|
112
|
+
: AppColors.amber600,
|
|
111
113
|
},
|
|
112
114
|
]}/>
|
|
113
115
|
<Text style={[
|
|
114
116
|
localStyles.pillBadgeText,
|
|
115
117
|
{
|
|
116
|
-
color: selectedCrash.isFatal
|
|
118
|
+
color: selectedCrash.isFatal
|
|
119
|
+
? AppColors.redErrorText
|
|
120
|
+
: AppColors.amberWarmText,
|
|
117
121
|
},
|
|
118
122
|
]}>
|
|
119
123
|
{selectedCrash.isFatal ? t('crash.fatalCrash') : t('crash.handledException')}
|
|
@@ -124,11 +128,11 @@ const CrashDetail = React.memo(() => {
|
|
|
124
128
|
<View style={[
|
|
125
129
|
localStyles.pillBadge,
|
|
126
130
|
{
|
|
127
|
-
backgroundColor:
|
|
128
|
-
borderColor:
|
|
131
|
+
backgroundColor: AppColors.violetSoftBg,
|
|
132
|
+
borderColor: AppColors.violetSoftBorder,
|
|
129
133
|
},
|
|
130
134
|
]}>
|
|
131
|
-
<Text style={[localStyles.pillBadgeText, { color:
|
|
135
|
+
<Text style={[localStyles.pillBadgeText, { color: AppColors.purple700 }]}>
|
|
132
136
|
{selectedCrash.type.toUpperCase()}
|
|
133
137
|
</Text>
|
|
134
138
|
</View>
|
|
@@ -138,12 +142,12 @@ const CrashDetail = React.memo(() => {
|
|
|
138
142
|
{/* Copy Report Action */}
|
|
139
143
|
<TouchableScale onPress={handleCopyReport} hitSlop={10} style={[
|
|
140
144
|
localStyles.actionButton,
|
|
141
|
-
copiedSuccess && { backgroundColor:
|
|
145
|
+
copiedSuccess && { backgroundColor: AppColors.mintGreenBg, borderColor: AppColors.mintGreenBorder },
|
|
142
146
|
]}>
|
|
143
|
-
{copiedSuccess ? (<CircleCheckIcon color=
|
|
147
|
+
{copiedSuccess ? (<CircleCheckIcon color={AppColors.mintGreenText} size={12}/>) : (<CopyIcon color={AppColors.purple} size={12}/>)}
|
|
144
148
|
<Text style={[
|
|
145
149
|
localStyles.actionButtonText,
|
|
146
|
-
copiedSuccess && { color:
|
|
150
|
+
copiedSuccess && { color: AppColors.mintGreenText },
|
|
147
151
|
]}>
|
|
148
152
|
{copiedSuccess ? t('crash.copied') : t('crash.copyReport')}
|
|
149
153
|
</Text>
|