react-native-inapp-inspector 2.0.1 → 2.0.3
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 +10 -0
- package/android/src/main/java/com/inappinspector/NetworkInspectorModule.kt +23 -0
- package/dist/commonjs/components/AppHeaderLogo.js +21 -2
- package/dist/commonjs/components/Inspector/InspectorHeader.js +84 -11
- package/dist/commonjs/components/Inspector/MainScreen.js +8 -0
- package/dist/commonjs/components/Inspector/NpmUpdateToast.d.ts +3 -0
- package/dist/commonjs/components/Inspector/NpmUpdateToast.js +288 -0
- package/dist/commonjs/components/Inspector/SettingsPanel.js +192 -1
- package/dist/commonjs/components/Inspector/TelemetryConsentModal.d.ts +3 -0
- package/dist/commonjs/components/Inspector/TelemetryConsentModal.js +359 -0
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/helpers/index.d.ts +1 -0
- package/dist/commonjs/helpers/index.js +1 -0
- package/dist/commonjs/helpers/telemetry.d.ts +99 -0
- package/dist/commonjs/helpers/telemetry.js +398 -0
- package/dist/commonjs/index.d.ts +2 -0
- package/dist/commonjs/index.js +37 -3
- package/dist/commonjs/native/NativeInspector.d.ts +5 -0
- package/dist/commonjs/native/NativeInspector.js +3 -2
- package/dist/commonjs/types/interfaces.d.ts +5 -0
- package/dist/esm/components/AppHeaderLogo.js +21 -2
- package/dist/esm/components/Inspector/InspectorHeader.js +85 -12
- package/dist/esm/components/Inspector/MainScreen.js +8 -0
- package/dist/esm/components/Inspector/NpmUpdateToast.d.ts +3 -0
- package/dist/esm/components/Inspector/NpmUpdateToast.js +251 -0
- package/dist/esm/components/Inspector/SettingsPanel.js +192 -1
- package/dist/esm/components/Inspector/TelemetryConsentModal.d.ts +3 -0
- package/dist/esm/components/Inspector/TelemetryConsentModal.js +322 -0
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/helpers/index.d.ts +1 -0
- package/dist/esm/helpers/index.js +1 -0
- package/dist/esm/helpers/telemetry.d.ts +99 -0
- package/dist/esm/helpers/telemetry.js +380 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +27 -3
- package/dist/esm/native/NativeInspector.d.ts +5 -0
- package/dist/esm/native/NativeInspector.js +3 -2
- package/dist/esm/types/interfaces.d.ts +5 -0
- package/ios/{NetworkInspectorModule.m → NetworkInspectorModule.mm} +175 -119
- package/package.json +2 -2
|
@@ -5,7 +5,23 @@ export const AppHeaderLogo = ({ size = 46, customIcon, }) => {
|
|
|
5
5
|
const [loadError, setLoadError] = useState(false);
|
|
6
6
|
// 1. If customIcon is explicitly provided, render it directly edge-to-edge
|
|
7
7
|
if (customIcon) {
|
|
8
|
-
|
|
8
|
+
if (React.isValidElement(customIcon)) {
|
|
9
|
+
const clonedIcon = React.cloneElement(customIcon, {
|
|
10
|
+
size,
|
|
11
|
+
width: size,
|
|
12
|
+
height: size,
|
|
13
|
+
});
|
|
14
|
+
return (<View style={[
|
|
15
|
+
logoStyles.container,
|
|
16
|
+
{ width: size, height: size, padding: 0, borderRadius: Math.round(size * 0.28) },
|
|
17
|
+
]}>
|
|
18
|
+
{clonedIcon}
|
|
19
|
+
</View>);
|
|
20
|
+
}
|
|
21
|
+
return (<View style={[
|
|
22
|
+
logoStyles.container,
|
|
23
|
+
{ width: size, height: size, borderRadius: Math.round(size * 0.28) },
|
|
24
|
+
]}>
|
|
9
25
|
<Image source={customIcon} style={logoStyles.image} resizeMode="cover"/>
|
|
10
26
|
</View>);
|
|
11
27
|
}
|
|
@@ -21,7 +37,10 @@ export const AppHeaderLogo = ({ size = 46, customIcon, }) => {
|
|
|
21
37
|
</View>);
|
|
22
38
|
}
|
|
23
39
|
// 3. Fallback: Inspector's Signature Owl Brand Logo
|
|
24
|
-
return (<View style={[
|
|
40
|
+
return (<View style={[
|
|
41
|
+
logoStyles.container,
|
|
42
|
+
{ width: size, height: size, padding: 0, borderRadius: Math.round(size * 0.28) },
|
|
43
|
+
]}>
|
|
25
44
|
<BrandSquareIcon size={size}/>
|
|
26
45
|
</View>);
|
|
27
46
|
};
|
|
@@ -9,10 +9,36 @@ 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, getAppName,
|
|
12
|
+
import { getStatusColor, getAppName, formatTime, getSize } from '../../helpers';
|
|
13
|
+
import { getTelemetryConsentStatus } from '../../helpers/telemetry';
|
|
13
14
|
import { WhiteBackNavigation, TrashIcon, SettingsIcon, CloseWhite, ClockIcon, SizeIcon, AppleIcon, AndroidIcon, NpmIcon, ResetIcon, } from '../NetworkIcons';
|
|
14
15
|
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, settingsPage, setSettingsPage, resetToDefaults, closeModal, detailTitle, activeTab, environment, selectedCrash, setSelectedCrash, } = useInspector();
|
|
16
|
+
const { modalHeightPercent, appIcon, selected, setSelected, selectedEvent, setSelectedEvent, selectedLog, setSelectedLog, selectedReduxSlice, setSelectedReduxSlice, selectedReduxAction, setSelectedReduxAction, reduxState, reduxLastActionMap, showHeaderInfo, setShowHeaderInfo, updateAvailable, latestNpmVersion, clearAnim, activePulseAnim, unreadPulseAnim, runClearAllWithAnimation, settingsPage, setSettingsPage, resetToDefaults, closeModal, detailTitle, activeTab, environment, visible, selectedCrash, setSelectedCrash, } = useInspector();
|
|
17
|
+
const [isTelemetryActive, setIsTelemetryActive] = React.useState(false);
|
|
18
|
+
const telemetryPulseAnim = React.useRef(new Animated.Value(1)).current;
|
|
19
|
+
React.useEffect(() => {
|
|
20
|
+
getTelemetryConsentStatus().then(status => {
|
|
21
|
+
setIsTelemetryActive(status === 'granted');
|
|
22
|
+
});
|
|
23
|
+
}, [visible]);
|
|
24
|
+
React.useEffect(() => {
|
|
25
|
+
if (!isTelemetryActive)
|
|
26
|
+
return;
|
|
27
|
+
const pulse = Animated.loop(Animated.sequence([
|
|
28
|
+
Animated.timing(telemetryPulseAnim, {
|
|
29
|
+
toValue: 2.2,
|
|
30
|
+
duration: 1500,
|
|
31
|
+
useNativeDriver: true,
|
|
32
|
+
}),
|
|
33
|
+
Animated.timing(telemetryPulseAnim, {
|
|
34
|
+
toValue: 1,
|
|
35
|
+
duration: 0,
|
|
36
|
+
useNativeDriver: true,
|
|
37
|
+
}),
|
|
38
|
+
]));
|
|
39
|
+
pulse.start();
|
|
40
|
+
return () => pulse.stop();
|
|
41
|
+
}, [isTelemetryActive, telemetryPulseAnim]);
|
|
16
42
|
const envConfig = useMemo(() => {
|
|
17
43
|
const rawEnv = (environment || (__DEV__ ? 'DEV' : 'PROD')).trim();
|
|
18
44
|
const clean = rawEnv.toUpperCase();
|
|
@@ -175,7 +201,7 @@ const InspectorHeader = React.memo(() => {
|
|
|
175
201
|
minWidth: 0,
|
|
176
202
|
marginRight: 6,
|
|
177
203
|
}}>
|
|
178
|
-
<AppHeaderLogo size={
|
|
204
|
+
<AppHeaderLogo size={46} customIcon={appIcon}/>
|
|
179
205
|
<View style={{ gap: 2.5, flex: 1, minWidth: 0 }}>
|
|
180
206
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, minWidth: 0 }}>
|
|
181
207
|
<Text style={[styles.headerTitle, { flexShrink: 1 }]} numberOfLines={1} ellipsizeMode="tail">
|
|
@@ -222,11 +248,11 @@ const InspectorHeader = React.memo(() => {
|
|
|
222
248
|
</Pressable>)}
|
|
223
249
|
</View>
|
|
224
250
|
|
|
225
|
-
{/*
|
|
251
|
+
{/* OS & NPM Version Representation */}
|
|
226
252
|
<View style={{
|
|
227
253
|
flexDirection: 'row',
|
|
228
254
|
alignItems: 'center',
|
|
229
|
-
gap:
|
|
255
|
+
gap: 5,
|
|
230
256
|
minWidth: 0,
|
|
231
257
|
}}>
|
|
232
258
|
<View style={{
|
|
@@ -236,21 +262,21 @@ const InspectorHeader = React.memo(() => {
|
|
|
236
262
|
borderRadius: 5,
|
|
237
263
|
paddingHorizontal: 6,
|
|
238
264
|
paddingVertical: 2,
|
|
239
|
-
gap:
|
|
265
|
+
gap: 4,
|
|
240
266
|
borderWidth: 1,
|
|
241
267
|
borderColor: `${AppColors.white}2E`,
|
|
242
|
-
flexShrink:
|
|
243
|
-
minWidth: 0,
|
|
268
|
+
flexShrink: 0,
|
|
244
269
|
}}>
|
|
245
|
-
{Platform.OS === 'ios' ? (<AppleIcon color={`${AppColors.white}
|
|
270
|
+
{Platform.OS === 'ios' ? (<AppleIcon color={`${AppColors.white}E6`} size={10}/>) : (<AndroidIcon color={`${AppColors.white}E6`} size={10}/>)}
|
|
246
271
|
<Text style={{
|
|
247
272
|
fontFamily: AppFonts.interMedium,
|
|
248
273
|
fontSize: 9.5,
|
|
249
274
|
color: `${AppColors.white}EB`,
|
|
250
275
|
letterSpacing: 0.1,
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
276
|
+
}} numberOfLines={1}>
|
|
277
|
+
{Platform.OS === 'ios'
|
|
278
|
+
? `iOS ${Platform.Version}`
|
|
279
|
+
: `Android ${Platform.Version}`}
|
|
254
280
|
</Text>
|
|
255
281
|
</View>
|
|
256
282
|
|
|
@@ -279,6 +305,53 @@ const InspectorHeader = React.memo(() => {
|
|
|
279
305
|
v{LIB_VERSION}
|
|
280
306
|
</Text>
|
|
281
307
|
</Pressable>
|
|
308
|
+
|
|
309
|
+
{isTelemetryActive && (<Pressable onPress={() => Alert.alert('Anonymous Telemetry Active', 'Anonymous diagnostics (such as React Native version, JavaScript engine, and device model) are currently enabled to help improve library tooling.\n\nNo user data or network payloads are captured. You can toggle this anytime in Settings.', [{ text: 'Got it' }])} style={{
|
|
310
|
+
flexDirection: 'row',
|
|
311
|
+
alignItems: 'center',
|
|
312
|
+
backgroundColor: 'rgba(16, 185, 129, 0.22)',
|
|
313
|
+
borderRadius: 5,
|
|
314
|
+
paddingHorizontal: 5.5,
|
|
315
|
+
paddingVertical: 2,
|
|
316
|
+
gap: 4.5,
|
|
317
|
+
borderWidth: 1,
|
|
318
|
+
borderColor: 'rgba(52, 211, 153, 0.45)',
|
|
319
|
+
flexShrink: 0,
|
|
320
|
+
}}>
|
|
321
|
+
<View style={{
|
|
322
|
+
width: 6,
|
|
323
|
+
height: 6,
|
|
324
|
+
alignItems: 'center',
|
|
325
|
+
justifyContent: 'center',
|
|
326
|
+
}}>
|
|
327
|
+
<Animated.View style={{
|
|
328
|
+
position: 'absolute',
|
|
329
|
+
width: 6,
|
|
330
|
+
height: 6,
|
|
331
|
+
borderRadius: 3,
|
|
332
|
+
backgroundColor: '#34D399',
|
|
333
|
+
opacity: telemetryPulseAnim.interpolate({
|
|
334
|
+
inputRange: [1, 2.2],
|
|
335
|
+
outputRange: [0.8, 0],
|
|
336
|
+
}),
|
|
337
|
+
transform: [{ scale: telemetryPulseAnim }],
|
|
338
|
+
}}/>
|
|
339
|
+
<View style={{
|
|
340
|
+
width: 4,
|
|
341
|
+
height: 4,
|
|
342
|
+
borderRadius: 2,
|
|
343
|
+
backgroundColor: '#10B981',
|
|
344
|
+
}}/>
|
|
345
|
+
</View>
|
|
346
|
+
<Text style={{
|
|
347
|
+
fontFamily: AppFonts.interBold,
|
|
348
|
+
fontSize: 9,
|
|
349
|
+
color: '#6EE7B7',
|
|
350
|
+
letterSpacing: 0.2,
|
|
351
|
+
}}>
|
|
352
|
+
LIVE
|
|
353
|
+
</Text>
|
|
354
|
+
</Pressable>)}
|
|
282
355
|
</View>
|
|
283
356
|
</View>
|
|
284
357
|
</View>) : null}
|
|
@@ -18,6 +18,8 @@ import PerformanceTab from './PerformanceTab';
|
|
|
18
18
|
import CrashTab from './CrashTab';
|
|
19
19
|
import CrashDetail from './CrashDetail';
|
|
20
20
|
import SettingsPanel from './SettingsPanel';
|
|
21
|
+
import TelemetryConsentModal from './TelemetryConsentModal';
|
|
22
|
+
import NpmUpdateToast from './NpmUpdateToast';
|
|
21
23
|
import Toast from '../Toast';
|
|
22
24
|
import styles from '../../styles';
|
|
23
25
|
import { AppColors } from '../../styles/AppColors';
|
|
@@ -166,11 +168,17 @@ const MainScreen = () => {
|
|
|
166
168
|
|
|
167
169
|
{/* Bottom floating toast notification */}
|
|
168
170
|
<Toast />
|
|
171
|
+
|
|
172
|
+
{/* NPM Version Update Toast with timeout progress bar */}
|
|
173
|
+
<NpmUpdateToast />
|
|
169
174
|
</View>
|
|
170
175
|
</View>
|
|
171
176
|
</ErrorBoundary>)}
|
|
172
177
|
{hasNavigationContext && (<NavigationTracker onStateChange={setNavState}/>)}
|
|
173
178
|
</Modal>
|
|
179
|
+
|
|
180
|
+
{/* Anonymous Telemetry Consent Dialog at root viewport */}
|
|
181
|
+
<TelemetryConsentModal />
|
|
174
182
|
</>);
|
|
175
183
|
};
|
|
176
184
|
export default MainScreen;
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
2
|
+
import { Animated, Linking, Platform, StyleSheet, Text, TouchableOpacity, View, } from 'react-native';
|
|
3
|
+
import Svg, { Path } from 'react-native-svg';
|
|
4
|
+
import { useInspector } from './InspectorContext';
|
|
5
|
+
import { LIB_VERSION } from '../../constants';
|
|
6
|
+
import { NpmIcon } from '../NetworkIcons';
|
|
7
|
+
const CloseSvg = ({ size = 13, color = '#94A3B8' }) => (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
8
|
+
<Path d="M18 6L6 18M6 6l12 12" stroke={color} strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"/>
|
|
9
|
+
</Svg>);
|
|
10
|
+
const ExternalLinkSvg = ({ size = 12, color = '#FFFFFF' }) => (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
|
|
11
|
+
<Path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
12
|
+
<Path d="M15 3h6v6" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
13
|
+
<Path d="M10 14L21 3" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
|
|
14
|
+
</Svg>);
|
|
15
|
+
const TOAST_TIMEOUT_MS = 6500;
|
|
16
|
+
export const NpmUpdateToast = () => {
|
|
17
|
+
const { updateAvailable, latestNpmVersion, showUpdateToast } = useInspector();
|
|
18
|
+
const [dismissed, setDismissed] = useState(false);
|
|
19
|
+
const [visible, setVisible] = useState(false);
|
|
20
|
+
const translateYAnim = useRef(new Animated.Value(50)).current;
|
|
21
|
+
const opacityAnim = useRef(new Animated.Value(0)).current;
|
|
22
|
+
const progressAnim = useRef(new Animated.Value(1)).current;
|
|
23
|
+
const timerRef = useRef(null);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (showUpdateToast && updateAvailable && latestNpmVersion && !dismissed) {
|
|
26
|
+
setVisible(true);
|
|
27
|
+
// Slide and fade in
|
|
28
|
+
Animated.parallel([
|
|
29
|
+
Animated.spring(translateYAnim, {
|
|
30
|
+
toValue: 0,
|
|
31
|
+
friction: 8,
|
|
32
|
+
tension: 40,
|
|
33
|
+
useNativeDriver: true,
|
|
34
|
+
}),
|
|
35
|
+
Animated.timing(opacityAnim, {
|
|
36
|
+
toValue: 1,
|
|
37
|
+
duration: 250,
|
|
38
|
+
useNativeDriver: true,
|
|
39
|
+
}),
|
|
40
|
+
]).start();
|
|
41
|
+
// Progress bar countdown from 1 to 0
|
|
42
|
+
progressAnim.setValue(1);
|
|
43
|
+
Animated.timing(progressAnim, {
|
|
44
|
+
toValue: 0,
|
|
45
|
+
duration: TOAST_TIMEOUT_MS,
|
|
46
|
+
useNativeDriver: false,
|
|
47
|
+
}).start();
|
|
48
|
+
// Auto-hide after timeout
|
|
49
|
+
timerRef.current = setTimeout(() => {
|
|
50
|
+
handleDismiss();
|
|
51
|
+
}, TOAST_TIMEOUT_MS);
|
|
52
|
+
}
|
|
53
|
+
return () => {
|
|
54
|
+
if (timerRef.current) {
|
|
55
|
+
clearTimeout(timerRef.current);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}, [showUpdateToast, updateAvailable, latestNpmVersion, dismissed]);
|
|
59
|
+
const handleDismiss = () => {
|
|
60
|
+
Animated.parallel([
|
|
61
|
+
Animated.timing(opacityAnim, {
|
|
62
|
+
toValue: 0,
|
|
63
|
+
duration: 200,
|
|
64
|
+
useNativeDriver: true,
|
|
65
|
+
}),
|
|
66
|
+
Animated.timing(translateYAnim, {
|
|
67
|
+
toValue: 50,
|
|
68
|
+
duration: 200,
|
|
69
|
+
useNativeDriver: true,
|
|
70
|
+
}),
|
|
71
|
+
]).start(() => {
|
|
72
|
+
setVisible(false);
|
|
73
|
+
setDismissed(true);
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
const handleOpenNpm = () => {
|
|
77
|
+
Linking.openURL('https://www.npmjs.com/package/react-native-inapp-inspector').catch(() => { });
|
|
78
|
+
handleDismiss();
|
|
79
|
+
};
|
|
80
|
+
if (!showUpdateToast || !visible || !updateAvailable || !latestNpmVersion)
|
|
81
|
+
return null;
|
|
82
|
+
const progressWidth = progressAnim.interpolate({
|
|
83
|
+
inputRange: [0, 1],
|
|
84
|
+
outputRange: ['0%', '100%'],
|
|
85
|
+
});
|
|
86
|
+
return (<Animated.View style={[
|
|
87
|
+
styles.container,
|
|
88
|
+
{
|
|
89
|
+
opacity: opacityAnim,
|
|
90
|
+
transform: [{ translateY: translateYAnim }],
|
|
91
|
+
},
|
|
92
|
+
]}>
|
|
93
|
+
<View style={styles.contentRow}>
|
|
94
|
+
{/* Left NPM Badge Icon */}
|
|
95
|
+
<View style={styles.npmIconContainer}>
|
|
96
|
+
<NpmIcon size={14} color="#CB3837"/>
|
|
97
|
+
</View>
|
|
98
|
+
|
|
99
|
+
{/* Text Details */}
|
|
100
|
+
<View style={styles.textContainer}>
|
|
101
|
+
<View style={styles.titleRow}>
|
|
102
|
+
<Text style={styles.titleText}>Update Available</Text>
|
|
103
|
+
<View style={styles.versionPill}>
|
|
104
|
+
<Text style={styles.versionPillText}>v{latestNpmVersion}</Text>
|
|
105
|
+
</View>
|
|
106
|
+
</View>
|
|
107
|
+
<Text style={styles.subtitleText} numberOfLines={1}>
|
|
108
|
+
Installed: v{LIB_VERSION} • New version is ready
|
|
109
|
+
</Text>
|
|
110
|
+
</View>
|
|
111
|
+
|
|
112
|
+
{/* Action Button: View */}
|
|
113
|
+
<TouchableOpacity style={styles.actionButton} onPress={handleOpenNpm} activeOpacity={0.8}>
|
|
114
|
+
<Text style={styles.actionButtonText}>View</Text>
|
|
115
|
+
<ExternalLinkSvg size={10} color="#FFFFFF"/>
|
|
116
|
+
</TouchableOpacity>
|
|
117
|
+
|
|
118
|
+
{/* Close Button */}
|
|
119
|
+
<TouchableOpacity style={styles.closeButton} onPress={handleDismiss} hitSlop={8} activeOpacity={0.7}>
|
|
120
|
+
<CloseSvg size={12} color="#94A3B8"/>
|
|
121
|
+
</TouchableOpacity>
|
|
122
|
+
</View>
|
|
123
|
+
|
|
124
|
+
{/* Animated Linear Timeout Progress Bar */}
|
|
125
|
+
<View style={styles.progressBarTrack}>
|
|
126
|
+
<Animated.View style={[styles.progressBarFill, { width: progressWidth }]}/>
|
|
127
|
+
</View>
|
|
128
|
+
</Animated.View>);
|
|
129
|
+
};
|
|
130
|
+
const fontStack = Platform.select({
|
|
131
|
+
ios: {
|
|
132
|
+
fontFamily: 'System',
|
|
133
|
+
},
|
|
134
|
+
android: {
|
|
135
|
+
fontFamily: 'sans-serif',
|
|
136
|
+
},
|
|
137
|
+
default: {},
|
|
138
|
+
});
|
|
139
|
+
const styles = StyleSheet.create({
|
|
140
|
+
container: {
|
|
141
|
+
position: 'absolute',
|
|
142
|
+
bottom: 24,
|
|
143
|
+
left: 16,
|
|
144
|
+
right: 16,
|
|
145
|
+
backgroundColor: '#0F172A',
|
|
146
|
+
borderRadius: 16,
|
|
147
|
+
borderWidth: 1,
|
|
148
|
+
borderColor: '#334155',
|
|
149
|
+
shadowColor: '#000000',
|
|
150
|
+
shadowOffset: { width: 0, height: 10 },
|
|
151
|
+
shadowOpacity: 0.35,
|
|
152
|
+
shadowRadius: 20,
|
|
153
|
+
elevation: 20,
|
|
154
|
+
overflow: 'hidden',
|
|
155
|
+
zIndex: 99999,
|
|
156
|
+
},
|
|
157
|
+
contentRow: {
|
|
158
|
+
flexDirection: 'row',
|
|
159
|
+
alignItems: 'center',
|
|
160
|
+
paddingVertical: 12,
|
|
161
|
+
paddingHorizontal: 14,
|
|
162
|
+
gap: 10,
|
|
163
|
+
},
|
|
164
|
+
npmIconContainer: {
|
|
165
|
+
width: 32,
|
|
166
|
+
height: 32,
|
|
167
|
+
borderRadius: 10,
|
|
168
|
+
backgroundColor: '#FFFFFF',
|
|
169
|
+
justifyContent: 'center',
|
|
170
|
+
alignItems: 'center',
|
|
171
|
+
borderWidth: 1,
|
|
172
|
+
borderColor: '#CBD5E1',
|
|
173
|
+
flexShrink: 0,
|
|
174
|
+
},
|
|
175
|
+
textContainer: {
|
|
176
|
+
flex: 1,
|
|
177
|
+
minWidth: 0,
|
|
178
|
+
},
|
|
179
|
+
titleRow: {
|
|
180
|
+
flexDirection: 'row',
|
|
181
|
+
alignItems: 'center',
|
|
182
|
+
gap: 6,
|
|
183
|
+
},
|
|
184
|
+
titleText: {
|
|
185
|
+
fontSize: 13,
|
|
186
|
+
fontWeight: '700',
|
|
187
|
+
color: '#F8FAFC',
|
|
188
|
+
letterSpacing: -0.2,
|
|
189
|
+
...fontStack,
|
|
190
|
+
},
|
|
191
|
+
versionPill: {
|
|
192
|
+
backgroundColor: '#7C3AED26',
|
|
193
|
+
paddingHorizontal: 6,
|
|
194
|
+
paddingVertical: 1.5,
|
|
195
|
+
borderRadius: 6,
|
|
196
|
+
borderWidth: 1,
|
|
197
|
+
borderColor: '#7C3AED59',
|
|
198
|
+
},
|
|
199
|
+
versionPillText: {
|
|
200
|
+
fontSize: 10.5,
|
|
201
|
+
fontWeight: '700',
|
|
202
|
+
color: '#A78BFA',
|
|
203
|
+
...fontStack,
|
|
204
|
+
},
|
|
205
|
+
subtitleText: {
|
|
206
|
+
fontSize: 11,
|
|
207
|
+
color: '#94A3B8',
|
|
208
|
+
marginTop: 1.5,
|
|
209
|
+
...fontStack,
|
|
210
|
+
},
|
|
211
|
+
actionButton: {
|
|
212
|
+
flexDirection: 'row',
|
|
213
|
+
alignItems: 'center',
|
|
214
|
+
gap: 4,
|
|
215
|
+
backgroundColor: '#7C3AED',
|
|
216
|
+
paddingVertical: 6,
|
|
217
|
+
paddingHorizontal: 10,
|
|
218
|
+
borderRadius: 8,
|
|
219
|
+
flexShrink: 0,
|
|
220
|
+
shadowColor: '#7C3AED',
|
|
221
|
+
shadowOffset: { width: 0, height: 2 },
|
|
222
|
+
shadowOpacity: 0.3,
|
|
223
|
+
shadowRadius: 4,
|
|
224
|
+
elevation: 3,
|
|
225
|
+
},
|
|
226
|
+
actionButtonText: {
|
|
227
|
+
fontSize: 11.5,
|
|
228
|
+
fontWeight: '700',
|
|
229
|
+
color: '#FFFFFF',
|
|
230
|
+
...fontStack,
|
|
231
|
+
},
|
|
232
|
+
closeButton: {
|
|
233
|
+
width: 24,
|
|
234
|
+
height: 24,
|
|
235
|
+
borderRadius: 12,
|
|
236
|
+
backgroundColor: '#1E293B',
|
|
237
|
+
justifyContent: 'center',
|
|
238
|
+
alignItems: 'center',
|
|
239
|
+
flexShrink: 0,
|
|
240
|
+
},
|
|
241
|
+
progressBarTrack: {
|
|
242
|
+
height: 3,
|
|
243
|
+
width: '100%',
|
|
244
|
+
backgroundColor: '#1E293B',
|
|
245
|
+
},
|
|
246
|
+
progressBarFill: {
|
|
247
|
+
height: '100%',
|
|
248
|
+
backgroundColor: '#7C3AED',
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
export default NpmUpdateToast;
|
|
@@ -17,11 +17,26 @@ import { clearPerformanceEvents } from '../../customHooks/performanceTracker';
|
|
|
17
17
|
import { isReduxConnected } from '../../customHooks/reduxLogger';
|
|
18
18
|
import { isAnalyticsConnected } from '../../customHooks/analyticsLogger';
|
|
19
19
|
import { useTranslation } from '../../i18n';
|
|
20
|
+
import { getTelemetryConsentStatus, setTelemetryConsent, sendSessionTelemetryPing, } from '../../helpers/telemetry';
|
|
20
21
|
import { SignalIcon, TerminalIcon, AnalyticsIcon, SettingsIcon, SunIcon, MoonIcon, ScreenIcon, MotionIcon, LayersIcon, EyeIcon, CheckIcon, TrashIcon, PackageIcon, ReduxIcon, PerformanceIcon, CrashIcon, ShieldAlertIcon, ForwardChevronIcon, ChevronDownIcon, } from '../NetworkIcons';
|
|
21
22
|
const SettingsPanel = () => {
|
|
22
23
|
const { t } = useTranslation();
|
|
23
|
-
const { settingsPage, setSettingsPage, settingsActiveSubTab, setSettingsActiveSubTab, tabVisibility, toggleTabVisibility, defaultTab, setDefaultTab, isDark, setIsDark, modalHeightPercent, setModalHeightPercent, modalAnimationType, setModalAnimationType, showDuplicateLogs, setShowDuplicateLogs, showConsoleLevels, setShowConsoleLevels, resetToDefaults, storage, logs, consoleLogs, analyticsEvents, reduxState, maxNetworkLogs, setMaxNetworkLogs, maxConsoleLogs, setMaxConsoleLogs, maxAnalyticsEventsLimit, setMaxAnalyticsEventsLimit, isAutoRamLimitEnabled, setIsAutoRamLimitEnabled, deviceFreeRamMb, reduxAutoRefresh, setReduxAutoRefreshState, reduxExpandDepth, setReduxExpandDepth, switchActiveTab, setSelected, setSelectedEvent, setReduxState, crashRecords, maxCrashLogs, setMaxCrashLogs, } = useInspector();
|
|
24
|
+
const { settingsPage, setSettingsPage, settingsActiveSubTab, setSettingsActiveSubTab, tabVisibility, toggleTabVisibility, defaultTab, setDefaultTab, isDark, setIsDark, modalHeightPercent, setModalHeightPercent, modalAnimationType, setModalAnimationType, showDuplicateLogs, setShowDuplicateLogs, showUpdateToast, setShowUpdateToast, showConsoleLevels, setShowConsoleLevels, resetToDefaults, storage, logs, consoleLogs, analyticsEvents, reduxState, maxNetworkLogs, setMaxNetworkLogs, maxConsoleLogs, setMaxConsoleLogs, maxAnalyticsEventsLimit, setMaxAnalyticsEventsLimit, isAutoRamLimitEnabled, setIsAutoRamLimitEnabled, deviceFreeRamMb, reduxAutoRefresh, setReduxAutoRefreshState, reduxExpandDepth, setReduxExpandDepth, switchActiveTab, setSelected, setSelectedEvent, setReduxState, crashRecords, maxCrashLogs, setMaxCrashLogs, } = useInspector();
|
|
24
25
|
const [stagedHeight, setStagedHeight] = useState(modalHeightPercent);
|
|
26
|
+
const [telemetryConsent, setTelemetryConsentState] = useState(true);
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
getTelemetryConsentStatus().then(status => {
|
|
29
|
+
setTelemetryConsentState(status === 'granted');
|
|
30
|
+
});
|
|
31
|
+
}, []);
|
|
32
|
+
const handleToggleTelemetry = async () => {
|
|
33
|
+
const nextVal = !telemetryConsent;
|
|
34
|
+
setTelemetryConsentState(nextVal);
|
|
35
|
+
await setTelemetryConsent(nextVal);
|
|
36
|
+
if (nextVal) {
|
|
37
|
+
sendSessionTelemetryPing({ force: true });
|
|
38
|
+
}
|
|
39
|
+
};
|
|
25
40
|
useEffect(() => {
|
|
26
41
|
setStagedHeight(modalHeightPercent);
|
|
27
42
|
}, [modalHeightPercent]);
|
|
@@ -1315,6 +1330,182 @@ const SettingsPanel = () => {
|
|
|
1315
1330
|
</View>)}
|
|
1316
1331
|
</View>
|
|
1317
1332
|
</View>
|
|
1333
|
+
|
|
1334
|
+
{/* Section 4: Privacy & Diagnostics */}
|
|
1335
|
+
<View style={{
|
|
1336
|
+
backgroundColor: AppColors.primaryLight,
|
|
1337
|
+
borderRadius: 14,
|
|
1338
|
+
borderWidth: 1,
|
|
1339
|
+
borderColor: AppColors.grayBorderSecondary,
|
|
1340
|
+
overflow: 'hidden',
|
|
1341
|
+
padding: 14,
|
|
1342
|
+
gap: 12,
|
|
1343
|
+
}}>
|
|
1344
|
+
<Text style={{
|
|
1345
|
+
fontFamily: AppFonts.interBold,
|
|
1346
|
+
fontSize: 11,
|
|
1347
|
+
lineHeight: 14,
|
|
1348
|
+
color: AppColors.grayTextWeak,
|
|
1349
|
+
letterSpacing: 0.8,
|
|
1350
|
+
}}>
|
|
1351
|
+
PRIVACY & ANONYMOUS DIAGNOSTICS
|
|
1352
|
+
</Text>
|
|
1353
|
+
|
|
1354
|
+
<View style={{
|
|
1355
|
+
flexDirection: 'row',
|
|
1356
|
+
alignItems: 'center',
|
|
1357
|
+
justifyContent: 'space-between',
|
|
1358
|
+
}}>
|
|
1359
|
+
<View style={{
|
|
1360
|
+
flexDirection: 'row',
|
|
1361
|
+
alignItems: 'center',
|
|
1362
|
+
gap: 10,
|
|
1363
|
+
flex: 1,
|
|
1364
|
+
marginRight: 10,
|
|
1365
|
+
}}>
|
|
1366
|
+
<View style={{
|
|
1367
|
+
width: 32,
|
|
1368
|
+
height: 32,
|
|
1369
|
+
borderRadius: 8,
|
|
1370
|
+
backgroundColor: AppColors.purpleShade50,
|
|
1371
|
+
alignItems: 'center',
|
|
1372
|
+
justifyContent: 'center',
|
|
1373
|
+
}}>
|
|
1374
|
+
<ShieldAlertIcon color={AppColors.purple} size={15}/>
|
|
1375
|
+
</View>
|
|
1376
|
+
<View style={{ flex: 1 }}>
|
|
1377
|
+
<Text style={{
|
|
1378
|
+
fontFamily: AppFonts.interBold,
|
|
1379
|
+
fontSize: 13.5,
|
|
1380
|
+
lineHeight: 18,
|
|
1381
|
+
color: AppColors.primaryBlack,
|
|
1382
|
+
}}>
|
|
1383
|
+
Help Improve In-App Inspector
|
|
1384
|
+
</Text>
|
|
1385
|
+
<Text style={{
|
|
1386
|
+
fontFamily: AppFonts.interRegular,
|
|
1387
|
+
fontSize: 11,
|
|
1388
|
+
lineHeight: 15,
|
|
1389
|
+
color: AppColors.grayText,
|
|
1390
|
+
marginTop: 1,
|
|
1391
|
+
}}>
|
|
1392
|
+
Share non-identifiable technical metrics (RN version, JS engine, device model). No user data or network payloads are captured.
|
|
1393
|
+
</Text>
|
|
1394
|
+
</View>
|
|
1395
|
+
</View>
|
|
1396
|
+
|
|
1397
|
+
<TouchableScale accessible={true} accessibilityRole="switch" accessibilityLabel="Toggle Anonymous Telemetry" accessibilityState={{ checked: telemetryConsent }} onPress={handleToggleTelemetry} style={{
|
|
1398
|
+
width: 42,
|
|
1399
|
+
height: 24,
|
|
1400
|
+
borderRadius: 12,
|
|
1401
|
+
backgroundColor: telemetryConsent
|
|
1402
|
+
? AppColors.purple
|
|
1403
|
+
: AppColors.grayBorderSecondary,
|
|
1404
|
+
padding: 2,
|
|
1405
|
+
justifyContent: 'center',
|
|
1406
|
+
alignItems: telemetryConsent ? 'flex-end' : 'flex-start',
|
|
1407
|
+
}}>
|
|
1408
|
+
<View style={{
|
|
1409
|
+
width: 20,
|
|
1410
|
+
height: 20,
|
|
1411
|
+
borderRadius: 10,
|
|
1412
|
+
backgroundColor: AppColors.white,
|
|
1413
|
+
shadowColor: AppColors.black,
|
|
1414
|
+
shadowOpacity: 0.18,
|
|
1415
|
+
shadowRadius: 2,
|
|
1416
|
+
shadowOffset: { width: 0, height: 1 },
|
|
1417
|
+
}}/>
|
|
1418
|
+
</TouchableScale>
|
|
1419
|
+
</View>
|
|
1420
|
+
</View>
|
|
1421
|
+
|
|
1422
|
+
{/* Section 5: Notifications & Toasts */}
|
|
1423
|
+
<View style={{
|
|
1424
|
+
backgroundColor: AppColors.primaryLight,
|
|
1425
|
+
borderRadius: 14,
|
|
1426
|
+
borderWidth: 1,
|
|
1427
|
+
borderColor: AppColors.grayBorderSecondary,
|
|
1428
|
+
overflow: 'hidden',
|
|
1429
|
+
padding: 14,
|
|
1430
|
+
gap: 12,
|
|
1431
|
+
}}>
|
|
1432
|
+
<Text style={{
|
|
1433
|
+
fontFamily: AppFonts.interBold,
|
|
1434
|
+
fontSize: 11,
|
|
1435
|
+
lineHeight: 14,
|
|
1436
|
+
color: AppColors.grayTextWeak,
|
|
1437
|
+
letterSpacing: 0.8,
|
|
1438
|
+
}}>
|
|
1439
|
+
NOTIFICATIONS & TOASTS
|
|
1440
|
+
</Text>
|
|
1441
|
+
|
|
1442
|
+
<View style={{
|
|
1443
|
+
flexDirection: 'row',
|
|
1444
|
+
alignItems: 'center',
|
|
1445
|
+
justifyContent: 'space-between',
|
|
1446
|
+
}}>
|
|
1447
|
+
<View style={{
|
|
1448
|
+
flexDirection: 'row',
|
|
1449
|
+
alignItems: 'center',
|
|
1450
|
+
gap: 10,
|
|
1451
|
+
flex: 1,
|
|
1452
|
+
marginRight: 10,
|
|
1453
|
+
}}>
|
|
1454
|
+
<View style={{
|
|
1455
|
+
width: 32,
|
|
1456
|
+
height: 32,
|
|
1457
|
+
borderRadius: 8,
|
|
1458
|
+
backgroundColor: AppColors.purpleShade50,
|
|
1459
|
+
alignItems: 'center',
|
|
1460
|
+
justifyContent: 'center',
|
|
1461
|
+
}}>
|
|
1462
|
+
<PackageIcon color={AppColors.purple} size={15}/>
|
|
1463
|
+
</View>
|
|
1464
|
+
<View style={{ flex: 1 }}>
|
|
1465
|
+
<Text style={{
|
|
1466
|
+
fontFamily: AppFonts.interBold,
|
|
1467
|
+
fontSize: 13.5,
|
|
1468
|
+
lineHeight: 18,
|
|
1469
|
+
color: AppColors.primaryBlack,
|
|
1470
|
+
}}>
|
|
1471
|
+
NPM Update Toast
|
|
1472
|
+
</Text>
|
|
1473
|
+
<Text style={{
|
|
1474
|
+
fontFamily: AppFonts.interRegular,
|
|
1475
|
+
fontSize: 11,
|
|
1476
|
+
lineHeight: 15,
|
|
1477
|
+
color: AppColors.grayText,
|
|
1478
|
+
marginTop: 1,
|
|
1479
|
+
}}>
|
|
1480
|
+
Show a floating toast banner with countdown progress when a newer release is published on npm.
|
|
1481
|
+
</Text>
|
|
1482
|
+
</View>
|
|
1483
|
+
</View>
|
|
1484
|
+
|
|
1485
|
+
<TouchableScale accessible={true} accessibilityRole="switch" accessibilityLabel="Toggle NPM Update Toast" accessibilityState={{ checked: showUpdateToast }} onPress={() => setShowUpdateToast(prev => !prev)} style={{
|
|
1486
|
+
width: 42,
|
|
1487
|
+
height: 24,
|
|
1488
|
+
borderRadius: 12,
|
|
1489
|
+
backgroundColor: showUpdateToast
|
|
1490
|
+
? AppColors.purple
|
|
1491
|
+
: AppColors.grayBorderSecondary,
|
|
1492
|
+
padding: 2,
|
|
1493
|
+
justifyContent: 'center',
|
|
1494
|
+
alignItems: showUpdateToast ? 'flex-end' : 'flex-start',
|
|
1495
|
+
}}>
|
|
1496
|
+
<View style={{
|
|
1497
|
+
width: 20,
|
|
1498
|
+
height: 20,
|
|
1499
|
+
borderRadius: 10,
|
|
1500
|
+
backgroundColor: AppColors.white,
|
|
1501
|
+
shadowColor: AppColors.black,
|
|
1502
|
+
shadowOpacity: 0.18,
|
|
1503
|
+
shadowRadius: 2,
|
|
1504
|
+
shadowOffset: { width: 0, height: 1 },
|
|
1505
|
+
}}/>
|
|
1506
|
+
</TouchableScale>
|
|
1507
|
+
</View>
|
|
1508
|
+
</View>
|
|
1318
1509
|
</View>)}
|
|
1319
1510
|
<View style={{ height: 48 }}/>
|
|
1320
1511
|
</ScrollView>
|