react-native-inapp-inspector 2.0.3 → 2.0.5

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 (46) hide show
  1. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  2. package/android/.gradle/8.9/checksums/md5-checksums.bin +0 -0
  3. package/android/.gradle/8.9/checksums/sha1-checksums.bin +0 -0
  4. package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
  5. package/android/.gradle/8.9/executionHistory/executionHistory.lock +0 -0
  6. package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
  7. package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
  8. package/android/.gradle/8.9/gc.properties +0 -0
  9. package/android/.gradle/9.2.0/checksums/checksums.lock +0 -0
  10. package/android/.gradle/9.2.0/fileChanges/last-build.bin +0 -0
  11. package/android/.gradle/9.2.0/fileHashes/fileHashes.bin +0 -0
  12. package/android/.gradle/9.2.0/fileHashes/fileHashes.lock +0 -0
  13. package/android/.gradle/9.2.0/gc.properties +0 -0
  14. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  15. package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
  16. package/android/.gradle/vcs-1/gc.properties +0 -0
  17. package/android/build/reports/problems/problems-report.html +659 -0
  18. package/dist/commonjs/components/Inspector/InspectorHeader.js +87 -50
  19. package/dist/commonjs/components/Inspector/MainScreen.js +1 -0
  20. package/dist/commonjs/components/Inspector/NetworkTab.js +317 -12
  21. package/dist/commonjs/components/Inspector/NpmUpdateToast.js +88 -39
  22. package/dist/commonjs/components/Inspector/SettingsPanel.js +525 -359
  23. package/dist/commonjs/components/Inspector/UpdateAvailableModal.d.ts +8 -0
  24. package/dist/commonjs/components/Inspector/UpdateAvailableModal.js +469 -0
  25. package/dist/commonjs/constants/version.d.ts +1 -1
  26. package/dist/commonjs/constants/version.js +1 -1
  27. package/dist/commonjs/helpers/searchQueryParser.d.ts +8 -3
  28. package/dist/commonjs/helpers/searchQueryParser.js +85 -31
  29. package/dist/commonjs/helpers/telemetry.js +4 -4
  30. package/dist/commonjs/index.js +58 -7
  31. package/dist/commonjs/types/interfaces.d.ts +9 -0
  32. package/dist/esm/components/Inspector/InspectorHeader.js +87 -50
  33. package/dist/esm/components/Inspector/MainScreen.js +1 -0
  34. package/dist/esm/components/Inspector/NetworkTab.js +317 -12
  35. package/dist/esm/components/Inspector/NpmUpdateToast.js +88 -39
  36. package/dist/esm/components/Inspector/SettingsPanel.js +526 -360
  37. package/dist/esm/components/Inspector/UpdateAvailableModal.d.ts +8 -0
  38. package/dist/esm/components/Inspector/UpdateAvailableModal.js +432 -0
  39. package/dist/esm/constants/version.d.ts +1 -1
  40. package/dist/esm/constants/version.js +1 -1
  41. package/dist/esm/helpers/searchQueryParser.d.ts +8 -3
  42. package/dist/esm/helpers/searchQueryParser.js +85 -31
  43. package/dist/esm/helpers/telemetry.js +4 -4
  44. package/dist/esm/index.js +58 -7
  45. package/dist/esm/types/interfaces.d.ts +9 -0
  46. package/package.json +1 -1
@@ -90,6 +90,10 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
90
90
  const [selected, setSelected] = (0, react_1.useState)(null);
91
91
  const [selectedLogs, setSelectedLogs] = (0, react_1.useState)(new Set());
92
92
  const [search, setSearch] = (0, react_1.useState)('');
93
+ const [searchScope, setSearchScope] = (0, react_1.useState)('all');
94
+ const [isRegexSearch, setIsRegexSearch] = (0, react_1.useState)(false);
95
+ const [isCaseSensitive, setIsCaseSensitive] = (0, react_1.useState)(false);
96
+ const [quickFilter, setQuickFilter] = (0, react_1.useState)('all');
93
97
  const [detailSearch, setDetailSearch] = (0, react_1.useState)('');
94
98
  const [reduxSearch, setReduxSearch] = (0, react_1.useState)('');
95
99
  const [selectedReduxSlice, setSelectedReduxSlice] = (0, react_1.useState)(null);
@@ -1041,7 +1045,40 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
1041
1045
  }, [selected]);
1042
1046
  const filteredLogs = (0, react_1.useMemo)(() => {
1043
1047
  let result = logs.filter(log => {
1044
- // Status Filter Check
1048
+ // 1. Quick Filter Check (All, Errors, Success, Slow, GET, POST, GraphQL)
1049
+ if (quickFilter && quickFilter !== 'all') {
1050
+ if (quickFilter === 'errors') {
1051
+ const isErr = log.status === 0 ||
1052
+ log.status == null ||
1053
+ (typeof log.status === 'number' && log.status >= 400);
1054
+ if (!isErr)
1055
+ return false;
1056
+ }
1057
+ else if (quickFilter === 'success') {
1058
+ const isSuccess = typeof log.status === 'number' &&
1059
+ log.status >= 200 &&
1060
+ log.status < 400;
1061
+ if (!isSuccess)
1062
+ return false;
1063
+ }
1064
+ else if (quickFilter === 'slow') {
1065
+ if ((log.duration || 0) < 500)
1066
+ return false;
1067
+ }
1068
+ else if (quickFilter === 'graphql') {
1069
+ const isGql = (log.url || '').toLowerCase().includes('graphql') ||
1070
+ (log.client || '').toLowerCase().includes('graphql') ||
1071
+ (log.client || '').toLowerCase().includes('apollo');
1072
+ if (!isGql)
1073
+ return false;
1074
+ }
1075
+ else {
1076
+ if (log.method?.toUpperCase() !== quickFilter.toUpperCase()) {
1077
+ return false;
1078
+ }
1079
+ }
1080
+ }
1081
+ // 2. Status Filter Check (from filters accordion dropdown)
1045
1082
  if (statusFilters.size > 0) {
1046
1083
  const matched = [...statusFilters].some(f => {
1047
1084
  if (f === 'ALL')
@@ -1057,7 +1094,7 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
1057
1094
  if (!matched)
1058
1095
  return false;
1059
1096
  }
1060
- // Method Filter Check
1097
+ // 3. Method Filter Check (from filters accordion dropdown)
1061
1098
  if (methodFilters.size > 0) {
1062
1099
  const matchedMethod = [...methodFilters].some(m => {
1063
1100
  if (m === 'ALL')
@@ -1067,10 +1104,14 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
1067
1104
  if (!matchedMethod)
1068
1105
  return false;
1069
1106
  }
1070
- // Advanced Search Query Engine Check (Supports method:POST, is:error, slow:>1s, status:200, headers, body, etc.)
1107
+ // 4. Advanced Search Query Engine Check with Scope, Regex and Case-Sensitivity
1071
1108
  if (search && search.trim().length > 0) {
1072
1109
  const routePath = logRouteMapRef.current.get(log.id)?.path || '';
1073
- const isMatch = (0, helpers_1.matchNetworkLogQuery)(log, search, routePath);
1110
+ const isMatch = (0, helpers_1.matchNetworkLogQuery)(log, search, routePath, {
1111
+ scope: searchScope,
1112
+ isRegex: isRegexSearch,
1113
+ isCaseSensitive: isCaseSensitive,
1114
+ });
1074
1115
  if (!isMatch)
1075
1116
  return false;
1076
1117
  }
@@ -1079,9 +1120,7 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
1079
1120
  if (sortOrder === 'oldest') {
1080
1121
  result = [...result].reverse();
1081
1122
  }
1082
- // #9 — collapse consecutive identical requests (same method + url +
1083
- // status) into one row carrying a ×N counter, unless the user opted in
1084
- // to seeing every duplicate via Settings → "Show Duplicate Logs".
1123
+ // #9 — collapse consecutive identical requests
1085
1124
  if (!showDuplicateLogs) {
1086
1125
  const collapsed = [];
1087
1126
  for (const log of result) {
@@ -1105,6 +1144,10 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
1105
1144
  }, [
1106
1145
  logs,
1107
1146
  search,
1147
+ searchScope,
1148
+ isRegexSearch,
1149
+ isCaseSensitive,
1150
+ quickFilter,
1108
1151
  statusFilters,
1109
1152
  methodFilters,
1110
1153
  sortOrder,
@@ -1655,6 +1698,14 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
1655
1698
  groupedData,
1656
1699
  search,
1657
1700
  setSearch,
1701
+ searchScope,
1702
+ setSearchScope,
1703
+ isRegexSearch,
1704
+ setIsRegexSearch,
1705
+ isCaseSensitive,
1706
+ setIsCaseSensitive,
1707
+ quickFilter,
1708
+ setQuickFilter,
1658
1709
  statusFilters,
1659
1710
  setStatusFilters,
1660
1711
  methodFilters,
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { ViewStyle } from 'react-native';
3
3
  import { Animated, PanResponderInstance } from 'react-native';
4
4
  import type { ActiveTab, BreadcrumbType, CrashType, GroupedListItem, LocalFilter, LogFilter, Method, SettingsPage, SettingsSubTab, SortOrder, StatusFilter } from './index';
5
+ export type SearchScope = 'all' | 'url' | 'reqBody' | 'resBody' | 'headers';
5
6
  export interface ParsedStackFrame {
6
7
  method: string;
7
8
  file: string;
@@ -214,6 +215,14 @@ export interface InspectorContextValue {
214
215
  groupedData: GroupedListItem[];
215
216
  search: string;
216
217
  setSearch: React.Dispatch<React.SetStateAction<string>>;
218
+ searchScope: SearchScope;
219
+ setSearchScope: React.Dispatch<React.SetStateAction<SearchScope>>;
220
+ isRegexSearch: boolean;
221
+ setIsRegexSearch: React.Dispatch<React.SetStateAction<boolean>>;
222
+ isCaseSensitive: boolean;
223
+ setIsCaseSensitive: React.Dispatch<React.SetStateAction<boolean>>;
224
+ quickFilter: string;
225
+ setQuickFilter: React.Dispatch<React.SetStateAction<string>>;
217
226
  statusFilters: Set<StatusFilter>;
218
227
  setStatusFilters: React.Dispatch<React.SetStateAction<Set<StatusFilter>>>;
219
228
  methodFilters: Set<Method>;
@@ -11,9 +11,11 @@ import { METHOD_COLORS } from '../../constants';
11
11
  import { LIB_VERSION } from '../../constants';
12
12
  import { getStatusColor, getAppName, formatTime, getSize } from '../../helpers';
13
13
  import { getTelemetryConsentStatus } from '../../helpers/telemetry';
14
+ import { UpdateAvailableModal } from './UpdateAvailableModal';
14
15
  import { WhiteBackNavigation, TrashIcon, SettingsIcon, CloseWhite, ClockIcon, SizeIcon, AppleIcon, AndroidIcon, NpmIcon, ResetIcon, } from '../NetworkIcons';
15
16
  const InspectorHeader = React.memo(() => {
16
17
  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();
18
+ const [showUpdateModal, setShowUpdateModal] = React.useState(false);
17
19
  const [isTelemetryActive, setIsTelemetryActive] = React.useState(false);
18
20
  const telemetryPulseAnim = React.useRef(new Animated.Value(1)).current;
19
21
  React.useEffect(() => {
@@ -77,7 +79,8 @@ const InspectorHeader = React.memo(() => {
77
79
  const isDetailView = (activeTab === 'apis' && selected != null) ||
78
80
  (activeTab === 'analytics' && selectedEvent != null) ||
79
81
  (activeTab === 'logs' && selectedLog != null) ||
80
- (activeTab === 'redux' && (selectedReduxSlice != null || selectedReduxAction != null)) ||
82
+ (activeTab === 'redux' &&
83
+ (selectedReduxSlice != null || selectedReduxAction != null)) ||
81
84
  (activeTab === 'crash' && selectedCrash != null);
82
85
  const isSettingsView = settingsPage !== null;
83
86
  const isAnySelected = isDetailView || isSettingsView;
@@ -102,7 +105,8 @@ const InspectorHeader = React.memo(() => {
102
105
  }
103
106
  }, [settingsPage]);
104
107
  const headerTopPadding = Platform.OS === 'ios' && modalHeightPercent >= 95 ? 44 : 0;
105
- return (<LinearGradient colors={['#4F46E5', '#7C3AED']} start={{ x: 0, y: 0 }} end={{ x: 1, y: 1 }} style={styles.headerGradient}>
108
+ return (<>
109
+ <LinearGradient colors={['#4F46E5', '#7C3AED']} start={{ x: 0, y: 0 }} end={{ x: 1, y: 1 }} style={styles.headerGradient}>
106
110
  <View style={{ paddingTop: headerTopPadding, width: '100%' }}>
107
111
  <View style={styles.header}>
108
112
  <View style={[
@@ -203,7 +207,12 @@ const InspectorHeader = React.memo(() => {
203
207
  }}>
204
208
  <AppHeaderLogo size={46} customIcon={appIcon}/>
205
209
  <View style={{ gap: 2.5, flex: 1, minWidth: 0 }}>
206
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, minWidth: 0 }}>
210
+ <View style={{
211
+ flexDirection: 'row',
212
+ alignItems: 'center',
213
+ gap: 6,
214
+ minWidth: 0,
215
+ }}>
207
216
  <Text style={[styles.headerTitle, { flexShrink: 1 }]} numberOfLines={1} ellipsizeMode="tail">
208
217
  {getAppName()}
209
218
  </Text>
@@ -217,34 +226,41 @@ const InspectorHeader = React.memo(() => {
217
226
  marginBottom: 0,
218
227
  },
219
228
  ]}>
220
- <Text style={[
221
- styles.envBadgeText,
222
- { color: envConfig.text },
223
- ]}>
229
+ <Text style={[styles.envBadgeText, { color: envConfig.text }]}>
224
230
  {envConfig.label}
225
231
  </Text>
226
232
  </View>
227
- {updateAvailable && (<Pressable hitSlop={10} onPress={() => Alert.alert('Update Available', `react-native-inapp-inspector v${latestNpmVersion} is available on NPM (installed: v${LIB_VERSION}).`, [
228
- { text: 'Later', style: 'cancel' },
229
- {
230
- text: 'View on NPM',
231
- onPress: () => Linking.openURL('https://www.npmjs.com/package/react-native-inapp-inspector').catch(() => { }),
232
- },
233
- ])} style={{
233
+ {updateAvailable && (<Pressable hitSlop={10} onPress={() => setShowUpdateModal(true)} style={{
234
+ flexDirection: 'row',
234
235
  alignItems: 'center',
235
- justifyContent: 'center',
236
+ backgroundColor: '#F59E0B',
237
+ borderRadius: 5,
238
+ paddingHorizontal: 5.5,
239
+ paddingVertical: 1.5,
240
+ gap: 3.5,
241
+ shadowColor: '#F59E0B',
242
+ shadowOffset: { width: 0, height: 1.5 },
243
+ shadowOpacity: 0.35,
244
+ shadowRadius: 3,
245
+ elevation: 3,
236
246
  flexShrink: 0,
237
247
  }}>
238
248
  <Animated.View style={{
239
- width: 8,
240
- height: 8,
241
- borderRadius: 4,
242
- backgroundColor: AppColors.liveGreen,
243
- borderWidth: 1,
244
- borderColor: `${AppColors.white}E6`,
249
+ width: 5,
250
+ height: 5,
251
+ borderRadius: 2.5,
252
+ backgroundColor: '#FFFFFF',
245
253
  opacity: activePulseAnim,
246
254
  transform: [{ scale: unreadPulseAnim }],
247
255
  }}/>
256
+ <Text style={{
257
+ fontFamily: AppFonts.interBold,
258
+ fontSize: 9,
259
+ color: '#FFFFFF',
260
+ letterSpacing: 0.3,
261
+ }}>
262
+ UPDATE ⚡
263
+ </Text>
248
264
  </Pressable>)}
249
265
  </View>
250
266
 
@@ -280,7 +296,14 @@ const InspectorHeader = React.memo(() => {
280
296
  </Text>
281
297
  </View>
282
298
 
283
- <Pressable onPress={() => Linking.openURL('https://www.npmjs.com/package/react-native-inapp-inspector').catch(() => { })} style={{
299
+ <Pressable onPress={() => {
300
+ if (updateAvailable) {
301
+ setShowUpdateModal(true);
302
+ }
303
+ else {
304
+ Linking.openURL('https://www.npmjs.com/package/react-native-inapp-inspector').catch(() => { });
305
+ }
306
+ }} style={{
284
307
  backgroundColor: '#FFFFFF',
285
308
  borderRadius: 5,
286
309
  paddingHorizontal: 6,
@@ -304,6 +327,13 @@ const InspectorHeader = React.memo(() => {
304
327
  }} numberOfLines={1}>
305
328
  v{LIB_VERSION}
306
329
  </Text>
330
+ {updateAvailable && (<Text style={{
331
+ fontFamily: AppFonts.interBold,
332
+ fontSize: 9,
333
+ color: '#F59E0B',
334
+ }}>
335
+
336
+ </Text>)}
307
337
  </Pressable>
308
338
 
309
339
  {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={{
@@ -444,9 +474,7 @@ const InspectorHeader = React.memo(() => {
444
474
  },
445
475
  ]}>
446
476
  <Text style={styles.headerMethodText}>
447
- {selectedEvent.source === 'firebase'
448
- ? 'FB'
449
- : 'MAN'}
477
+ {selectedEvent.source === 'firebase' ? 'FB' : 'MAN'}
450
478
  </Text>
451
479
  </View>
452
480
  <Text style={styles.headerDetailTitle} numberOfLines={1} ellipsizeMode="middle">
@@ -489,9 +517,7 @@ const InspectorHeader = React.memo(() => {
489
517
  </View>
490
518
  <Text style={styles.headerDetailTitle} numberOfLines={1} ellipsizeMode="middle">
491
519
  console.
492
- {(selectedLog.sourceMethod) ||
493
- selectedLog.type ||
494
- 'log'}
520
+ {selectedLog.sourceMethod || selectedLog.type || 'log'}
495
521
  </Text>
496
522
  </View>
497
523
  <View style={styles.headerDetailSubRow}>
@@ -504,10 +530,10 @@ const InspectorHeader = React.memo(() => {
504
530
  borderRadius: 20,
505
531
  backgroundColor: `${AppColors.white}29`,
506
532
  }}>
507
- <ClockIcon color={AppColors.white} size={11}/>
508
- <Text style={styles.headerSubTitle}>
509
- {formatTime(selectedLog.timestamp)}
510
- </Text>
533
+ <ClockIcon color={AppColors.white} size={11}/>
534
+ <Text style={styles.headerSubTitle}>
535
+ {formatTime(selectedLog.timestamp)}
536
+ </Text>
511
537
  </View>
512
538
  </View>
513
539
  </View>) : activeTab === 'redux' && selectedReduxSlice != null ? ((() => {
@@ -527,9 +553,7 @@ const InspectorHeader = React.memo(() => {
527
553
  backgroundColor: `${AppColors.purple}4D`,
528
554
  },
529
555
  ]}>
530
- <Text style={styles.headerMethodText}>
531
- SLICE
532
- </Text>
556
+ <Text style={styles.headerMethodText}>SLICE</Text>
533
557
  </View>
534
558
  <Text style={styles.headerDetailTitle} numberOfLines={1} ellipsizeMode="middle">
535
559
  {selectedReduxSlice}
@@ -540,20 +564,26 @@ const InspectorHeader = React.memo(() => {
540
564
  styles.headerStatusDot,
541
565
  { backgroundColor: AppColors.liveGreen },
542
566
  ]}/>
543
- <Text style={styles.headerSubTitle}>
544
- Live
567
+ <Text style={styles.headerSubTitle}>Live</Text>
568
+ <Text style={[styles.headerSubTitle, { opacity: 0.6 }]}>
569
+
545
570
  </Text>
546
- <Text style={[styles.headerSubTitle, { opacity: 0.6 }]}>•</Text>
547
571
  <Text style={styles.headerSubTitle}>
548
572
  {keyCount} keys
549
573
  </Text>
550
- <Text style={[styles.headerSubTitle, { opacity: 0.6 }]}>•</Text>
551
- <Text style={styles.headerSubTitle}>
552
- {sliceSize}
574
+ <Text style={[styles.headerSubTitle, { opacity: 0.6 }]}>
575
+
553
576
  </Text>
577
+ <Text style={styles.headerSubTitle}>{sliceSize}</Text>
554
578
  {lastAction?.timestamp && (<>
555
- <Text style={[styles.headerSubTitle, { opacity: 0.6 }]}>•</Text>
556
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 3 }}>
579
+ <Text style={[styles.headerSubTitle, { opacity: 0.6 }]}>
580
+
581
+ </Text>
582
+ <View style={{
583
+ flexDirection: 'row',
584
+ alignItems: 'center',
585
+ gap: 3,
586
+ }}>
557
587
  <ClockIcon color={AppColors.white} size={10}/>
558
588
  <Text style={styles.headerSubTitle}>
559
589
  {lastAction.timestamp}
@@ -570,9 +600,7 @@ const InspectorHeader = React.memo(() => {
570
600
  backgroundColor: `${AppColors.brandPurple}4D`,
571
601
  },
572
602
  ]}>
573
- <Text style={styles.headerMethodText}>
574
- ACTION
575
- </Text>
603
+ <Text style={styles.headerMethodText}>ACTION</Text>
576
604
  </View>
577
605
  <Text style={styles.headerDetailTitle} numberOfLines={1} ellipsizeMode="middle">
578
606
  {selectedReduxAction.type}
@@ -598,7 +626,9 @@ const InspectorHeader = React.memo(() => {
598
626
  },
599
627
  ]}>
600
628
  <Text style={styles.headerMethodText}>
601
- {selectedCrash.isFatal ? 'FATAL' : selectedCrash.type.toUpperCase()}
629
+ {selectedCrash.isFatal
630
+ ? 'FATAL'
631
+ : selectedCrash.type.toUpperCase()}
602
632
  </Text>
603
633
  </View>
604
634
  <Text style={styles.headerDetailTitle} numberOfLines={1} ellipsizeMode="middle">
@@ -615,10 +645,13 @@ const InspectorHeader = React.memo(() => {
615
645
  },
616
646
  ]}/>
617
647
  <Text style={styles.headerSubTitle}>
618
- {selectedCrash.timeStr || new Date(selectedCrash.timestamp).toLocaleTimeString()}
648
+ {selectedCrash.timeStr ||
649
+ new Date(selectedCrash.timestamp).toLocaleTimeString()}
619
650
  </Text>
620
651
  {selectedCrash.deviceInfo?.platform && (<>
621
- <Text style={[styles.headerSubTitle, { opacity: 0.6 }]}>•</Text>
652
+ <Text style={[styles.headerSubTitle, { opacity: 0.6 }]}>
653
+
654
+ </Text>
622
655
  <Text style={styles.headerSubTitle}>
623
656
  {selectedCrash.deviceInfo.platform.toUpperCase()}
624
657
  </Text>
@@ -690,6 +723,10 @@ const InspectorHeader = React.memo(() => {
690
723
  </View>
691
724
  </View>
692
725
  </View>
693
- </LinearGradient>);
726
+ </LinearGradient>
727
+
728
+ {/* Dedicated Update Available Details Modal */}
729
+ <UpdateAvailableModal visible={showUpdateModal} latestVersion={latestNpmVersion} onClose={() => setShowUpdateModal(false)}/>
730
+ </>);
694
731
  });
695
732
  export default InspectorHeader;
@@ -68,6 +68,7 @@ const MainScreen = () => {
68
68
  return (<>
69
69
  {(Platform.OS === 'ios' || Platform.OS === 'android') &&
70
70
  isEnabled &&
71
+ !visible &&
71
72
  !useNativeFab && <FabLauncher />}
72
73
  <Modal visible={visible} animationType={modalAnimationType} transparent statusBarTranslucent={true}>
73
74
  {visible && (<ErrorBoundary onClose={closeModal}>