react-native-inapp-inspector 2.2.8 → 2.3.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/android/src/main/java/com/inappinspector/NetworkInspectorModule.kt +22 -7
- package/dist/commonjs/components/AppHeaderLogo.js +45 -12
- package/dist/commonjs/components/BrandSquareIcon.js +92 -49
- package/dist/commonjs/components/EmptyState.d.ts +7 -2
- package/dist/commonjs/components/EmptyState.js +11 -5
- package/dist/commonjs/components/Inspector/AnalyticsTab.js +29 -18
- package/dist/commonjs/components/Inspector/ConsoleTab.js +6 -1
- package/dist/commonjs/components/Inspector/CrashDetail.js +43 -9
- package/dist/commonjs/components/Inspector/CrashTab.js +34 -3
- package/dist/commonjs/components/Inspector/DeviceInfoTab.js +80 -16
- package/dist/commonjs/components/Inspector/InspectorHeader.js +16 -3
- package/dist/commonjs/components/Inspector/NetworkDetail.js +3 -2
- package/dist/commonjs/components/Inspector/NetworkTab.js +103 -4
- package/dist/commonjs/components/Inspector/ReduxTab.js +19 -3
- package/dist/commonjs/components/Inspector/SettingsPanel.js +4 -4
- package/dist/commonjs/components/Inspector/StorageTab.js +19 -3
- package/dist/commonjs/components/JsonViewer.js +2 -0
- package/dist/commonjs/components/MetaAccordion.js +15 -3
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/customHooks/networkLogger.d.ts +3 -0
- package/dist/commonjs/customHooks/networkLogger.js +10 -1
- package/dist/commonjs/helpers/index.d.ts +6 -0
- package/dist/commonjs/helpers/index.js +196 -6
- package/dist/commonjs/helpers/searchQueryParser.js +62 -20
- package/dist/commonjs/i18n/index.d.ts +13 -0
- package/dist/commonjs/i18n/index.js +35 -2
- package/dist/commonjs/index.d.ts +3 -0
- package/dist/commonjs/index.js +80 -11
- package/dist/commonjs/native/NativeInspector.js +13 -0
- package/dist/commonjs/native/NativeNetworkInspector.d.ts +1 -1
- package/dist/commonjs/styles/AppColors.d.ts +4 -0
- package/dist/commonjs/styles/AppColors.js +11 -1
- package/dist/commonjs/styles/AppFonts.d.ts +11 -0
- package/dist/commonjs/styles/AppFonts.js +11 -1
- package/dist/commonjs/types/interfaces.d.ts +1 -0
- package/dist/esm/components/AppHeaderLogo.js +45 -12
- package/dist/esm/components/BrandSquareIcon.js +92 -49
- package/dist/esm/components/EmptyState.d.ts +7 -2
- package/dist/esm/components/EmptyState.js +11 -5
- package/dist/esm/components/Inspector/AnalyticsTab.js +30 -19
- package/dist/esm/components/Inspector/ConsoleTab.js +6 -1
- package/dist/esm/components/Inspector/CrashDetail.js +43 -9
- package/dist/esm/components/Inspector/CrashTab.js +34 -3
- package/dist/esm/components/Inspector/DeviceInfoTab.js +80 -16
- package/dist/esm/components/Inspector/InspectorHeader.js +17 -4
- package/dist/esm/components/Inspector/NetworkDetail.js +3 -2
- package/dist/esm/components/Inspector/NetworkTab.js +103 -4
- package/dist/esm/components/Inspector/ReduxTab.js +19 -3
- package/dist/esm/components/Inspector/SettingsPanel.js +4 -4
- package/dist/esm/components/Inspector/StorageTab.js +19 -3
- package/dist/esm/components/JsonViewer.js +2 -0
- package/dist/esm/components/MetaAccordion.js +15 -3
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/customHooks/networkLogger.d.ts +3 -0
- package/dist/esm/customHooks/networkLogger.js +8 -0
- package/dist/esm/helpers/index.d.ts +6 -0
- package/dist/esm/helpers/index.js +193 -5
- package/dist/esm/helpers/searchQueryParser.js +62 -20
- package/dist/esm/i18n/index.d.ts +13 -0
- package/dist/esm/i18n/index.js +33 -3
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +68 -12
- package/dist/esm/native/NativeInspector.js +13 -0
- package/dist/esm/native/NativeNetworkInspector.d.ts +1 -1
- package/dist/esm/styles/AppColors.d.ts +4 -0
- package/dist/esm/styles/AppColors.js +9 -0
- package/dist/esm/styles/AppFonts.d.ts +11 -0
- package/dist/esm/styles/AppFonts.js +9 -0
- package/dist/esm/types/interfaces.d.ts +1 -0
- package/ios/NetworkInspectorModule.mm +11 -1
- package/package.json +1 -1
- package/src/components/AppHeaderLogo.tsx +46 -12
- package/src/components/BrandSquareIcon.tsx +127 -78
- package/src/components/EmptyState.tsx +27 -5
- package/src/components/Inspector/AnalyticsTab.tsx +48 -27
- package/src/components/Inspector/ConsoleTab.tsx +10 -0
- package/src/components/Inspector/CrashDetail.tsx +65 -9
- package/src/components/Inspector/CrashTab.tsx +41 -3
- package/src/components/Inspector/DeviceInfoTab.tsx +87 -16
- package/src/components/Inspector/InspectorHeader.tsx +24 -4
- package/src/components/Inspector/NetworkDetail.tsx +4 -4
- package/src/components/Inspector/NetworkTab.tsx +129 -4
- package/src/components/Inspector/ReduxTab.tsx +26 -8
- package/src/components/Inspector/SettingsPanel.tsx +4 -4
- package/src/components/Inspector/StorageTab.tsx +21 -3
- package/src/components/JsonViewer.tsx +2 -0
- package/src/components/MetaAccordion.tsx +18 -3
- package/src/constants/version.ts +1 -1
- package/src/customHooks/networkLogger.ts +15 -0
- package/src/helpers/index.ts +213 -5
- package/src/helpers/searchQueryParser.ts +63 -20
- package/src/i18n/index.ts +38 -3
- package/src/index.tsx +89 -11
- package/src/native/NativeInspector.ts +13 -0
- package/src/native/NativeNetworkInspector.ts +1 -1
- package/src/styles/AppColors.ts +9 -0
- package/src/styles/AppFonts.ts +16 -0
- package/src/types/interfaces.ts +1 -0
|
@@ -4,6 +4,7 @@ import { animateNextLayout, useInspector } from './InspectorContext';
|
|
|
4
4
|
import AnalyticsEventCard from '../AnalyticsEventCard';
|
|
5
5
|
import AnalyticsFilterModal from './AnalyticsFilterModal';
|
|
6
6
|
import EndOfListFooter from '../EndOfListFooter';
|
|
7
|
+
import EmptyState from '../EmptyState';
|
|
7
8
|
import styles from '../../styles';
|
|
8
9
|
import { AppColors } from '../../styles/AppColors';
|
|
9
10
|
import { AppFonts } from '../../styles/AppFonts';
|
|
@@ -12,7 +13,7 @@ import Svg, { Path, Rect, Line, Circle, G, Defs, LinearGradient as SvgLinearGrad
|
|
|
12
13
|
import TouchableScale from '../TouchableScale';
|
|
13
14
|
import { getCurrentUserId, getCurrentUserProperties, getDefaultEventParameters, getCollectionEnabled, } from '../../customHooks/analyticsLogger';
|
|
14
15
|
import { getEventCategory, } from '../../helpers';
|
|
15
|
-
import { LayersIcon, SearchIcon, ClearIcon, TrashIcon,
|
|
16
|
+
import { LayersIcon, SearchIcon, ClearIcon, TrashIcon, HeaderPauseIcon, FilterIcon, CartIcon, GlobeIcon, BoltIcon, SparkleIcon, PinIcon, MoneyIcon, } from '../NetworkIcons';
|
|
16
17
|
const AnalyticsHeader = React.memo(() => {
|
|
17
18
|
const { t } = useTranslation();
|
|
18
19
|
const { analyticsEvents, filteredAnalyticsEvents, analyticsFilters, setAnalyticsFilters, analyticsHeaderExpanded, setAnalyticsHeaderExpanded, } = useInspector();
|
|
@@ -850,9 +851,29 @@ const AnalyticsTab = React.memo(() => {
|
|
|
850
851
|
<View style={styles.searchContainer}>
|
|
851
852
|
<SearchIcon color={AppColors.grayTextWeak} size={16}/>
|
|
852
853
|
<TextInput placeholder="Search events..." placeholderTextColor={AppColors.grayTextWeak} value={analyticsSearch} onChangeText={setAnalyticsSearch} style={styles.searchInput} autoCorrect={false} autoCapitalize="none"/>
|
|
853
|
-
{analyticsSearch.length > 0 && (<
|
|
854
|
-
|
|
855
|
-
|
|
854
|
+
{analyticsSearch.length > 0 && (<View style={{
|
|
855
|
+
flexDirection: 'row',
|
|
856
|
+
alignItems: 'center',
|
|
857
|
+
gap: 4,
|
|
858
|
+
}}>
|
|
859
|
+
<View style={{
|
|
860
|
+
backgroundColor: `${AppColors.purple}20`,
|
|
861
|
+
borderRadius: 10,
|
|
862
|
+
paddingHorizontal: 6,
|
|
863
|
+
paddingVertical: 1.5,
|
|
864
|
+
}}>
|
|
865
|
+
<Text style={{
|
|
866
|
+
color: AppColors.purple,
|
|
867
|
+
fontSize: 9.5,
|
|
868
|
+
fontFamily: AppFonts.interBold,
|
|
869
|
+
}}>
|
|
870
|
+
{filteredAnalyticsEvents.length}
|
|
871
|
+
</Text>
|
|
872
|
+
</View>
|
|
873
|
+
<Pressable onPress={() => setAnalyticsSearch('')} hitSlop={10} style={styles.clearBtn}>
|
|
874
|
+
<ClearIcon color={AppColors.grayTextWeak} size={13}/>
|
|
875
|
+
</Pressable>
|
|
876
|
+
</View>)}
|
|
856
877
|
</View>
|
|
857
878
|
<View style={styles.toolbarRight}>
|
|
858
879
|
<TouchableOpacity style={[
|
|
@@ -907,21 +928,11 @@ const AnalyticsTab = React.memo(() => {
|
|
|
907
928
|
<AnalyticsFilterModal visible={isFilterModalOpen} onClose={() => setIsFilterModalOpen(false)}/>
|
|
908
929
|
|
|
909
930
|
<View style={{ flex: 1 }} onLayout={() => setIsAnalyticsLayoutReady(true)}>
|
|
910
|
-
{isAnalyticsLayoutReady ? (<FlatList data={filteredAnalyticsEvents} keyExtractor={keyExtractor} ListHeaderComponent={AnalyticsHeader} renderItem={renderItem} initialNumToRender={12} maxToRenderPerBatch={8} windowSize={5} removeClippedSubviews={true} renderToHardwareTextureAndroid={true} ListEmptyComponent={<
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
{analyticsSearch.length > 0
|
|
916
|
-
? 'No matching events'
|
|
917
|
-
: 'No analytics events yet'}
|
|
918
|
-
</Text>
|
|
919
|
-
<Text style={styles.emptySub}>
|
|
920
|
-
{analyticsSearch.length > 0
|
|
921
|
-
? 'Try adjusting your search.'
|
|
922
|
-
: 'Call setupAnalyticsLogger(analytics()) at app start.'}
|
|
923
|
-
</Text>
|
|
924
|
-
</View>} ListFooterComponent={filteredAnalyticsEvents.length > 0 ? (<EndOfListFooter count={filteredAnalyticsEvents.length} label="events"/>) : null} contentContainerStyle={[
|
|
931
|
+
{isAnalyticsLayoutReady ? (<FlatList data={filteredAnalyticsEvents} keyExtractor={keyExtractor} ListHeaderComponent={AnalyticsHeader} renderItem={renderItem} initialNumToRender={12} maxToRenderPerBatch={8} windowSize={5} removeClippedSubviews={true} renderToHardwareTextureAndroid={true} ListEmptyComponent={<EmptyState isSearch={analyticsSearch.length > 0} searchQuery={analyticsSearch} customTitle={analyticsSearch.length > 0
|
|
932
|
+
? 'No matching analytics events'
|
|
933
|
+
: 'No analytics events yet'} customSub={analyticsSearch.length > 0
|
|
934
|
+
? `No events matched "${analyticsSearch}"`
|
|
935
|
+
: 'Call setupAnalyticsLogger(analytics()) at app start.'} onClearSearch={() => setAnalyticsSearch('')}/>} ListFooterComponent={filteredAnalyticsEvents.length > 0 ? (<EndOfListFooter count={filteredAnalyticsEvents.length} label="events"/>) : null} contentContainerStyle={[
|
|
925
936
|
styles.listContent,
|
|
926
937
|
filteredAnalyticsEvents.length === 0 && {
|
|
927
938
|
flexGrow: 1,
|
|
@@ -480,7 +480,12 @@ const ConsoleTab = React.memo(() => {
|
|
|
480
480
|
</ScrollView>
|
|
481
481
|
</View>
|
|
482
482
|
|
|
483
|
-
<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}
|
|
483
|
+
<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} searchQuery={logSearch} customTitle={logSearch.length > 0
|
|
484
|
+
? 'No matching console logs'
|
|
485
|
+
: 'No console logs'} onClearSearch={() => {
|
|
486
|
+
setLogSearch('');
|
|
487
|
+
setLogFilters(new Set());
|
|
488
|
+
}}/>} ListFooterComponent={filteredConsoleLogs.length > 0 ? (<EndOfListFooter count={filteredConsoleLogs.length} label="logs"/>) : null} contentContainerStyle={[
|
|
484
489
|
styles.listContent,
|
|
485
490
|
filteredConsoleLogs.length === 0 && { flexGrow: 1 },
|
|
486
491
|
]} keyboardShouldPersistTaps="handled"/>
|
|
@@ -6,6 +6,7 @@ import TouchableScale from '../TouchableScale';
|
|
|
6
6
|
import SegmentedTabs from '../SegmentedTabs';
|
|
7
7
|
import HighlightText from '../HighlightText';
|
|
8
8
|
import JsonViewer from '../JsonViewer';
|
|
9
|
+
import CopyButton from '../CopyButton';
|
|
9
10
|
import { AppColors } from '../../styles/AppColors';
|
|
10
11
|
import { AppFonts } from '../../styles/AppFonts';
|
|
11
12
|
import { exportCrashReport, parseCrashStackTrace, } from '../../customHooks/crashHandler';
|
|
@@ -328,9 +329,20 @@ const CrashDetail = React.memo(() => {
|
|
|
328
329
|
{activeSubTab === 'diagnostics' && (<View style={{ gap: 12 }}>
|
|
329
330
|
{/* Device & OS Diagnostics Card */}
|
|
330
331
|
<View style={localStyles.sectionCard}>
|
|
331
|
-
<View style={
|
|
332
|
-
|
|
333
|
-
|
|
332
|
+
<View style={[
|
|
333
|
+
localStyles.sectionCardHeader,
|
|
334
|
+
{ justifyContent: 'space-between' },
|
|
335
|
+
]}>
|
|
336
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
337
|
+
<GlobeIcon color={AppColors.purple} size={14}/>
|
|
338
|
+
<Text style={localStyles.sectionCardTitle}>
|
|
339
|
+
{t('crash.deviceEnvironment')}
|
|
340
|
+
</Text>
|
|
341
|
+
</View>
|
|
342
|
+
<CopyButton value={selectedCrash.deviceInfo || {
|
|
343
|
+
platform: Platform.OS,
|
|
344
|
+
osVersion: String(Platform.Version),
|
|
345
|
+
}} label="Diagnostics"/>
|
|
334
346
|
</View>
|
|
335
347
|
|
|
336
348
|
<View style={localStyles.diagGrid}>
|
|
@@ -380,9 +392,17 @@ const CrashDetail = React.memo(() => {
|
|
|
380
392
|
|
|
381
393
|
{/* Memory Metrics Card */}
|
|
382
394
|
{selectedCrash.memoryInfo && (<View style={localStyles.sectionCard}>
|
|
383
|
-
<View style={
|
|
384
|
-
|
|
385
|
-
|
|
395
|
+
<View style={[
|
|
396
|
+
localStyles.sectionCardHeader,
|
|
397
|
+
{ justifyContent: 'space-between' },
|
|
398
|
+
]}>
|
|
399
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
400
|
+
<LayersIcon color={AppColors.purple} size={14}/>
|
|
401
|
+
<Text style={localStyles.sectionCardTitle}>
|
|
402
|
+
{t('crash.jsHeapMemory')}
|
|
403
|
+
</Text>
|
|
404
|
+
</View>
|
|
405
|
+
<CopyButton value={selectedCrash.memoryInfo} label="Memory Info"/>
|
|
386
406
|
</View>
|
|
387
407
|
|
|
388
408
|
<View style={localStyles.diagGrid}>
|
|
@@ -405,6 +425,13 @@ const CrashDetail = React.memo(() => {
|
|
|
405
425
|
|
|
406
426
|
{/* 3. BREADCRUMBS TAB */}
|
|
407
427
|
{activeSubTab === 'breadcrumbs' && (<View style={{ gap: 10 }}>
|
|
428
|
+
{selectedCrash.breadcrumbs && selectedCrash.breadcrumbs.length > 0 && (<View style={{
|
|
429
|
+
flexDirection: 'row',
|
|
430
|
+
justifyContent: 'flex-end',
|
|
431
|
+
marginBottom: 2,
|
|
432
|
+
}}>
|
|
433
|
+
<CopyButton value={selectedCrash.breadcrumbs} label="All Breadcrumbs"/>
|
|
434
|
+
</View>)}
|
|
408
435
|
{selectedCrash.breadcrumbs && selectedCrash.breadcrumbs.length > 0 ? (selectedCrash.breadcrumbs.map((b, idx) => (<View key={`crumb_${idx}`} style={localStyles.breadcrumbCard}>
|
|
409
436
|
<View style={localStyles.breadcrumbHeader}>
|
|
410
437
|
<View style={localStyles.breadcrumbTag}>
|
|
@@ -412,9 +439,16 @@ const CrashDetail = React.memo(() => {
|
|
|
412
439
|
{b.type.toUpperCase()}
|
|
413
440
|
</Text>
|
|
414
441
|
</View>
|
|
415
|
-
<
|
|
416
|
-
|
|
417
|
-
|
|
442
|
+
<View style={{
|
|
443
|
+
flexDirection: 'row',
|
|
444
|
+
alignItems: 'center',
|
|
445
|
+
gap: 8,
|
|
446
|
+
}}>
|
|
447
|
+
<Text style={localStyles.breadcrumbTime}>
|
|
448
|
+
{new Date(b.timestamp).toLocaleTimeString()}
|
|
449
|
+
</Text>
|
|
450
|
+
<CopyButton value={`${new Date(b.timestamp).toLocaleTimeString()} [${b.type.toUpperCase()}] ${b.message}${b.data ? ' ' + JSON.stringify(b.data) : ''}`} label="Breadcrumb"/>
|
|
451
|
+
</View>
|
|
418
452
|
</View>
|
|
419
453
|
<Text style={localStyles.breadcrumbMessage}>{b.message}</Text>
|
|
420
454
|
{b.data && (<View style={{ marginTop: 6 }}>
|
|
@@ -307,9 +307,25 @@ const CrashTab = React.memo(() => {
|
|
|
307
307
|
<View style={localStyles.searchInputWrapper}>
|
|
308
308
|
<SearchIcon color={AppColors.grayTextWeak} size={14}/>
|
|
309
309
|
<TextInput value={searchQuery} onChangeText={setSearchQuery} placeholder={t('crash.searchPlaceholder')} placeholderTextColor={AppColors.grayTextWeak} style={localStyles.searchInput} autoCapitalize="none" autoCorrect={false}/>
|
|
310
|
-
{searchQuery.length > 0 && (<
|
|
311
|
-
<
|
|
312
|
-
|
|
310
|
+
{searchQuery.length > 0 && (<View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
|
|
311
|
+
<View style={{
|
|
312
|
+
backgroundColor: `${AppColors.purple}20`,
|
|
313
|
+
borderRadius: 10,
|
|
314
|
+
paddingHorizontal: 6,
|
|
315
|
+
paddingVertical: 1.5,
|
|
316
|
+
}}>
|
|
317
|
+
<Text style={{
|
|
318
|
+
color: AppColors.purple,
|
|
319
|
+
fontSize: 9.5,
|
|
320
|
+
fontFamily: AppFonts.interBold,
|
|
321
|
+
}}>
|
|
322
|
+
{filteredList.length}
|
|
323
|
+
</Text>
|
|
324
|
+
</View>
|
|
325
|
+
<TouchableOpacity onPress={() => setSearchQuery('')} hitSlop={8}>
|
|
326
|
+
<ClearIcon color={AppColors.grayTextWeak} size={14}/>
|
|
327
|
+
</TouchableOpacity>
|
|
328
|
+
</View>)}
|
|
313
329
|
</View>
|
|
314
330
|
|
|
315
331
|
{/* Crash Filters */}
|
|
@@ -381,6 +397,21 @@ const CrashTab = React.memo(() => {
|
|
|
381
397
|
? t('crash.emptySearchSubtitle')
|
|
382
398
|
: t('crash.emptySubtitle')}
|
|
383
399
|
</Text>
|
|
400
|
+
{searchQuery.length > 0 && (<TouchableScale onPress={() => setSearchQuery('')} style={{
|
|
401
|
+
marginTop: 10,
|
|
402
|
+
paddingHorizontal: 14,
|
|
403
|
+
paddingVertical: 7,
|
|
404
|
+
borderRadius: 8,
|
|
405
|
+
backgroundColor: AppColors.purple,
|
|
406
|
+
}}>
|
|
407
|
+
<Text style={{
|
|
408
|
+
fontFamily: AppFonts.interBold,
|
|
409
|
+
fontSize: 11.5,
|
|
410
|
+
color: AppColors.white,
|
|
411
|
+
}}>
|
|
412
|
+
Clear Search
|
|
413
|
+
</Text>
|
|
414
|
+
</TouchableScale>)}
|
|
384
415
|
</ScrollView>) : (<FlatList data={filteredList} keyExtractor={item => item.id} renderItem={renderCard} initialNumToRender={12} maxToRenderPerBatch={8} windowSize={5} removeClippedSubviews={true} renderToHardwareTextureAndroid={true} contentContainerStyle={localStyles.listContent} showsVerticalScrollIndicator={false} ListFooterComponent={<EndOfListFooter />}/>)}
|
|
385
416
|
|
|
386
417
|
{/* Filter Modal */}
|
|
@@ -21,9 +21,9 @@ const InfoRow = React.memo(({ label, value, copyable = true, badge, subtext, isL
|
|
|
21
21
|
styles.infoRow,
|
|
22
22
|
isLast && { borderBottomWidth: 0 },
|
|
23
23
|
]}>
|
|
24
|
-
<View style={{ flex: 1, paddingRight: 8 }}>
|
|
25
|
-
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
26
|
-
<Text style={styles.infoLabel}>{label}</Text>
|
|
24
|
+
<View style={{ flex: 1, paddingRight: 8, minWidth: 0 }}>
|
|
25
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap', gap: 6 }}>
|
|
26
|
+
<Text style={styles.infoLabel} numberOfLines={2}>{label}</Text>
|
|
27
27
|
{badge && (<View style={[
|
|
28
28
|
styles.infoBadge,
|
|
29
29
|
{ backgroundColor: badge.bg, borderColor: `${badge.color}33` },
|
|
@@ -33,15 +33,22 @@ const InfoRow = React.memo(({ label, value, copyable = true, badge, subtext, isL
|
|
|
33
33
|
</Text>
|
|
34
34
|
</View>)}
|
|
35
35
|
</View>
|
|
36
|
-
{subtext && <Text style={styles.infoSubtext}>{subtext}</Text>}
|
|
36
|
+
{subtext && <Text style={styles.infoSubtext} numberOfLines={2}>{subtext}</Text>}
|
|
37
37
|
</View>
|
|
38
|
-
<View style={{
|
|
38
|
+
<View style={{
|
|
39
|
+
flexDirection: 'row',
|
|
40
|
+
alignItems: 'center',
|
|
41
|
+
gap: 6,
|
|
42
|
+
flexShrink: 0,
|
|
43
|
+
maxWidth: '58%',
|
|
44
|
+
justifyContent: 'flex-end',
|
|
45
|
+
}}>
|
|
39
46
|
<Text style={[
|
|
40
47
|
styles.infoValue,
|
|
41
48
|
typeof value === 'boolean' && {
|
|
42
49
|
color: value ? AppColors.emerald500 : AppColors.errorColor,
|
|
43
50
|
},
|
|
44
|
-
]} numberOfLines={
|
|
51
|
+
]} numberOfLines={3} selectable>
|
|
45
52
|
{displayValue}
|
|
46
53
|
</Text>
|
|
47
54
|
{copyable && value !== null && value !== undefined && (<TouchableScale hitSlop={8} onPress={() => {
|
|
@@ -348,6 +355,14 @@ export const DeviceInfoTab = React.memo(() => {
|
|
|
348
355
|
return true;
|
|
349
356
|
return text.toLowerCase().includes(search.toLowerCase().trim());
|
|
350
357
|
};
|
|
358
|
+
const hasAnyMatch = useMemo(() => {
|
|
359
|
+
if (!search.trim())
|
|
360
|
+
return true;
|
|
361
|
+
const query = search.toLowerCase().trim();
|
|
362
|
+
return (JSON.stringify(fullDeviceData).toLowerCase().includes(query) ||
|
|
363
|
+
ipAddress.toLowerCase().includes(query) ||
|
|
364
|
+
pseudoUDID.toLowerCase().includes(query));
|
|
365
|
+
}, [search, fullDeviceData, ipAddress, pseudoUDID]);
|
|
351
366
|
return (<View style={styles.container}>
|
|
352
367
|
{/* ─── Top Sub-Tabs Navigation Bar ─── */}
|
|
353
368
|
<View style={styles.subTabsWrapper}>
|
|
@@ -397,11 +412,11 @@ export const DeviceInfoTab = React.memo(() => {
|
|
|
397
412
|
<View style={styles.heroIconWrap}>
|
|
398
413
|
{Platform.OS === 'ios' ? (<AppleIcon size={20} color={AppColors.white}/>) : (<AndroidIcon size={20} color={AppColors.white}/>)}
|
|
399
414
|
</View>
|
|
400
|
-
<View style={{ flex: 1 }}>
|
|
401
|
-
<Text style={styles.heroTitle}>
|
|
415
|
+
<View style={{ flex: 1, minWidth: 0 }}>
|
|
416
|
+
<Text style={styles.heroTitle} numberOfLines={1} ellipsizeMode="tail">
|
|
402
417
|
{deviceMetrics?.deviceModel || Platform.constants?.Model || (Platform.OS === 'ios' ? 'Apple iPhone' : 'Android Device')}
|
|
403
418
|
</Text>
|
|
404
|
-
<Text style={styles.heroSubtitle}>
|
|
419
|
+
<Text style={styles.heroSubtitle} numberOfLines={1} ellipsizeMode="tail">
|
|
405
420
|
{Platform.OS === 'ios' ? `iOS ${Platform.Version}` : `Android ${Platform.Version} (API ${deviceMetrics?.apiLevel || Platform.Version})`}
|
|
406
421
|
</Text>
|
|
407
422
|
</View>
|
|
@@ -414,22 +429,22 @@ export const DeviceInfoTab = React.memo(() => {
|
|
|
414
429
|
{/* Quick Metrics Strip */}
|
|
415
430
|
<View style={styles.heroMetricsStrip}>
|
|
416
431
|
<View style={styles.heroMetricItem}>
|
|
417
|
-
<Text style={styles.heroMetricLabel}>IP ADDRESS</Text>
|
|
418
|
-
<Text style={styles.heroMetricValue} numberOfLines={1}>
|
|
432
|
+
<Text style={styles.heroMetricLabel} numberOfLines={1} ellipsizeMode="tail">IP ADDRESS</Text>
|
|
433
|
+
<Text style={styles.heroMetricValue} numberOfLines={1} ellipsizeMode="tail">
|
|
419
434
|
{ipAddress}
|
|
420
435
|
</Text>
|
|
421
436
|
</View>
|
|
422
437
|
<View style={styles.heroMetricDivider}/>
|
|
423
438
|
<View style={styles.heroMetricItem}>
|
|
424
|
-
<Text style={styles.heroMetricLabel}>RAM (USED/TOTAL)</Text>
|
|
425
|
-
<Text style={styles.heroMetricValue}>
|
|
439
|
+
<Text style={styles.heroMetricLabel} numberOfLines={1} ellipsizeMode="tail">RAM (USED/TOTAL)</Text>
|
|
440
|
+
<Text style={styles.heroMetricValue} numberOfLines={1} ellipsizeMode="tail">
|
|
426
441
|
{usedRamMb}/{totalRamMb} MB
|
|
427
442
|
</Text>
|
|
428
443
|
</View>
|
|
429
444
|
<View style={styles.heroMetricDivider}/>
|
|
430
445
|
<View style={styles.heroMetricItem}>
|
|
431
|
-
<Text style={styles.heroMetricLabel}>UPTIME</Text>
|
|
432
|
-
<Text style={styles.heroMetricValue}>{deviceUptime}</Text>
|
|
446
|
+
<Text style={styles.heroMetricLabel} numberOfLines={1} ellipsizeMode="tail">UPTIME</Text>
|
|
447
|
+
<Text style={styles.heroMetricValue} numberOfLines={1} ellipsizeMode="tail">{deviceUptime}</Text>
|
|
433
448
|
</View>
|
|
434
449
|
</View>
|
|
435
450
|
</View>
|
|
@@ -542,6 +557,18 @@ export const DeviceInfoTab = React.memo(() => {
|
|
|
542
557
|
{isMatch('Sandbox Integrity') && (<InfoRow label="App Sandbox File Isolation" value="Enforced by OS Kernel" isLast/>)}
|
|
543
558
|
</View>)}
|
|
544
559
|
|
|
560
|
+
{/* Empty search results state */}
|
|
561
|
+
{search.trim().length > 0 && !hasAnyMatch && (<View style={styles.emptySearchCard}>
|
|
562
|
+
<SearchIcon size={26} color={AppColors.grayTextWeak}/>
|
|
563
|
+
<Text style={styles.emptySearchTitle}>No matching device specifications</Text>
|
|
564
|
+
<Text style={styles.emptySearchSubtitle}>
|
|
565
|
+
No properties matched “{search}”
|
|
566
|
+
</Text>
|
|
567
|
+
<TouchableScale onPress={() => setSearch('')} style={styles.clearSearchBtn}>
|
|
568
|
+
<Text style={styles.clearSearchBtnText}>Clear Search</Text>
|
|
569
|
+
</TouchableScale>
|
|
570
|
+
</View>)}
|
|
571
|
+
|
|
545
572
|
<View style={{ height: 48 }}/>
|
|
546
573
|
</ScrollView>
|
|
547
574
|
</View>);
|
|
@@ -713,6 +740,8 @@ const styles = StyleSheet.create({
|
|
|
713
740
|
heroMetricItem: {
|
|
714
741
|
flex: 1,
|
|
715
742
|
alignItems: 'center',
|
|
743
|
+
paddingHorizontal: 2,
|
|
744
|
+
minWidth: 0,
|
|
716
745
|
},
|
|
717
746
|
heroMetricLabel: {
|
|
718
747
|
fontFamily: AppFonts.interBold,
|
|
@@ -784,7 +813,7 @@ const styles = StyleSheet.create({
|
|
|
784
813
|
fontFamily: AppFonts.interBold,
|
|
785
814
|
fontSize: 12,
|
|
786
815
|
color: AppColors.primaryBlack,
|
|
787
|
-
|
|
816
|
+
flexShrink: 1,
|
|
788
817
|
textAlign: 'right',
|
|
789
818
|
},
|
|
790
819
|
rowCopyBtn: {
|
|
@@ -792,5 +821,40 @@ const styles = StyleSheet.create({
|
|
|
792
821
|
borderRadius: 4,
|
|
793
822
|
backgroundColor: AppColors.grayBackground,
|
|
794
823
|
},
|
|
824
|
+
emptySearchCard: {
|
|
825
|
+
backgroundColor: AppColors.primaryLight,
|
|
826
|
+
borderRadius: 14,
|
|
827
|
+
borderWidth: 1,
|
|
828
|
+
borderColor: AppColors.grayBorderSecondary,
|
|
829
|
+
padding: 24,
|
|
830
|
+
alignItems: 'center',
|
|
831
|
+
justifyContent: 'center',
|
|
832
|
+
gap: 8,
|
|
833
|
+
marginTop: 8,
|
|
834
|
+
},
|
|
835
|
+
emptySearchTitle: {
|
|
836
|
+
fontFamily: AppFonts.interBold,
|
|
837
|
+
fontSize: 13.5,
|
|
838
|
+
color: AppColors.primaryBlack,
|
|
839
|
+
textAlign: 'center',
|
|
840
|
+
},
|
|
841
|
+
emptySearchSubtitle: {
|
|
842
|
+
fontFamily: AppFonts.interRegular,
|
|
843
|
+
fontSize: 11.5,
|
|
844
|
+
color: AppColors.grayTextWeak,
|
|
845
|
+
textAlign: 'center',
|
|
846
|
+
},
|
|
847
|
+
clearSearchBtn: {
|
|
848
|
+
marginTop: 6,
|
|
849
|
+
paddingHorizontal: 14,
|
|
850
|
+
paddingVertical: 6.5,
|
|
851
|
+
borderRadius: 8,
|
|
852
|
+
backgroundColor: AppColors.purple,
|
|
853
|
+
},
|
|
854
|
+
clearSearchBtnText: {
|
|
855
|
+
fontFamily: AppFonts.interBold,
|
|
856
|
+
fontSize: 11,
|
|
857
|
+
color: AppColors.white,
|
|
858
|
+
},
|
|
795
859
|
});
|
|
796
860
|
export default DeviceInfoTab;
|
|
@@ -9,12 +9,27 @@ 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, formatTime, getSize } from '../../helpers';
|
|
12
|
+
import { getStatusColor, getAppName, formatTime, getSize, getAppVersionAndBuild, } from '../../helpers';
|
|
13
|
+
import { getNativeDeviceMetrics } from '../../native/NativeInspector';
|
|
13
14
|
import { UpdateAvailableModal } from './UpdateAvailableModal';
|
|
14
15
|
import { WhiteBackNavigation, TrashIcon, SettingsIcon, CloseWhite, ClockIcon, SizeIcon, AppleIcon, AndroidIcon, NpmIcon, ResetIcon, BoltIcon, } 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();
|
|
17
18
|
const [showUpdateModal, setShowUpdateModal] = React.useState(false);
|
|
19
|
+
const [appVersionString, setAppVersionString] = React.useState(() => {
|
|
20
|
+
return getAppVersionAndBuild().formatted;
|
|
21
|
+
});
|
|
22
|
+
React.useEffect(() => {
|
|
23
|
+
getNativeDeviceMetrics()
|
|
24
|
+
.then(metrics => {
|
|
25
|
+
if (metrics?.appVersion) {
|
|
26
|
+
const v = metrics.appVersion;
|
|
27
|
+
const b = metrics.appBuild || '1';
|
|
28
|
+
setAppVersionString(`${v} (${b})`);
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
.catch(() => { });
|
|
32
|
+
}, []);
|
|
18
33
|
const envConfig = useMemo(() => {
|
|
19
34
|
const rawEnv = (environment || (__DEV__ ? 'DEV' : 'PROD')).trim();
|
|
20
35
|
const clean = rawEnv.toUpperCase();
|
|
@@ -265,9 +280,7 @@ const InspectorHeader = React.memo(() => {
|
|
|
265
280
|
color: `${AppColors.white}EB`,
|
|
266
281
|
letterSpacing: 0.1,
|
|
267
282
|
}} numberOfLines={1}>
|
|
268
|
-
{
|
|
269
|
-
? `iOS ${Platform.Version}`
|
|
270
|
-
: `Android ${Platform.Version}`}
|
|
283
|
+
{appVersionString}
|
|
271
284
|
</Text>
|
|
272
285
|
</View>
|
|
273
286
|
|
|
@@ -396,8 +396,9 @@ const NetworkDetail = React.memo(() => {
|
|
|
396
396
|
selected.responseHeaders?.['Content-Type']} url={selected.url}/>
|
|
397
397
|
|
|
398
398
|
{(() => {
|
|
399
|
-
const routeInfo = logRouteMapRef.current.get(selected.id)
|
|
400
|
-
|
|
399
|
+
const routeInfo = logRouteMapRef.current.get(selected.id) ||
|
|
400
|
+
selected?.routeInfo;
|
|
401
|
+
if (!routeInfo || !routeInfo.path || routeInfo.path === 'Navigators')
|
|
401
402
|
return null;
|
|
402
403
|
return <SourcePageCard routeInfo={routeInfo}/>;
|
|
403
404
|
})()}
|
|
@@ -469,7 +469,7 @@ const NetworkTab = React.memo(() => {
|
|
|
469
469
|
<View style={{
|
|
470
470
|
backgroundColor: `${AppColors.purple}20`,
|
|
471
471
|
borderRadius: 10,
|
|
472
|
-
paddingHorizontal:
|
|
472
|
+
paddingHorizontal: 6,
|
|
473
473
|
paddingVertical: 1.5,
|
|
474
474
|
}}>
|
|
475
475
|
<Text style={{
|
|
@@ -522,14 +522,104 @@ const NetworkTab = React.memo(() => {
|
|
|
522
522
|
styles.toolbarBtn,
|
|
523
523
|
filtersAccordion.isOpen &&
|
|
524
524
|
styles.toolbarBtnActive,
|
|
525
|
-
]} onPress={filtersAccordion.toggleOpen} hitSlop={
|
|
525
|
+
]} onPress={filtersAccordion.toggleOpen} hitSlop={6}>
|
|
526
526
|
<FilterIcon color={filtersAccordion.isOpen
|
|
527
527
|
? AppColors.purple
|
|
528
|
-
: AppColors.grayTextStrong} size={
|
|
528
|
+
: AppColors.grayTextStrong} size={16}/>
|
|
529
|
+
{(statusFilters.size > 0 ||
|
|
530
|
+
methodFilters.size > 0) && (<View style={styles.activeFilterDot}/>)}
|
|
529
531
|
</TouchableScale>
|
|
530
532
|
</View>
|
|
531
533
|
</View>
|
|
532
534
|
|
|
535
|
+
{/* Quick Search Scope & Filter Tags Row */}
|
|
536
|
+
<View style={{ paddingBottom: 4 }}>
|
|
537
|
+
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{
|
|
538
|
+
paddingHorizontal: 12,
|
|
539
|
+
flexDirection: 'row',
|
|
540
|
+
alignItems: 'center',
|
|
541
|
+
gap: 5,
|
|
542
|
+
paddingVertical: 2,
|
|
543
|
+
}}>
|
|
544
|
+
{/* Search Scopes */}
|
|
545
|
+
{SEARCH_SCOPES.map(s => {
|
|
546
|
+
const isActive = searchScope === s.id;
|
|
547
|
+
return (<TouchableScale key={`scope_${s.id}`} onPress={() => setSearchScope(s.id)} hitSlop={6} style={{
|
|
548
|
+
flexDirection: 'row',
|
|
549
|
+
alignItems: 'center',
|
|
550
|
+
gap: 3.5,
|
|
551
|
+
paddingHorizontal: 7.5,
|
|
552
|
+
paddingVertical: 3.5,
|
|
553
|
+
borderRadius: 6,
|
|
554
|
+
backgroundColor: isActive
|
|
555
|
+
? AppColors.purple
|
|
556
|
+
: AppColors.grayBackground,
|
|
557
|
+
borderWidth: 1,
|
|
558
|
+
borderColor: isActive
|
|
559
|
+
? AppColors.purple
|
|
560
|
+
: AppColors.grayBorderSecondary,
|
|
561
|
+
}}>
|
|
562
|
+
{s.icon(isActive ? AppColors.white : AppColors.grayTextWeak)}
|
|
563
|
+
<Text style={{
|
|
564
|
+
fontFamily: AppFonts.interBold,
|
|
565
|
+
fontSize: 10,
|
|
566
|
+
color: isActive ? AppColors.white : AppColors.grayText,
|
|
567
|
+
}}>
|
|
568
|
+
{s.label}
|
|
569
|
+
</Text>
|
|
570
|
+
</TouchableScale>);
|
|
571
|
+
})}
|
|
572
|
+
|
|
573
|
+
<View style={{
|
|
574
|
+
width: 1,
|
|
575
|
+
height: 14,
|
|
576
|
+
backgroundColor: AppColors.dividerColor,
|
|
577
|
+
marginHorizontal: 3,
|
|
578
|
+
}}/>
|
|
579
|
+
|
|
580
|
+
{/* Quick Query Suggestions */}
|
|
581
|
+
{QUICK_API_QUERY_TAGS.map(q => {
|
|
582
|
+
const isActive = search.includes(q.query);
|
|
583
|
+
return (<TouchableScale key={`quick_${q.query}`} onPress={() => {
|
|
584
|
+
setSearch(prev => {
|
|
585
|
+
if (prev.includes(q.query)) {
|
|
586
|
+
return prev
|
|
587
|
+
.replace(q.query, '')
|
|
588
|
+
.replace(/\s+/g, ' ')
|
|
589
|
+
.trim();
|
|
590
|
+
}
|
|
591
|
+
return `${prev} ${q.query}`.trim();
|
|
592
|
+
});
|
|
593
|
+
}} hitSlop={6} style={{
|
|
594
|
+
flexDirection: 'row',
|
|
595
|
+
alignItems: 'center',
|
|
596
|
+
gap: 3.5,
|
|
597
|
+
paddingHorizontal: 7.5,
|
|
598
|
+
paddingVertical: 3.5,
|
|
599
|
+
borderRadius: 6,
|
|
600
|
+
backgroundColor: isActive
|
|
601
|
+
? `${AppColors.purple}20`
|
|
602
|
+
: AppColors.grayBackground,
|
|
603
|
+
borderWidth: 1,
|
|
604
|
+
borderColor: isActive
|
|
605
|
+
? AppColors.purple
|
|
606
|
+
: AppColors.grayBorderSecondary,
|
|
607
|
+
}}>
|
|
608
|
+
{q.icon(isActive ? AppColors.purple : AppColors.grayTextWeak)}
|
|
609
|
+
<Text style={{
|
|
610
|
+
fontFamily: AppFonts.interMedium,
|
|
611
|
+
fontSize: 10,
|
|
612
|
+
color: isActive
|
|
613
|
+
? AppColors.purple
|
|
614
|
+
: AppColors.grayText,
|
|
615
|
+
}}>
|
|
616
|
+
{q.label}
|
|
617
|
+
</Text>
|
|
618
|
+
</TouchableScale>);
|
|
619
|
+
})}
|
|
620
|
+
</ScrollView>
|
|
621
|
+
</View>
|
|
622
|
+
|
|
533
623
|
{/* Quick Filter Horizontal Chips Bar with Live Counts */}
|
|
534
624
|
<View style={{ marginBottom: 8 }}>
|
|
535
625
|
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{
|
|
@@ -815,7 +905,16 @@ const NetworkTab = React.memo(() => {
|
|
|
815
905
|
? `${logs.length} requests`
|
|
816
906
|
: `${filteredLogs.length} of ${logs.length} filtered requests`}
|
|
817
907
|
</Text>)}
|
|
818
|
-
</View>} ListEmptyComponent={<EmptyState isSearch={search.length > 0 ||
|
|
908
|
+
</View>} ListEmptyComponent={<EmptyState isSearch={search.length > 0 ||
|
|
909
|
+
statusFilters.size > 0 ||
|
|
910
|
+
methodFilters.size > 0} searchQuery={search} customTitle={search.length > 0
|
|
911
|
+
? 'No matching API requests'
|
|
912
|
+
: 'No network activity'} onClearSearch={() => {
|
|
913
|
+
setSearch('');
|
|
914
|
+
setStatusFilters(new Set());
|
|
915
|
+
setMethodFilters(new Set());
|
|
916
|
+
setSearchScope('all');
|
|
917
|
+
}}/>} ListFooterComponent={groupedData.length > 0 ? (<EndOfListFooter count={filteredLogs.length} label="requests"/>) : null} contentContainerStyle={[
|
|
819
918
|
styles.listContent,
|
|
820
919
|
filteredLogs.length === 0 && { flexGrow: 1 },
|
|
821
920
|
]} keyboardShouldPersistTaps="handled"/>
|
|
@@ -396,9 +396,25 @@ const ReduxTab = React.memo(() => {
|
|
|
396
396
|
<View style={styles.searchContainer}>
|
|
397
397
|
<SearchIcon color={AppColors.grayTextWeak} size={15}/>
|
|
398
398
|
<TextInput accessible={true} accessibilityRole="search" accessibilityLabel="Search Redux slices or actions" accessibilityHint="Type to filter state slices in real time" placeholder="Search Redux slices or actions..." placeholderTextColor={AppColors.grayTextWeak} value={reduxSearch} onChangeText={setReduxSearch} style={styles.searchInput} autoCorrect={false} autoCapitalize="none"/>
|
|
399
|
-
{reduxSearch.length > 0 && (<
|
|
400
|
-
<
|
|
401
|
-
|
|
399
|
+
{reduxSearch.length > 0 && (<View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
|
|
400
|
+
<View style={{
|
|
401
|
+
backgroundColor: `${AppColors.purple}20`,
|
|
402
|
+
borderRadius: 10,
|
|
403
|
+
paddingHorizontal: 6,
|
|
404
|
+
paddingVertical: 1.5,
|
|
405
|
+
}}>
|
|
406
|
+
<Text style={{
|
|
407
|
+
color: AppColors.purple,
|
|
408
|
+
fontSize: 9.5,
|
|
409
|
+
fontFamily: AppFonts.interBold,
|
|
410
|
+
}}>
|
|
411
|
+
{filteredSlices.length}
|
|
412
|
+
</Text>
|
|
413
|
+
</View>
|
|
414
|
+
<Pressable accessible={true} accessibilityRole="button" accessibilityLabel="Clear search input" onPress={() => setReduxSearch('')} hitSlop={10}>
|
|
415
|
+
<ClearIcon color={AppColors.grayTextWeak} size={14}/>
|
|
416
|
+
</Pressable>
|
|
417
|
+
</View>)}
|
|
402
418
|
</View>
|
|
403
419
|
|
|
404
420
|
{/* Pause / Resume Live Updates Button */}
|
|
@@ -108,8 +108,8 @@ const SettingsPanel = () => {
|
|
|
108
108
|
bundle: Boolean(tabVisibility?.bundle),
|
|
109
109
|
performance: Boolean(tabVisibility?.performance),
|
|
110
110
|
crash: Boolean(tabVisibility?.crash),
|
|
111
|
-
device: Boolean(tabVisibility?.device
|
|
112
|
-
storage: Boolean(tabVisibility?.storage
|
|
111
|
+
device: Boolean(tabVisibility?.device),
|
|
112
|
+
storage: Boolean(tabVisibility?.storage),
|
|
113
113
|
}));
|
|
114
114
|
// Synchronize staged state with tabVisibility when tabVisibility updates
|
|
115
115
|
useEffect(() => {
|
|
@@ -121,8 +121,8 @@ const SettingsPanel = () => {
|
|
|
121
121
|
bundle: Boolean(tabVisibility?.bundle),
|
|
122
122
|
performance: Boolean(tabVisibility?.performance),
|
|
123
123
|
crash: Boolean(tabVisibility?.crash),
|
|
124
|
-
device: Boolean(tabVisibility?.device
|
|
125
|
-
storage: Boolean(tabVisibility?.storage
|
|
124
|
+
device: Boolean(tabVisibility?.device),
|
|
125
|
+
storage: Boolean(tabVisibility?.storage),
|
|
126
126
|
});
|
|
127
127
|
}, [tabVisibility]);
|
|
128
128
|
const hasUnsavedChanges = useMemo(() => {
|