react-native-inapp-inspector 1.1.11 → 1.1.12
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/AnalyticsDetail.js +57 -486
- package/dist/commonjs/components/AnalyticsEventCard.js +25 -25
- package/dist/commonjs/components/ConsoleLogCard.js +3 -1
- package/dist/commonjs/components/ReduxTreeView.d.ts +0 -14
- package/dist/commonjs/components/ReduxTreeView.js +43 -448
- package/dist/commonjs/components/TouchableScale.js +8 -0
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/helpers/index.d.ts +2 -0
- package/dist/commonjs/helpers/index.js +89 -1
- package/dist/commonjs/index.js +299 -272
- package/dist/esm/components/AnalyticsDetail.js +58 -487
- package/dist/esm/components/AnalyticsEventCard.js +25 -25
- package/dist/esm/components/ConsoleLogCard.js +3 -1
- package/dist/esm/components/ReduxTreeView.d.ts +0 -14
- package/dist/esm/components/ReduxTreeView.js +43 -441
- package/dist/esm/components/TouchableScale.js +9 -1
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/helpers/index.d.ts +2 -0
- package/dist/esm/helpers/index.js +87 -1
- package/dist/esm/index.js +302 -275
- package/example/package-lock.json +33 -74
- package/example/package.json +1 -1
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useRef, useState, useCallback } from 'react';
|
|
2
|
-
import { Alert, Animated, StyleSheet, FlatList,
|
|
2
|
+
import { Alert, Animated, StyleSheet, FlatList, Modal, PanResponder, Platform, Pressable, ScrollView, Text, TextInput, View, Linking, Image, InteractionManager, ActivityIndicator, StatusBar, TouchableOpacity, UIManager, LogBox, } from 'react-native';
|
|
3
3
|
import Svg, { Circle, Path } from 'react-native-svg';
|
|
4
4
|
import LinearGradient from 'react-native-linear-gradient';
|
|
5
5
|
import { useNavigationState, NavigationContext } from '@react-navigation/native';
|
|
@@ -11,7 +11,7 @@ import CopyButton from './components/CopyButton';
|
|
|
11
11
|
import SectionHeader from './components/SectionHeader';
|
|
12
12
|
import EmptyState from './components/EmptyState';
|
|
13
13
|
import JsonViewer from './components/JsonViewer';
|
|
14
|
-
import { ReduxTreeView,
|
|
14
|
+
import { ReduxTreeView, } from './components/ReduxTreeView';
|
|
15
15
|
import DomainHeader from './components/DomainHeader';
|
|
16
16
|
import DiffViewer from './components/DiffViewer';
|
|
17
17
|
import LogCard from './components/LogCard';
|
|
@@ -22,7 +22,7 @@ import HighlightText from './components/HighlightText';
|
|
|
22
22
|
import CodeSnippet from './components/CodeSnippet';
|
|
23
23
|
import AnimatedEntrance from './components/AnimatedEntrance';
|
|
24
24
|
// Helpers
|
|
25
|
-
import { formatDateTime, getStatusColor, getNavigationInfo, deduplicateLogs, getDomainColor, formatDisplayUrl, getFetchCommand, getCurlCommand, getSize, } from './helpers';
|
|
25
|
+
import { formatDateTime, getStatusColor, getNavigationInfo, deduplicateLogs, getDomainColor, formatDisplayUrl, getFetchCommand, getCurlCommand, getSize, getBundleIdentifier, getAppName, } from './helpers';
|
|
26
26
|
// #5 — settings persistence
|
|
27
27
|
import { loadSettings, saveSettings, setCustomStorage, isPersistentStorageAvailable, clearPersistedSettings, } from './helpers/settingsStore';
|
|
28
28
|
// Assets
|
|
@@ -111,7 +111,8 @@ const NavigationTracker = ({ onStateChange }) => {
|
|
|
111
111
|
return null;
|
|
112
112
|
};
|
|
113
113
|
const animateNextLayout = () => {
|
|
114
|
-
LayoutAnimation
|
|
114
|
+
// Disabled LayoutAnimation to prevent iOS NSRangeException crashes under Fabric
|
|
115
|
+
// and Android rendering performance freezes.
|
|
115
116
|
};
|
|
116
117
|
const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
|
|
117
118
|
// Set custom storage synchronously during render phase
|
|
@@ -135,7 +136,6 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
|
|
|
135
136
|
const [search, setSearch] = useState('');
|
|
136
137
|
const [detailSearch, setDetailSearch] = useState('');
|
|
137
138
|
const [reduxSearch, setReduxSearch] = useState('');
|
|
138
|
-
const [reduxActiveSubTab, setReduxActiveSubTab] = useState('timeline');
|
|
139
139
|
const [apiDetailActiveTab, setApiDetailActiveTab] = useState('response');
|
|
140
140
|
useEffect(() => {
|
|
141
141
|
LogBox.ignoreAllLogs(enabled);
|
|
@@ -193,7 +193,7 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
|
|
|
193
193
|
const [maxConsoleLogs, setMaxConsoleLogs] = useState(200);
|
|
194
194
|
const [showConsoleLevels, setShowConsoleLevels] = useState({
|
|
195
195
|
info: true,
|
|
196
|
-
warn:
|
|
196
|
+
warn: false,
|
|
197
197
|
error: true,
|
|
198
198
|
});
|
|
199
199
|
const visibleConsoleLogs = useMemo(() => {
|
|
@@ -702,7 +702,13 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
|
|
|
702
702
|
const task = InteractionManager.runAfterInteractions(() => {
|
|
703
703
|
setIsReady(true);
|
|
704
704
|
});
|
|
705
|
-
|
|
705
|
+
const fallbackTimer = setTimeout(() => {
|
|
706
|
+
setIsReady(true);
|
|
707
|
+
}, 350);
|
|
708
|
+
return () => {
|
|
709
|
+
task.cancel();
|
|
710
|
+
clearTimeout(fallbackTimer);
|
|
711
|
+
};
|
|
706
712
|
}
|
|
707
713
|
else {
|
|
708
714
|
setIsReady(false);
|
|
@@ -715,9 +721,10 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
|
|
|
715
721
|
setupConsoleLogger();
|
|
716
722
|
autoSetupAnalyticsLogger();
|
|
717
723
|
let timeoutId;
|
|
724
|
+
let isFirstNetworkCall = true;
|
|
718
725
|
const unsubscribe = subscribeNetworkLogs((raw) => {
|
|
719
726
|
clearTimeout(timeoutId);
|
|
720
|
-
|
|
727
|
+
const updateNetworkState = () => {
|
|
721
728
|
const deduped = deduplicateLogs(raw);
|
|
722
729
|
const incoming = new Set(deduped.map(l => l.id));
|
|
723
730
|
const freshIds = new Set();
|
|
@@ -736,13 +743,24 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
|
|
|
736
743
|
setTimeout(() => setNewLogIds(new Set()), 1200);
|
|
737
744
|
}
|
|
738
745
|
setLogs(deduped);
|
|
739
|
-
}
|
|
746
|
+
};
|
|
747
|
+
if (isFirstNetworkCall) {
|
|
748
|
+
isFirstNetworkCall = false;
|
|
749
|
+
const deduped = deduplicateLogs(raw);
|
|
750
|
+
const incoming = new Set(deduped.map(l => l.id));
|
|
751
|
+
prevLogIdsRef.current = incoming;
|
|
752
|
+
setLogs(deduped);
|
|
753
|
+
}
|
|
754
|
+
else {
|
|
755
|
+
timeoutId = setTimeout(updateNetworkState, 250);
|
|
756
|
+
}
|
|
740
757
|
});
|
|
741
758
|
// ─── Analytics subscription ──────────────────────────────────────────────
|
|
742
759
|
let analyticsTimeoutId;
|
|
760
|
+
let isFirstAnalyticsCall = true;
|
|
743
761
|
const unsubscribeAnalytics = subscribeAnalyticsEvents((raw) => {
|
|
744
762
|
clearTimeout(analyticsTimeoutId);
|
|
745
|
-
|
|
763
|
+
const updateAnalyticsState = () => {
|
|
746
764
|
const incoming = new Set(raw.map(e => e.id));
|
|
747
765
|
const freshIds = new Set();
|
|
748
766
|
incoming.forEach(id => {
|
|
@@ -760,15 +778,31 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
|
|
|
760
778
|
setTimeout(() => setNewEventIds(new Set()), 1200);
|
|
761
779
|
}
|
|
762
780
|
setAnalyticsEvents(raw);
|
|
763
|
-
}
|
|
781
|
+
};
|
|
782
|
+
if (isFirstAnalyticsCall) {
|
|
783
|
+
isFirstAnalyticsCall = false;
|
|
784
|
+
const incoming = new Set(raw.map(e => e.id));
|
|
785
|
+
prevEventIdsRef.current = incoming;
|
|
786
|
+
setAnalyticsEvents(raw);
|
|
787
|
+
}
|
|
788
|
+
else {
|
|
789
|
+
analyticsTimeoutId = setTimeout(updateAnalyticsState, 200);
|
|
790
|
+
}
|
|
764
791
|
});
|
|
765
792
|
// ─── Console subscription ────────────────────────────────────────────────
|
|
766
793
|
let consoleTimeoutId;
|
|
794
|
+
let isFirstConsoleCall = true;
|
|
767
795
|
const unsubscribeConsole = subscribeConsoleLogs((raw) => {
|
|
768
796
|
clearTimeout(consoleTimeoutId);
|
|
769
|
-
|
|
797
|
+
if (isFirstConsoleCall) {
|
|
798
|
+
isFirstConsoleCall = false;
|
|
770
799
|
setConsoleLogs(raw);
|
|
771
|
-
}
|
|
800
|
+
}
|
|
801
|
+
else {
|
|
802
|
+
consoleTimeoutId = setTimeout(() => {
|
|
803
|
+
setConsoleLogs(raw);
|
|
804
|
+
}, 200);
|
|
805
|
+
}
|
|
772
806
|
});
|
|
773
807
|
setWebViewLogs(getWebViewLogs());
|
|
774
808
|
setWebViewNavHistory(getWebViewNavHistory());
|
|
@@ -778,24 +812,34 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
|
|
|
778
812
|
setWebViewHtmlUrl(getWebViewHtmlUrl());
|
|
779
813
|
// ─── WebView subscription ────────────────────────────────────────────────
|
|
780
814
|
let webViewTimeoutId;
|
|
815
|
+
let isFirstWebViewCall = true;
|
|
781
816
|
const unsubscribeWebView = subscribeWebView(() => {
|
|
782
817
|
clearTimeout(webViewTimeoutId);
|
|
783
|
-
|
|
818
|
+
const updateWebViewState = () => {
|
|
784
819
|
setWebViewLogs(getWebViewLogs());
|
|
785
820
|
setWebViewNavHistory(getWebViewNavHistory());
|
|
786
821
|
setWebViewHtml(getWebViewHtml());
|
|
787
822
|
setWebViewCss(getWebViewCss());
|
|
788
823
|
setWebViewJs(getWebViewJs());
|
|
789
824
|
setWebViewHtmlUrl(getWebViewHtmlUrl());
|
|
790
|
-
}
|
|
825
|
+
};
|
|
826
|
+
if (isFirstWebViewCall) {
|
|
827
|
+
isFirstWebViewCall = false;
|
|
828
|
+
updateWebViewState();
|
|
829
|
+
}
|
|
830
|
+
else {
|
|
831
|
+
webViewTimeoutId = setTimeout(updateWebViewState, 200);
|
|
832
|
+
}
|
|
791
833
|
});
|
|
792
|
-
|
|
834
|
+
const initialReduxState = getReduxState();
|
|
835
|
+
setReduxState(initialReduxState && typeof initialReduxState === 'object' ? { ...initialReduxState } : initialReduxState);
|
|
793
836
|
setReduxActionHistory([...getActionHistory()]);
|
|
794
837
|
setReduxLastActionMap({ ...getLastActionForReducer() });
|
|
795
838
|
const unsubscribeRedux = subscribeReduxState(() => {
|
|
796
839
|
// New references each time guarantee the Redux tab updates live, even when
|
|
797
840
|
// the root state object reference is unchanged or auto-refresh is paused.
|
|
798
|
-
|
|
841
|
+
const freshState = getReduxState();
|
|
842
|
+
setReduxState(freshState && typeof freshState === 'object' ? { ...freshState } : freshState);
|
|
799
843
|
setReduxActionHistory([...getActionHistory()]);
|
|
800
844
|
setReduxLastActionMap({ ...getLastActionForReducer() });
|
|
801
845
|
});
|
|
@@ -2106,6 +2150,97 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
|
|
|
2106
2150
|
</TouchableScale>
|
|
2107
2151
|
</View>
|
|
2108
2152
|
|
|
2153
|
+
{/* Divider */}
|
|
2154
|
+
<View style={{
|
|
2155
|
+
height: 1,
|
|
2156
|
+
backgroundColor: AppColors.dividerColor,
|
|
2157
|
+
}}/>
|
|
2158
|
+
|
|
2159
|
+
{/* Logs Console Levels */}
|
|
2160
|
+
<View style={{
|
|
2161
|
+
paddingVertical: 12,
|
|
2162
|
+
paddingHorizontal: 14,
|
|
2163
|
+
}}>
|
|
2164
|
+
<View style={{
|
|
2165
|
+
flexDirection: 'row',
|
|
2166
|
+
alignItems: 'center',
|
|
2167
|
+
gap: 8,
|
|
2168
|
+
}}>
|
|
2169
|
+
<View style={{
|
|
2170
|
+
width: 20,
|
|
2171
|
+
height: 20,
|
|
2172
|
+
borderRadius: 6,
|
|
2173
|
+
backgroundColor: AppColors.purpleShade50,
|
|
2174
|
+
borderWidth: 1,
|
|
2175
|
+
borderColor: 'rgba(104,75,155,0.2)',
|
|
2176
|
+
alignItems: 'center',
|
|
2177
|
+
justifyContent: 'center',
|
|
2178
|
+
}}>
|
|
2179
|
+
<TerminalIcon color={AppColors.purple} size={11}/>
|
|
2180
|
+
</View>
|
|
2181
|
+
<View style={{ flex: 1 }}>
|
|
2182
|
+
<Text style={{
|
|
2183
|
+
fontFamily: AppFonts.interBold,
|
|
2184
|
+
fontSize: 13,
|
|
2185
|
+
color: AppColors.primaryBlack,
|
|
2186
|
+
}}>
|
|
2187
|
+
Logs Console Levels
|
|
2188
|
+
</Text>
|
|
2189
|
+
<Text style={{
|
|
2190
|
+
fontFamily: AppFonts.interRegular,
|
|
2191
|
+
fontSize: 11,
|
|
2192
|
+
color: AppColors.grayText,
|
|
2193
|
+
marginTop: 1,
|
|
2194
|
+
}}>
|
|
2195
|
+
Toggle which log levels are visible in the Logs tab
|
|
2196
|
+
</Text>
|
|
2197
|
+
</View>
|
|
2198
|
+
</View>
|
|
2199
|
+
|
|
2200
|
+
<View style={{
|
|
2201
|
+
flexDirection: 'row',
|
|
2202
|
+
gap: 8,
|
|
2203
|
+
marginTop: 10,
|
|
2204
|
+
}}>
|
|
2205
|
+
{([
|
|
2206
|
+
{ key: 'info', label: 'Info', color: '#3B82F6' },
|
|
2207
|
+
{ key: 'warn', label: 'Warning', color: '#F59E0B' },
|
|
2208
|
+
{ key: 'error', label: 'Error', color: '#EF4444' },
|
|
2209
|
+
]).map(level => {
|
|
2210
|
+
const isActive = showConsoleLevels[level.key];
|
|
2211
|
+
return (<TouchableScale key={level.key} onPress={() => setShowConsoleLevels(prev => ({
|
|
2212
|
+
...prev,
|
|
2213
|
+
[level.key]: !prev[level.key],
|
|
2214
|
+
}))} style={{
|
|
2215
|
+
flex: 1,
|
|
2216
|
+
flexDirection: 'row',
|
|
2217
|
+
alignItems: 'center',
|
|
2218
|
+
justifyContent: 'center',
|
|
2219
|
+
gap: 6,
|
|
2220
|
+
paddingVertical: 8,
|
|
2221
|
+
borderRadius: 8,
|
|
2222
|
+
borderWidth: 1.5,
|
|
2223
|
+
borderColor: isActive ? level.color : AppColors.grayBorderSecondary,
|
|
2224
|
+
backgroundColor: isActive ? `${level.color}0D` : AppColors.primaryLight,
|
|
2225
|
+
}}>
|
|
2226
|
+
<View style={{
|
|
2227
|
+
width: 8,
|
|
2228
|
+
height: 8,
|
|
2229
|
+
borderRadius: 4,
|
|
2230
|
+
backgroundColor: isActive ? level.color : AppColors.grayBorderSecondary,
|
|
2231
|
+
}}/>
|
|
2232
|
+
<Text style={{
|
|
2233
|
+
fontFamily: AppFonts.interBold,
|
|
2234
|
+
fontSize: 11,
|
|
2235
|
+
color: isActive ? level.color : AppColors.grayText,
|
|
2236
|
+
}}>
|
|
2237
|
+
{level.label}
|
|
2238
|
+
</Text>
|
|
2239
|
+
</TouchableScale>);
|
|
2240
|
+
})}
|
|
2241
|
+
</View>
|
|
2242
|
+
</View>
|
|
2243
|
+
|
|
2109
2244
|
{/* Divider */}
|
|
2110
2245
|
<View style={{
|
|
2111
2246
|
height: 1,
|
|
@@ -3435,129 +3570,8 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
|
|
|
3435
3570
|
<Text style={styles.emptySub}>Connected store state is empty.</Text>
|
|
3436
3571
|
</View>);
|
|
3437
3572
|
}
|
|
3438
|
-
// Build hierarchical tree: Store -> Reducers -> Action -> Data.
|
|
3439
|
-
// These come from component state (refreshed on every dispatch) so the view stays live.
|
|
3440
3573
|
const lastActionMap = reduxLastActionMap;
|
|
3441
|
-
const actionHistory = reduxActionHistory;
|
|
3442
3574
|
return (<ScrollView style={styles.detailScroll} contentContainerStyle={{ paddingBottom: 24 }}>
|
|
3443
|
-
{/* Tab View Selection Segments */}
|
|
3444
|
-
<View style={{
|
|
3445
|
-
flexDirection: 'row',
|
|
3446
|
-
backgroundColor: AppColors.grayBackground,
|
|
3447
|
-
borderRadius: 10,
|
|
3448
|
-
padding: 3,
|
|
3449
|
-
marginHorizontal: 16,
|
|
3450
|
-
marginTop: 12,
|
|
3451
|
-
marginBottom: 12,
|
|
3452
|
-
borderWidth: 1,
|
|
3453
|
-
borderColor: AppColors.dividerColor,
|
|
3454
|
-
}}>
|
|
3455
|
-
<TouchableOpacity onPress={() => {
|
|
3456
|
-
animateNextLayout();
|
|
3457
|
-
setReduxActiveSubTab('timeline');
|
|
3458
|
-
}} style={{
|
|
3459
|
-
flex: 1,
|
|
3460
|
-
paddingVertical: 6,
|
|
3461
|
-
alignItems: 'center',
|
|
3462
|
-
justifyContent: 'center',
|
|
3463
|
-
borderRadius: 8,
|
|
3464
|
-
backgroundColor: reduxActiveSubTab === 'timeline'
|
|
3465
|
-
? AppColors.purple
|
|
3466
|
-
: 'transparent',
|
|
3467
|
-
}}>
|
|
3468
|
-
<View style={{
|
|
3469
|
-
flexDirection: 'row',
|
|
3470
|
-
alignItems: 'center',
|
|
3471
|
-
justifyContent: 'center',
|
|
3472
|
-
gap: 6,
|
|
3473
|
-
}}>
|
|
3474
|
-
<ReduxTimelineIcon color={reduxActiveSubTab === 'timeline'
|
|
3475
|
-
? '#FFFFFF'
|
|
3476
|
-
: AppColors.grayText} size={13}/>
|
|
3477
|
-
<Text style={{
|
|
3478
|
-
fontFamily: AppFonts.interBold,
|
|
3479
|
-
fontSize: 11,
|
|
3480
|
-
color: reduxActiveSubTab === 'timeline'
|
|
3481
|
-
? '#FFFFFF'
|
|
3482
|
-
: AppColors.grayText,
|
|
3483
|
-
}}>
|
|
3484
|
-
Action Timeline
|
|
3485
|
-
</Text>
|
|
3486
|
-
<View style={{
|
|
3487
|
-
minWidth: 18,
|
|
3488
|
-
paddingHorizontal: 5,
|
|
3489
|
-
height: 16,
|
|
3490
|
-
borderRadius: 8,
|
|
3491
|
-
alignItems: 'center',
|
|
3492
|
-
justifyContent: 'center',
|
|
3493
|
-
backgroundColor: reduxActiveSubTab === 'timeline'
|
|
3494
|
-
? 'rgba(255,255,255,0.28)'
|
|
3495
|
-
: AppColors.dividerColor,
|
|
3496
|
-
}}>
|
|
3497
|
-
<Text style={{
|
|
3498
|
-
fontFamily: AppFonts.interBold,
|
|
3499
|
-
fontSize: 9,
|
|
3500
|
-
color: reduxActiveSubTab === 'timeline'
|
|
3501
|
-
? '#FFFFFF'
|
|
3502
|
-
: AppColors.grayText,
|
|
3503
|
-
}}>
|
|
3504
|
-
{actionHistory.length}
|
|
3505
|
-
</Text>
|
|
3506
|
-
</View>
|
|
3507
|
-
</View>
|
|
3508
|
-
</TouchableOpacity>
|
|
3509
|
-
<TouchableOpacity onPress={() => {
|
|
3510
|
-
animateNextLayout();
|
|
3511
|
-
setReduxActiveSubTab('tree');
|
|
3512
|
-
}} style={{
|
|
3513
|
-
flex: 1,
|
|
3514
|
-
paddingVertical: 6,
|
|
3515
|
-
alignItems: 'center',
|
|
3516
|
-
justifyContent: 'center',
|
|
3517
|
-
borderRadius: 8,
|
|
3518
|
-
backgroundColor: reduxActiveSubTab === 'tree' ? AppColors.purple : 'transparent',
|
|
3519
|
-
}}>
|
|
3520
|
-
<View style={{
|
|
3521
|
-
flexDirection: 'row',
|
|
3522
|
-
alignItems: 'center',
|
|
3523
|
-
justifyContent: 'center',
|
|
3524
|
-
gap: 6,
|
|
3525
|
-
}}>
|
|
3526
|
-
<ReduxTreeIcon color={reduxActiveSubTab === 'tree' ? '#FFFFFF' : AppColors.grayText} size={13}/>
|
|
3527
|
-
<Text style={{
|
|
3528
|
-
fontFamily: AppFonts.interBold,
|
|
3529
|
-
fontSize: 11,
|
|
3530
|
-
color: reduxActiveSubTab === 'tree'
|
|
3531
|
-
? '#FFFFFF'
|
|
3532
|
-
: AppColors.grayText,
|
|
3533
|
-
}}>
|
|
3534
|
-
Store Tree
|
|
3535
|
-
</Text>
|
|
3536
|
-
<View style={{
|
|
3537
|
-
minWidth: 18,
|
|
3538
|
-
paddingHorizontal: 5,
|
|
3539
|
-
height: 16,
|
|
3540
|
-
borderRadius: 8,
|
|
3541
|
-
alignItems: 'center',
|
|
3542
|
-
justifyContent: 'center',
|
|
3543
|
-
backgroundColor: reduxActiveSubTab === 'tree'
|
|
3544
|
-
? 'rgba(255,255,255,0.28)'
|
|
3545
|
-
: AppColors.dividerColor,
|
|
3546
|
-
}}>
|
|
3547
|
-
<Text style={{
|
|
3548
|
-
fontFamily: AppFonts.interBold,
|
|
3549
|
-
fontSize: 9,
|
|
3550
|
-
color: reduxActiveSubTab === 'tree'
|
|
3551
|
-
? '#FFFFFF'
|
|
3552
|
-
: AppColors.grayText,
|
|
3553
|
-
}}>
|
|
3554
|
-
{reducerKeys.length}
|
|
3555
|
-
</Text>
|
|
3556
|
-
</View>
|
|
3557
|
-
</View>
|
|
3558
|
-
</TouchableOpacity>
|
|
3559
|
-
</View>
|
|
3560
|
-
|
|
3561
3575
|
{/* Search Bar */}
|
|
3562
3576
|
<View style={{
|
|
3563
3577
|
flexDirection: 'row',
|
|
@@ -3565,15 +3579,14 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
|
|
|
3565
3579
|
backgroundColor: AppColors.grayBackground,
|
|
3566
3580
|
borderRadius: 8,
|
|
3567
3581
|
marginHorizontal: 16,
|
|
3582
|
+
marginTop: 16,
|
|
3568
3583
|
marginBottom: 12,
|
|
3569
3584
|
paddingHorizontal: 10,
|
|
3570
3585
|
borderWidth: 1,
|
|
3571
3586
|
borderColor: AppColors.dividerColor,
|
|
3572
3587
|
height: 36,
|
|
3573
3588
|
}}>
|
|
3574
|
-
<TextInput placeholder={
|
|
3575
|
-
? 'Search actions or payloads...'
|
|
3576
|
-
: 'Search Redux keys or values...'} placeholderTextColor={AppColors.grayTextWeak} value={reduxSearch} onChangeText={setReduxSearch} style={{
|
|
3589
|
+
<TextInput placeholder="Search Redux keys or values..." placeholderTextColor={AppColors.grayTextWeak} value={reduxSearch} onChangeText={setReduxSearch} style={{
|
|
3577
3590
|
flex: 1,
|
|
3578
3591
|
fontFamily: AppFonts.interRegular,
|
|
3579
3592
|
fontSize: 12,
|
|
@@ -3594,7 +3607,7 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
|
|
|
3594
3607
|
marginHorizontal: 16,
|
|
3595
3608
|
padding: 12,
|
|
3596
3609
|
}}>
|
|
3597
|
-
|
|
3610
|
+
<ReduxTreeView state={reduxState} lastActionMap={lastActionMap} search={reduxSearch}/>
|
|
3598
3611
|
</View>
|
|
3599
3612
|
</ScrollView>);
|
|
3600
3613
|
};
|
|
@@ -3720,14 +3733,50 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
|
|
|
3720
3733
|
}}>
|
|
3721
3734
|
<BrandSquareIcon size={45}/>
|
|
3722
3735
|
</View>
|
|
3723
|
-
<View style={{ gap:
|
|
3724
|
-
<
|
|
3725
|
-
|
|
3736
|
+
<View style={{ gap: 2, flex: 1 }}>
|
|
3737
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
3738
|
+
<Text style={[styles.headerTitle]} numberOfLines={1}>
|
|
3739
|
+
{getAppName()}{' '}
|
|
3740
|
+
<Text style={{ fontFamily: AppFonts.interRegular, fontSize: 13, color: 'rgba(255,255,255,0.6)' }}>
|
|
3741
|
+
(Inspector)
|
|
3742
|
+
</Text>
|
|
3743
|
+
</Text>
|
|
3744
|
+
{/* #1 — pulsing dot when a newer version is on NPM */}
|
|
3745
|
+
{updateAvailable && (<Pressable hitSlop={10} onPress={() => Alert.alert('Update Available', `react-native-inapp-inspector v${latestNpmVersion} is available on NPM (installed: v${LIB_VERSION}).`, [
|
|
3746
|
+
{ text: 'Later', style: 'cancel' },
|
|
3747
|
+
{
|
|
3748
|
+
text: 'View on NPM',
|
|
3749
|
+
onPress: () => Linking.openURL('https://www.npmjs.com/package/react-native-inapp-inspector').catch(() => { }),
|
|
3750
|
+
},
|
|
3751
|
+
])} style={{
|
|
3752
|
+
alignItems: 'center',
|
|
3753
|
+
justifyContent: 'center',
|
|
3754
|
+
}}>
|
|
3755
|
+
<Animated.View style={{
|
|
3756
|
+
width: 8,
|
|
3757
|
+
height: 8,
|
|
3758
|
+
borderRadius: 4,
|
|
3759
|
+
backgroundColor: '#4ADE80',
|
|
3760
|
+
borderWidth: 1,
|
|
3761
|
+
borderColor: 'rgba(255,255,255,0.9)',
|
|
3762
|
+
opacity: activePulseAnim,
|
|
3763
|
+
transform: [{ scale: unreadPulseAnim }],
|
|
3764
|
+
}}/>
|
|
3765
|
+
</Pressable>)}
|
|
3766
|
+
</View>
|
|
3767
|
+
<Text style={{
|
|
3768
|
+
fontFamily: AppFonts.interRegular,
|
|
3769
|
+
fontSize: 10.5,
|
|
3770
|
+
color: 'rgba(255, 255, 255, 0.6)',
|
|
3771
|
+
marginTop: 1,
|
|
3772
|
+
}} selectable={true} numberOfLines={1}>
|
|
3773
|
+
{getBundleIdentifier()}
|
|
3726
3774
|
</Text>
|
|
3727
3775
|
<View style={{
|
|
3728
3776
|
flexDirection: 'row',
|
|
3729
3777
|
alignItems: 'center',
|
|
3730
3778
|
gap: 6,
|
|
3779
|
+
marginTop: 4,
|
|
3731
3780
|
}}>
|
|
3732
3781
|
{/* OS chip */}
|
|
3733
3782
|
<View style={{
|
|
@@ -3804,29 +3853,6 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
|
|
|
3804
3853
|
</Text>
|
|
3805
3854
|
</View>
|
|
3806
3855
|
</View>
|
|
3807
|
-
|
|
3808
|
-
{/* #1 — pulsing dot when a newer version is on NPM */}
|
|
3809
|
-
{updateAvailable && (<Pressable hitSlop={10} onPress={() => Alert.alert('Update Available', `react-native-inapp-inspector v${latestNpmVersion} is available on NPM (installed: v${LIB_VERSION}).`, [
|
|
3810
|
-
{ text: 'Later', style: 'cancel' },
|
|
3811
|
-
{
|
|
3812
|
-
text: 'View on NPM',
|
|
3813
|
-
onPress: () => Linking.openURL('https://www.npmjs.com/package/react-native-inapp-inspector').catch(() => { }),
|
|
3814
|
-
},
|
|
3815
|
-
])} style={{
|
|
3816
|
-
alignItems: 'center',
|
|
3817
|
-
justifyContent: 'center',
|
|
3818
|
-
}}>
|
|
3819
|
-
<Animated.View style={{
|
|
3820
|
-
width: 8,
|
|
3821
|
-
height: 8,
|
|
3822
|
-
borderRadius: 4,
|
|
3823
|
-
backgroundColor: '#4ADE80',
|
|
3824
|
-
borderWidth: 1,
|
|
3825
|
-
borderColor: 'rgba(255,255,255,0.9)',
|
|
3826
|
-
opacity: activePulseAnim,
|
|
3827
|
-
transform: [{ scale: unreadPulseAnim }],
|
|
3828
|
-
}}/>
|
|
3829
|
-
</Pressable>)}
|
|
3830
3856
|
</View>
|
|
3831
3857
|
</View>
|
|
3832
3858
|
</View>) : null}
|
|
@@ -4111,14 +4137,6 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
|
|
|
4111
4137
|
</Pressable>)}
|
|
4112
4138
|
</View>
|
|
4113
4139
|
{analyticsSubTab === 'ga_events' && (<View style={styles.toolbarRight}>
|
|
4114
|
-
<TouchableScale style={[
|
|
4115
|
-
styles.toolbarBtn,
|
|
4116
|
-
!hideScreenView && styles.toolbarBtnActive,
|
|
4117
|
-
]} onPress={() => setHideScreenView(prev => !prev)} hitSlop={10}>
|
|
4118
|
-
<ScreenIcon color={!hideScreenView
|
|
4119
|
-
? AppColors.purple
|
|
4120
|
-
: AppColors.grayTextStrong} size={18}/>
|
|
4121
|
-
</TouchableScale>
|
|
4122
4140
|
<TouchableScale style={{
|
|
4123
4141
|
flexDirection: 'row',
|
|
4124
4142
|
alignItems: 'center',
|
|
@@ -4249,109 +4267,118 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, }) => {
|
|
|
4249
4267
|
|
|
4250
4268
|
{isReady ? (activeTab === 'insights' ? (<ScrollView style={styles.insightsContainer} contentContainerStyle={styles.insightsContent} showsVerticalScrollIndicator={false}>
|
|
4251
4269
|
{renderInsightsDashboard()}
|
|
4252
|
-
</ScrollView>) : activeTab === 'analytics' ? (
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
<Svg width={14} height={14} viewBox="0 0 24 24" fill={color}>
|
|
4274
|
-
<Circle cx="12" cy="12" r="10" opacity="0.3"/>
|
|
4275
|
-
<Path d="M7 14l3-3 4 4 6-6" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" fill="none"/>
|
|
4276
|
-
</Svg>
|
|
4277
|
-
</View>
|
|
4278
|
-
<Text style={styles.analyticsTopEventName} numberOfLines={2}>
|
|
4279
|
-
{name}
|
|
4270
|
+
</ScrollView>) : activeTab === 'analytics' ? (<View style={{ flex: 1 }}>
|
|
4271
|
+
{selectedEvent != null ? (<AnalyticsDetail event={selectedEvent}/>) : analyticsSubTab === 'top_events' ? (<FlatList data={topEventsArray} keyExtractor={item => item[0]} contentContainerStyle={[
|
|
4272
|
+
styles.listContent,
|
|
4273
|
+
{ paddingHorizontal: 16, paddingTop: 12 },
|
|
4274
|
+
]} renderItem={({ item: [name, count], index }) => {
|
|
4275
|
+
const maxCount = topEventsArray[0]?.[1] || 1;
|
|
4276
|
+
const pct = Math.max(6, (count / maxCount) * 100);
|
|
4277
|
+
const color = getEventColor(name);
|
|
4278
|
+
return (<AnimatedEntrance index={index} distance={8} style={[
|
|
4279
|
+
styles.analyticsTopEventsCard,
|
|
4280
|
+
{ marginBottom: 6, paddingVertical: 10, paddingHorizontal: 12 },
|
|
4281
|
+
]}>
|
|
4282
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>
|
|
4283
|
+
<Text style={{
|
|
4284
|
+
fontFamily: AppFonts.interBold,
|
|
4285
|
+
fontSize: 11,
|
|
4286
|
+
color: color,
|
|
4287
|
+
width: 18,
|
|
4288
|
+
textAlign: 'center',
|
|
4289
|
+
}}>
|
|
4290
|
+
{index + 1}
|
|
4280
4291
|
</Text>
|
|
4292
|
+
<View style={{ flex: 1, gap: 4 }}>
|
|
4293
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
|
4294
|
+
<Text style={{
|
|
4295
|
+
fontFamily: AppFonts.interMedium,
|
|
4296
|
+
fontSize: 12.5,
|
|
4297
|
+
color: AppColors.primaryBlack,
|
|
4298
|
+
flex: 1,
|
|
4299
|
+
}} numberOfLines={1}>
|
|
4300
|
+
{name}
|
|
4301
|
+
</Text>
|
|
4302
|
+
<Text style={{
|
|
4303
|
+
fontFamily: AppFonts.interBold,
|
|
4304
|
+
fontSize: 12,
|
|
4305
|
+
color: color,
|
|
4306
|
+
marginLeft: 8,
|
|
4307
|
+
}}>
|
|
4308
|
+
{count}
|
|
4309
|
+
</Text>
|
|
4310
|
+
</View>
|
|
4311
|
+
<View style={{ height: 3, backgroundColor: AppColors.grayBackground, borderRadius: 2, overflow: 'hidden' }}>
|
|
4312
|
+
<LinearGradient colors={[color, `${color}88`]} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={{
|
|
4313
|
+
height: '100%',
|
|
4314
|
+
borderRadius: 2,
|
|
4315
|
+
width: `${pct}%`,
|
|
4316
|
+
}}/>
|
|
4317
|
+
</View>
|
|
4318
|
+
</View>
|
|
4281
4319
|
</View>
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
width: `${Math.max(6, (count / maxCount) * 100)}%`,
|
|
4287
|
-
},
|
|
4288
|
-
]}/>
|
|
4289
|
-
</View>
|
|
4290
|
-
<Text style={styles.analyticsTopEventCount}>
|
|
4291
|
-
{count}
|
|
4292
|
-
</Text>
|
|
4320
|
+
</AnimatedEntrance>);
|
|
4321
|
+
}} ListEmptyComponent={<View style={styles.emptyContainer}>
|
|
4322
|
+
<View style={styles.emptyIconWrap}>
|
|
4323
|
+
<EmptyRadarIcon color={AppColors.purple} size={32}/>
|
|
4293
4324
|
</View>
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
item.
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
if (routeInfo &&
|
|
4327
|
-
routeInfo.path !== 'Navigators') {
|
|
4328
|
-
const parts = routeInfo.path.split(' ➔ ');
|
|
4329
|
-
screenName = parts[parts.length - 1];
|
|
4325
|
+
<Text style={styles.emptyTitle}>No Top Events</Text>
|
|
4326
|
+
</View>}/>) : (<FlatList data={filteredAnalyticsEvents} keyExtractor={item => item.id.toString()} renderItem={({ item, index }) => {
|
|
4327
|
+
const prev = filteredAnalyticsEvents[index + 1];
|
|
4328
|
+
const next = filteredAnalyticsEvents[index - 1];
|
|
4329
|
+
const msSincePrev = prev
|
|
4330
|
+
? item.timestamp - prev.timestamp
|
|
4331
|
+
: undefined;
|
|
4332
|
+
const thisMin = Math.floor(item.timestamp / 60000);
|
|
4333
|
+
const nextMin = next
|
|
4334
|
+
? Math.floor(next.timestamp / 60000)
|
|
4335
|
+
: -1;
|
|
4336
|
+
const showTimestamp = index === 0 || thisMin !== nextMin;
|
|
4337
|
+
return (<AnimatedEntrance index={index} distance={8}>
|
|
4338
|
+
<AnalyticsEventCard event={item} onPress={() => {
|
|
4339
|
+
animateNextLayout();
|
|
4340
|
+
setSelectedEvent(item);
|
|
4341
|
+
}} isNew={newEventIds.has(item.id)} searchStr={analyticsSearch} isFirst={index === 0} isLast={index === filteredAnalyticsEvents.length - 1} msSincePrev={msSincePrev} showTimestamp={showTimestamp} computedScreenName={(() => {
|
|
4342
|
+
let screenName = item.screenName ||
|
|
4343
|
+
item.screenClass ||
|
|
4344
|
+
item.pageTitle ||
|
|
4345
|
+
item.pageLocation ||
|
|
4346
|
+
item.params?.firebase_screen ||
|
|
4347
|
+
item.params?.screen_name ||
|
|
4348
|
+
item.params?.firebase_screen_class ||
|
|
4349
|
+
item.params?.screen_class;
|
|
4350
|
+
const routeInfo = logRouteMapRef.current.get(item.id + 1000000);
|
|
4351
|
+
if (!screenName) {
|
|
4352
|
+
if (routeInfo &&
|
|
4353
|
+
routeInfo.path !== 'Navigators') {
|
|
4354
|
+
const parts = routeInfo.path.split(' ➔ ');
|
|
4355
|
+
screenName = parts[parts.length - 1];
|
|
4356
|
+
}
|
|
4330
4357
|
}
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
</
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
}
|
|
4354
|
-
|
|
4358
|
+
return screenName;
|
|
4359
|
+
})()}/>
|
|
4360
|
+
</AnimatedEntrance>);
|
|
4361
|
+
}} initialNumToRender={20} maxToRenderPerBatch={20} windowSize={5} removeClippedSubviews={false} ListEmptyComponent={<View style={styles.emptyContainer}>
|
|
4362
|
+
<View style={styles.emptyIconWrap}>
|
|
4363
|
+
<EmptyRadarIcon color={AppColors.purple} size={32}/>
|
|
4364
|
+
</View>
|
|
4365
|
+
<Text style={styles.emptyTitle}>
|
|
4366
|
+
{analyticsSearch.length > 0
|
|
4367
|
+
? 'No matching events'
|
|
4368
|
+
: 'No analytics events yet'}
|
|
4369
|
+
</Text>
|
|
4370
|
+
<Text style={styles.emptySub}>
|
|
4371
|
+
{analyticsSearch.length > 0
|
|
4372
|
+
? 'Try adjusting your search.'
|
|
4373
|
+
: 'Call setupAnalyticsLogger(analytics()) at app start.'}
|
|
4374
|
+
</Text>
|
|
4375
|
+
</View>} contentContainerStyle={[
|
|
4376
|
+
styles.listContent,
|
|
4377
|
+
filteredAnalyticsEvents.length === 0 && {
|
|
4378
|
+
flexGrow: 1,
|
|
4379
|
+
},
|
|
4380
|
+
]} keyboardShouldPersistTaps="handled"/>)}
|
|
4381
|
+
</View>) : activeTab === 'apis' && selected == null ? (<View style={{ flex: 1 }}>
|
|
4355
4382
|
<FlatList ref={apisListRef} data={groupedData} keyExtractor={item => item?.id?.toString()} renderItem={renderItem} initialNumToRender={10} maxToRenderPerBatch={10} windowSize={5} removeClippedSubviews={true} ListHeaderComponent={<View style={{ marginTop: 8 }}>
|
|
4356
4383
|
<View style={styles.toolbarRow}>
|
|
4357
4384
|
<View style={styles.searchContainer}>
|