react-native-inapp-inspector 2.3.0 → 2.3.2
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/dist/commonjs/components/Inspector/MainScreen.js +7 -11
- package/dist/commonjs/components/Inspector/StorageTab.js +104 -89
- package/dist/commonjs/components/SkeletonPlaceholder.d.ts +6 -0
- package/dist/commonjs/components/SkeletonPlaceholder.js +208 -0
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/customHooks/storageInspector.d.ts +2 -2
- package/dist/commonjs/customHooks/storageInspector.js +17 -13
- package/dist/commonjs/index.js +28 -24
- package/dist/commonjs/types/interfaces.d.ts +1 -2
- package/dist/esm/components/Inspector/MainScreen.js +8 -12
- package/dist/esm/components/Inspector/StorageTab.js +105 -90
- package/dist/esm/components/SkeletonPlaceholder.d.ts +6 -0
- package/dist/esm/components/SkeletonPlaceholder.js +173 -0
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/customHooks/storageInspector.d.ts +2 -2
- package/dist/esm/customHooks/storageInspector.js +17 -13
- package/dist/esm/index.js +29 -25
- package/dist/esm/types/interfaces.d.ts +1 -2
- package/package.json +1 -1
- package/src/components/Inspector/MainScreen.tsx +7 -11
- package/src/components/Inspector/StorageTab.tsx +170 -114
- package/src/components/SkeletonPlaceholder.tsx +206 -0
- package/src/constants/version.ts +1 -1
- package/src/customHooks/storageInspector.ts +18 -14
- package/src/index.tsx +35 -26
- package/src/types/interfaces.ts +1 -2
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import React, {useEffect, useRef} from 'react';
|
|
2
|
+
import {Animated, StyleSheet, View} from 'react-native';
|
|
3
|
+
import {AppColors} from '../styles/AppColors';
|
|
4
|
+
|
|
5
|
+
interface SkeletonPlaceholderProps {
|
|
6
|
+
cardCount?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const SkeletonPlaceholder = React.memo(function SkeletonPlaceholder({
|
|
10
|
+
cardCount = 4,
|
|
11
|
+
}: SkeletonPlaceholderProps) {
|
|
12
|
+
const shimmerAnim = useRef(new Animated.Value(0.35)).current;
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const animation = Animated.loop(
|
|
16
|
+
Animated.sequence([
|
|
17
|
+
Animated.timing(shimmerAnim, {
|
|
18
|
+
toValue: 0.85,
|
|
19
|
+
duration: 750,
|
|
20
|
+
useNativeDriver: true,
|
|
21
|
+
}),
|
|
22
|
+
Animated.timing(shimmerAnim, {
|
|
23
|
+
toValue: 0.35,
|
|
24
|
+
duration: 750,
|
|
25
|
+
useNativeDriver: true,
|
|
26
|
+
}),
|
|
27
|
+
]),
|
|
28
|
+
);
|
|
29
|
+
animation.start();
|
|
30
|
+
return () => animation.stop();
|
|
31
|
+
}, [shimmerAnim]);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<View style={skeletonStyles.container}>
|
|
35
|
+
{/* ─── Search & Scope Toolbar Skeleton ─── */}
|
|
36
|
+
<View style={skeletonStyles.toolbarSkeleton}>
|
|
37
|
+
<Animated.View
|
|
38
|
+
style={[
|
|
39
|
+
skeletonStyles.searchBarSkeleton,
|
|
40
|
+
{opacity: shimmerAnim},
|
|
41
|
+
]}
|
|
42
|
+
/>
|
|
43
|
+
<View style={skeletonStyles.actionButtonsRow}>
|
|
44
|
+
<Animated.View
|
|
45
|
+
style={[skeletonStyles.iconButtonSkeleton, {opacity: shimmerAnim}]}
|
|
46
|
+
/>
|
|
47
|
+
<Animated.View
|
|
48
|
+
style={[skeletonStyles.iconButtonSkeleton, {opacity: shimmerAnim}]}
|
|
49
|
+
/>
|
|
50
|
+
</View>
|
|
51
|
+
</View>
|
|
52
|
+
|
|
53
|
+
{/* ─── Quick Filter Chips Skeleton Strip ─── */}
|
|
54
|
+
<View style={skeletonStyles.chipStripSkeleton}>
|
|
55
|
+
<Animated.View
|
|
56
|
+
style={[skeletonStyles.chipSkeleton, {width: 48, opacity: shimmerAnim}]}
|
|
57
|
+
/>
|
|
58
|
+
<Animated.View
|
|
59
|
+
style={[skeletonStyles.chipSkeleton, {width: 68, opacity: shimmerAnim}]}
|
|
60
|
+
/>
|
|
61
|
+
<Animated.View
|
|
62
|
+
style={[skeletonStyles.chipSkeleton, {width: 76, opacity: shimmerAnim}]}
|
|
63
|
+
/>
|
|
64
|
+
<Animated.View
|
|
65
|
+
style={[skeletonStyles.chipSkeleton, {width: 58, opacity: shimmerAnim}]}
|
|
66
|
+
/>
|
|
67
|
+
</View>
|
|
68
|
+
|
|
69
|
+
{/* ─── List Cards Skeleton ─── */}
|
|
70
|
+
{Array.from({length: cardCount}).map((_, i) => (
|
|
71
|
+
<Animated.View
|
|
72
|
+
key={`skeleton_card_${i}`}
|
|
73
|
+
style={[skeletonStyles.cardSkeleton, {opacity: shimmerAnim}]}>
|
|
74
|
+
{/* Top row: Status pill + Method + Time */}
|
|
75
|
+
<View style={skeletonStyles.cardTopRow}>
|
|
76
|
+
<View style={skeletonStyles.badgeGroup}>
|
|
77
|
+
<View style={skeletonStyles.statusBadgeSkeleton} />
|
|
78
|
+
<View style={skeletonStyles.methodBadgeSkeleton} />
|
|
79
|
+
</View>
|
|
80
|
+
<View style={skeletonStyles.timeSkeleton} />
|
|
81
|
+
</View>
|
|
82
|
+
|
|
83
|
+
{/* Middle row: URL lines */}
|
|
84
|
+
<View style={skeletonStyles.urlLineLong} />
|
|
85
|
+
<View style={skeletonStyles.urlLineShort} />
|
|
86
|
+
|
|
87
|
+
{/* Bottom row: Latency & Size */}
|
|
88
|
+
<View style={skeletonStyles.cardBottomRow}>
|
|
89
|
+
<View style={skeletonStyles.metaPillSkeleton} />
|
|
90
|
+
<View style={skeletonStyles.metaPillSkeleton} />
|
|
91
|
+
</View>
|
|
92
|
+
</Animated.View>
|
|
93
|
+
))}
|
|
94
|
+
</View>
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const skeletonStyles = StyleSheet.create({
|
|
99
|
+
container: {
|
|
100
|
+
flex: 1,
|
|
101
|
+
paddingHorizontal: 12,
|
|
102
|
+
paddingTop: 8,
|
|
103
|
+
},
|
|
104
|
+
toolbarSkeleton: {
|
|
105
|
+
flexDirection: 'row',
|
|
106
|
+
alignItems: 'center',
|
|
107
|
+
gap: 8,
|
|
108
|
+
marginBottom: 8,
|
|
109
|
+
},
|
|
110
|
+
searchBarSkeleton: {
|
|
111
|
+
flex: 1,
|
|
112
|
+
height: 36,
|
|
113
|
+
borderRadius: 8,
|
|
114
|
+
backgroundColor: AppColors.graySurface,
|
|
115
|
+
borderWidth: 1,
|
|
116
|
+
borderColor: AppColors.dividerColor,
|
|
117
|
+
},
|
|
118
|
+
actionButtonsRow: {
|
|
119
|
+
flexDirection: 'row',
|
|
120
|
+
gap: 6,
|
|
121
|
+
},
|
|
122
|
+
iconButtonSkeleton: {
|
|
123
|
+
width: 36,
|
|
124
|
+
height: 36,
|
|
125
|
+
borderRadius: 8,
|
|
126
|
+
backgroundColor: AppColors.graySurface,
|
|
127
|
+
borderWidth: 1,
|
|
128
|
+
borderColor: AppColors.dividerColor,
|
|
129
|
+
},
|
|
130
|
+
chipStripSkeleton: {
|
|
131
|
+
flexDirection: 'row',
|
|
132
|
+
gap: 6,
|
|
133
|
+
marginBottom: 10,
|
|
134
|
+
},
|
|
135
|
+
chipSkeleton: {
|
|
136
|
+
height: 24,
|
|
137
|
+
borderRadius: 6,
|
|
138
|
+
backgroundColor: AppColors.graySurface,
|
|
139
|
+
borderWidth: 1,
|
|
140
|
+
borderColor: AppColors.dividerColor,
|
|
141
|
+
},
|
|
142
|
+
cardSkeleton: {
|
|
143
|
+
backgroundColor: AppColors.primaryLight,
|
|
144
|
+
borderRadius: 10,
|
|
145
|
+
padding: 12,
|
|
146
|
+
marginBottom: 8,
|
|
147
|
+
borderWidth: 1,
|
|
148
|
+
borderColor: AppColors.dividerColor,
|
|
149
|
+
},
|
|
150
|
+
cardTopRow: {
|
|
151
|
+
flexDirection: 'row',
|
|
152
|
+
justifyContent: 'space-between',
|
|
153
|
+
alignItems: 'center',
|
|
154
|
+
marginBottom: 8,
|
|
155
|
+
},
|
|
156
|
+
badgeGroup: {
|
|
157
|
+
flexDirection: 'row',
|
|
158
|
+
alignItems: 'center',
|
|
159
|
+
gap: 6,
|
|
160
|
+
},
|
|
161
|
+
statusBadgeSkeleton: {
|
|
162
|
+
width: 38,
|
|
163
|
+
height: 18,
|
|
164
|
+
borderRadius: 4,
|
|
165
|
+
backgroundColor: AppColors.graySurface,
|
|
166
|
+
},
|
|
167
|
+
methodBadgeSkeleton: {
|
|
168
|
+
width: 44,
|
|
169
|
+
height: 18,
|
|
170
|
+
borderRadius: 4,
|
|
171
|
+
backgroundColor: AppColors.graySurface,
|
|
172
|
+
},
|
|
173
|
+
timeSkeleton: {
|
|
174
|
+
width: 48,
|
|
175
|
+
height: 12,
|
|
176
|
+
borderRadius: 4,
|
|
177
|
+
backgroundColor: AppColors.graySurface,
|
|
178
|
+
},
|
|
179
|
+
urlLineLong: {
|
|
180
|
+
height: 13,
|
|
181
|
+
borderRadius: 4,
|
|
182
|
+
backgroundColor: AppColors.graySurface,
|
|
183
|
+
marginBottom: 5,
|
|
184
|
+
width: '90%',
|
|
185
|
+
},
|
|
186
|
+
urlLineShort: {
|
|
187
|
+
height: 11,
|
|
188
|
+
borderRadius: 4,
|
|
189
|
+
backgroundColor: AppColors.graySurface,
|
|
190
|
+
marginBottom: 8,
|
|
191
|
+
width: '55%',
|
|
192
|
+
},
|
|
193
|
+
cardBottomRow: {
|
|
194
|
+
flexDirection: 'row',
|
|
195
|
+
gap: 8,
|
|
196
|
+
marginTop: 2,
|
|
197
|
+
},
|
|
198
|
+
metaPillSkeleton: {
|
|
199
|
+
width: 52,
|
|
200
|
+
height: 14,
|
|
201
|
+
borderRadius: 4,
|
|
202
|
+
backgroundColor: AppColors.graySurface,
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
export default SkeletonPlaceholder;
|
package/src/constants/version.ts
CHANGED
|
@@ -273,13 +273,13 @@ export const calculateByteSize = (str: string): number => {
|
|
|
273
273
|
};
|
|
274
274
|
|
|
275
275
|
/**
|
|
276
|
-
* Determine type and format value safely
|
|
276
|
+
* Determine type and format value safely without blocking JS thread
|
|
277
277
|
*/
|
|
278
278
|
export const analyzeStorageValue = (
|
|
279
279
|
rawVal: any,
|
|
280
280
|
): {
|
|
281
281
|
strVal: string;
|
|
282
|
-
|
|
282
|
+
parsedValue?: any;
|
|
283
283
|
type: 'json' | 'string' | 'number' | 'boolean' | 'null';
|
|
284
284
|
byteSize: number;
|
|
285
285
|
} => {
|
|
@@ -288,14 +288,14 @@ export const analyzeStorageValue = (
|
|
|
288
288
|
}
|
|
289
289
|
|
|
290
290
|
const strVal = typeof rawVal === 'string' ? rawVal : String(rawVal);
|
|
291
|
-
const byteSize =
|
|
291
|
+
const byteSize = strVal.length * 2;
|
|
292
292
|
|
|
293
293
|
if (typeof rawVal === 'number') {
|
|
294
|
-
return {strVal,
|
|
294
|
+
return {strVal, parsedValue: rawVal, type: 'number', byteSize};
|
|
295
295
|
}
|
|
296
296
|
|
|
297
297
|
if (typeof rawVal === 'boolean') {
|
|
298
|
-
return {strVal,
|
|
298
|
+
return {strVal, parsedValue: rawVal, type: 'boolean', byteSize};
|
|
299
299
|
}
|
|
300
300
|
|
|
301
301
|
const trimmed = strVal.trim();
|
|
@@ -303,18 +303,22 @@ export const analyzeStorageValue = (
|
|
|
303
303
|
(trimmed.startsWith('{') && trimmed.endsWith('}')) ||
|
|
304
304
|
(trimmed.startsWith('[') && trimmed.endsWith(']'))
|
|
305
305
|
) {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
306
|
+
// For small/medium JSON, parse eagerly; for huge payloads (>10KB), parse lazily on expand
|
|
307
|
+
if (trimmed.length < 10000) {
|
|
308
|
+
try {
|
|
309
|
+
const parsed = JSON.parse(trimmed);
|
|
310
|
+
return {strVal, parsedValue: parsed, type: 'json', byteSize};
|
|
311
|
+
} catch {}
|
|
312
|
+
}
|
|
313
|
+
return {strVal, type: 'json', byteSize};
|
|
310
314
|
}
|
|
311
315
|
|
|
312
316
|
if (trimmed === 'true' || trimmed === 'false') {
|
|
313
|
-
return {strVal,
|
|
317
|
+
return {strVal, parsedValue: trimmed === 'true', type: 'boolean', byteSize};
|
|
314
318
|
}
|
|
315
319
|
|
|
316
320
|
if (!isNaN(Number(trimmed)) && trimmed !== '') {
|
|
317
|
-
return {strVal,
|
|
321
|
+
return {strVal, parsedValue: Number(trimmed), type: 'number', byteSize};
|
|
318
322
|
}
|
|
319
323
|
|
|
320
324
|
return {strVal, type: 'string', byteSize};
|
|
@@ -346,7 +350,7 @@ export const fetchStorageEntries = async (
|
|
|
346
350
|
entries.push({
|
|
347
351
|
key: k,
|
|
348
352
|
value: analysis.strVal,
|
|
349
|
-
parsedValue: analysis.
|
|
353
|
+
parsedValue: analysis.parsedValue,
|
|
350
354
|
type: analysis.type,
|
|
351
355
|
byteSize: analysis.byteSize,
|
|
352
356
|
});
|
|
@@ -358,7 +362,7 @@ export const fetchStorageEntries = async (
|
|
|
358
362
|
entries.push({
|
|
359
363
|
key: k,
|
|
360
364
|
value: analysis.strVal,
|
|
361
|
-
parsedValue: analysis.
|
|
365
|
+
parsedValue: analysis.parsedValue,
|
|
362
366
|
type: analysis.type,
|
|
363
367
|
byteSize: analysis.byteSize,
|
|
364
368
|
});
|
|
@@ -397,7 +401,7 @@ export const fetchStorageEntries = async (
|
|
|
397
401
|
entries.push({
|
|
398
402
|
key: k,
|
|
399
403
|
value: analysis.strVal,
|
|
400
|
-
parsedValue: analysis.
|
|
404
|
+
parsedValue: analysis.parsedValue,
|
|
401
405
|
type: analysis.type,
|
|
402
406
|
byteSize: analysis.byteSize,
|
|
403
407
|
});
|
package/src/index.tsx
CHANGED
|
@@ -146,14 +146,13 @@ import {toggleGlobalTheme} from './styles';
|
|
|
146
146
|
|
|
147
147
|
const NetworkInspector = ({
|
|
148
148
|
enabled = true,
|
|
149
|
-
isEnabled = true,
|
|
150
149
|
storage,
|
|
151
150
|
navigationRef,
|
|
152
151
|
appIcon,
|
|
153
152
|
environment,
|
|
154
|
-
initialVisible,
|
|
153
|
+
initialVisible = false,
|
|
155
154
|
visible: controlledVisible,
|
|
156
|
-
}: NetworkInspectorProps): React.JSX.Element => {
|
|
155
|
+
}: NetworkInspectorProps): React.JSX.Element | null => {
|
|
157
156
|
// Set custom storage synchronously during render phase
|
|
158
157
|
setCustomStorage(storage || null);
|
|
159
158
|
|
|
@@ -170,7 +169,7 @@ const NetworkInspector = ({
|
|
|
170
169
|
|
|
171
170
|
const [logs, setLogs] = useState<NetworkLog[]>([]);
|
|
172
171
|
const [visible, setVisible] = useState<boolean>(
|
|
173
|
-
|
|
172
|
+
controlledVisible ?? initialVisible ?? false,
|
|
174
173
|
);
|
|
175
174
|
|
|
176
175
|
useEffect(() => {
|
|
@@ -945,24 +944,29 @@ const NetworkInspector = ({
|
|
|
945
944
|
|
|
946
945
|
// 100% Native Main-Thread Floating Button Lifecycle
|
|
947
946
|
useEffect(() => {
|
|
948
|
-
if (!useNativeFab || !
|
|
947
|
+
if (!useNativeFab || !enabled) {
|
|
949
948
|
if (useNativeFab) {
|
|
950
|
-
hideNativeFloatingButton();
|
|
949
|
+
hideNativeFloatingButton().catch(() => {});
|
|
951
950
|
}
|
|
952
951
|
return;
|
|
953
952
|
}
|
|
954
953
|
|
|
955
954
|
if (visible) {
|
|
956
|
-
hideNativeFloatingButton();
|
|
955
|
+
hideNativeFloatingButton().catch(() => {});
|
|
957
956
|
} else {
|
|
958
|
-
showNativeFloatingButton();
|
|
957
|
+
showNativeFloatingButton().catch(() => {});
|
|
959
958
|
setNativeFloatingButtonBadge(
|
|
960
959
|
logs.length > 0 || analyticsEvents.length > 0,
|
|
961
|
-
);
|
|
960
|
+
).catch(() => {});
|
|
962
961
|
}
|
|
962
|
+
|
|
963
|
+
return () => {
|
|
964
|
+
if (useNativeFab) {
|
|
965
|
+
hideNativeFloatingButton().catch(() => {});
|
|
966
|
+
}
|
|
967
|
+
};
|
|
963
968
|
}, [
|
|
964
969
|
useNativeFab,
|
|
965
|
-
isEnabled,
|
|
966
970
|
enabled,
|
|
967
971
|
visible,
|
|
968
972
|
logs.length,
|
|
@@ -1027,21 +1031,12 @@ const NetworkInspector = ({
|
|
|
1027
1031
|
if (freshCrashes.length > 0) {
|
|
1028
1032
|
setCrashRecords(freshCrashes);
|
|
1029
1033
|
}
|
|
1030
|
-
}
|
|
1031
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1032
|
-
}, [visible]);
|
|
1033
1034
|
|
|
1034
|
-
|
|
1035
|
-
if (visible) {
|
|
1036
|
-
const task = InteractionManager.runAfterInteractions(() => {
|
|
1035
|
+
const frame = requestAnimationFrame(() => {
|
|
1037
1036
|
setIsReady(true);
|
|
1038
1037
|
});
|
|
1039
|
-
const fallbackTimer = setTimeout(() => {
|
|
1040
|
-
setIsReady(true);
|
|
1041
|
-
}, 350);
|
|
1042
1038
|
return () => {
|
|
1043
|
-
|
|
1044
|
-
clearTimeout(fallbackTimer);
|
|
1039
|
+
cancelAnimationFrame(frame);
|
|
1045
1040
|
};
|
|
1046
1041
|
} else {
|
|
1047
1042
|
setIsReady(false);
|
|
@@ -1982,7 +1977,8 @@ const NetworkInspector = ({
|
|
|
1982
1977
|
setVisible,
|
|
1983
1978
|
closeModal,
|
|
1984
1979
|
isReady,
|
|
1985
|
-
|
|
1980
|
+
enabled,
|
|
1981
|
+
isEnabled: enabled,
|
|
1986
1982
|
appIcon,
|
|
1987
1983
|
environment,
|
|
1988
1984
|
modalHeightPercent,
|
|
@@ -2173,16 +2169,29 @@ const NetworkInspector = ({
|
|
|
2173
2169
|
};
|
|
2174
2170
|
|
|
2175
2171
|
const NetworkInspectorWrapper = (props: NetworkInspectorProps) => {
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2172
|
+
const enabled = props?.enabled ?? true;
|
|
2173
|
+
|
|
2174
|
+
useEffect(() => {
|
|
2175
|
+
if (!enabled) {
|
|
2176
|
+
hideNativeFloatingButton().catch(() => {});
|
|
2177
|
+
}
|
|
2178
|
+
return () => {
|
|
2179
|
+
hideNativeFloatingButton().catch(() => {});
|
|
2180
|
+
};
|
|
2181
|
+
}, [enabled]);
|
|
2182
|
+
|
|
2183
|
+
// If enabled is false, return null immediately with 0 overhead
|
|
2184
|
+
if (!enabled) {
|
|
2179
2185
|
return null;
|
|
2180
2186
|
}
|
|
2181
2187
|
|
|
2182
2188
|
return (
|
|
2183
2189
|
<I18nextProvider i18n={i18n}>
|
|
2184
2190
|
<ErrorBoundary fallbackType="inline">
|
|
2185
|
-
<NetworkInspector
|
|
2191
|
+
<NetworkInspector
|
|
2192
|
+
{...props}
|
|
2193
|
+
enabled={enabled}
|
|
2194
|
+
/>
|
|
2186
2195
|
</ErrorBoundary>
|
|
2187
2196
|
</I18nextProvider>
|
|
2188
2197
|
);
|
package/src/types/interfaces.ts
CHANGED
|
@@ -186,8 +186,6 @@ export interface PersistedSettings {
|
|
|
186
186
|
|
|
187
187
|
export interface NetworkInspectorProps {
|
|
188
188
|
enabled?: boolean;
|
|
189
|
-
isEnabled?: boolean;
|
|
190
|
-
forceEnable?: boolean;
|
|
191
189
|
storage?: InspectorStorage;
|
|
192
190
|
navigationRef?: any;
|
|
193
191
|
appIcon?: any;
|
|
@@ -206,6 +204,7 @@ export interface InspectorContextValue {
|
|
|
206
204
|
setVisible: React.Dispatch<React.SetStateAction<boolean>>;
|
|
207
205
|
closeModal: () => void;
|
|
208
206
|
isReady: boolean;
|
|
207
|
+
enabled: boolean;
|
|
209
208
|
isEnabled: boolean;
|
|
210
209
|
appIcon?: any;
|
|
211
210
|
environment?: string;
|