react-native-inapp-inspector 1.1.1 → 1.1.3
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/README.md +14 -0
- package/dist/commonjs/components/ConsoleLogCard.js +18 -0
- package/dist/commonjs/components/JsonViewer.d.ts +2 -1
- package/dist/commonjs/components/JsonViewer.js +6 -4
- package/dist/commonjs/components/LogCard.js +17 -0
- package/dist/commonjs/components/MetaAccordion.js +26 -6
- package/dist/commonjs/components/NetworkIcons.d.ts +9 -2
- package/dist/commonjs/components/NetworkIcons.js +59 -3
- package/dist/commonjs/components/ReduxTreeView.js +80 -5
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/customHooks/reduxLogger.d.ts +21 -7
- package/dist/commonjs/customHooks/reduxLogger.js +147 -48
- package/dist/commonjs/customHooks/webViewLogger.js +13 -8
- package/dist/commonjs/helpers/settingsStore.d.ts +24 -0
- package/dist/commonjs/helpers/settingsStore.js +74 -0
- package/dist/commonjs/index.d.ts +1 -1
- package/dist/commonjs/index.js +897 -170
- package/dist/commonjs/styles/index.d.ts +40 -0
- package/dist/commonjs/styles/index.js +45 -2
- package/dist/commonjs/types/index.d.ts +4 -0
- package/dist/esm/components/ConsoleLogCard.js +18 -0
- package/dist/esm/components/JsonViewer.d.ts +2 -1
- package/dist/esm/components/JsonViewer.js +6 -4
- package/dist/esm/components/LogCard.js +17 -0
- package/dist/esm/components/MetaAccordion.js +27 -7
- package/dist/esm/components/NetworkIcons.d.ts +9 -2
- package/dist/esm/components/NetworkIcons.js +51 -2
- package/dist/esm/components/ReduxTreeView.js +81 -6
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/customHooks/reduxLogger.d.ts +21 -7
- package/dist/esm/customHooks/reduxLogger.js +145 -47
- package/dist/esm/customHooks/webViewLogger.js +13 -8
- package/dist/esm/helpers/settingsStore.d.ts +24 -0
- package/dist/esm/helpers/settingsStore.js +67 -0
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +896 -172
- package/dist/esm/styles/index.d.ts +40 -0
- package/dist/esm/styles/index.js +45 -2
- package/dist/esm/types/index.d.ts +4 -0
- package/example/App.tsx +199 -61
- package/example/ios/example.xcodeproj/project.pbxproj +0 -8
- package/example/package-lock.json +4 -3
- 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, LayoutAnimation, Modal, Platform, Pressable, ScrollView, Text, TextInput, View, Linking, Image, InteractionManager, ActivityIndicator, StatusBar, TouchableOpacity, UIManager, LogBox, } from 'react-native';
|
|
2
|
+
import { Alert, Animated, StyleSheet, FlatList, LayoutAnimation, 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';
|
|
@@ -23,8 +23,10 @@ import CodeSnippet from './components/CodeSnippet';
|
|
|
23
23
|
import AnimatedEntrance from './components/AnimatedEntrance';
|
|
24
24
|
// Helpers
|
|
25
25
|
import { formatDateTime, getStatusColor, getNavigationInfo, deduplicateLogs, getDomainColor, formatDisplayUrl, getFetchCommand, getCurlCommand, getSize, } from './helpers';
|
|
26
|
+
// #5 — settings persistence
|
|
27
|
+
import { loadSettings, saveSettings } from './helpers/settingsStore';
|
|
26
28
|
// Assets
|
|
27
|
-
import { EmptyRadarIcon, FailIcon, SearchIcon, ScreenIcon, ClearIcon, SortArrowIcon, FilterIcon, InsightsIcon, GlobeIcon, DownloadIcon, CloseWhite, TrashIcon, WhiteBackNavigation, TerminalIcon, SignalIcon, AnalyticsIcon, SunIcon, MoonIcon, BrandCircleIcon, BrandSquareIcon, HtmlIcon, CssIcon, JsIcon, ClockIcon, EyeIcon, CheckIcon, SettingsIcon, RequestIcon, ResponseIcon, HeadersIcon, StatusIcon, } from './components/NetworkIcons';
|
|
29
|
+
import { EmptyRadarIcon, FailIcon, SearchIcon, ScreenIcon, ClearIcon, SortArrowIcon, FilterIcon, InsightsIcon, GlobeIcon, DownloadIcon, CloseWhite, TrashIcon, WhiteBackNavigation, TerminalIcon, SignalIcon, AnalyticsIcon, SunIcon, MoonIcon, BrandCircleIcon, BrandSquareIcon, HtmlIcon, CssIcon, JsIcon, ClockIcon, EyeIcon, CheckIcon, SettingsIcon, RequestIcon, ResponseIcon, HeadersIcon, StatusIcon, ChevronIcon, WipeIcon, LayersIcon, UserIcon, InfoCircleIcon, WarningTriangleIcon, ErrorCircleIcon, TrendingUpIcon, } from './components/NetworkIcons';
|
|
28
30
|
import ErrorBoundary from './components/ErrorBoundary';
|
|
29
31
|
// Stylesheet
|
|
30
32
|
import { AppColors } from './styles/AppColors';
|
|
@@ -150,6 +152,8 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
150
152
|
const [showNetworkMenu, setShowNetworkMenu] = useState(false);
|
|
151
153
|
const [showUiMenu, setShowUiMenu] = useState(false);
|
|
152
154
|
const [sortOrder, setSortOrder] = useState('newest');
|
|
155
|
+
// #7 — sort order for the Logs (console) tab
|
|
156
|
+
const [logSortOrder, setLogSortOrder] = useState('newest');
|
|
153
157
|
const [reqExpanded, setReqExpanded] = useState(undefined);
|
|
154
158
|
const [resExpanded, setResExpanded] = useState(undefined);
|
|
155
159
|
const [showReqDiff, setShowReqDiff] = useState(false);
|
|
@@ -235,6 +239,141 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
235
239
|
const [reduxExpandDepth, setReduxExpandDepth] = useState(1);
|
|
236
240
|
const [slowRequestThreshold, setSlowRequestThreshold] = useState(1000);
|
|
237
241
|
const [insightsShowConsoleAlerts, setInsightsShowConsoleAlerts] = useState(true);
|
|
242
|
+
// #6 — tab the inspector opens on. Shown with a DEFAULT badge in Settings.
|
|
243
|
+
const [defaultTab, setDefaultTab] = useState('apis');
|
|
244
|
+
// #9 — when false (default), consecutive identical entries in the API and
|
|
245
|
+
// Console lists are collapsed into one row with a ×N counter.
|
|
246
|
+
const [showDuplicateLogs, setShowDuplicateLogs] = useState(false);
|
|
247
|
+
// #5 — hydrate persisted settings once, then auto-save on any change.
|
|
248
|
+
const settingsHydratedRef = useRef(false);
|
|
249
|
+
useEffect(() => {
|
|
250
|
+
let cancelled = false;
|
|
251
|
+
loadSettings().then(saved => {
|
|
252
|
+
if (cancelled)
|
|
253
|
+
return;
|
|
254
|
+
if (saved.isDark != null) {
|
|
255
|
+
setIsDark(saved.isDark);
|
|
256
|
+
toggleGlobalTheme(saved.isDark);
|
|
257
|
+
}
|
|
258
|
+
if (saved.modalHeightPercent != null)
|
|
259
|
+
setModalHeightPercent(saved.modalHeightPercent);
|
|
260
|
+
if (saved.tabVisibility)
|
|
261
|
+
setTabVisibility(prev => ({
|
|
262
|
+
...prev,
|
|
263
|
+
...saved.tabVisibility,
|
|
264
|
+
apis: true, // APIs is always required
|
|
265
|
+
}));
|
|
266
|
+
if (saved.defaultTab)
|
|
267
|
+
setDefaultTab(saved.defaultTab);
|
|
268
|
+
if (saved.maxNetworkLogs != null)
|
|
269
|
+
setMaxNetworkLogs(saved.maxNetworkLogs);
|
|
270
|
+
if (saved.maxConsoleLogs != null)
|
|
271
|
+
setMaxConsoleLogs(saved.maxConsoleLogs);
|
|
272
|
+
if (saved.showConsoleLevels)
|
|
273
|
+
setShowConsoleLevels(saved.showConsoleLevels);
|
|
274
|
+
if (saved.webViewCaptureCssJs != null)
|
|
275
|
+
setWebViewCaptureCssJs(saved.webViewCaptureCssJs);
|
|
276
|
+
if (saved.reduxAutoRefresh != null)
|
|
277
|
+
setReduxAutoRefreshState(saved.reduxAutoRefresh);
|
|
278
|
+
if (saved.reduxExpandDepth != null)
|
|
279
|
+
setReduxExpandDepth(saved.reduxExpandDepth);
|
|
280
|
+
if (saved.slowRequestThreshold != null)
|
|
281
|
+
setSlowRequestThreshold(saved.slowRequestThreshold);
|
|
282
|
+
if (saved.insightsShowConsoleAlerts != null)
|
|
283
|
+
setInsightsShowConsoleAlerts(saved.insightsShowConsoleAlerts);
|
|
284
|
+
if (saved.showDuplicateLogs != null)
|
|
285
|
+
setShowDuplicateLogs(saved.showDuplicateLogs);
|
|
286
|
+
if (saved.defaultTab) {
|
|
287
|
+
const dt = saved.defaultTab;
|
|
288
|
+
const vis = {
|
|
289
|
+
...{
|
|
290
|
+
insights: true,
|
|
291
|
+
apis: true,
|
|
292
|
+
logs: true,
|
|
293
|
+
analytics: true,
|
|
294
|
+
webview: true,
|
|
295
|
+
redux: true,
|
|
296
|
+
},
|
|
297
|
+
...(saved.tabVisibility || {}),
|
|
298
|
+
apis: true,
|
|
299
|
+
};
|
|
300
|
+
setActiveTab(vis[dt] ? dt : 'apis');
|
|
301
|
+
}
|
|
302
|
+
settingsHydratedRef.current = true;
|
|
303
|
+
});
|
|
304
|
+
return () => {
|
|
305
|
+
cancelled = true;
|
|
306
|
+
};
|
|
307
|
+
}, []);
|
|
308
|
+
useEffect(() => {
|
|
309
|
+
if (!settingsHydratedRef.current)
|
|
310
|
+
return;
|
|
311
|
+
saveSettings({
|
|
312
|
+
isDark,
|
|
313
|
+
modalHeightPercent,
|
|
314
|
+
tabVisibility,
|
|
315
|
+
defaultTab,
|
|
316
|
+
maxNetworkLogs,
|
|
317
|
+
maxConsoleLogs,
|
|
318
|
+
showConsoleLevels,
|
|
319
|
+
webViewCaptureCssJs,
|
|
320
|
+
reduxAutoRefresh,
|
|
321
|
+
reduxExpandDepth,
|
|
322
|
+
slowRequestThreshold,
|
|
323
|
+
insightsShowConsoleAlerts,
|
|
324
|
+
showDuplicateLogs,
|
|
325
|
+
});
|
|
326
|
+
}, [
|
|
327
|
+
isDark,
|
|
328
|
+
modalHeightPercent,
|
|
329
|
+
tabVisibility,
|
|
330
|
+
defaultTab,
|
|
331
|
+
maxNetworkLogs,
|
|
332
|
+
maxConsoleLogs,
|
|
333
|
+
showConsoleLevels,
|
|
334
|
+
webViewCaptureCssJs,
|
|
335
|
+
reduxAutoRefresh,
|
|
336
|
+
reduxExpandDepth,
|
|
337
|
+
slowRequestThreshold,
|
|
338
|
+
insightsShowConsoleAlerts,
|
|
339
|
+
showDuplicateLogs,
|
|
340
|
+
]);
|
|
341
|
+
// #1 — check NPM for a newer published version; surfaces an animated dot
|
|
342
|
+
// in the header next to the npm chip when an update is available.
|
|
343
|
+
const [latestNpmVersion, setLatestNpmVersion] = useState(null);
|
|
344
|
+
const updateAvailable = useMemo(() => {
|
|
345
|
+
if (!latestNpmVersion)
|
|
346
|
+
return false;
|
|
347
|
+
const parse = (v) => v
|
|
348
|
+
.replace(/^v/, '')
|
|
349
|
+
.split('.')
|
|
350
|
+
.map(n => parseInt(n, 10) || 0);
|
|
351
|
+
const cur = parse(LIB_VERSION);
|
|
352
|
+
const latest = parse(latestNpmVersion);
|
|
353
|
+
for (let i = 0; i < 3; i++) {
|
|
354
|
+
if ((latest[i] || 0) > (cur[i] || 0))
|
|
355
|
+
return true;
|
|
356
|
+
if ((latest[i] || 0) < (cur[i] || 0))
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
return false;
|
|
360
|
+
}, [latestNpmVersion]);
|
|
361
|
+
useEffect(() => {
|
|
362
|
+
let cancelled = false;
|
|
363
|
+
fetch('https://registry.npmjs.org/react-native-inapp-inspector/latest')
|
|
364
|
+
.then(res => (res.ok ? res.json() : null))
|
|
365
|
+
.then(data => {
|
|
366
|
+
if (!cancelled && data && typeof data.version === 'string') {
|
|
367
|
+
setLatestNpmVersion(data.version);
|
|
368
|
+
}
|
|
369
|
+
})
|
|
370
|
+
.catch(() => {
|
|
371
|
+
// Offline / blocked — silently skip the update check.
|
|
372
|
+
});
|
|
373
|
+
return () => {
|
|
374
|
+
cancelled = true;
|
|
375
|
+
};
|
|
376
|
+
}, []);
|
|
238
377
|
useEffect(() => {
|
|
239
378
|
setReduxAutoRefresh(reduxAutoRefresh);
|
|
240
379
|
}, [reduxAutoRefresh]);
|
|
@@ -248,6 +387,10 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
248
387
|
animateNextLayout();
|
|
249
388
|
setActiveTab('apis');
|
|
250
389
|
}
|
|
390
|
+
// #6 — a hidden module can't be the default landing tab.
|
|
391
|
+
if (!nextVal && defaultTab === key) {
|
|
392
|
+
setDefaultTab('apis');
|
|
393
|
+
}
|
|
251
394
|
return newVisibility;
|
|
252
395
|
});
|
|
253
396
|
};
|
|
@@ -329,6 +472,68 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
329
472
|
const badgeAnim = useRef(new Animated.Value(1)).current;
|
|
330
473
|
const activePulseAnim = useRef(new Animated.Value(0.4)).current;
|
|
331
474
|
const unreadPulseAnim = useRef(new Animated.Value(1)).current;
|
|
475
|
+
// #4 — diagonal light streak sweeping across the floating launcher
|
|
476
|
+
const fabShineAnim = useRef(new Animated.Value(0)).current;
|
|
477
|
+
// #11 — header "clear all" icon spin/scale animation
|
|
478
|
+
const clearAnim = useRef(new Animated.Value(0)).current;
|
|
479
|
+
// #4 — draggable floating launcher (drag anywhere on screen)
|
|
480
|
+
const fabPan = useRef(new Animated.ValueXY({ x: 0, y: 0 })).current;
|
|
481
|
+
const fabPanRef = useRef({ x: 0, y: 0 });
|
|
482
|
+
useEffect(() => {
|
|
483
|
+
const idX = fabPan.x.addListener(v => (fabPanRef.current.x = v.value));
|
|
484
|
+
const idY = fabPan.y.addListener(v => (fabPanRef.current.y = v.value));
|
|
485
|
+
return () => {
|
|
486
|
+
fabPan.x.removeListener(idX);
|
|
487
|
+
fabPan.y.removeListener(idY);
|
|
488
|
+
};
|
|
489
|
+
}, [fabPan]);
|
|
490
|
+
const fabDraggedRef = useRef(false);
|
|
491
|
+
const fabPanResponder = useRef(PanResponder.create({
|
|
492
|
+
// Let taps fall through to the launcher; only hijack once the
|
|
493
|
+
// finger actually moves, so onPress still fires on a tap.
|
|
494
|
+
onStartShouldSetPanResponder: () => false,
|
|
495
|
+
onMoveShouldSetPanResponder: (_e, g) => Math.abs(g.dx) > 4 || Math.abs(g.dy) > 4,
|
|
496
|
+
onPanResponderGrant: () => {
|
|
497
|
+
fabDraggedRef.current = true;
|
|
498
|
+
fabPan.setOffset({
|
|
499
|
+
x: fabPanRef.current.x,
|
|
500
|
+
y: fabPanRef.current.y,
|
|
501
|
+
});
|
|
502
|
+
fabPan.setValue({ x: 0, y: 0 });
|
|
503
|
+
},
|
|
504
|
+
onPanResponderMove: Animated.event([null, { dx: fabPan.x, dy: fabPan.y }], {
|
|
505
|
+
useNativeDriver: false,
|
|
506
|
+
}),
|
|
507
|
+
onPanResponderRelease: () => {
|
|
508
|
+
fabPan.flattenOffset();
|
|
509
|
+
// small delay so the trailing tap (if any) is ignored
|
|
510
|
+
setTimeout(() => {
|
|
511
|
+
fabDraggedRef.current = false;
|
|
512
|
+
}, 50);
|
|
513
|
+
},
|
|
514
|
+
onPanResponderTerminate: () => {
|
|
515
|
+
fabPan.flattenOffset();
|
|
516
|
+
fabDraggedRef.current = false;
|
|
517
|
+
},
|
|
518
|
+
})).current;
|
|
519
|
+
// #2 — scroll-to-top button for the main APIs list, always visible at the
|
|
520
|
+
// bottom right of the list.
|
|
521
|
+
const apisListRef = useRef(null);
|
|
522
|
+
const runClearAllWithAnimation = useCallback(() => {
|
|
523
|
+
Animated.sequence([
|
|
524
|
+
Animated.timing(clearAnim, {
|
|
525
|
+
toValue: 1,
|
|
526
|
+
duration: 320,
|
|
527
|
+
useNativeDriver: true,
|
|
528
|
+
}),
|
|
529
|
+
Animated.timing(clearAnim, {
|
|
530
|
+
toValue: 0,
|
|
531
|
+
duration: 0,
|
|
532
|
+
useNativeDriver: true,
|
|
533
|
+
}),
|
|
534
|
+
]).start();
|
|
535
|
+
handleClearAll();
|
|
536
|
+
}, [clearAnim]);
|
|
332
537
|
useEffect(() => {
|
|
333
538
|
if (Platform.OS === 'android') {
|
|
334
539
|
UIManager.setLayoutAnimationEnabledExperimental?.(true);
|
|
@@ -350,6 +555,24 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
350
555
|
loop.start();
|
|
351
556
|
return () => loop.stop();
|
|
352
557
|
}, [pulseAnim]);
|
|
558
|
+
// #4 — sweep the shine streak across the launcher, pause, repeat.
|
|
559
|
+
useEffect(() => {
|
|
560
|
+
const loop = Animated.loop(Animated.sequence([
|
|
561
|
+
Animated.timing(fabShineAnim, {
|
|
562
|
+
toValue: 1,
|
|
563
|
+
duration: 1100,
|
|
564
|
+
useNativeDriver: true,
|
|
565
|
+
}),
|
|
566
|
+
Animated.delay(1600),
|
|
567
|
+
Animated.timing(fabShineAnim, {
|
|
568
|
+
toValue: 0,
|
|
569
|
+
duration: 0,
|
|
570
|
+
useNativeDriver: true,
|
|
571
|
+
}),
|
|
572
|
+
]));
|
|
573
|
+
loop.start();
|
|
574
|
+
return () => loop.stop();
|
|
575
|
+
}, [fabShineAnim]);
|
|
353
576
|
useEffect(() => {
|
|
354
577
|
const loop = Animated.loop(Animated.sequence([
|
|
355
578
|
Animated.timing(activePulseAnim, {
|
|
@@ -393,6 +616,16 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
393
616
|
}).start();
|
|
394
617
|
}
|
|
395
618
|
}, [newLogIds]);
|
|
619
|
+
// #6 — every time the inspector is opened, land on the chosen default tab.
|
|
620
|
+
useEffect(() => {
|
|
621
|
+
if (visible) {
|
|
622
|
+
const target = defaultTab === 'apis' || tabVisibility[defaultTab]
|
|
623
|
+
? defaultTab
|
|
624
|
+
: 'apis';
|
|
625
|
+
setActiveTab(target);
|
|
626
|
+
}
|
|
627
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
628
|
+
}, [visible]);
|
|
396
629
|
useEffect(() => {
|
|
397
630
|
if (visible) {
|
|
398
631
|
const task = InteractionManager.runAfterInteractions(() => {
|
|
@@ -546,8 +779,38 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
546
779
|
if (sortOrder === 'oldest') {
|
|
547
780
|
result = [...result].reverse();
|
|
548
781
|
}
|
|
782
|
+
// #9 — collapse consecutive identical requests (same method + url +
|
|
783
|
+
// status) into one row carrying a ×N counter, unless the user opted in
|
|
784
|
+
// to seeing every duplicate via Settings → "Show Duplicate Logs".
|
|
785
|
+
if (!showDuplicateLogs) {
|
|
786
|
+
const collapsed = [];
|
|
787
|
+
for (const log of result) {
|
|
788
|
+
const last = collapsed[collapsed.length - 1];
|
|
789
|
+
if (last &&
|
|
790
|
+
last.method === log.method &&
|
|
791
|
+
last.url === log.url &&
|
|
792
|
+
last.status === log.status) {
|
|
793
|
+
collapsed[collapsed.length - 1] = {
|
|
794
|
+
...last,
|
|
795
|
+
duplicateCount: (last.duplicateCount || 1) + 1,
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
else {
|
|
799
|
+
collapsed.push({ ...log, duplicateCount: 1 });
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
result = collapsed;
|
|
803
|
+
}
|
|
549
804
|
return result.slice(0, maxNetworkLogs);
|
|
550
|
-
}, [
|
|
805
|
+
}, [
|
|
806
|
+
logs,
|
|
807
|
+
search,
|
|
808
|
+
statusFilters,
|
|
809
|
+
methodFilters,
|
|
810
|
+
sortOrder,
|
|
811
|
+
maxNetworkLogs,
|
|
812
|
+
showDuplicateLogs,
|
|
813
|
+
]);
|
|
551
814
|
const availableMethods = useMemo(() => {
|
|
552
815
|
const methods = new Set();
|
|
553
816
|
logs.forEach(log => {
|
|
@@ -753,8 +1016,39 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
753
1016
|
result = result.filter(log => log.message.toLowerCase().includes(s) ||
|
|
754
1017
|
(log.caller ?? '').toLowerCase().includes(s));
|
|
755
1018
|
}
|
|
1019
|
+
// #7 — apply sort order (newest/oldest first)
|
|
1020
|
+
result = [...result].sort((a, b) => logSortOrder === 'newest'
|
|
1021
|
+
? b.timestamp - a.timestamp
|
|
1022
|
+
: a.timestamp - b.timestamp);
|
|
1023
|
+
// #9 — collapse consecutive identical messages into one row with a ×N
|
|
1024
|
+
// counter unless duplicates are explicitly enabled in Settings.
|
|
1025
|
+
if (!showDuplicateLogs) {
|
|
1026
|
+
const collapsed = [];
|
|
1027
|
+
for (const log of result) {
|
|
1028
|
+
const last = collapsed[collapsed.length - 1];
|
|
1029
|
+
if (last &&
|
|
1030
|
+
last.type === log.type &&
|
|
1031
|
+
last.sourceMethod === log.sourceMethod &&
|
|
1032
|
+
last.message === log.message) {
|
|
1033
|
+
collapsed[collapsed.length - 1] = {
|
|
1034
|
+
...last,
|
|
1035
|
+
duplicateCount: (last.duplicateCount || 1) + 1,
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
else {
|
|
1039
|
+
collapsed.push({ ...log, duplicateCount: 1 });
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
result = collapsed;
|
|
1043
|
+
}
|
|
756
1044
|
return result;
|
|
757
|
-
}, [
|
|
1045
|
+
}, [
|
|
1046
|
+
visibleConsoleLogs,
|
|
1047
|
+
logFilters,
|
|
1048
|
+
logSearch,
|
|
1049
|
+
logSortOrder,
|
|
1050
|
+
showDuplicateLogs,
|
|
1051
|
+
]);
|
|
758
1052
|
const filteredWebViewLogs = useMemo(() => {
|
|
759
1053
|
let result = webViewLogs;
|
|
760
1054
|
if (webViewSearch) {
|
|
@@ -880,6 +1174,22 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
880
1174
|
]);
|
|
881
1175
|
return;
|
|
882
1176
|
}
|
|
1177
|
+
if (activeTab === 'redux') {
|
|
1178
|
+
Alert.alert('Clear Redux Timeline', 'Are you sure you want to clear the dispatched action history?', [
|
|
1179
|
+
{ text: 'Cancel', style: 'cancel' },
|
|
1180
|
+
{
|
|
1181
|
+
text: 'Clear All',
|
|
1182
|
+
onPress: () => {
|
|
1183
|
+
clearActionHistory();
|
|
1184
|
+
setReduxActionHistory([]);
|
|
1185
|
+
setReduxLastActionMap({});
|
|
1186
|
+
},
|
|
1187
|
+
style: 'destructive',
|
|
1188
|
+
},
|
|
1189
|
+
]);
|
|
1190
|
+
return;
|
|
1191
|
+
}
|
|
1192
|
+
// Default: APIs tab. Only clears NETWORK logs — never touches the other tabs.
|
|
883
1193
|
if (selectedLogs.size > 0) {
|
|
884
1194
|
setLogs(prev => prev.filter(l => !selectedLogs.has(l.id)));
|
|
885
1195
|
setSelectedLogs(new Set());
|
|
@@ -887,10 +1197,26 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
887
1197
|
else {
|
|
888
1198
|
Alert.alert('Clear Logs', 'Are you sure you want to clear all network logs?', [
|
|
889
1199
|
{ text: 'Cancel', style: 'cancel' },
|
|
890
|
-
{
|
|
1200
|
+
{
|
|
1201
|
+
text: 'Clear All',
|
|
1202
|
+
onPress: clearNetworkOnly,
|
|
1203
|
+
style: 'destructive',
|
|
1204
|
+
},
|
|
891
1205
|
]);
|
|
892
1206
|
}
|
|
893
1207
|
}
|
|
1208
|
+
// Clears ONLY network logs + their derived selection/filter state.
|
|
1209
|
+
function clearNetworkOnly() {
|
|
1210
|
+
clearNetworkLogs();
|
|
1211
|
+
setLogs([]);
|
|
1212
|
+
setSelectedLogs(new Set());
|
|
1213
|
+
setSectionFilters({});
|
|
1214
|
+
setCollapsedSections(new Set());
|
|
1215
|
+
setStatusFilters(new Set());
|
|
1216
|
+
setMethodFilters(new Set());
|
|
1217
|
+
prevLogIdsRef.current = new Set();
|
|
1218
|
+
logRouteMapRef.current = new Map();
|
|
1219
|
+
}
|
|
894
1220
|
const detailTitle = useMemo(() => {
|
|
895
1221
|
if (!selected)
|
|
896
1222
|
return '';
|
|
@@ -1119,7 +1445,8 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
1119
1445
|
}}>
|
|
1120
1446
|
{tab.label}
|
|
1121
1447
|
</Text>
|
|
1122
|
-
{
|
|
1448
|
+
{/* #6 — badge marks the configured default tab */}
|
|
1449
|
+
{tab.key === defaultTab && (<View style={{
|
|
1123
1450
|
flexDirection: 'row',
|
|
1124
1451
|
alignItems: 'center',
|
|
1125
1452
|
backgroundColor: 'rgba(104,75,155,0.08)',
|
|
@@ -1224,7 +1551,7 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
1224
1551
|
})}
|
|
1225
1552
|
</View>
|
|
1226
1553
|
|
|
1227
|
-
{/* Preferences Section */}
|
|
1554
|
+
{/* UI Preferences Section */}
|
|
1228
1555
|
<View style={{ marginTop: 8 }}>
|
|
1229
1556
|
<Text style={{
|
|
1230
1557
|
fontFamily: AppFonts.interBold,
|
|
@@ -1233,7 +1560,7 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
1233
1560
|
letterSpacing: 0.6,
|
|
1234
1561
|
marginBottom: 8,
|
|
1235
1562
|
}}>
|
|
1236
|
-
PREFERENCES
|
|
1563
|
+
UI PREFERENCES
|
|
1237
1564
|
</Text>
|
|
1238
1565
|
<View style={{
|
|
1239
1566
|
backgroundColor: AppColors.primaryLight,
|
|
@@ -1387,6 +1714,169 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
1387
1714
|
})}
|
|
1388
1715
|
</View>
|
|
1389
1716
|
</View>
|
|
1717
|
+
|
|
1718
|
+
{/* Divider */}
|
|
1719
|
+
<View style={{
|
|
1720
|
+
height: 1,
|
|
1721
|
+
backgroundColor: AppColors.dividerColor,
|
|
1722
|
+
}}/>
|
|
1723
|
+
|
|
1724
|
+
{/* #6 — Default Tab */}
|
|
1725
|
+
<View style={{
|
|
1726
|
+
paddingVertical: 12,
|
|
1727
|
+
paddingHorizontal: 14,
|
|
1728
|
+
}}>
|
|
1729
|
+
<View style={{
|
|
1730
|
+
flexDirection: 'row',
|
|
1731
|
+
alignItems: 'center',
|
|
1732
|
+
gap: 8,
|
|
1733
|
+
}}>
|
|
1734
|
+
<View style={{
|
|
1735
|
+
width: 20,
|
|
1736
|
+
height: 20,
|
|
1737
|
+
borderRadius: 6,
|
|
1738
|
+
backgroundColor: AppColors.purpleShade50,
|
|
1739
|
+
borderWidth: 1,
|
|
1740
|
+
borderColor: 'rgba(104,75,155,0.2)',
|
|
1741
|
+
alignItems: 'center',
|
|
1742
|
+
justifyContent: 'center',
|
|
1743
|
+
}}>
|
|
1744
|
+
<LayersIcon color={AppColors.purple} size={11}/>
|
|
1745
|
+
</View>
|
|
1746
|
+
<View style={{ flex: 1 }}>
|
|
1747
|
+
<Text style={{
|
|
1748
|
+
fontFamily: AppFonts.interBold,
|
|
1749
|
+
fontSize: 13,
|
|
1750
|
+
color: AppColors.primaryBlack,
|
|
1751
|
+
}}>
|
|
1752
|
+
Default Tab
|
|
1753
|
+
</Text>
|
|
1754
|
+
<Text style={{
|
|
1755
|
+
fontFamily: AppFonts.interRegular,
|
|
1756
|
+
fontSize: 11,
|
|
1757
|
+
color: AppColors.grayText,
|
|
1758
|
+
marginTop: 1,
|
|
1759
|
+
}}>
|
|
1760
|
+
Tab shown when the inspector opens
|
|
1761
|
+
</Text>
|
|
1762
|
+
</View>
|
|
1763
|
+
</View>
|
|
1764
|
+
|
|
1765
|
+
{/* Segmented picker — only visible tabs are offered */}
|
|
1766
|
+
<View style={{
|
|
1767
|
+
flexDirection: 'row',
|
|
1768
|
+
flexWrap: 'wrap',
|
|
1769
|
+
backgroundColor: AppColors.grayBackground,
|
|
1770
|
+
borderRadius: 8,
|
|
1771
|
+
padding: 2.5,
|
|
1772
|
+
marginTop: 10,
|
|
1773
|
+
borderWidth: 1,
|
|
1774
|
+
borderColor: AppColors.dividerColor,
|
|
1775
|
+
gap: 2,
|
|
1776
|
+
}}>
|
|
1777
|
+
{settingsTabs
|
|
1778
|
+
.filter(tab => tab.key === 'apis' || tabVisibility[tab.key])
|
|
1779
|
+
.map(tab => {
|
|
1780
|
+
const isActive = defaultTab === tab.key;
|
|
1781
|
+
return (<TouchableScale key={tab.key} onPress={() => setDefaultTab(tab.key)} style={{
|
|
1782
|
+
paddingVertical: 6,
|
|
1783
|
+
paddingHorizontal: 10,
|
|
1784
|
+
alignItems: 'center',
|
|
1785
|
+
borderRadius: 6,
|
|
1786
|
+
backgroundColor: isActive
|
|
1787
|
+
? AppColors.purple
|
|
1788
|
+
: 'transparent',
|
|
1789
|
+
}}>
|
|
1790
|
+
<Text style={{
|
|
1791
|
+
fontFamily: AppFonts.interBold,
|
|
1792
|
+
fontSize: 11,
|
|
1793
|
+
color: isActive
|
|
1794
|
+
? '#FFFFFF'
|
|
1795
|
+
: AppColors.grayText,
|
|
1796
|
+
}}>
|
|
1797
|
+
{tab.label}
|
|
1798
|
+
</Text>
|
|
1799
|
+
</TouchableScale>);
|
|
1800
|
+
})}
|
|
1801
|
+
</View>
|
|
1802
|
+
</View>
|
|
1803
|
+
|
|
1804
|
+
{/* Divider */}
|
|
1805
|
+
<View style={{
|
|
1806
|
+
height: 1,
|
|
1807
|
+
backgroundColor: AppColors.dividerColor,
|
|
1808
|
+
}}/>
|
|
1809
|
+
|
|
1810
|
+
{/* #9 — Show Duplicate Logs */}
|
|
1811
|
+
<View style={{
|
|
1812
|
+
flexDirection: 'row',
|
|
1813
|
+
alignItems: 'center',
|
|
1814
|
+
paddingVertical: 12,
|
|
1815
|
+
paddingHorizontal: 14,
|
|
1816
|
+
gap: 12,
|
|
1817
|
+
}}>
|
|
1818
|
+
<View style={{
|
|
1819
|
+
flex: 1,
|
|
1820
|
+
flexDirection: 'row',
|
|
1821
|
+
alignItems: 'center',
|
|
1822
|
+
gap: 8,
|
|
1823
|
+
}}>
|
|
1824
|
+
<View style={{
|
|
1825
|
+
width: 20,
|
|
1826
|
+
height: 20,
|
|
1827
|
+
borderRadius: 6,
|
|
1828
|
+
backgroundColor: AppColors.purpleShade50,
|
|
1829
|
+
borderWidth: 1,
|
|
1830
|
+
borderColor: 'rgba(104,75,155,0.2)',
|
|
1831
|
+
alignItems: 'center',
|
|
1832
|
+
justifyContent: 'center',
|
|
1833
|
+
}}>
|
|
1834
|
+
<EyeIcon color={AppColors.purple} size={11}/>
|
|
1835
|
+
</View>
|
|
1836
|
+
<View style={{ flex: 1 }}>
|
|
1837
|
+
<Text style={{
|
|
1838
|
+
fontFamily: AppFonts.interBold,
|
|
1839
|
+
fontSize: 13,
|
|
1840
|
+
color: AppColors.primaryBlack,
|
|
1841
|
+
}}>
|
|
1842
|
+
Show Duplicate Logs
|
|
1843
|
+
</Text>
|
|
1844
|
+
<Text style={{
|
|
1845
|
+
fontFamily: AppFonts.interRegular,
|
|
1846
|
+
fontSize: 11,
|
|
1847
|
+
color: AppColors.grayText,
|
|
1848
|
+
marginTop: 1,
|
|
1849
|
+
}}>
|
|
1850
|
+
Off: repeated identical entries collapse into one row
|
|
1851
|
+
with a ×N count
|
|
1852
|
+
</Text>
|
|
1853
|
+
</View>
|
|
1854
|
+
</View>
|
|
1855
|
+
|
|
1856
|
+
{/* Toggle Switch */}
|
|
1857
|
+
<TouchableScale onPress={() => setShowDuplicateLogs(prev => !prev)} style={{
|
|
1858
|
+
width: 38,
|
|
1859
|
+
height: 22,
|
|
1860
|
+
borderRadius: 11,
|
|
1861
|
+
backgroundColor: showDuplicateLogs
|
|
1862
|
+
? AppColors.purple
|
|
1863
|
+
: AppColors.grayBorderSecondary,
|
|
1864
|
+
padding: 2,
|
|
1865
|
+
justifyContent: 'center',
|
|
1866
|
+
alignItems: showDuplicateLogs ? 'flex-end' : 'flex-start',
|
|
1867
|
+
}}>
|
|
1868
|
+
<View style={{
|
|
1869
|
+
width: 18,
|
|
1870
|
+
height: 18,
|
|
1871
|
+
borderRadius: 9,
|
|
1872
|
+
backgroundColor: '#FFFFFF',
|
|
1873
|
+
shadowColor: '#000',
|
|
1874
|
+
shadowOpacity: 0.15,
|
|
1875
|
+
shadowRadius: 1.5,
|
|
1876
|
+
shadowOffset: { width: 0, height: 1 },
|
|
1877
|
+
}}/>
|
|
1878
|
+
</TouchableScale>
|
|
1879
|
+
</View>
|
|
1390
1880
|
</View>
|
|
1391
1881
|
</View>
|
|
1392
1882
|
</ScrollView>
|
|
@@ -2489,50 +2979,6 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
2489
2979
|
const lastActionMap = reduxLastActionMap;
|
|
2490
2980
|
const actionHistory = reduxActionHistory;
|
|
2491
2981
|
return (<ScrollView style={styles.detailScroll} contentContainerStyle={{ paddingBottom: 24 }}>
|
|
2492
|
-
{/* Top Summary Card */}
|
|
2493
|
-
<View style={{
|
|
2494
|
-
backgroundColor: AppColors.primaryLight,
|
|
2495
|
-
borderRadius: 12,
|
|
2496
|
-
borderWidth: 1,
|
|
2497
|
-
borderColor: AppColors.grayBorderSecondary,
|
|
2498
|
-
padding: 14,
|
|
2499
|
-
marginHorizontal: 16,
|
|
2500
|
-
marginTop: 12,
|
|
2501
|
-
marginBottom: 12,
|
|
2502
|
-
flexDirection: 'row',
|
|
2503
|
-
alignItems: 'center',
|
|
2504
|
-
gap: 12,
|
|
2505
|
-
}}>
|
|
2506
|
-
<View style={{
|
|
2507
|
-
width: 44,
|
|
2508
|
-
height: 44,
|
|
2509
|
-
borderRadius: 10,
|
|
2510
|
-
backgroundColor: AppColors.purpleShade50,
|
|
2511
|
-
alignItems: 'center',
|
|
2512
|
-
justifyContent: 'center',
|
|
2513
|
-
}}>
|
|
2514
|
-
<TerminalIcon color={AppColors.purple} size={20}/>
|
|
2515
|
-
</View>
|
|
2516
|
-
<View style={{ flex: 1 }}>
|
|
2517
|
-
<Text style={{
|
|
2518
|
-
fontFamily: AppFonts.interBold,
|
|
2519
|
-
fontSize: 13,
|
|
2520
|
-
color: AppColors.primaryBlack,
|
|
2521
|
-
}}>
|
|
2522
|
-
Redux Store Snapshot
|
|
2523
|
-
</Text>
|
|
2524
|
-
<Text style={{
|
|
2525
|
-
fontFamily: AppFonts.interRegular,
|
|
2526
|
-
fontSize: 11,
|
|
2527
|
-
color: AppColors.grayText,
|
|
2528
|
-
marginTop: 2,
|
|
2529
|
-
}}>
|
|
2530
|
-
Total size: {getSize(reduxState)} • {reducerKeys.length} Reducers
|
|
2531
|
-
</Text>
|
|
2532
|
-
</View>
|
|
2533
|
-
<CopyButton value={() => reduxState} label="Overall Store"/>
|
|
2534
|
-
</View>
|
|
2535
|
-
|
|
2536
2982
|
{/* Tab View Selection Segments */}
|
|
2537
2983
|
<View style={{
|
|
2538
2984
|
flexDirection: 'row',
|
|
@@ -2540,6 +2986,7 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
2540
2986
|
borderRadius: 10,
|
|
2541
2987
|
padding: 3,
|
|
2542
2988
|
marginHorizontal: 16,
|
|
2989
|
+
marginTop: 12,
|
|
2543
2990
|
marginBottom: 12,
|
|
2544
2991
|
borderWidth: 1,
|
|
2545
2992
|
borderColor: AppColors.dividerColor,
|
|
@@ -2575,6 +3022,27 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
2575
3022
|
}}>
|
|
2576
3023
|
Action Timeline
|
|
2577
3024
|
</Text>
|
|
3025
|
+
<View style={{
|
|
3026
|
+
minWidth: 18,
|
|
3027
|
+
paddingHorizontal: 5,
|
|
3028
|
+
height: 16,
|
|
3029
|
+
borderRadius: 8,
|
|
3030
|
+
alignItems: 'center',
|
|
3031
|
+
justifyContent: 'center',
|
|
3032
|
+
backgroundColor: reduxActiveSubTab === 'timeline'
|
|
3033
|
+
? 'rgba(255,255,255,0.28)'
|
|
3034
|
+
: AppColors.dividerColor,
|
|
3035
|
+
}}>
|
|
3036
|
+
<Text style={{
|
|
3037
|
+
fontFamily: AppFonts.interBold,
|
|
3038
|
+
fontSize: 9,
|
|
3039
|
+
color: reduxActiveSubTab === 'timeline'
|
|
3040
|
+
? '#FFFFFF'
|
|
3041
|
+
: AppColors.grayText,
|
|
3042
|
+
}}>
|
|
3043
|
+
{actionHistory.length}
|
|
3044
|
+
</Text>
|
|
3045
|
+
</View>
|
|
2578
3046
|
</View>
|
|
2579
3047
|
</TouchableOpacity>
|
|
2580
3048
|
<TouchableOpacity onPress={() => {
|
|
@@ -2604,6 +3072,27 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
2604
3072
|
}}>
|
|
2605
3073
|
Store Tree
|
|
2606
3074
|
</Text>
|
|
3075
|
+
<View style={{
|
|
3076
|
+
minWidth: 18,
|
|
3077
|
+
paddingHorizontal: 5,
|
|
3078
|
+
height: 16,
|
|
3079
|
+
borderRadius: 8,
|
|
3080
|
+
alignItems: 'center',
|
|
3081
|
+
justifyContent: 'center',
|
|
3082
|
+
backgroundColor: reduxActiveSubTab === 'tree'
|
|
3083
|
+
? 'rgba(255,255,255,0.28)'
|
|
3084
|
+
: AppColors.dividerColor,
|
|
3085
|
+
}}>
|
|
3086
|
+
<Text style={{
|
|
3087
|
+
fontFamily: AppFonts.interBold,
|
|
3088
|
+
fontSize: 9,
|
|
3089
|
+
color: reduxActiveSubTab === 'tree'
|
|
3090
|
+
? '#FFFFFF'
|
|
3091
|
+
: AppColors.grayText,
|
|
3092
|
+
}}>
|
|
3093
|
+
{reducerKeys.length}
|
|
3094
|
+
</Text>
|
|
3095
|
+
</View>
|
|
2607
3096
|
</View>
|
|
2608
3097
|
</TouchableOpacity>
|
|
2609
3098
|
</View>
|
|
@@ -2650,21 +3139,50 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
2650
3139
|
};
|
|
2651
3140
|
return (<>
|
|
2652
3141
|
{hasNavigationContext && (<NavigationTracker onStateChange={setNavState}/>)}
|
|
2653
|
-
<
|
|
2654
|
-
<
|
|
2655
|
-
|
|
2656
|
-
|
|
3142
|
+
<Animated.View style={[styles.fabWrapper, { transform: fabPan.getTranslateTransform() }]} {...fabPanResponder.panHandlers}>
|
|
3143
|
+
<TouchableScale style={{ alignItems: 'center', justifyContent: 'center' }} onPress={() => {
|
|
3144
|
+
if (fabDraggedRef.current)
|
|
3145
|
+
return;
|
|
3146
|
+
setVisible(true);
|
|
3147
|
+
}} hitSlop={10}>
|
|
3148
|
+
<Animated.View style={[styles.fabPulseRing, { transform: [{ scale: pulseAnim }] }]}/>
|
|
3149
|
+
<BrandCircleIcon size={62}/>
|
|
3150
|
+
{/* #4 — shining sweep, clipped inside the circular launcher */}
|
|
3151
|
+
<View pointerEvents="none" style={styles.fabShineClip}>
|
|
3152
|
+
<Animated.View style={[
|
|
3153
|
+
styles.fabShineStreak,
|
|
3154
|
+
{
|
|
3155
|
+
transform: [
|
|
3156
|
+
{
|
|
3157
|
+
translateX: fabShineAnim.interpolate({
|
|
3158
|
+
inputRange: [0, 1],
|
|
3159
|
+
outputRange: [-48, 96],
|
|
3160
|
+
}),
|
|
3161
|
+
},
|
|
3162
|
+
{ rotate: '25deg' },
|
|
3163
|
+
],
|
|
3164
|
+
},
|
|
3165
|
+
]}>
|
|
3166
|
+
<LinearGradient colors={[
|
|
3167
|
+
'rgba(255,255,255,0)',
|
|
3168
|
+
'rgba(255,255,255,0.55)',
|
|
3169
|
+
'rgba(255,255,255,0)',
|
|
3170
|
+
]} start={{ x: 0, y: 0.5 }} end={{ x: 1, y: 0.5 }} style={{ flex: 1 }}/>
|
|
3171
|
+
</Animated.View>
|
|
3172
|
+
</View>
|
|
3173
|
+
{(logs.length > 0 || analyticsEvents.length > 0) && (<Animated.View style={[
|
|
2657
3174
|
styles.fabBadge,
|
|
2658
3175
|
hasErrors ? styles.fabBadgeError : styles.fabBadgeNormal,
|
|
2659
3176
|
{ transform: [{ scale: badgeAnim }] },
|
|
2660
3177
|
]}>
|
|
2661
|
-
|
|
2662
|
-
|
|
3178
|
+
<Text style={styles.fabBadgeText}>
|
|
3179
|
+
{logs.length + analyticsEvents.length > 99
|
|
2663
3180
|
? '99+'
|
|
2664
3181
|
: logs.length + analyticsEvents.length}
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
3182
|
+
</Text>
|
|
3183
|
+
</Animated.View>)}
|
|
3184
|
+
</TouchableScale>
|
|
3185
|
+
</Animated.View>
|
|
2668
3186
|
|
|
2669
3187
|
<Modal visible={visible} animationType="slide" transparent>
|
|
2670
3188
|
{visible && (<ErrorBoundary onClose={closeModal}>
|
|
@@ -2694,10 +3212,27 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
2694
3212
|
setSelectedEvent(null);
|
|
2695
3213
|
});
|
|
2696
3214
|
}} hitSlop={15} style={[
|
|
2697
|
-
|
|
3215
|
+
{
|
|
3216
|
+
width: 38,
|
|
3217
|
+
height: 38,
|
|
3218
|
+
borderRadius: 19,
|
|
3219
|
+
alignItems: 'center',
|
|
3220
|
+
justifyContent: 'center',
|
|
3221
|
+
backgroundColor: 'rgba(255,255,255,0.18)',
|
|
3222
|
+
borderWidth: 1,
|
|
3223
|
+
borderColor: 'rgba(255,255,255,0.30)',
|
|
3224
|
+
},
|
|
2698
3225
|
selected == null &&
|
|
2699
3226
|
selectedEvent == null && { display: 'none' },
|
|
2700
3227
|
]}>
|
|
3228
|
+
{/* Soft outer glow to fake a blurred circle */}
|
|
3229
|
+
<View style={{
|
|
3230
|
+
position: 'absolute',
|
|
3231
|
+
width: 48,
|
|
3232
|
+
height: 48,
|
|
3233
|
+
borderRadius: 24,
|
|
3234
|
+
backgroundColor: 'rgba(255,255,255,0.10)',
|
|
3235
|
+
}}/>
|
|
2701
3236
|
<WhiteBackNavigation />
|
|
2702
3237
|
</TouchableScale>
|
|
2703
3238
|
|
|
@@ -2708,8 +3243,8 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
2708
3243
|
flex: 1,
|
|
2709
3244
|
}}>
|
|
2710
3245
|
<View style={{
|
|
2711
|
-
width:
|
|
2712
|
-
height:
|
|
3246
|
+
width: 50,
|
|
3247
|
+
height: 50,
|
|
2713
3248
|
borderRadius: 10,
|
|
2714
3249
|
backgroundColor: 'rgba(255,255,255,0.13)',
|
|
2715
3250
|
borderWidth: 1.5,
|
|
@@ -2721,13 +3256,10 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
2721
3256
|
shadowRadius: 4,
|
|
2722
3257
|
shadowOffset: { width: 0, height: 2 },
|
|
2723
3258
|
}}>
|
|
2724
|
-
<BrandSquareIcon size={
|
|
3259
|
+
<BrandSquareIcon size={45}/>
|
|
2725
3260
|
</View>
|
|
2726
3261
|
<View style={{ gap: 3 }}>
|
|
2727
|
-
<Text style={[
|
|
2728
|
-
styles.headerTitle,
|
|
2729
|
-
{ fontSize: 17, letterSpacing: 0.2 },
|
|
2730
|
-
]}>
|
|
3262
|
+
<Text style={[styles.headerTitle]}>
|
|
2731
3263
|
RN InApp Inspector
|
|
2732
3264
|
</Text>
|
|
2733
3265
|
<View style={{
|
|
@@ -2810,6 +3342,29 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
2810
3342
|
</Text>
|
|
2811
3343
|
</View>
|
|
2812
3344
|
</View>
|
|
3345
|
+
|
|
3346
|
+
{/* #1 — pulsing dot when a newer version is on NPM */}
|
|
3347
|
+
{updateAvailable && (<Pressable hitSlop={10} onPress={() => Alert.alert('Update Available', `react-native-inapp-inspector v${latestNpmVersion} is available on NPM (installed: v${LIB_VERSION}).`, [
|
|
3348
|
+
{ text: 'Later', style: 'cancel' },
|
|
3349
|
+
{
|
|
3350
|
+
text: 'View on NPM',
|
|
3351
|
+
onPress: () => Linking.openURL('https://www.npmjs.com/package/react-native-inapp-inspector').catch(() => { }),
|
|
3352
|
+
},
|
|
3353
|
+
])} style={{
|
|
3354
|
+
alignItems: 'center',
|
|
3355
|
+
justifyContent: 'center',
|
|
3356
|
+
}}>
|
|
3357
|
+
<Animated.View style={{
|
|
3358
|
+
width: 8,
|
|
3359
|
+
height: 8,
|
|
3360
|
+
borderRadius: 4,
|
|
3361
|
+
backgroundColor: '#4ADE80',
|
|
3362
|
+
borderWidth: 1,
|
|
3363
|
+
borderColor: 'rgba(255,255,255,0.9)',
|
|
3364
|
+
opacity: activePulseAnim,
|
|
3365
|
+
transform: [{ scale: unreadPulseAnim }],
|
|
3366
|
+
}}/>
|
|
3367
|
+
</Pressable>)}
|
|
2813
3368
|
</View>
|
|
2814
3369
|
</View>
|
|
2815
3370
|
</View>) : null}
|
|
@@ -2834,21 +3389,48 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
2834
3389
|
</Text>
|
|
2835
3390
|
</View>
|
|
2836
3391
|
<View style={styles.headerDetailSubRow}>
|
|
2837
|
-
<View style={
|
|
3392
|
+
<View style={{
|
|
3393
|
+
flexDirection: 'row',
|
|
3394
|
+
alignItems: 'center',
|
|
3395
|
+
gap: 5,
|
|
3396
|
+
paddingHorizontal: 8,
|
|
3397
|
+
paddingVertical: 3,
|
|
3398
|
+
borderRadius: 20,
|
|
3399
|
+
backgroundColor: `${getStatusColor(selected.status)}26`,
|
|
3400
|
+
borderWidth: 1,
|
|
3401
|
+
borderColor: `${getStatusColor(selected.status)}55`,
|
|
3402
|
+
}}>
|
|
3403
|
+
<View style={[
|
|
2838
3404
|
styles.headerStatusDot,
|
|
2839
3405
|
{
|
|
2840
3406
|
backgroundColor: getStatusColor(selected.status),
|
|
2841
3407
|
},
|
|
2842
3408
|
]}/>
|
|
2843
|
-
|
|
2844
|
-
|
|
3409
|
+
<Text style={[
|
|
3410
|
+
styles.headerSubTitle,
|
|
3411
|
+
{ fontFamily: AppFonts.interBold },
|
|
3412
|
+
]}>
|
|
3413
|
+
{selected.status === 0
|
|
2845
3414
|
? 'Failed'
|
|
2846
|
-
: selected.status ?? 'Pending'}
|
|
2847
|
-
|
|
2848
|
-
|
|
3415
|
+
: selected.status ?? 'Pending'}
|
|
3416
|
+
</Text>
|
|
3417
|
+
</View>
|
|
3418
|
+
<View style={{
|
|
3419
|
+
flexDirection: 'row',
|
|
3420
|
+
alignItems: 'center',
|
|
3421
|
+
gap: 4,
|
|
3422
|
+
paddingHorizontal: 8,
|
|
3423
|
+
paddingVertical: 3,
|
|
3424
|
+
borderRadius: 20,
|
|
3425
|
+
backgroundColor: 'rgba(255,255,255,0.16)',
|
|
3426
|
+
}}>
|
|
3427
|
+
<ClockIcon color="#FFFFFF" size={11}/>
|
|
3428
|
+
<Text style={styles.headerSubTitle}>
|
|
3429
|
+
{selected.duration != null
|
|
2849
3430
|
? `${selected.duration}ms`
|
|
2850
|
-
: '
|
|
2851
|
-
|
|
3431
|
+
: '—'}
|
|
3432
|
+
</Text>
|
|
3433
|
+
</View>
|
|
2852
3434
|
</View>
|
|
2853
3435
|
</View>) : selectedEvent != null ? (<View style={styles.headerDetailCenter}>
|
|
2854
3436
|
<View style={styles.headerDetailRow}>
|
|
@@ -2891,7 +3473,50 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
2891
3473
|
</View>) : null}
|
|
2892
3474
|
</View>
|
|
2893
3475
|
|
|
2894
|
-
<View style={
|
|
3476
|
+
<View style={[
|
|
3477
|
+
styles.headerRight,
|
|
3478
|
+
selected == null &&
|
|
3479
|
+
selectedEvent == null && {
|
|
3480
|
+
flexShrink: 0,
|
|
3481
|
+
minWidth: 116,
|
|
3482
|
+
},
|
|
3483
|
+
]}>
|
|
3484
|
+
{selected == null && selectedEvent == null && (<TouchableScale onPress={() => {
|
|
3485
|
+
Alert.alert('Clear Everything', 'This clears all tabs — APIs, Logs, Analytics, WebView and Redux timeline. Continue?', [
|
|
3486
|
+
{ text: 'Cancel', style: 'cancel' },
|
|
3487
|
+
{
|
|
3488
|
+
text: 'Clear All',
|
|
3489
|
+
onPress: runClearAllWithAnimation,
|
|
3490
|
+
style: 'destructive',
|
|
3491
|
+
},
|
|
3492
|
+
]);
|
|
3493
|
+
}} hitSlop={15} style={[
|
|
3494
|
+
styles.closeButtonSquare,
|
|
3495
|
+
{
|
|
3496
|
+
marginRight: 8,
|
|
3497
|
+
backgroundColor: 'rgba(255,255,255,0.15)',
|
|
3498
|
+
},
|
|
3499
|
+
]}>
|
|
3500
|
+
<Animated.View style={{
|
|
3501
|
+
transform: [
|
|
3502
|
+
{
|
|
3503
|
+
rotate: clearAnim.interpolate({
|
|
3504
|
+
inputRange: [0, 1],
|
|
3505
|
+
outputRange: ['0deg', '-25deg'],
|
|
3506
|
+
}),
|
|
3507
|
+
},
|
|
3508
|
+
{
|
|
3509
|
+
scale: clearAnim.interpolate({
|
|
3510
|
+
inputRange: [0, 0.5, 1],
|
|
3511
|
+
outputRange: [1, 1.25, 1],
|
|
3512
|
+
}),
|
|
3513
|
+
},
|
|
3514
|
+
],
|
|
3515
|
+
}}>
|
|
3516
|
+
<WipeIcon color="#FFFFFF" size={16}/>
|
|
3517
|
+
</Animated.View>
|
|
3518
|
+
</TouchableScale>)}
|
|
3519
|
+
|
|
2895
3520
|
{selected == null && selectedEvent == null && (<TouchableScale onPress={() => setSettingsPage('main')} hitSlop={15} style={[
|
|
2896
3521
|
styles.closeButtonSquare,
|
|
2897
3522
|
{
|
|
@@ -3068,7 +3693,16 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
3068
3693
|
animateNextLayout();
|
|
3069
3694
|
setAnalyticsSubTab('ga_events');
|
|
3070
3695
|
}}>
|
|
3071
|
-
<
|
|
3696
|
+
<View style={{
|
|
3697
|
+
flexDirection: 'row',
|
|
3698
|
+
alignItems: 'center',
|
|
3699
|
+
gap: 6,
|
|
3700
|
+
}}>
|
|
3701
|
+
{/* #7 */}
|
|
3702
|
+
<AnalyticsIcon size={13} color={analyticsSubTab === 'ga_events'
|
|
3703
|
+
? AppColors.purple
|
|
3704
|
+
: AppColors.grayTextStrong}/>
|
|
3705
|
+
<Text style={[
|
|
3072
3706
|
{
|
|
3073
3707
|
fontFamily: AppFonts.interMedium,
|
|
3074
3708
|
fontSize: 13,
|
|
@@ -3079,12 +3713,13 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
3079
3713
|
color: AppColors.purple,
|
|
3080
3714
|
},
|
|
3081
3715
|
]}>
|
|
3082
|
-
|
|
3083
|
-
|
|
3716
|
+
GA Events (
|
|
3717
|
+
{analyticsSearch
|
|
3084
3718
|
? filteredAnalyticsEvents.length
|
|
3085
3719
|
: analyticsEvents.length}
|
|
3086
|
-
|
|
3087
|
-
|
|
3720
|
+
)
|
|
3721
|
+
</Text>
|
|
3722
|
+
</View>
|
|
3088
3723
|
</Pressable>
|
|
3089
3724
|
<Pressable style={[
|
|
3090
3725
|
{
|
|
@@ -3105,7 +3740,16 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
3105
3740
|
animateNextLayout();
|
|
3106
3741
|
setAnalyticsSubTab('top_events');
|
|
3107
3742
|
}}>
|
|
3108
|
-
<
|
|
3743
|
+
<View style={{
|
|
3744
|
+
flexDirection: 'row',
|
|
3745
|
+
alignItems: 'center',
|
|
3746
|
+
gap: 6,
|
|
3747
|
+
}}>
|
|
3748
|
+
{/* #7 */}
|
|
3749
|
+
<TrendingUpIcon size={13} color={analyticsSubTab === 'top_events'
|
|
3750
|
+
? AppColors.purple
|
|
3751
|
+
: AppColors.grayTextStrong}/>
|
|
3752
|
+
<Text style={[
|
|
3109
3753
|
{
|
|
3110
3754
|
fontFamily: AppFonts.interMedium,
|
|
3111
3755
|
fontSize: 13,
|
|
@@ -3116,8 +3760,9 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
3116
3760
|
color: AppColors.purple,
|
|
3117
3761
|
},
|
|
3118
3762
|
]}>
|
|
3119
|
-
|
|
3120
|
-
|
|
3763
|
+
Top Events ({topEventsArray.length})
|
|
3764
|
+
</Text>
|
|
3765
|
+
</View>
|
|
3121
3766
|
</Pressable>
|
|
3122
3767
|
</View>
|
|
3123
3768
|
</View>)}
|
|
@@ -3226,50 +3871,53 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
3226
3871
|
filteredAnalyticsEvents.length === 0 && {
|
|
3227
3872
|
flexGrow: 1,
|
|
3228
3873
|
},
|
|
3229
|
-
]} keyboardShouldPersistTaps="handled"/>)) : activeTab === 'apis' && selected == null ? (<
|
|
3230
|
-
|
|
3231
|
-
<View style={styles.
|
|
3232
|
-
<
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3874
|
+
]} keyboardShouldPersistTaps="handled"/>)) : activeTab === 'apis' && selected == null ? (<View style={{ flex: 1 }}>
|
|
3875
|
+
<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 }}>
|
|
3876
|
+
<View style={styles.toolbarRow}>
|
|
3877
|
+
<View style={styles.searchContainer}>
|
|
3878
|
+
<SearchIcon color={AppColors.grayTextWeak} size={16}/>
|
|
3879
|
+
<TextInput placeholder="Search endpoints..." placeholderTextColor={AppColors.grayTextWeak} value={search} onChangeText={setSearch} style={styles.searchInput} autoCorrect={false} autoCapitalize="none"/>
|
|
3880
|
+
{search.length > 0 && (<Pressable onPress={() => setSearch('')} hitSlop={10} style={styles.clearBtn}>
|
|
3881
|
+
<ClearIcon color={AppColors.grayTextWeak} size={14}/>
|
|
3882
|
+
</Pressable>)}
|
|
3883
|
+
</View>
|
|
3238
3884
|
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3885
|
+
<View style={styles.toolbarRight}>
|
|
3886
|
+
<TouchableScale style={styles.toolbarBtn} onPress={handleDelete} hitSlop={10}>
|
|
3887
|
+
<TrashIcon color={AppColors.grayTextStrong} size={18}/>
|
|
3888
|
+
{selectedLogs.size > 0 && (<View style={styles.trashBadge}>
|
|
3889
|
+
<Text style={styles.trashBadgeText}>
|
|
3890
|
+
{selectedLogs.size}
|
|
3891
|
+
</Text>
|
|
3892
|
+
</View>)}
|
|
3893
|
+
</TouchableScale>
|
|
3248
3894
|
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3895
|
+
<TouchableScale style={styles.toolbarBtn} onPress={() => setSortOrder(o => o === 'newest' ? 'oldest' : 'newest')} hitSlop={10}>
|
|
3896
|
+
<SortArrowIcon direction={sortOrder === 'newest' ? 'down' : 'up'} color={AppColors.grayTextStrong} size={18}/>
|
|
3897
|
+
</TouchableScale>
|
|
3252
3898
|
|
|
3253
|
-
|
|
3899
|
+
<TouchableScale style={[
|
|
3254
3900
|
styles.toolbarBtn,
|
|
3255
3901
|
filtersAccordion.isOpen &&
|
|
3256
3902
|
styles.toolbarBtnActive,
|
|
3257
3903
|
]} onPress={filtersAccordion.toggleOpen} hitSlop={10}>
|
|
3258
|
-
|
|
3904
|
+
<FilterIcon color={filtersAccordion.isOpen
|
|
3259
3905
|
? AppColors.purple
|
|
3260
3906
|
: AppColors.grayTextStrong} size={18}/>
|
|
3261
|
-
|
|
3907
|
+
</TouchableScale>
|
|
3908
|
+
</View>
|
|
3262
3909
|
</View>
|
|
3263
|
-
</View>
|
|
3264
3910
|
|
|
3265
|
-
|
|
3911
|
+
<Animated.View style={[
|
|
3266
3912
|
filtersAccordion.bodyStyle,
|
|
3267
3913
|
{ overflow: 'hidden' },
|
|
3268
3914
|
]}>
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3915
|
+
<View style={styles.filtersContainer}>
|
|
3916
|
+
<Text style={styles.filtersHeading}>
|
|
3917
|
+
STATUS
|
|
3918
|
+
</Text>
|
|
3919
|
+
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.statusRowContent}>
|
|
3920
|
+
{STATUS_FILTERS.map(filter => {
|
|
3273
3921
|
const isAll = filter === 'ALL';
|
|
3274
3922
|
const active = isAll
|
|
3275
3923
|
? statusFilters.size === 0
|
|
@@ -3288,38 +3936,38 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
3288
3936
|
});
|
|
3289
3937
|
}
|
|
3290
3938
|
}} hitSlop={10}>
|
|
3291
|
-
|
|
3939
|
+
{active ? (<View style={[
|
|
3292
3940
|
styles.statusFilterChip,
|
|
3293
3941
|
styles.statusFilterActive,
|
|
3294
3942
|
{ overflow: 'hidden' },
|
|
3295
3943
|
]}>
|
|
3296
|
-
|
|
3944
|
+
<LinearGradient colors={[
|
|
3297
3945
|
AppColors.purpleShade50,
|
|
3298
3946
|
'#EAE5FF',
|
|
3299
3947
|
]} style={StyleSheet.absoluteFill} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }}/>
|
|
3300
|
-
|
|
3948
|
+
<Text style={[
|
|
3301
3949
|
styles.statusFilterText,
|
|
3302
3950
|
{ color: AppColors.purple },
|
|
3303
3951
|
]}>
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3952
|
+
{filter}
|
|
3953
|
+
</Text>
|
|
3954
|
+
</View>) : (<View style={styles.statusFilterChip}>
|
|
3955
|
+
<Text style={styles.statusFilterText}>
|
|
3956
|
+
{filter}
|
|
3957
|
+
</Text>
|
|
3958
|
+
</View>)}
|
|
3959
|
+
</TouchableScale>);
|
|
3312
3960
|
})}
|
|
3313
|
-
|
|
3961
|
+
</ScrollView>
|
|
3314
3962
|
|
|
3315
|
-
|
|
3963
|
+
<Text style={[
|
|
3316
3964
|
styles.filtersHeading,
|
|
3317
3965
|
{ marginTop: 16 },
|
|
3318
3966
|
]}>
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3967
|
+
METHOD
|
|
3968
|
+
</Text>
|
|
3969
|
+
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.statusRowContent}>
|
|
3970
|
+
{availableMethods.map(filter => {
|
|
3323
3971
|
const isAll = filter === 'ALL';
|
|
3324
3972
|
const active = isAll
|
|
3325
3973
|
? methodFilters.size === 0
|
|
@@ -3338,43 +3986,55 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
3338
3986
|
});
|
|
3339
3987
|
}
|
|
3340
3988
|
}} hitSlop={10}>
|
|
3341
|
-
|
|
3989
|
+
{active ? (<View style={[
|
|
3342
3990
|
styles.statusFilterChip,
|
|
3343
3991
|
styles.statusFilterActive,
|
|
3344
3992
|
{ overflow: 'hidden' },
|
|
3345
3993
|
]}>
|
|
3346
|
-
|
|
3994
|
+
<LinearGradient colors={[
|
|
3347
3995
|
AppColors.purpleShade50,
|
|
3348
3996
|
'#EAE5FF',
|
|
3349
3997
|
]} style={StyleSheet.absoluteFill} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }}/>
|
|
3350
|
-
|
|
3998
|
+
<Text style={[
|
|
3351
3999
|
styles.statusFilterText,
|
|
3352
4000
|
{ color: AppColors.purple },
|
|
3353
4001
|
]}>
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
4002
|
+
{filter}
|
|
4003
|
+
</Text>
|
|
4004
|
+
</View>) : (<View style={styles.statusFilterChip}>
|
|
4005
|
+
<Text style={styles.statusFilterText}>
|
|
4006
|
+
{filter}
|
|
4007
|
+
</Text>
|
|
4008
|
+
</View>)}
|
|
4009
|
+
</TouchableScale>);
|
|
3362
4010
|
})}
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
4011
|
+
</ScrollView>
|
|
4012
|
+
</View>
|
|
4013
|
+
</Animated.View>
|
|
3366
4014
|
|
|
3367
|
-
|
|
4015
|
+
{(search ||
|
|
3368
4016
|
statusFilters.size > 0 ||
|
|
3369
4017
|
methodFilters.size > 0) && (<Text style={styles.resultCount}>
|
|
3370
|
-
|
|
4018
|
+
{filteredLogs.length === logs.length
|
|
3371
4019
|
? `${logs.length} requests`
|
|
3372
4020
|
: `${filteredLogs.length} of ${logs.length} filtered requests`}
|
|
3373
|
-
|
|
3374
|
-
|
|
4021
|
+
</Text>)}
|
|
4022
|
+
</View>} ListEmptyComponent={<EmptyState isSearch={search.length > 0 || statusFilters.size > 0}/>} contentContainerStyle={[
|
|
3375
4023
|
styles.listContent,
|
|
3376
4024
|
filteredLogs.length === 0 && { flexGrow: 1 },
|
|
3377
|
-
]} keyboardShouldPersistTaps="handled"/>
|
|
4025
|
+
]} keyboardShouldPersistTaps="handled"/>
|
|
4026
|
+
{/* #2 — always-visible scroll-to-top, bottom right */}
|
|
4027
|
+
<TouchableScale onPress={() => {
|
|
4028
|
+
apisListRef.current?.scrollToOffset({
|
|
4029
|
+
offset: 0,
|
|
4030
|
+
animated: true,
|
|
4031
|
+
});
|
|
4032
|
+
}} hitSlop={10} style={styles.scrollTopBtn}>
|
|
4033
|
+
<View style={{ transform: [{ rotate: '180deg' }] }}>
|
|
4034
|
+
<ChevronIcon color="#FFFFFF" size={18}/>
|
|
4035
|
+
</View>
|
|
4036
|
+
</TouchableScale>
|
|
4037
|
+
</View>) : activeTab === 'logs' ? (<View style={{ flex: 1 }}>
|
|
3378
4038
|
<View style={{
|
|
3379
4039
|
backgroundColor: '#FFFFFF',
|
|
3380
4040
|
borderBottomWidth: 1,
|
|
@@ -3394,6 +4054,9 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
3394
4054
|
</View>
|
|
3395
4055
|
|
|
3396
4056
|
<View style={styles.toolbarRight}>
|
|
4057
|
+
<TouchableScale style={styles.toolbarBtn} onPress={() => setLogSortOrder(o => o === 'newest' ? 'oldest' : 'newest')} hitSlop={10}>
|
|
4058
|
+
<SortArrowIcon color={AppColors.grayTextStrong} size={18} direction={logSortOrder === 'newest' ? 'down' : 'up'}/>
|
|
4059
|
+
</TouchableScale>
|
|
3397
4060
|
<TouchableScale style={styles.toolbarBtn} onPress={handleDelete} hitSlop={10}>
|
|
3398
4061
|
<TrashIcon color={AppColors.grayTextStrong} size={18}/>
|
|
3399
4062
|
</TouchableScale>
|
|
@@ -3419,15 +4082,25 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
3419
4082
|
backgroundColor: '#F4EBFF',
|
|
3420
4083
|
},
|
|
3421
4084
|
]}>
|
|
3422
|
-
|
|
4085
|
+
{/* #7 */}
|
|
4086
|
+
<View style={{
|
|
4087
|
+
flexDirection: 'row',
|
|
4088
|
+
alignItems: 'center',
|
|
4089
|
+
gap: 5,
|
|
4090
|
+
}}>
|
|
4091
|
+
<LayersIcon size={12} color={active
|
|
4092
|
+
? AppColors.purpleShade700
|
|
4093
|
+
: AppColors.grayTextStrong}/>
|
|
4094
|
+
<Text numberOfLines={1} style={[
|
|
3423
4095
|
styles.statusFilterText,
|
|
3424
4096
|
active && {
|
|
3425
4097
|
color: AppColors.purpleShade700,
|
|
3426
4098
|
fontFamily: AppFonts.interBold,
|
|
3427
4099
|
},
|
|
3428
4100
|
]}>
|
|
3429
|
-
|
|
3430
|
-
|
|
4101
|
+
All ({logCounts.all})
|
|
4102
|
+
</Text>
|
|
4103
|
+
</View>
|
|
3431
4104
|
</View>
|
|
3432
4105
|
</TouchableScale>);
|
|
3433
4106
|
})()}
|
|
@@ -3453,15 +4126,25 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
3453
4126
|
backgroundColor: '#F1F5F9',
|
|
3454
4127
|
},
|
|
3455
4128
|
]}>
|
|
3456
|
-
|
|
4129
|
+
{/* #7 */}
|
|
4130
|
+
<View style={{
|
|
4131
|
+
flexDirection: 'row',
|
|
4132
|
+
alignItems: 'center',
|
|
4133
|
+
gap: 5,
|
|
4134
|
+
}}>
|
|
4135
|
+
<UserIcon size={12} color={active
|
|
4136
|
+
? '#334155'
|
|
4137
|
+
: AppColors.grayTextStrong}/>
|
|
4138
|
+
<Text numberOfLines={1} style={[
|
|
3457
4139
|
styles.statusFilterText,
|
|
3458
4140
|
active && {
|
|
3459
4141
|
color: '#334155',
|
|
3460
4142
|
fontFamily: AppFonts.interBold,
|
|
3461
4143
|
},
|
|
3462
4144
|
]}>
|
|
3463
|
-
|
|
3464
|
-
|
|
4145
|
+
User Log ({logCounts['user-log']})
|
|
4146
|
+
</Text>
|
|
4147
|
+
</View>
|
|
3465
4148
|
</View>
|
|
3466
4149
|
</TouchableScale>);
|
|
3467
4150
|
})()}
|
|
@@ -3487,15 +4170,25 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
3487
4170
|
backgroundColor: AppColors.purpleShade50,
|
|
3488
4171
|
},
|
|
3489
4172
|
]}>
|
|
3490
|
-
|
|
4173
|
+
{/* #7 */}
|
|
4174
|
+
<View style={{
|
|
4175
|
+
flexDirection: 'row',
|
|
4176
|
+
alignItems: 'center',
|
|
4177
|
+
gap: 5,
|
|
4178
|
+
}}>
|
|
4179
|
+
<InfoCircleIcon size={12} color={active
|
|
4180
|
+
? AppColors.purple
|
|
4181
|
+
: AppColors.grayTextStrong}/>
|
|
4182
|
+
<Text numberOfLines={1} style={[
|
|
3491
4183
|
styles.statusFilterText,
|
|
3492
4184
|
active && {
|
|
3493
4185
|
color: AppColors.purple,
|
|
3494
4186
|
fontFamily: AppFonts.interBold,
|
|
3495
4187
|
},
|
|
3496
4188
|
]}>
|
|
3497
|
-
|
|
3498
|
-
|
|
4189
|
+
Info ({logCounts.info})
|
|
4190
|
+
</Text>
|
|
4191
|
+
</View>
|
|
3499
4192
|
</View>
|
|
3500
4193
|
</TouchableScale>);
|
|
3501
4194
|
})()}
|
|
@@ -3521,7 +4214,17 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
3521
4214
|
backgroundColor: '#FFFDF6',
|
|
3522
4215
|
},
|
|
3523
4216
|
]}>
|
|
3524
|
-
|
|
4217
|
+
{/* #7 */}
|
|
4218
|
+
<View style={{
|
|
4219
|
+
flexDirection: 'row',
|
|
4220
|
+
alignItems: 'center',
|
|
4221
|
+
gap: 5,
|
|
4222
|
+
}}>
|
|
4223
|
+
<WarningTriangleIcon size={12} color={active
|
|
4224
|
+
? AppColors.darkOrange ||
|
|
4225
|
+
AppColors.lightOrange
|
|
4226
|
+
: AppColors.grayTextStrong}/>
|
|
4227
|
+
<Text numberOfLines={1} style={[
|
|
3525
4228
|
styles.statusFilterText,
|
|
3526
4229
|
active && {
|
|
3527
4230
|
color: AppColors.darkOrange ||
|
|
@@ -3529,8 +4232,9 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
3529
4232
|
fontFamily: AppFonts.interBold,
|
|
3530
4233
|
},
|
|
3531
4234
|
]}>
|
|
3532
|
-
|
|
3533
|
-
|
|
4235
|
+
Warning ({logCounts.warn})
|
|
4236
|
+
</Text>
|
|
4237
|
+
</View>
|
|
3534
4238
|
</View>
|
|
3535
4239
|
</TouchableScale>);
|
|
3536
4240
|
})()}
|
|
@@ -3556,15 +4260,25 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
3556
4260
|
backgroundColor: '#FFF5F6',
|
|
3557
4261
|
},
|
|
3558
4262
|
]}>
|
|
3559
|
-
|
|
4263
|
+
{/* #7 */}
|
|
4264
|
+
<View style={{
|
|
4265
|
+
flexDirection: 'row',
|
|
4266
|
+
alignItems: 'center',
|
|
4267
|
+
gap: 5,
|
|
4268
|
+
}}>
|
|
4269
|
+
<ErrorCircleIcon size={12} color={active
|
|
4270
|
+
? AppColors.errorColor
|
|
4271
|
+
: AppColors.grayTextStrong}/>
|
|
4272
|
+
<Text numberOfLines={1} style={[
|
|
3560
4273
|
styles.statusFilterText,
|
|
3561
4274
|
active && {
|
|
3562
4275
|
color: AppColors.errorColor,
|
|
3563
4276
|
fontFamily: AppFonts.interBold,
|
|
3564
4277
|
},
|
|
3565
4278
|
]}>
|
|
3566
|
-
|
|
3567
|
-
|
|
4279
|
+
Error ({logCounts.error})
|
|
4280
|
+
</Text>
|
|
4281
|
+
</View>
|
|
3568
4282
|
</View>
|
|
3569
4283
|
</TouchableScale>);
|
|
3570
4284
|
})()}
|
|
@@ -3590,15 +4304,25 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
3590
4304
|
backgroundColor: `${AppColors.skyBlue}15`,
|
|
3591
4305
|
},
|
|
3592
4306
|
]}>
|
|
3593
|
-
|
|
4307
|
+
{/* #7 */}
|
|
4308
|
+
<View style={{
|
|
4309
|
+
flexDirection: 'row',
|
|
4310
|
+
alignItems: 'center',
|
|
4311
|
+
gap: 5,
|
|
4312
|
+
}}>
|
|
4313
|
+
<AnalyticsIcon size={12} color={active
|
|
4314
|
+
? AppColors.skyBlue
|
|
4315
|
+
: AppColors.grayTextStrong}/>
|
|
4316
|
+
<Text numberOfLines={1} style={[
|
|
3594
4317
|
styles.statusFilterText,
|
|
3595
4318
|
active && {
|
|
3596
4319
|
color: AppColors.skyBlue,
|
|
3597
4320
|
fontFamily: AppFonts.interBold,
|
|
3598
4321
|
},
|
|
3599
4322
|
]}>
|
|
3600
|
-
|
|
3601
|
-
|
|
4323
|
+
Analytics ({logCounts.analytics})
|
|
4324
|
+
</Text>
|
|
4325
|
+
</View>
|
|
3602
4326
|
</View>
|
|
3603
4327
|
</TouchableScale>);
|
|
3604
4328
|
})()}
|
|
@@ -4633,7 +5357,7 @@ const NetworkInspector = ({ enabled = true, }) => {
|
|
|
4633
5357
|
if (!resExpanded && !showResDiff)
|
|
4634
5358
|
setResExpanded(true);
|
|
4635
5359
|
}}/>
|
|
4636
|
-
{showResDiff ? (<DiffViewer oldData={prevResponseData} newData={selected.response} forceOpen={resExpanded}/>) : (<JsonViewer data={selected.response} search={detailSearch} forceOpen={resExpanded}/>)}
|
|
5360
|
+
{showResDiff ? (<DiffViewer oldData={prevResponseData} newData={selected.response} forceOpen={resExpanded}/>) : (<JsonViewer data={selected.response} search={detailSearch} forceOpen={resExpanded} wrap/>)}
|
|
4637
5361
|
</View>
|
|
4638
5362
|
</>)}
|
|
4639
5363
|
</ScrollView>
|
|
@@ -4671,4 +5395,4 @@ export { setupConsoleLogger, clearConsoleLogs, subscribeConsoleLogs, } from './c
|
|
|
4671
5395
|
export { setupAnalyticsLogger, logAnalyticsEvent, subscribeAnalyticsEvents, clearAnalyticsEvents, } from './customHooks/analyticsLogger';
|
|
4672
5396
|
export { WebView, getWebViewLogs, getWebViewNavHistory, getWebViewHtml, getWebViewCss, getWebViewJs, getWebViewHtmlUrl, clearWebViewData, subscribeWebView, } from './customHooks/webViewLogger';
|
|
4673
5397
|
export { default as ErrorBoundary } from './components/ErrorBoundary';
|
|
4674
|
-
export { connectReduxStore, getReduxState, subscribeReduxState, } from './customHooks/reduxLogger';
|
|
5398
|
+
export { connectReduxStore, inspectorReduxMiddleware, getReduxState, subscribeReduxState, getActionHistory, clearActionHistory, } from './customHooks/reduxLogger';
|