react-native-inapp-inspector 2.3.8 → 2.3.10
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/AnalyticsTab.js +27 -2
- package/dist/commonjs/components/Inspector/BundleTab.js +3 -0
- package/dist/commonjs/components/Inspector/ConsoleTab.js +24 -1
- package/dist/commonjs/components/Inspector/CrashTab.js +30 -1
- package/dist/commonjs/components/Inspector/FeatureUnderDevNotice.d.ts +7 -0
- package/dist/commonjs/components/Inspector/FeatureUnderDevNotice.js +111 -0
- package/dist/commonjs/components/Inspector/InspectorHeader.js +225 -108
- package/dist/commonjs/components/Inspector/NetworkDetail.js +2 -1
- package/dist/commonjs/components/Inspector/NetworkFilterModal.d.ts +19 -0
- package/dist/commonjs/components/Inspector/NetworkFilterModal.js +648 -0
- package/dist/commonjs/components/Inspector/NetworkTab.js +89 -147
- package/dist/commonjs/components/Inspector/NpmUpdateToast.js +1 -1
- package/dist/commonjs/components/Inspector/PerformanceTab.js +3 -0
- package/dist/commonjs/components/Inspector/ReduxTab.js +24 -1
- package/dist/commonjs/components/Inspector/SettingsPanel.js +73 -88
- package/dist/commonjs/components/Inspector/StorageTab.js +37 -11
- package/dist/commonjs/components/Inspector/UpdateAvailableModal.js +2 -0
- package/dist/commonjs/components/LogCard.js +35 -26
- package/dist/commonjs/components/NetworkIcons.d.ts +1 -0
- package/dist/commonjs/components/NetworkIcons.js +6 -1
- package/dist/commonjs/constants/index.js +3 -0
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/helpers/telemetry.js +34 -0
- package/dist/commonjs/index.js +3 -0
- package/dist/commonjs/styles/index.d.ts +11 -0
- package/dist/commonjs/styles/index.js +20 -5
- package/dist/commonjs/types/enums.d.ts +3 -0
- package/dist/commonjs/types/enums.js +3 -0
- package/dist/esm/components/Inspector/AnalyticsTab.js +29 -4
- package/dist/esm/components/Inspector/BundleTab.js +3 -0
- package/dist/esm/components/Inspector/ConsoleTab.js +26 -3
- package/dist/esm/components/Inspector/CrashTab.js +32 -3
- package/dist/esm/components/Inspector/FeatureUnderDevNotice.d.ts +7 -0
- package/dist/esm/components/Inspector/FeatureUnderDevNotice.js +74 -0
- package/dist/esm/components/Inspector/InspectorHeader.js +226 -109
- package/dist/esm/components/Inspector/NetworkDetail.js +2 -1
- package/dist/esm/components/Inspector/NetworkFilterModal.d.ts +19 -0
- package/dist/esm/components/Inspector/NetworkFilterModal.js +607 -0
- package/dist/esm/components/Inspector/NetworkTab.js +91 -149
- package/dist/esm/components/Inspector/NpmUpdateToast.js +1 -1
- package/dist/esm/components/Inspector/PerformanceTab.js +3 -0
- package/dist/esm/components/Inspector/ReduxTab.js +26 -3
- package/dist/esm/components/Inspector/SettingsPanel.js +73 -88
- package/dist/esm/components/Inspector/StorageTab.js +39 -13
- package/dist/esm/components/Inspector/UpdateAvailableModal.js +2 -0
- package/dist/esm/components/LogCard.js +36 -27
- package/dist/esm/components/NetworkIcons.d.ts +1 -0
- package/dist/esm/components/NetworkIcons.js +4 -0
- package/dist/esm/constants/index.js +3 -0
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/helpers/telemetry.js +34 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/styles/index.d.ts +11 -0
- package/dist/esm/styles/index.js +20 -5
- package/dist/esm/types/enums.d.ts +3 -0
- package/dist/esm/types/enums.js +3 -0
- package/package.json +2 -2
- package/src/components/Inspector/AnalyticsTab.tsx +71 -43
- package/src/components/Inspector/BundleTab.tsx +3 -0
- package/src/components/Inspector/ConsoleTab.tsx +28 -1
- package/src/components/Inspector/CrashTab.tsx +45 -14
- package/src/components/Inspector/FeatureUnderDevNotice.tsx +94 -0
- package/src/components/Inspector/InspectorHeader.tsx +257 -115
- package/src/components/Inspector/NetworkDetail.tsx +2 -1
- package/src/components/Inspector/NetworkFilterModal.tsx +710 -0
- package/src/components/Inspector/NetworkTab.tsx +127 -204
- package/src/components/Inspector/NpmUpdateToast.tsx +1 -1
- package/src/components/Inspector/PerformanceTab.tsx +3 -0
- package/src/components/Inspector/ReduxTab.tsx +28 -1
- package/src/components/Inspector/SettingsPanel.tsx +93 -132
- package/src/components/Inspector/StorageTab.tsx +56 -27
- package/src/components/Inspector/UpdateAvailableModal.tsx +2 -0
- package/src/components/LogCard.tsx +43 -31
- package/src/components/NetworkIcons.tsx +27 -0
- package/src/constants/index.ts +3 -0
- package/src/constants/version.ts +1 -1
- package/src/helpers/telemetry.ts +36 -0
- package/src/index.tsx +5 -0
- package/src/styles/index.ts +20 -5
- package/src/types/enums.ts +3 -0
|
@@ -1,99 +1,53 @@
|
|
|
1
1
|
import React, { useCallback, useMemo, useRef } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { FlatList, Pressable, ScrollView, Text, TextInput, View, } from 'react-native';
|
|
3
3
|
import { animateNextLayout, useInspector } from './InspectorContext';
|
|
4
|
-
import useAccordion from '../../customHooks/useAccordion';
|
|
5
4
|
import TouchableScale from '../TouchableScale';
|
|
6
5
|
import AnimatedEntrance from '../AnimatedEntrance';
|
|
7
6
|
import DomainHeader from '../DomainHeader';
|
|
8
7
|
import LogCard from '../LogCard';
|
|
9
8
|
import EmptyState from '../EmptyState';
|
|
10
9
|
import EndOfListFooter from '../EndOfListFooter';
|
|
10
|
+
import NetworkFilterModal from './NetworkFilterModal';
|
|
11
11
|
import styles from '../../styles';
|
|
12
12
|
import { AppColors } from '../../styles/AppColors';
|
|
13
13
|
import { AppFonts } from '../../styles/AppFonts';
|
|
14
|
-
import {
|
|
14
|
+
import { METHOD_COLORS } from '../../constants';
|
|
15
15
|
import { useTranslation } from '../../i18n';
|
|
16
16
|
import { SearchIcon, ClearIcon, TrashIcon, SortArrowIcon, FilterIcon, ChevronIcon, CircleCheckIcon, CircleXIcon, CircleAlertIcon, LayersIcon, HeaderPauseIcon, ClockIcon, GlobeIcon, RequestIcon, ResponseIcon, HeadersIcon, AtomIcon, BoltIcon, ShieldAlertIcon, } from '../NetworkIcons';
|
|
17
|
-
const STATUS_META = {
|
|
18
|
-
ALL: { color: AppColors.grayText, Icon: LayersIcon },
|
|
19
|
-
'2xx': { color: AppColors.greenColor, Icon: CircleCheckIcon },
|
|
20
|
-
'200': { color: AppColors.greenColor, Icon: CircleCheckIcon },
|
|
21
|
-
'3xx': { color: AppColors.warningIconGold, Icon: CircleAlertIcon },
|
|
22
|
-
'300': { color: AppColors.warningIconGold, Icon: CircleAlertIcon },
|
|
23
|
-
'4xx': { color: AppColors.darkOrange, Icon: CircleAlertIcon },
|
|
24
|
-
'400': { color: AppColors.darkOrange, Icon: CircleAlertIcon },
|
|
25
|
-
'404': { color: AppColors.darkOrange, Icon: CircleAlertIcon },
|
|
26
|
-
'5xx': { color: AppColors.errorColor, Icon: CircleXIcon },
|
|
27
|
-
'500': { color: AppColors.errorColor, Icon: CircleXIcon },
|
|
28
|
-
Failed: { color: AppColors.errorColor, Icon: CircleXIcon },
|
|
29
|
-
};
|
|
30
|
-
const getStatusMeta = (filter) => {
|
|
31
|
-
if (STATUS_META[filter])
|
|
32
|
-
return STATUS_META[filter];
|
|
33
|
-
const num = parseInt(filter, 10);
|
|
34
|
-
if (!isNaN(num)) {
|
|
35
|
-
if (num >= 200 && num < 300)
|
|
36
|
-
return { color: AppColors.greenColor, Icon: CircleCheckIcon };
|
|
37
|
-
if (num >= 300 && num < 400)
|
|
38
|
-
return { color: AppColors.warningIconGold, Icon: CircleAlertIcon };
|
|
39
|
-
if (num >= 400 && num < 500)
|
|
40
|
-
return { color: AppColors.darkOrange, Icon: CircleAlertIcon };
|
|
41
|
-
if (num >= 500)
|
|
42
|
-
return { color: AppColors.errorColor, Icon: CircleXIcon };
|
|
43
|
-
}
|
|
44
|
-
if (filter.startsWith('2'))
|
|
45
|
-
return { color: AppColors.greenColor, Icon: CircleCheckIcon };
|
|
46
|
-
if (filter.startsWith('3'))
|
|
47
|
-
return { color: AppColors.warningIconGold, Icon: CircleAlertIcon };
|
|
48
|
-
if (filter.startsWith('4'))
|
|
49
|
-
return { color: AppColors.darkOrange, Icon: CircleAlertIcon };
|
|
50
|
-
if (filter.startsWith('5'))
|
|
51
|
-
return { color: AppColors.errorColor, Icon: CircleXIcon };
|
|
52
|
-
return { color: AppColors.grayText, Icon: LayersIcon };
|
|
53
|
-
};
|
|
54
|
-
const FilterChip = React.memo(({ label, color, Icon, badge, active, onPress, }) => {
|
|
55
|
-
const iconColor = active ? color : AppColors.grayTextWeak;
|
|
56
|
-
return (<TouchableScale style={styles.statusFilterWrap} onPress={onPress} hitSlop={10}>
|
|
57
|
-
<View style={[
|
|
58
|
-
styles.statusFilterChip,
|
|
59
|
-
{
|
|
60
|
-
paddingHorizontal: 9,
|
|
61
|
-
paddingVertical: 5,
|
|
62
|
-
borderRadius: 7,
|
|
63
|
-
borderWidth: 1,
|
|
64
|
-
borderColor: active ? color : `${AppColors.grayBorderSecondary}`,
|
|
65
|
-
backgroundColor: active ? `${color}14` : AppColors.grayBackground,
|
|
66
|
-
},
|
|
67
|
-
active && {
|
|
68
|
-
shadowColor: color,
|
|
69
|
-
shadowOffset: { width: 0, height: 1 },
|
|
70
|
-
shadowOpacity: 0.2,
|
|
71
|
-
shadowRadius: 2,
|
|
72
|
-
elevation: 1.5,
|
|
73
|
-
},
|
|
74
|
-
]}>
|
|
75
|
-
{badge != null ? (<View style={[
|
|
76
|
-
styles.methodBadgeMini,
|
|
77
|
-
{ backgroundColor: badge },
|
|
78
|
-
]}/>) : (Icon && (<Icon color={iconColor} size={13}/>))}
|
|
79
|
-
<Text numberOfLines={1} style={[
|
|
80
|
-
styles.statusFilterText,
|
|
81
|
-
{
|
|
82
|
-
color: active ? color : AppColors.grayText,
|
|
83
|
-
fontFamily: active ? AppFonts.interBold : AppFonts.interMedium,
|
|
84
|
-
},
|
|
85
|
-
]}>
|
|
86
|
-
{label}
|
|
87
|
-
</Text>
|
|
88
|
-
</View>
|
|
89
|
-
</TouchableScale>);
|
|
90
|
-
});
|
|
91
17
|
const NetworkTab = React.memo(() => {
|
|
92
18
|
const { groupedData, search, setSearch, searchScope, setSearchScope, isRegexSearch, setIsRegexSearch, isCaseSensitive, setIsCaseSensitive, quickFilter, setQuickFilter, handleDelete, selectedLogs, sortOrder, setSortOrder, statusFilters, setStatusFilters, methodFilters, setMethodFilters, availableMethods, filteredLogs, logs, toggleSectionFilter, toggleSectionCollapse, minStart, totalRange, newLogIds, toggleSelect, setSelected, isNetworkPaused, setIsNetworkPaused, } = useInspector();
|
|
93
19
|
const { t } = useTranslation();
|
|
94
|
-
const filtersAccordion = useAccordion(false, 300, 260);
|
|
95
20
|
const apisListRef = useRef(null);
|
|
21
|
+
const [isFilterModalOpen, setIsFilterModalOpen] = React.useState(false);
|
|
96
22
|
const [isSearchFocused, setIsSearchFocused] = React.useState(false);
|
|
23
|
+
const modalFilterState = useMemo(() => ({
|
|
24
|
+
statusCodes: statusFilters.size > 0 ? new Set(statusFilters) : new Set(['all']),
|
|
25
|
+
methods: methodFilters.size > 0 ? new Set(methodFilters) : new Set(['all']),
|
|
26
|
+
latency: 'all',
|
|
27
|
+
protocol: 'all',
|
|
28
|
+
sortBy: sortOrder === 'newest' ? 'time_desc' : 'time_asc',
|
|
29
|
+
}), [statusFilters, methodFilters, sortOrder]);
|
|
30
|
+
const handleApplyNetworkFilters = useCallback((newFilters) => {
|
|
31
|
+
if (newFilters.statusCodes.has('all') ||
|
|
32
|
+
newFilters.statusCodes.size === 0) {
|
|
33
|
+
setStatusFilters(new Set());
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
setStatusFilters(new Set(newFilters.statusCodes));
|
|
37
|
+
}
|
|
38
|
+
if (newFilters.methods.has('all') || newFilters.methods.size === 0) {
|
|
39
|
+
setMethodFilters(new Set());
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
setMethodFilters(new Set([...newFilters.methods].map(m => m)));
|
|
43
|
+
}
|
|
44
|
+
if (newFilters.sortBy === 'time_asc') {
|
|
45
|
+
setSortOrder('oldest');
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
setSortOrder('newest');
|
|
49
|
+
}
|
|
50
|
+
}, [setStatusFilters, setMethodFilters, setSortOrder]);
|
|
97
51
|
const quickCounts = useMemo(() => {
|
|
98
52
|
let errorCount = 0;
|
|
99
53
|
let successCount = 0;
|
|
@@ -520,10 +474,12 @@ const NetworkTab = React.memo(() => {
|
|
|
520
474
|
|
|
521
475
|
<TouchableScale style={[
|
|
522
476
|
styles.toolbarBtn,
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
477
|
+
(statusFilters.size > 0 || methodFilters.size > 0) && {
|
|
478
|
+
borderColor: AppColors.purple,
|
|
479
|
+
backgroundColor: `${AppColors.purple}15`,
|
|
480
|
+
},
|
|
481
|
+
]} onPress={() => setIsFilterModalOpen(true)} hitSlop={6}>
|
|
482
|
+
<FilterIcon color={statusFilters.size > 0 || methodFilters.size > 0
|
|
527
483
|
? AppColors.purple
|
|
528
484
|
: AppColors.grayTextStrong} size={16}/>
|
|
529
485
|
{(statusFilters.size > 0 ||
|
|
@@ -685,6 +641,51 @@ const NetworkTab = React.memo(() => {
|
|
|
685
641
|
</View>
|
|
686
642
|
</TouchableScale>);
|
|
687
643
|
})}
|
|
644
|
+
|
|
645
|
+
<TouchableScale onPress={() => setIsFilterModalOpen(true)}>
|
|
646
|
+
<View style={{
|
|
647
|
+
flexDirection: 'row',
|
|
648
|
+
alignItems: 'center',
|
|
649
|
+
paddingHorizontal: 10,
|
|
650
|
+
paddingVertical: 4.5,
|
|
651
|
+
borderRadius: 8,
|
|
652
|
+
backgroundColor: statusFilters.size > 0 || methodFilters.size > 0
|
|
653
|
+
? `${AppColors.purple}20`
|
|
654
|
+
: AppColors.grayBackground,
|
|
655
|
+
borderWidth: 1,
|
|
656
|
+
borderColor: statusFilters.size > 0 || methodFilters.size > 0
|
|
657
|
+
? AppColors.purple
|
|
658
|
+
: AppColors.grayBorderSecondary,
|
|
659
|
+
gap: 5,
|
|
660
|
+
}}>
|
|
661
|
+
<FilterIcon size={11} color={statusFilters.size > 0 || methodFilters.size > 0
|
|
662
|
+
? AppColors.purple
|
|
663
|
+
: AppColors.grayText}/>
|
|
664
|
+
<Text style={{
|
|
665
|
+
fontFamily: AppFonts.interBold,
|
|
666
|
+
fontSize: 10.5,
|
|
667
|
+
color: statusFilters.size > 0 || methodFilters.size > 0
|
|
668
|
+
? AppColors.purple
|
|
669
|
+
: AppColors.grayText,
|
|
670
|
+
}}>
|
|
671
|
+
More Filters
|
|
672
|
+
</Text>
|
|
673
|
+
{(statusFilters.size > 0 || methodFilters.size > 0) && (<View style={{
|
|
674
|
+
backgroundColor: AppColors.purple,
|
|
675
|
+
paddingHorizontal: 4.5,
|
|
676
|
+
paddingVertical: 1,
|
|
677
|
+
borderRadius: 6,
|
|
678
|
+
}}>
|
|
679
|
+
<Text style={{
|
|
680
|
+
fontFamily: AppFonts.interBold,
|
|
681
|
+
fontSize: 8.5,
|
|
682
|
+
color: AppColors.white,
|
|
683
|
+
}}>
|
|
684
|
+
{statusFilters.size + methodFilters.size}
|
|
685
|
+
</Text>
|
|
686
|
+
</View>)}
|
|
687
|
+
</View>
|
|
688
|
+
</TouchableScale>
|
|
688
689
|
</ScrollView>
|
|
689
690
|
</View>
|
|
690
691
|
|
|
@@ -833,86 +834,24 @@ const NetworkTab = React.memo(() => {
|
|
|
833
834
|
</TouchableScale>
|
|
834
835
|
</View>)}
|
|
835
836
|
|
|
836
|
-
<Animated.View style={[
|
|
837
|
-
filtersAccordion.bodyStyle,
|
|
838
|
-
{ overflow: 'hidden' },
|
|
839
|
-
]}>
|
|
840
|
-
<View style={styles.filtersContainer}>
|
|
841
|
-
<Text style={styles.filtersHeading}>
|
|
842
|
-
STATUS
|
|
843
|
-
</Text>
|
|
844
|
-
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.statusRowContent}>
|
|
845
|
-
{STATUS_FILTERS.map(filter => {
|
|
846
|
-
const isAll = filter === 'ALL';
|
|
847
|
-
const active = isAll
|
|
848
|
-
? statusFilters.size === 0
|
|
849
|
-
: statusFilters.has(filter);
|
|
850
|
-
const meta = getStatusMeta(filter);
|
|
851
|
-
return (<FilterChip key={filter} label={filter} color={meta.color} Icon={meta.Icon} active={active} onPress={() => {
|
|
852
|
-
if (isAll) {
|
|
853
|
-
setStatusFilters(new Set());
|
|
854
|
-
}
|
|
855
|
-
else {
|
|
856
|
-
setStatusFilters(prev => {
|
|
857
|
-
const next = new Set(prev);
|
|
858
|
-
next.has(filter)
|
|
859
|
-
? next.delete(filter)
|
|
860
|
-
: next.add(filter);
|
|
861
|
-
return next;
|
|
862
|
-
});
|
|
863
|
-
}
|
|
864
|
-
}}/>);
|
|
865
|
-
})}
|
|
866
|
-
</ScrollView>
|
|
867
|
-
|
|
868
|
-
<Text style={[
|
|
869
|
-
styles.filtersHeading,
|
|
870
|
-
{ marginTop: 16 },
|
|
871
|
-
]}>
|
|
872
|
-
METHOD
|
|
873
|
-
</Text>
|
|
874
|
-
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.statusRowContent}>
|
|
875
|
-
{availableMethods.map(filter => {
|
|
876
|
-
const isAll = filter === 'ALL';
|
|
877
|
-
const active = isAll
|
|
878
|
-
? methodFilters.size === 0
|
|
879
|
-
: methodFilters.has(filter);
|
|
880
|
-
const methodColor = METHOD_COLORS[filter] ??
|
|
881
|
-
METHOD_COLORS.ALL;
|
|
882
|
-
return (<FilterChip key={filter} label={filter} color={methodColor} badge={methodColor} active={active} onPress={() => {
|
|
883
|
-
if (isAll) {
|
|
884
|
-
setMethodFilters(new Set());
|
|
885
|
-
}
|
|
886
|
-
else {
|
|
887
|
-
setMethodFilters(prev => {
|
|
888
|
-
const next = new Set(prev);
|
|
889
|
-
next.has(filter)
|
|
890
|
-
? next.delete(filter)
|
|
891
|
-
: next.add(filter);
|
|
892
|
-
return next;
|
|
893
|
-
});
|
|
894
|
-
}
|
|
895
|
-
}}/>);
|
|
896
|
-
})}
|
|
897
|
-
</ScrollView>
|
|
898
|
-
</View>
|
|
899
|
-
</Animated.View>
|
|
900
|
-
|
|
901
837
|
{(search ||
|
|
902
838
|
statusFilters.size > 0 ||
|
|
903
|
-
methodFilters.size > 0
|
|
839
|
+
methodFilters.size > 0 ||
|
|
840
|
+
quickFilter !== 'all') && (<Text style={styles.resultCount}>
|
|
904
841
|
{filteredLogs.length === logs.length
|
|
905
842
|
? `${logs.length} requests`
|
|
906
843
|
: `${filteredLogs.length} of ${logs.length} filtered requests`}
|
|
907
844
|
</Text>)}
|
|
908
845
|
</View>} ListEmptyComponent={<EmptyState isSearch={search.length > 0 ||
|
|
909
846
|
statusFilters.size > 0 ||
|
|
910
|
-
methodFilters.size > 0
|
|
847
|
+
methodFilters.size > 0 ||
|
|
848
|
+
quickFilter !== 'all'} searchQuery={search} customTitle={search.length > 0
|
|
911
849
|
? 'No matching API requests'
|
|
912
850
|
: 'No network activity'} onClearSearch={() => {
|
|
913
851
|
setSearch('');
|
|
914
852
|
setStatusFilters(new Set());
|
|
915
853
|
setMethodFilters(new Set());
|
|
854
|
+
setQuickFilter('all');
|
|
916
855
|
setSearchScope('all');
|
|
917
856
|
}}/>} ListFooterComponent={groupedData.length > 0 ? (<EndOfListFooter count={filteredLogs.length} label="requests"/>) : null} contentContainerStyle={[
|
|
918
857
|
styles.listContent,
|
|
@@ -940,6 +879,9 @@ const NetworkTab = React.memo(() => {
|
|
|
940
879
|
<ChevronIcon color={AppColors.white} size={18}/>
|
|
941
880
|
</View>
|
|
942
881
|
</TouchableScale>
|
|
882
|
+
|
|
883
|
+
|
|
884
|
+
<NetworkFilterModal visible={isFilterModalOpen} onClose={() => setIsFilterModalOpen(false)} filters={modalFilterState} onApply={handleApplyNetworkFilters} searchQuery={search}/>
|
|
943
885
|
</View>);
|
|
944
886
|
});
|
|
945
887
|
export default NetworkTab;
|
|
@@ -92,7 +92,7 @@ export const NpmUpdateToast = () => {
|
|
|
92
92
|
Linking.openURL('https://www.npmjs.com/package/react-native-inapp-inspector').catch(() => { });
|
|
93
93
|
handleDismiss();
|
|
94
94
|
};
|
|
95
|
-
if (!showUpdateToast || !visible || !updateAvailable || !latestNpmVersion)
|
|
95
|
+
if (typeof __DEV__ === 'undefined' || !__DEV__ || !showUpdateToast || !visible || !updateAvailable || !latestNpmVersion)
|
|
96
96
|
return null;
|
|
97
97
|
const progressWidth = progressAnim.interpolate({
|
|
98
98
|
inputRange: [0, 1],
|
|
@@ -5,6 +5,7 @@ import TouchableScale from '../TouchableScale';
|
|
|
5
5
|
import CopyButton from '../CopyButton';
|
|
6
6
|
import HighlightText from '../HighlightText';
|
|
7
7
|
import EndOfListFooter from '../EndOfListFooter';
|
|
8
|
+
import FeatureUnderDevNotice from './FeatureUnderDevNotice';
|
|
8
9
|
import { AppColors } from '../../styles/AppColors';
|
|
9
10
|
import { AppFonts } from '../../styles/AppFonts';
|
|
10
11
|
import { useTranslation } from '../../i18n';
|
|
@@ -356,6 +357,8 @@ const PerformanceTab = React.memo(() => {
|
|
|
356
357
|
</TouchableScale>
|
|
357
358
|
</View>
|
|
358
359
|
|
|
360
|
+
<FeatureUnderDevNotice featureName="Performance & FPS Profiler"/>
|
|
361
|
+
|
|
359
362
|
|
|
360
363
|
<View style={perfStyles.categoryScrollWrapper}>
|
|
361
364
|
<ScrollView horizontal showsHorizontalScrollIndicator={false} style={perfStyles.categoryScrollView} contentContainerStyle={perfStyles.categoryScroll}>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback, useMemo, useState } from 'react';
|
|
1
|
+
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import { FlatList, Pressable, StyleSheet, Text, TextInput, TouchableOpacity, View, } from 'react-native';
|
|
3
3
|
import { useTranslation } from '../../i18n';
|
|
4
4
|
import { useInspector } from './InspectorContext';
|
|
@@ -12,7 +12,7 @@ import styles from '../../styles';
|
|
|
12
12
|
import { AppColors } from '../../styles/AppColors';
|
|
13
13
|
import { AppFonts } from '../../styles/AppFonts';
|
|
14
14
|
import { getActionHistory, getReduxAutoRefresh, setReduxAutoRefresh, } from '../../customHooks/reduxLogger';
|
|
15
|
-
import { TerminalIcon, SearchIcon, ClearIcon, LayersIcon, ClockIcon, ForwardChevronIcon, SortArrowIcon, BoltIcon, AtomIcon, ScreenIcon, ListenerIcon, DocIcon, LoadingSpinnerIcon, CircleAlertIcon, TextAaIcon, HeaderPauseIcon, } from '../NetworkIcons';
|
|
15
|
+
import { TerminalIcon, SearchIcon, ClearIcon, LayersIcon, ClockIcon, ForwardChevronIcon, SortArrowIcon, BoltIcon, AtomIcon, ScreenIcon, ListenerIcon, DocIcon, LoadingSpinnerIcon, CircleAlertIcon, TextAaIcon, HeaderPauseIcon, ChevronIcon, } from '../NetworkIcons';
|
|
16
16
|
const getOriginBadge = (originType) => {
|
|
17
17
|
switch (originType) {
|
|
18
18
|
case 'saga':
|
|
@@ -60,6 +60,7 @@ const getOriginBadge = (originType) => {
|
|
|
60
60
|
const ReduxTab = React.memo(() => {
|
|
61
61
|
const { t } = useTranslation();
|
|
62
62
|
const { reduxState, reduxLastActionMap, reduxSearch, setReduxSearch, setSelectedReduxSlice, } = useInspector();
|
|
63
|
+
const listRef = useRef(null);
|
|
63
64
|
const [sortMode, setSortMode] = useState('latest');
|
|
64
65
|
const [isReduxPaused, setIsReduxPaused] = useState(!getReduxAutoRefresh());
|
|
65
66
|
const handleTogglePause = useCallback(() => {
|
|
@@ -453,10 +454,32 @@ const ReduxTab = React.memo(() => {
|
|
|
453
454
|
</View>
|
|
454
455
|
|
|
455
456
|
|
|
456
|
-
<FlatList data={filteredSlices} keyExtractor={item => item.id} renderItem={renderSliceItem} initialNumToRender={12} maxToRenderPerBatch={8} windowSize={5} removeClippedSubviews={true} renderToHardwareTextureAndroid={true} ListEmptyComponent={<EmptyState isSearch={reduxSearch.length > 0}/>} ListFooterComponent={filteredSlices.length > 0 ? (<EndOfListFooter count={filteredSlices.length} label="slices"/>) : null} contentContainerStyle={[
|
|
457
|
+
<FlatList ref={listRef} data={filteredSlices} keyExtractor={item => item.id} renderItem={renderSliceItem} initialNumToRender={12} maxToRenderPerBatch={8} windowSize={5} removeClippedSubviews={true} renderToHardwareTextureAndroid={true} ListEmptyComponent={<EmptyState isSearch={reduxSearch.length > 0}/>} ListFooterComponent={filteredSlices.length > 0 ? (<EndOfListFooter count={filteredSlices.length} label="slices"/>) : null} contentContainerStyle={[
|
|
457
458
|
reduxTabStyles.listContent,
|
|
458
459
|
filteredSlices.length === 0 && { flexGrow: 1 },
|
|
459
460
|
]} keyboardShouldPersistTaps="handled"/>
|
|
461
|
+
|
|
462
|
+
<TouchableScale onPress={() => {
|
|
463
|
+
try {
|
|
464
|
+
listRef.current?.scrollToOffset({
|
|
465
|
+
offset: 0,
|
|
466
|
+
animated: true,
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
catch {
|
|
470
|
+
try {
|
|
471
|
+
listRef.current?.scrollToIndex({
|
|
472
|
+
index: 0,
|
|
473
|
+
animated: true,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
catch { }
|
|
477
|
+
}
|
|
478
|
+
}} hitSlop={12} style={styles.scrollTopBtn}>
|
|
479
|
+
<View style={{ transform: [{ rotate: '180deg' }] }}>
|
|
480
|
+
<ChevronIcon color={AppColors.white} size={18}/>
|
|
481
|
+
</View>
|
|
482
|
+
</TouchableScale>
|
|
460
483
|
</View>);
|
|
461
484
|
});
|
|
462
485
|
const reduxTabStyles = StyleSheet.create({
|
|
@@ -275,11 +275,12 @@ const SettingsPanel = () => {
|
|
|
275
275
|
}}>
|
|
276
276
|
<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, paddingBottom: 100, gap: 14 }} showsVerticalScrollIndicator={false}>
|
|
277
277
|
|
|
278
|
+
|
|
278
279
|
<View style={{
|
|
279
|
-
flexDirection: 'row',
|
|
280
280
|
backgroundColor: AppColors.primaryLight,
|
|
281
|
-
borderRadius:
|
|
282
|
-
|
|
281
|
+
borderRadius: 13,
|
|
282
|
+
paddingVertical: 4,
|
|
283
|
+
paddingHorizontal: 4,
|
|
283
284
|
borderWidth: 1,
|
|
284
285
|
borderColor: AppColors.grayBorderSecondary,
|
|
285
286
|
shadowColor: AppColors.black,
|
|
@@ -287,95 +288,79 @@ const SettingsPanel = () => {
|
|
|
287
288
|
shadowRadius: 4,
|
|
288
289
|
shadowOffset: { width: 0, height: 2 },
|
|
289
290
|
}}>
|
|
290
|
-
<
|
|
291
|
-
animateNextLayout();
|
|
292
|
-
setSettingsActiveSubTab('module');
|
|
293
|
-
}} style={{
|
|
294
|
-
flex: 1,
|
|
295
|
-
paddingVertical: 9,
|
|
291
|
+
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{
|
|
296
292
|
flexDirection: 'row',
|
|
297
293
|
alignItems: 'center',
|
|
298
|
-
justifyContent: 'center',
|
|
299
294
|
gap: 6,
|
|
300
|
-
|
|
301
|
-
backgroundColor: settingsActiveSubTab === 'module'
|
|
302
|
-
? AppColors.purple
|
|
303
|
-
: 'transparent',
|
|
304
|
-
}}>
|
|
305
|
-
<LayersIcon color={settingsActiveSubTab === 'module'
|
|
306
|
-
? AppColors.white
|
|
307
|
-
: AppColors.grayText} size={13}/>
|
|
308
|
-
<Text style={{
|
|
309
|
-
fontFamily: AppFonts.interBold,
|
|
310
|
-
fontSize: 12.5,
|
|
311
|
-
lineHeight: 16,
|
|
312
|
-
color: settingsActiveSubTab === 'module'
|
|
313
|
-
? AppColors.white
|
|
314
|
-
: AppColors.grayText,
|
|
315
|
-
}}>
|
|
316
|
-
{t('settings.modulesAndTools')}
|
|
317
|
-
</Text>
|
|
318
|
-
</TouchableScale>
|
|
319
|
-
|
|
320
|
-
<TouchableScale accessible={true} accessibilityRole="tab" accessibilityLabel={t('settings.uiPreferences')} accessibilityState={{ selected: settingsActiveSubTab === 'ui' }} onPress={() => {
|
|
321
|
-
animateNextLayout();
|
|
322
|
-
setSettingsActiveSubTab('ui');
|
|
323
|
-
}} style={{
|
|
324
|
-
flex: 1,
|
|
325
|
-
paddingVertical: 9,
|
|
326
|
-
flexDirection: 'row',
|
|
327
|
-
alignItems: 'center',
|
|
328
|
-
justifyContent: 'center',
|
|
329
|
-
gap: 5,
|
|
330
|
-
borderRadius: 9,
|
|
331
|
-
backgroundColor: settingsActiveSubTab === 'ui'
|
|
332
|
-
? AppColors.purple
|
|
333
|
-
: 'transparent',
|
|
334
|
-
}}>
|
|
335
|
-
<ScreenIcon color={settingsActiveSubTab === 'ui'
|
|
336
|
-
? AppColors.white
|
|
337
|
-
: AppColors.grayText} size={13}/>
|
|
338
|
-
<Text numberOfLines={1} style={{
|
|
339
|
-
fontFamily: AppFonts.interBold,
|
|
340
|
-
fontSize: 12,
|
|
341
|
-
lineHeight: 16,
|
|
342
|
-
color: settingsActiveSubTab === 'ui'
|
|
343
|
-
? AppColors.white
|
|
344
|
-
: AppColors.grayText,
|
|
345
|
-
}}>
|
|
346
|
-
{t('settings.uiPreferences')}
|
|
347
|
-
</Text>
|
|
348
|
-
</TouchableScale>
|
|
349
|
-
|
|
350
|
-
<TouchableScale accessible={true} accessibilityRole="tab" accessibilityLabel={t('settings.ramLimits')} accessibilityState={{ selected: settingsActiveSubTab === 'limits' }} onPress={() => {
|
|
351
|
-
animateNextLayout();
|
|
352
|
-
setSettingsActiveSubTab('limits');
|
|
353
|
-
}} style={{
|
|
354
|
-
flex: 1,
|
|
355
|
-
paddingVertical: 9,
|
|
356
|
-
flexDirection: 'row',
|
|
357
|
-
alignItems: 'center',
|
|
358
|
-
justifyContent: 'center',
|
|
359
|
-
gap: 5,
|
|
360
|
-
borderRadius: 9,
|
|
361
|
-
backgroundColor: settingsActiveSubTab === 'limits'
|
|
362
|
-
? AppColors.purple
|
|
363
|
-
: 'transparent',
|
|
364
|
-
}}>
|
|
365
|
-
<BrainIcon color={settingsActiveSubTab === 'limits'
|
|
366
|
-
? AppColors.white
|
|
367
|
-
: AppColors.grayText} size={13}/>
|
|
368
|
-
<Text numberOfLines={1} style={{
|
|
369
|
-
fontFamily: AppFonts.interBold,
|
|
370
|
-
fontSize: 12,
|
|
371
|
-
lineHeight: 16,
|
|
372
|
-
color: settingsActiveSubTab === 'limits'
|
|
373
|
-
? AppColors.white
|
|
374
|
-
: AppColors.grayText,
|
|
295
|
+
paddingHorizontal: 2,
|
|
375
296
|
}}>
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
297
|
+
{[
|
|
298
|
+
{
|
|
299
|
+
key: 'module',
|
|
300
|
+
label: t('settings.modulesAndTools', 'Modules & Tools'),
|
|
301
|
+
Icon: LayersIcon,
|
|
302
|
+
badge: `${allModules.filter(m => stagedTabVisibility?.[m.key] || m.key === 'apis').length}/${allModules.length}`,
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
key: 'ui',
|
|
306
|
+
label: t('settings.uiPreferences', 'UI Preferences'),
|
|
307
|
+
Icon: ScreenIcon,
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
key: 'limits',
|
|
311
|
+
label: t('settings.ramLimits', 'RAM & Limits'),
|
|
312
|
+
Icon: BrainIcon,
|
|
313
|
+
},
|
|
314
|
+
].map(tab => {
|
|
315
|
+
const isActive = settingsActiveSubTab === tab.key;
|
|
316
|
+
const IconComp = tab.Icon;
|
|
317
|
+
return (<TouchableScale key={tab.key} accessible={true} accessibilityRole="tab" accessibilityLabel={tab.label} accessibilityState={{ selected: isActive }} onPress={() => {
|
|
318
|
+
animateNextLayout();
|
|
319
|
+
setSettingsActiveSubTab(tab.key);
|
|
320
|
+
}} style={{
|
|
321
|
+
paddingVertical: 8,
|
|
322
|
+
paddingHorizontal: 14,
|
|
323
|
+
flexDirection: 'row',
|
|
324
|
+
alignItems: 'center',
|
|
325
|
+
justifyContent: 'center',
|
|
326
|
+
gap: 6,
|
|
327
|
+
borderRadius: 9,
|
|
328
|
+
backgroundColor: isActive
|
|
329
|
+
? AppColors.purple
|
|
330
|
+
: 'transparent',
|
|
331
|
+
}}>
|
|
332
|
+
<IconComp color={isActive ? AppColors.white : AppColors.grayText} size={13}/>
|
|
333
|
+
<Text style={{
|
|
334
|
+
fontFamily: AppFonts.interBold,
|
|
335
|
+
fontSize: 12.5,
|
|
336
|
+
lineHeight: 16,
|
|
337
|
+
color: isActive
|
|
338
|
+
? AppColors.white
|
|
339
|
+
: AppColors.grayText,
|
|
340
|
+
}}>
|
|
341
|
+
{tab.label}
|
|
342
|
+
</Text>
|
|
343
|
+
{tab.badge && (<View style={{
|
|
344
|
+
backgroundColor: isActive
|
|
345
|
+
? 'rgba(255,255,255,0.25)'
|
|
346
|
+
: `${AppColors.purple}18`,
|
|
347
|
+
paddingHorizontal: 5.5,
|
|
348
|
+
paddingVertical: 1,
|
|
349
|
+
borderRadius: 10,
|
|
350
|
+
}}>
|
|
351
|
+
<Text style={{
|
|
352
|
+
fontFamily: AppFonts.interBold,
|
|
353
|
+
fontSize: 9.5,
|
|
354
|
+
color: isActive
|
|
355
|
+
? AppColors.white
|
|
356
|
+
: AppColors.purple,
|
|
357
|
+
}}>
|
|
358
|
+
{tab.badge}
|
|
359
|
+
</Text>
|
|
360
|
+
</View>)}
|
|
361
|
+
</TouchableScale>);
|
|
362
|
+
})}
|
|
363
|
+
</ScrollView>
|
|
379
364
|
</View>
|
|
380
365
|
|
|
381
366
|
{settingsActiveSubTab === 'module' && (<View style={{ gap: 12 }}>
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import React, { useState, useEffect, useMemo, useCallback } from 'react';
|
|
1
|
+
import React, { useState, useEffect, useMemo, useCallback, useRef } from 'react';
|
|
2
2
|
import { View, Text, ScrollView, FlatList, TextInput, Modal, Alert, StyleSheet, ActivityIndicator, Platform, } from 'react-native';
|
|
3
3
|
import TouchableScale from '../TouchableScale';
|
|
4
|
+
import globalStyles from '../../styles';
|
|
4
5
|
import { AppColors } from '../../styles/AppColors';
|
|
5
6
|
import { AppFonts } from '../../styles/AppFonts';
|
|
6
7
|
import { useTranslation } from '../../i18n';
|
|
7
|
-
import { DatabaseIcon, SearchIcon, ClearIcon, PlusIcon, PencilIcon, TrashIcon, CopyIcon, CheckIcon, LayersIcon, ResetIcon, } from '../NetworkIcons';
|
|
8
|
+
import { DatabaseIcon, SearchIcon, ClearIcon, PlusIcon, PencilIcon, TrashIcon, CopyIcon, CheckIcon, LayersIcon, ResetIcon, ChevronIcon, } from '../NetworkIcons';
|
|
8
9
|
import { fetchStorageEntries, setStorageEntry, removeStorageEntry, clearStorageDriver, isAsyncStorageConnected, isMMKVConnected, getRegisteredMMKVInstanceIds, subscribeToStorageChanges, } from '../../customHooks/storageInspector';
|
|
9
10
|
import { copyToClipboard } from '../../helpers';
|
|
10
11
|
import { showToast } from '../../helpers/toast';
|
|
@@ -95,6 +96,7 @@ const StorageEntryCard = React.memo(function StorageEntryCard({ entry, isExpande
|
|
|
95
96
|
});
|
|
96
97
|
export const StorageTab = React.memo(() => {
|
|
97
98
|
const { t } = useTranslation();
|
|
99
|
+
const listRef = useRef(null);
|
|
98
100
|
const [activeDriver, setActiveDriver] = useState('asyncStorage');
|
|
99
101
|
const [activeMMKVId, setActiveMMKVId] = useState('default');
|
|
100
102
|
const [entries, setEntries] = useState([]);
|
|
@@ -360,19 +362,43 @@ export const StorageTab = React.memo(() => {
|
|
|
360
362
|
{isLoading ? (<View style={styles.loadingContainer}>
|
|
361
363
|
<ActivityIndicator size="small" color={AppColors.purple}/>
|
|
362
364
|
<Text style={styles.loadingText}>Loading storage entries...</Text>
|
|
363
|
-
</View>) : (
|
|
364
|
-
|
|
365
|
-
<
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
{
|
|
365
|
+
</View>) : (<>
|
|
366
|
+
<FlatList ref={listRef} data={filteredEntries} keyExtractor={item => item.key} renderItem={renderItem} initialNumToRender={10} maxToRenderPerBatch={10} windowSize={5} removeClippedSubviews={Platform.OS === 'android'} style={styles.scrollArea} contentContainerStyle={styles.scrollContent} showsVerticalScrollIndicator={false} ListEmptyComponent={<View style={styles.emptyContainer}>
|
|
367
|
+
<View style={styles.emptyIconWrap}>
|
|
368
|
+
<DatabaseIcon size={28} color={AppColors.grayTextWeak}/>
|
|
369
|
+
</View>
|
|
370
|
+
<Text style={styles.emptyTitle}>
|
|
371
|
+
{search ? 'No matching keys found' : `No ${activeDriver === 'asyncStorage' ? 'AsyncStorage' : 'MMKV'} Keys`}
|
|
372
|
+
</Text>
|
|
373
|
+
<Text style={styles.emptySubtitle}>
|
|
374
|
+
{search
|
|
372
375
|
? 'Try modifying your search query'
|
|
373
376
|
: `Storage is auto-detected. Tap "+ Add" above to create your first ${activeDriver === 'asyncStorage' ? 'AsyncStorage' : 'MMKV'} key!`}
|
|
374
|
-
|
|
375
|
-
|
|
377
|
+
</Text>
|
|
378
|
+
</View>} ListFooterComponent={<View style={{ height: 60 }}/>}/>
|
|
379
|
+
|
|
380
|
+
<TouchableScale onPress={() => {
|
|
381
|
+
try {
|
|
382
|
+
listRef.current?.scrollToOffset({
|
|
383
|
+
offset: 0,
|
|
384
|
+
animated: true,
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
try {
|
|
389
|
+
listRef.current?.scrollToIndex({
|
|
390
|
+
index: 0,
|
|
391
|
+
animated: true,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
catch { }
|
|
395
|
+
}
|
|
396
|
+
}} hitSlop={12} style={globalStyles.scrollTopBtn}>
|
|
397
|
+
<View style={{ transform: [{ rotate: '180deg' }] }}>
|
|
398
|
+
<ChevronIcon color={AppColors.white} size={18}/>
|
|
399
|
+
</View>
|
|
400
|
+
</TouchableScale>
|
|
401
|
+
</>)}
|
|
376
402
|
|
|
377
403
|
|
|
378
404
|
<Modal visible={modalVisible} transparent animationType="fade" onRequestClose={() => setModalVisible(false)}>
|
|
@@ -32,6 +32,8 @@ export const UpdateAvailableModal = ({ visible, latestVersion, onClose, }) => {
|
|
|
32
32
|
const fadeAnim = useState(new Animated.Value(0))[0];
|
|
33
33
|
const scaleAnim = useState(new Animated.Value(0.92))[0];
|
|
34
34
|
const installCommand = `npm install react-native-inapp-inspector@latest`;
|
|
35
|
+
if (typeof __DEV__ === 'undefined' || !__DEV__)
|
|
36
|
+
return null;
|
|
35
37
|
useEffect(() => {
|
|
36
38
|
if (visible) {
|
|
37
39
|
setCopied(false);
|