react-native-inapp-inspector 1.1.16 → 1.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +13 -0
  2. package/dist/commonjs/components/AnimatedEntrance.js +3 -0
  3. package/dist/commonjs/components/AppHeaderLogo.d.ts +8 -0
  4. package/dist/commonjs/components/AppHeaderLogo.js +85 -0
  5. package/dist/commonjs/components/Inspector/BundleTab.d.ts +41 -0
  6. package/dist/commonjs/components/Inspector/BundleTab.js +2309 -0
  7. package/dist/commonjs/components/Inspector/InspectorHeader.js +185 -52
  8. package/dist/commonjs/components/Inspector/MainScreen.js +86 -7
  9. package/dist/commonjs/components/Inspector/ReduxDetail.d.ts +3 -0
  10. package/dist/commonjs/components/Inspector/ReduxDetail.js +576 -0
  11. package/dist/commonjs/components/Inspector/ReduxTab.js +433 -49
  12. package/dist/commonjs/components/Inspector/SettingsPanel.js +8 -2
  13. package/dist/commonjs/components/Inspector/TabBar.js +9 -6
  14. package/dist/commonjs/components/JsonViewer.js +91 -30
  15. package/dist/commonjs/components/LogCard.js +3 -4
  16. package/dist/commonjs/components/NetworkIcons.d.ts +8 -0
  17. package/dist/commonjs/components/NetworkIcons.js +54 -1
  18. package/dist/commonjs/constants/version.d.ts +1 -1
  19. package/dist/commonjs/constants/version.js +1 -1
  20. package/dist/commonjs/customHooks/consoleLogger.js +37 -9
  21. package/dist/commonjs/customHooks/reduxLogger.js +4 -2
  22. package/dist/commonjs/helpers/index.js +6 -2
  23. package/dist/commonjs/index.js +15 -1
  24. package/dist/commonjs/styles/AppColors.js +163 -163
  25. package/dist/commonjs/styles/index.d.ts +14 -0
  26. package/dist/commonjs/styles/index.js +14 -0
  27. package/dist/commonjs/types/enums.d.ts +2 -2
  28. package/dist/commonjs/types/interfaces.d.ts +11 -0
  29. package/dist/esm/components/AnimatedEntrance.js +4 -1
  30. package/dist/esm/components/AppHeaderLogo.d.ts +8 -0
  31. package/dist/esm/components/AppHeaderLogo.js +45 -0
  32. package/dist/esm/components/Inspector/BundleTab.d.ts +41 -0
  33. package/dist/esm/components/Inspector/BundleTab.js +2271 -0
  34. package/dist/esm/components/Inspector/InspectorHeader.js +151 -51
  35. package/dist/esm/components/Inspector/MainScreen.js +53 -7
  36. package/dist/esm/components/Inspector/ReduxDetail.d.ts +3 -0
  37. package/dist/esm/components/Inspector/ReduxDetail.js +538 -0
  38. package/dist/esm/components/Inspector/ReduxTab.js +402 -51
  39. package/dist/esm/components/Inspector/SettingsPanel.js +9 -3
  40. package/dist/esm/components/Inspector/TabBar.js +10 -7
  41. package/dist/esm/components/JsonViewer.js +92 -31
  42. package/dist/esm/components/LogCard.js +3 -4
  43. package/dist/esm/components/NetworkIcons.d.ts +8 -0
  44. package/dist/esm/components/NetworkIcons.js +47 -1
  45. package/dist/esm/constants/version.d.ts +1 -1
  46. package/dist/esm/constants/version.js +1 -1
  47. package/dist/esm/customHooks/consoleLogger.js +37 -9
  48. package/dist/esm/customHooks/reduxLogger.js +4 -2
  49. package/dist/esm/helpers/index.js +6 -2
  50. package/dist/esm/index.js +15 -1
  51. package/dist/esm/styles/AppColors.js +163 -163
  52. package/dist/esm/styles/index.d.ts +14 -0
  53. package/dist/esm/styles/index.js +14 -0
  54. package/dist/esm/types/enums.d.ts +2 -2
  55. package/dist/esm/types/interfaces.d.ts +11 -0
  56. package/example/App.tsx +5 -1
  57. package/example/assets/app_icon.png +0 -0
  58. package/example/ios/Podfile.lock +3 -3
  59. package/example/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json +18 -9
  60. package/example/ios/example/Images.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
  61. package/example/package-lock.json +824 -10
  62. package/example/package.json +6 -2
  63. package/package.json +4 -2
@@ -1,21 +1,58 @@
1
- import React from 'react';
1
+ import React, { useMemo } from 'react';
2
2
  import { Alert, Animated, Linking, Platform, Pressable, Text, View, } from 'react-native';
3
3
  import LinearGradient from 'react-native-linear-gradient';
4
4
  import { useInspector } from './InspectorContext';
5
5
  import TouchableScale from '../TouchableScale';
6
- import BrandSquareIcon from '../BrandSquareIcon';
6
+ import AppHeaderLogo from '../AppHeaderLogo';
7
7
  import styles from '../../styles';
8
8
  import { AppColors } from '../../styles/AppColors';
9
9
  import { AppFonts } from '../../styles/AppFonts';
10
10
  import { METHOD_COLORS } from '../../constants';
11
11
  import { LIB_VERSION } from '../../constants';
12
- import { getStatusColor } from '../../helpers';
13
- import { getAppName } from '../../helpers';
14
- import { getBundleIdentifier } from '../../helpers';
15
- import { formatTime } from '../../helpers';
12
+ import { getStatusColor, getAppName, getBundleIdentifier, formatTime, getSize } from '../../helpers';
16
13
  import { WhiteBackNavigation, TrashIcon, SettingsIcon, CloseWhite, ChevronIcon, ClockIcon, } from '../NetworkIcons';
17
14
  const InspectorHeader = React.memo(() => {
18
- const { modalHeightPercent, selected, setSelected, selectedEvent, setSelectedEvent, selectedLog, setSelectedLog, showHeaderInfo, setShowHeaderInfo, updateAvailable, latestNpmVersion, clearAnim, activePulseAnim, unreadPulseAnim, runClearAllWithAnimation, setSettingsPage, closeModal, detailTitle, } = useInspector();
15
+ const { modalHeightPercent, appIcon, selected, setSelected, selectedEvent, setSelectedEvent, selectedLog, setSelectedLog, selectedReduxSlice, setSelectedReduxSlice, selectedReduxAction, setSelectedReduxAction, reduxState, reduxLastActionMap, showHeaderInfo, setShowHeaderInfo, updateAvailable, latestNpmVersion, clearAnim, activePulseAnim, unreadPulseAnim, runClearAllWithAnimation, setSettingsPage, closeModal, detailTitle, activeTab, environment, } = useInspector();
16
+ const envConfig = useMemo(() => {
17
+ const rawEnv = (environment || (__DEV__ ? 'DEV' : 'PROD')).trim();
18
+ const clean = rawEnv.toUpperCase();
19
+ if (clean === 'DEV' || clean.includes('DEV') || clean === 'LOCAL') {
20
+ return {
21
+ label: rawEnv,
22
+ bg: 'rgba(16, 185, 129, 0.25)', // emerald
23
+ border: 'rgba(110, 231, 183, 0.55)',
24
+ text: '#A7F3D0',
25
+ };
26
+ }
27
+ if (clean === 'UAT' || clean === 'QA' || clean === 'TEST') {
28
+ return {
29
+ label: rawEnv,
30
+ bg: 'rgba(245, 158, 11, 0.28)', // amber
31
+ border: 'rgba(252, 211, 77, 0.6)',
32
+ text: '#FDE68A',
33
+ };
34
+ }
35
+ if (clean === 'PREPROD' || clean === 'STAGE' || clean === 'STAGING') {
36
+ return {
37
+ label: rawEnv,
38
+ bg: 'rgba(139, 92, 246, 0.28)', // purple
39
+ border: 'rgba(196, 181, 253, 0.6)',
40
+ text: '#DDD6FE',
41
+ };
42
+ }
43
+ // PROD / Live
44
+ return {
45
+ label: rawEnv,
46
+ bg: 'rgba(244, 63, 94, 0.25)', // rose
47
+ border: 'rgba(253, 164, 175, 0.55)',
48
+ text: '#FECDD3',
49
+ };
50
+ }, [environment]);
51
+ const isDetailView = (activeTab === 'apis' && selected != null) ||
52
+ (activeTab === 'analytics' && selectedEvent != null) ||
53
+ (activeTab === 'logs' && selectedLog != null) ||
54
+ (activeTab === 'redux' && (selectedReduxSlice != null || selectedReduxAction != null));
55
+ const isAnySelected = isDetailView;
19
56
  const headerTopPadding = Platform.OS === 'ios' && modalHeightPercent >= 95 ? 44 : 0;
20
57
  return (<LinearGradient colors={[AppColors.purple, AppColors.brandPurple]} style={styles.headerGradient}>
21
58
  <View style={{ paddingTop: headerTopPadding, width: '100%' }}>
@@ -26,11 +63,7 @@ const InspectorHeader = React.memo(() => {
26
63
  flexDirection: 'row',
27
64
  alignItems: 'center',
28
65
  gap: 16,
29
- flex: selected == null &&
30
- selectedEvent == null &&
31
- selectedLog == null
32
- ? 5
33
- : 1,
66
+ flex: !isDetailView ? 5 : 1,
34
67
  },
35
68
  ]}>
36
69
  <TouchableScale onPress={() => {
@@ -38,6 +71,8 @@ const InspectorHeader = React.memo(() => {
38
71
  setSelected(null);
39
72
  setSelectedEvent(null);
40
73
  setSelectedLog(null);
74
+ setSelectedReduxSlice(null);
75
+ setSelectedReduxAction(null);
41
76
  });
42
77
  }} hitSlop={15} style={[
43
78
  {
@@ -50,9 +85,7 @@ const InspectorHeader = React.memo(() => {
50
85
  borderWidth: 1,
51
86
  borderColor: `${AppColors.white}4D`,
52
87
  },
53
- selected == null &&
54
- selectedEvent == null &&
55
- selectedLog == null && { display: 'none' },
88
+ !isAnySelected && { display: 'none' },
56
89
  ]}>
57
90
  {/* Soft outer glow to fake a blurred circle */}
58
91
  <View style={{
@@ -65,36 +98,32 @@ const InspectorHeader = React.memo(() => {
65
98
  <WhiteBackNavigation />
66
99
  </TouchableScale>
67
100
 
68
- {selected == null &&
69
- selectedEvent == null &&
70
- selectedLog == null ? (<TouchableScale onPress={() => setShowHeaderInfo(prev => !prev)} style={{
101
+ {!isAnySelected ? (<TouchableScale onPress={() => setShowHeaderInfo(prev => !prev)} style={{
71
102
  flexDirection: 'row',
72
103
  alignItems: 'flex-start',
73
104
  gap: 10,
74
105
  flex: 1,
75
106
  }}>
76
- <View style={{
77
- width: 46,
78
- height: 46,
79
- borderRadius: 12,
80
- alignItems: 'center',
81
- justifyContent: 'center',
82
- backgroundColor: `${AppColors.white}21`,
83
- borderWidth: 1.5,
84
- borderColor: `${AppColors.white}40`,
85
- shadowColor: AppColors.black,
86
- shadowOpacity: 0.15,
87
- shadowRadius: 4,
88
- shadowOffset: { width: 0, height: 2 },
89
- flexShrink: 0,
90
- }}>
91
- <BrandSquareIcon size={42}/>
92
- </View>
107
+ <AppHeaderLogo size={46} customIcon={appIcon}/>
93
108
  <View style={{ gap: 2, flex: 1 }}>
94
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
109
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
95
110
  <Text style={[styles.headerTitle]} numberOfLines={1}>
96
111
  {getAppName()}
97
112
  </Text>
113
+ <View style={[
114
+ styles.envBadge,
115
+ {
116
+ backgroundColor: envConfig.bg,
117
+ borderColor: envConfig.border,
118
+ },
119
+ ]}>
120
+ <Text style={[
121
+ styles.envBadgeText,
122
+ { color: envConfig.text },
123
+ ]}>
124
+ {envConfig.label}
125
+ </Text>
126
+ </View>
98
127
  {updateAvailable && (<Pressable hitSlop={10} onPress={() => Alert.alert('Update Available', `react-native-inapp-inspector v${latestNpmVersion} is available on NPM (installed: v${LIB_VERSION}).`, [
99
128
  { text: 'Later', style: 'cancel' },
100
129
  {
@@ -233,10 +262,8 @@ const InspectorHeader = React.memo(() => {
233
262
  </TouchableScale>) : null}
234
263
  </View>
235
264
 
236
- {(selected != null ||
237
- selectedEvent != null ||
238
- selectedLog != null) && (<View style={styles.headerCenter}>
239
- {selected != null ? (<View style={styles.headerDetailCenter}>
265
+ {isDetailView && (<View style={styles.headerCenter}>
266
+ {activeTab === 'apis' && selected != null ? (<View style={styles.headerDetailCenter}>
240
267
  <View style={styles.headerDetailRow}>
241
268
  <View style={[
242
269
  styles.headerMethodBadge,
@@ -297,7 +324,7 @@ const InspectorHeader = React.memo(() => {
297
324
  </Text>
298
325
  </View>
299
326
  </View>
300
- </View>) : selectedEvent != null ? (<View style={styles.headerDetailCenter}>
327
+ </View>) : activeTab === 'analytics' && selectedEvent != null ? (<View style={styles.headerDetailCenter}>
301
328
  <View style={styles.headerDetailRow}>
302
329
  <View style={[
303
330
  styles.headerMethodBadge,
@@ -335,7 +362,7 @@ const InspectorHeader = React.memo(() => {
335
362
  {selectedEvent.source}
336
363
  </Text>
337
364
  </View>
338
- </View>) : selectedLog != null ? (<View style={styles.headerDetailCenter}>
365
+ </View>) : activeTab === 'logs' && selectedLog != null ? (<View style={styles.headerDetailCenter}>
339
366
  <View style={styles.headerDetailRow}>
340
367
  <View style={[
341
368
  styles.headerMethodBadge,
@@ -368,27 +395,100 @@ const InspectorHeader = React.memo(() => {
368
395
  borderRadius: 20,
369
396
  backgroundColor: `${AppColors.white}29`,
370
397
  }}>
371
- <ClockIcon color={AppColors.white} size={11}/>
372
- <Text style={styles.headerSubTitle}>
373
- {formatTime(selectedLog.timestamp)}
398
+ <ClockIcon color={AppColors.white} size={11}/>
399
+ <Text style={styles.headerSubTitle}>
400
+ {formatTime(selectedLog.timestamp)}
401
+ </Text>
402
+ </View>
403
+ </View>
404
+ </View>) : activeTab === 'redux' && selectedReduxSlice != null ? ((() => {
405
+ const sliceData = reduxState?.[selectedReduxSlice];
406
+ const keyCount = sliceData && typeof sliceData === 'object'
407
+ ? Object.keys(sliceData).length
408
+ : typeof sliceData !== 'undefined'
409
+ ? 1
410
+ : 0;
411
+ const sliceSize = getSize(sliceData);
412
+ const lastAction = reduxLastActionMap[selectedReduxSlice];
413
+ return (<View style={styles.headerDetailCenter}>
414
+ <View style={styles.headerDetailRow}>
415
+ <View style={[
416
+ styles.headerMethodBadge,
417
+ {
418
+ backgroundColor: `${AppColors.purple}4D`,
419
+ },
420
+ ]}>
421
+ <Text style={styles.headerMethodText}>
422
+ SLICE
423
+ </Text>
424
+ </View>
425
+ <Text style={styles.headerDetailTitle} numberOfLines={1} ellipsizeMode="middle">
426
+ {selectedReduxSlice}
427
+ </Text>
428
+ </View>
429
+ <View style={styles.headerDetailSubRow}>
430
+ <View style={[
431
+ styles.headerStatusDot,
432
+ { backgroundColor: AppColors.liveGreen },
433
+ ]}/>
434
+ <Text style={styles.headerSubTitle}>
435
+ Live
436
+ </Text>
437
+ <Text style={[styles.headerSubTitle, { opacity: 0.6 }]}>•</Text>
438
+ <Text style={styles.headerSubTitle}>
439
+ {keyCount} keys
440
+ </Text>
441
+ <Text style={[styles.headerSubTitle, { opacity: 0.6 }]}>•</Text>
442
+ <Text style={styles.headerSubTitle}>
443
+ {sliceSize}
444
+ </Text>
445
+ {lastAction?.timestamp && (<>
446
+ <Text style={[styles.headerSubTitle, { opacity: 0.6 }]}>•</Text>
447
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 3 }}>
448
+ <ClockIcon color={AppColors.white} size={10}/>
449
+ <Text style={styles.headerSubTitle}>
450
+ {lastAction.timestamp}
451
+ </Text>
452
+ </View>
453
+ </>)}
454
+ </View>
455
+ </View>);
456
+ })()) : activeTab === 'redux' && selectedReduxAction != null ? (<View style={styles.headerDetailCenter}>
457
+ <View style={styles.headerDetailRow}>
458
+ <View style={[
459
+ styles.headerMethodBadge,
460
+ {
461
+ backgroundColor: `${AppColors.brandPurple}4D`,
462
+ },
463
+ ]}>
464
+ <Text style={styles.headerMethodText}>
465
+ ACTION
374
466
  </Text>
375
467
  </View>
468
+ <Text style={styles.headerDetailTitle} numberOfLines={1} ellipsizeMode="middle">
469
+ {selectedReduxAction.type}
470
+ </Text>
471
+ </View>
472
+ <View style={styles.headerDetailSubRow}>
473
+ <View style={[
474
+ styles.headerStatusDot,
475
+ { backgroundColor: AppColors.purple },
476
+ ]}/>
477
+ <Text style={styles.headerSubTitle}>
478
+ {selectedReduxAction.timestamp || 'Dispatched'}
479
+ </Text>
376
480
  </View>
377
481
  </View>) : null}
378
482
  </View>)}
379
483
 
380
484
  <View style={[
381
485
  styles.headerRight,
382
- selected == null &&
383
- selectedEvent == null &&
384
- selectedLog == null && {
486
+ !isAnySelected && {
385
487
  flexShrink: 0,
386
488
  minWidth: 116,
387
489
  },
388
490
  ]}>
389
- {selected == null &&
390
- selectedEvent == null &&
391
- selectedLog == null && (<TouchableScale onPress={() => {
491
+ {!isAnySelected && (<TouchableScale onPress={() => {
392
492
  Alert.alert('Clear Everything', 'This clears all tabs — APIs, Logs, Analytics and Redux timeline. Continue?', [
393
493
  { text: 'Cancel', style: 'cancel' },
394
494
  {
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useState, useEffect } from 'react';
2
2
  import { ActivityIndicator, Modal, Pressable, StatusBar, StyleSheet, Text, View, } from 'react-native';
3
3
  import { useInspector } from './InspectorContext';
4
4
  import ErrorBoundary from '../ErrorBoundary';
@@ -10,13 +10,31 @@ import NetworkDetail from './NetworkDetail';
10
10
  import LogDetail from './LogDetail';
11
11
  import ConsoleTab from './ConsoleTab';
12
12
  import AnalyticsTab from './AnalyticsTab';
13
+ import AnalyticsDetail from '../AnalyticsDetail';
13
14
  import ReduxTab from './ReduxTab';
15
+ import ReduxDetail from './ReduxDetail';
16
+ import BundleTab from './BundleTab';
14
17
  import SettingsPanel from './SettingsPanel';
15
18
  import styles from '../../styles';
16
19
  import { AppColors } from '../../styles/AppColors';
17
20
  import NavigationTracker from './NavigationTracker';
18
21
  const MainScreen = () => {
19
- const { visible, modalAnimationType, closeModal, modalHeightPercent, selected, selectedEvent, selectedLog, settingsPage, activeTab, isReady, isEnabled, hasNavigationContext, setNavState, } = useInspector();
22
+ const { visible, modalAnimationType, closeModal, modalHeightPercent, selected, selectedEvent, selectedLog, selectedReduxSlice, selectedReduxAction, settingsPage, activeTab, isReady, isEnabled, hasNavigationContext, setNavState, } = useInspector();
23
+ // Lazy-mount each tab on first visit and cache it in memory for 0ms instantaneous switching
24
+ const [mountedTabs, setMountedTabs] = useState({
25
+ [activeTab]: true,
26
+ });
27
+ useEffect(() => {
28
+ setMountedTabs(prev => {
29
+ if (prev[activeTab])
30
+ return prev;
31
+ return { ...prev, [activeTab]: true };
32
+ });
33
+ }, [activeTab]);
34
+ const isDetailActive = (activeTab === 'apis' && selected != null) ||
35
+ (activeTab === 'analytics' && selectedEvent != null) ||
36
+ (activeTab === 'logs' && selectedLog != null) ||
37
+ (activeTab === 'redux' && (selectedReduxSlice != null || selectedReduxAction != null));
20
38
  return (<>
21
39
  {isEnabled && <FabLauncher />}
22
40
  <Modal visible={visible} animationType={modalAnimationType} transparent>
@@ -32,12 +50,40 @@ const MainScreen = () => {
32
50
  <InspectorHeader />
33
51
 
34
52
  {/* ─── Horizontal Scrollable Tab Bar inside Content ─── */}
35
- {selected == null &&
36
- selectedEvent == null &&
37
- selectedLog == null &&
38
- settingsPage === null ? (<TabBar />) : null}
53
+ {!isDetailActive && settingsPage === null ? (<TabBar />) : null}
39
54
 
40
- {isReady ? (activeTab === 'analytics' ? (<AnalyticsTab />) : activeTab === 'apis' && selected == null ? (<NetworkTab />) : activeTab === 'logs' && selectedLog == null ? (<ConsoleTab />) : activeTab === 'logs' ? (<LogDetail />) : activeTab === 'redux' ? (<ReduxTab />) : (<NetworkDetail />)) : (<View style={styles.empty}>
55
+ {isReady ? (isDetailActive ? (activeTab === 'apis' && selected != null ? (<NetworkDetail />) : activeTab === 'analytics' && selectedEvent != null ? (<AnalyticsDetail event={selectedEvent}/>) : activeTab === 'logs' && selectedLog != null ? (<LogDetail />) : activeTab === 'redux' ? (<ReduxDetail />) : null) : (<>
56
+ <View style={{
57
+ flex: 1,
58
+ display: activeTab === 'apis' ? 'flex' : 'none',
59
+ }}>
60
+ {mountedTabs.apis && <NetworkTab />}
61
+ </View>
62
+ <View style={{
63
+ flex: 1,
64
+ display: activeTab === 'logs' ? 'flex' : 'none',
65
+ }}>
66
+ {mountedTabs.logs && <ConsoleTab />}
67
+ </View>
68
+ <View style={{
69
+ flex: 1,
70
+ display: activeTab === 'analytics' ? 'flex' : 'none',
71
+ }}>
72
+ {mountedTabs.analytics && <AnalyticsTab />}
73
+ </View>
74
+ <View style={{
75
+ flex: 1,
76
+ display: activeTab === 'redux' ? 'flex' : 'none',
77
+ }}>
78
+ {mountedTabs.redux && <ReduxTab />}
79
+ </View>
80
+ <View style={{
81
+ flex: 1,
82
+ display: activeTab === 'bundle' ? 'flex' : 'none',
83
+ }}>
84
+ {mountedTabs.bundle && <BundleTab />}
85
+ </View>
86
+ </>)) : (<View style={styles.empty}>
41
87
  <ActivityIndicator size="large" color={AppColors.purple}/>
42
88
  <Text style={[styles.emptySub, { marginTop: 12 }]}>
43
89
  Loading logs...
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ declare const ReduxDetail: React.MemoExoticComponent<() => React.JSX.Element>;
3
+ export default ReduxDetail;