react-native-inapp-inspector 2.3.18 → 2.3.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.kt +47 -0
  2. package/dist/commonjs/capture.d.ts +2 -0
  3. package/dist/commonjs/capture.js +3 -0
  4. package/dist/commonjs/components/EmptyState.d.ts +1 -0
  5. package/dist/commonjs/components/EmptyState.js +2 -2
  6. package/dist/commonjs/components/Inspector/FabLauncher.js +494 -39
  7. package/dist/commonjs/components/Inspector/InspectorHeader.js +45 -26
  8. package/dist/commonjs/components/Inspector/MainScreen.js +4 -3
  9. package/dist/commonjs/components/Inspector/MediaGalleryTab.js +418 -13
  10. package/dist/commonjs/components/Inspector/MediaPreviewModal.js +376 -88
  11. package/dist/commonjs/components/Inspector/NetworkDetail.js +49 -63
  12. package/dist/commonjs/components/Inspector/SettingsPanel.js +499 -119
  13. package/dist/commonjs/components/Inspector/TabBar.js +166 -4
  14. package/dist/commonjs/components/JsonViewer.js +4 -3
  15. package/dist/commonjs/components/NetworkIcons.d.ts +4 -0
  16. package/dist/commonjs/components/NetworkIcons.js +26 -1
  17. package/dist/commonjs/components/SegmentedTabs.js +1 -0
  18. package/dist/commonjs/constants/version.d.ts +1 -1
  19. package/dist/commonjs/constants/version.js +1 -1
  20. package/dist/commonjs/i18n/locales/en.json +20 -2
  21. package/dist/commonjs/index.js +45 -30
  22. package/dist/commonjs/native/NativeInspector.d.ts +3 -0
  23. package/dist/commonjs/native/NativeInspector.js +14 -1
  24. package/dist/commonjs/native/NativeNetworkInspector.d.ts +1 -0
  25. package/dist/commonjs/styles/AppColors.js +78 -78
  26. package/dist/commonjs/types/enums.d.ts +2 -0
  27. package/dist/commonjs/types/enums.js +2 -0
  28. package/dist/commonjs/types/interfaces.d.ts +5 -0
  29. package/dist/esm/capture.d.ts +2 -0
  30. package/dist/esm/capture.js +4 -1
  31. package/dist/esm/components/EmptyState.d.ts +1 -0
  32. package/dist/esm/components/EmptyState.js +2 -2
  33. package/dist/esm/components/Inspector/FabLauncher.js +462 -40
  34. package/dist/esm/components/Inspector/InspectorHeader.js +46 -27
  35. package/dist/esm/components/Inspector/MainScreen.js +4 -3
  36. package/dist/esm/components/Inspector/MediaGalleryTab.js +419 -14
  37. package/dist/esm/components/Inspector/MediaPreviewModal.js +379 -91
  38. package/dist/esm/components/Inspector/NetworkDetail.js +49 -63
  39. package/dist/esm/components/Inspector/SettingsPanel.js +500 -120
  40. package/dist/esm/components/Inspector/TabBar.js +135 -6
  41. package/dist/esm/components/JsonViewer.js +4 -3
  42. package/dist/esm/components/NetworkIcons.d.ts +4 -0
  43. package/dist/esm/components/NetworkIcons.js +21 -0
  44. package/dist/esm/components/SegmentedTabs.js +1 -0
  45. package/dist/esm/constants/version.d.ts +1 -1
  46. package/dist/esm/constants/version.js +1 -1
  47. package/dist/esm/i18n/locales/en.json +20 -2
  48. package/dist/esm/index.js +44 -29
  49. package/dist/esm/native/NativeInspector.d.ts +3 -0
  50. package/dist/esm/native/NativeInspector.js +12 -0
  51. package/dist/esm/native/NativeNetworkInspector.d.ts +1 -0
  52. package/dist/esm/styles/AppColors.js +78 -78
  53. package/dist/esm/types/enums.d.ts +2 -0
  54. package/dist/esm/types/enums.js +2 -0
  55. package/dist/esm/types/interfaces.d.ts +5 -0
  56. package/ios/NetworkInspectorModule.mm +241 -136
  57. package/package.json +1 -1
  58. package/react-native-inapp-inspector.podspec +1 -1
  59. package/src/capture.ts +8 -0
  60. package/src/components/EmptyState.tsx +3 -1
  61. package/src/components/Inspector/FabLauncher.tsx +562 -52
  62. package/src/components/Inspector/InspectorHeader.tsx +60 -22
  63. package/src/components/Inspector/MainScreen.tsx +3 -2
  64. package/src/components/Inspector/MediaGalleryTab.tsx +502 -28
  65. package/src/components/Inspector/MediaPreviewModal.tsx +465 -117
  66. package/src/components/Inspector/NetworkDetail.tsx +80 -97
  67. package/src/components/Inspector/SettingsPanel.tsx +708 -152
  68. package/src/components/Inspector/TabBar.tsx +174 -6
  69. package/src/components/JsonViewer.tsx +4 -2
  70. package/src/components/NetworkIcons.tsx +56 -0
  71. package/src/components/SegmentedTabs.tsx +1 -0
  72. package/src/constants/version.ts +1 -1
  73. package/src/i18n/locales/en.json +20 -4
  74. package/src/index.tsx +46 -31
  75. package/src/native/NativeInspector.ts +17 -0
  76. package/src/native/NativeNetworkInspector.ts +1 -0
  77. package/src/styles/AppColors.ts +80 -80
  78. package/src/types/enums.ts +2 -0
  79. package/src/types/interfaces.ts +5 -0
@@ -1,20 +1,73 @@
1
- import React from 'react';
2
- import { Platform, ScrollView, Text, View, } from 'react-native';
1
+ import React, { useRef, useState } from 'react';
2
+ import { Platform, ScrollView, Text, TouchableOpacity, View, } from 'react-native';
3
3
  import { useInspector } from './InspectorContext';
4
4
  import styles from '../../styles';
5
5
  import { AppColors } from '../../styles/AppColors';
6
6
  import { AppFonts } from '../../styles/AppFonts';
7
7
  import TouchableScale from '../TouchableScale';
8
- import { SignalIcon, TerminalIcon, AnalyticsIcon, PackageIcon, ReduxIcon, PerformanceIcon, CrashIcon, SmartphoneIcon, DatabaseIcon, QrCodeIcon, } from '../NetworkIcons';
8
+ import { SignalIcon, TerminalIcon, AnalyticsIcon, PackageIcon, ReduxIcon, PerformanceIcon, CrashIcon, SmartphoneIcon, DatabaseIcon, QrCodeIcon, ScreencastIcon, ChevronIcon, } from '../NetworkIcons';
9
9
  import { isReduxConnected } from '../../customHooks/reduxLogger';
10
10
  import { isAnalyticsConnected } from '../../customHooks/analyticsLogger';
11
11
  import { isLocalDebugEnvironment } from '../../helpers';
12
12
  const TabBar = React.memo(() => {
13
- const { activeTab, switchActiveTab, tabVisibility, logs, consoleLogs, analyticsEvents, crashRecords, lastReadApisCount, lastReadLogsCount, lastReadCrashesCount, unreadPulseAnim, } = useInspector();
13
+ const { activeTab, switchActiveTab, tabVisibility, logs, consoleLogs, analyticsEvents, crashRecords, lastReadApisCount, lastReadLogsCount, lastReadCrashesCount, mediaCount, } = useInspector();
14
14
  const isReduxAvail = isReduxConnected();
15
15
  const isAnalyticsAvail = isAnalyticsConnected();
16
- return (<View style={styles.tabBarContainer}>
17
- <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{ paddingRight: 16 }}>
16
+ const isMediaActive = activeTab === 'media';
17
+ const showMediaTab = (tabVisibility?.media ?? true) && mediaCount > 0;
18
+ const scrollViewRef = useRef(null);
19
+ const [canScrollLeft, setCanScrollLeft] = useState(false);
20
+ const [canScrollRight, setCanScrollRight] = useState(true);
21
+ const scrollOffsetRef = useRef(0);
22
+ const handleScroll = (event) => {
23
+ const { contentOffset, layoutMeasurement, contentSize } = event.nativeEvent;
24
+ const x = contentOffset.x;
25
+ scrollOffsetRef.current = x;
26
+ const hasLeft = x > 8;
27
+ const hasRight = x < contentSize.width - layoutMeasurement.width - 8;
28
+ if (canScrollLeft !== hasLeft)
29
+ setCanScrollLeft(hasLeft);
30
+ if (canScrollRight !== hasRight)
31
+ setCanScrollRight(hasRight);
32
+ };
33
+ const scrollTabs = (offset) => {
34
+ scrollViewRef.current?.scrollTo({
35
+ x: Math.max(0, scrollOffsetRef.current + offset),
36
+ animated: true,
37
+ });
38
+ };
39
+ return (<View style={[
40
+ styles.tabBarContainer,
41
+ {
42
+ flexDirection: 'row',
43
+ alignItems: 'center',
44
+ paddingHorizontal: 0,
45
+ },
46
+ ]}>
47
+
48
+ {canScrollLeft && (<TouchableOpacity onPress={() => scrollTabs(-160)} hitSlop={{ top: 6, bottom: 6, left: 6, right: 6 }} activeOpacity={0.7} style={{
49
+ paddingLeft: 6,
50
+ paddingRight: 2,
51
+ justifyContent: 'center',
52
+ alignItems: 'center',
53
+ }}>
54
+ <View style={{
55
+ width: 22,
56
+ height: 22,
57
+ borderRadius: 11,
58
+ backgroundColor: '#E5E7EB',
59
+ alignItems: 'center',
60
+ justifyContent: 'center',
61
+ }}>
62
+ <ChevronIcon direction="left" size={13} color={AppColors.purple}/>
63
+ </View>
64
+ </TouchableOpacity>)}
65
+
66
+
67
+ <ScrollView ref={scrollViewRef} horizontal showsHorizontalScrollIndicator={false} onScroll={handleScroll} scrollEventThrottle={16} style={{ flex: 1 }} contentContainerStyle={{
68
+ paddingLeft: canScrollLeft ? 4 : 12,
69
+ paddingRight: showMediaTab ? 6 : 12,
70
+ }}>
18
71
  {[
19
72
  {
20
73
  id: 1,
@@ -178,6 +231,82 @@ const TabBar = React.memo(() => {
178
231
  </TouchableScale>);
179
232
  })}
180
233
  </ScrollView>
234
+
235
+
236
+ {canScrollRight && (<TouchableOpacity onPress={() => scrollTabs(160)} hitSlop={{ top: 6, bottom: 6, left: 6, right: 6 }} activeOpacity={0.7} style={{
237
+ paddingLeft: 2,
238
+ paddingRight: showMediaTab ? 4 : 8,
239
+ justifyContent: 'center',
240
+ alignItems: 'center',
241
+ }}>
242
+ <View style={{
243
+ width: 22,
244
+ height: 22,
245
+ borderRadius: 11,
246
+ backgroundColor: '#E5E7EB',
247
+ alignItems: 'center',
248
+ justifyContent: 'center',
249
+ }}>
250
+ <ChevronIcon direction="right" size={13} color={AppColors.purple}/>
251
+ </View>
252
+ </TouchableOpacity>)}
253
+
254
+
255
+ {showMediaTab && (<View style={{
256
+ paddingLeft: 6,
257
+ paddingRight: 10,
258
+ borderLeftWidth: 1,
259
+ borderLeftColor: '#E5E7EB',
260
+ backgroundColor: '#ffffff',
261
+ justifyContent: 'center',
262
+ shadowColor: '#000000',
263
+ shadowOffset: { width: -3, height: 0 },
264
+ shadowOpacity: 0.12,
265
+ shadowRadius: 4,
266
+ elevation: 4,
267
+ zIndex: 10,
268
+ }}>
269
+ <TouchableScale key="media" onPress={() => {
270
+ switchActiveTab('media');
271
+ }} accessibilityRole="button" accessibilityLabel="Media Gallery" style={[
272
+ styles.contentTabButton,
273
+ {
274
+ marginRight: 0,
275
+ borderRadius: 8,
276
+ backgroundColor: isMediaActive ? AppColors.purple : '#eeeeee',
277
+ borderColor: isMediaActive ? AppColors.purple : '#E5E7EB',
278
+ borderWidth: 1,
279
+ paddingHorizontal: 10,
280
+ paddingVertical: 7,
281
+ alignItems: 'center',
282
+ justifyContent: 'center',
283
+ },
284
+ isMediaActive && styles.contentTabButtonActive,
285
+ ]}>
286
+ <View style={{
287
+ position: 'relative',
288
+ alignItems: 'center',
289
+ justifyContent: 'center',
290
+ }}>
291
+ <ScreencastIcon color={isMediaActive ? AppColors.white : AppColors.grayText} size={15}/>
292
+ {mediaCount > 0 && (<View style={{
293
+ position: 'absolute',
294
+ top: -3,
295
+ right: -4,
296
+ width: 7,
297
+ height: 7,
298
+ borderRadius: 3.5,
299
+ backgroundColor: isMediaActive
300
+ ? AppColors.emerald400
301
+ : AppColors.purple,
302
+ borderWidth: 1.2,
303
+ borderColor: isMediaActive
304
+ ? AppColors.purple
305
+ : AppColors.white,
306
+ }}/>)}
307
+ </View>
308
+ </TouchableScale>
309
+ </View>)}
181
310
  </View>);
182
311
  });
183
312
  export default TabBar;
@@ -141,7 +141,7 @@ const JsonViewer = React.memo(({ data, search, forceOpen, defaultExpandDepth = 1
141
141
  return (<View style={[localStyles.container, fullHeight && { flex: 1 }]}>
142
142
 
143
143
  {!hideTabs && (<View style={localStyles.toolbar}>
144
- <SegmentedTabs tabs={[
144
+ <SegmentedTabs style={{ flex: 1, minWidth: 0, marginRight: 8 }} tabs={[
145
145
  {
146
146
  key: 'pretty',
147
147
  label: t('network.jsonViewer.pretty'),
@@ -258,8 +258,9 @@ const localStyles = StyleSheet.create({
258
258
  backgroundColor: AppColors.slate50,
259
259
  borderBottomWidth: 1,
260
260
  borderBottomColor: AppColors.slate200,
261
- paddingHorizontal: 12,
262
- paddingVertical: 8,
261
+ paddingHorizontal: 8,
262
+ paddingVertical: 6,
263
+ gap: 8,
263
264
  },
264
265
  contentWrapper: {
265
266
  backgroundColor: AppColors.white,
@@ -151,3 +151,7 @@ export declare const PlayIcon: ({ color, size, }: IconProps) => React.JSX.Elemen
151
151
  export declare const PauseIcon: ({ color, size, }: IconProps) => React.JSX.Element;
152
152
  export declare const ImageIcon: ({ color, size, }: IconProps) => React.JSX.Element;
153
153
  export declare const FilmIcon: ({ color, size, }: IconProps) => React.JSX.Element;
154
+ export declare const ScreencastIcon: ({ color, size, }: IconProps) => React.JSX.Element;
155
+ export declare const ListIcon: ({ color, size }: IconProps) => React.JSX.Element;
156
+ export declare const GridIcon: ({ color, size }: IconProps) => React.JSX.Element;
157
+ export declare const MinimizeWindowIcon: ({ color, size }: IconProps) => React.JSX.Element;
@@ -828,3 +828,24 @@ export const FilmIcon = ({ color = AppColors.white, size = 18, }) => (<Svg width
828
828
  <Line x1="17" y1="17" x2="22" y2="17" stroke={color} strokeWidth="2"/>
829
829
  <Line x1="17" y1="7" x2="22" y2="7" stroke={color} strokeWidth="2"/>
830
830
  </Svg>);
831
+ export const ScreencastIcon = ({ color = AppColors.white, size = 18, }) => (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
832
+ <Path d="M2 16.1A5 5 0 0 1 5.9 20M2 12.05A9 9 0 0 1 9.95 20M2 20h.01" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
833
+ <Path d="M2 8V6a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-6" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
834
+ </Svg>);
835
+ export const ListIcon = ({ color = AppColors.white, size = 14 }) => (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
836
+ <Line x1="8" y1="6" x2="21" y2="6" stroke={color} strokeWidth="2" strokeLinecap="round"/>
837
+ <Line x1="8" y1="12" x2="21" y2="12" stroke={color} strokeWidth="2" strokeLinecap="round"/>
838
+ <Line x1="8" y1="18" x2="21" y2="18" stroke={color} strokeWidth="2" strokeLinecap="round"/>
839
+ <Circle cx="4" cy="6" r="1.5" fill={color}/>
840
+ <Circle cx="4" cy="12" r="1.5" fill={color}/>
841
+ <Circle cx="4" cy="18" r="1.5" fill={color}/>
842
+ </Svg>);
843
+ export const GridIcon = ({ color = AppColors.white, size = 14 }) => (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
844
+ <Rect x="3" y="3" width="7" height="7" rx="1.5" stroke={color} strokeWidth="2"/>
845
+ <Rect x="14" y="3" width="7" height="7" rx="1.5" stroke={color} strokeWidth="2"/>
846
+ <Rect x="14" y="14" width="7" height="7" rx="1.5" stroke={color} strokeWidth="2"/>
847
+ <Rect x="3" y="14" width="7" height="7" rx="1.5" stroke={color} strokeWidth="2"/>
848
+ </Svg>);
849
+ export const MinimizeWindowIcon = ({ color = AppColors.white, size = 14 }) => (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
850
+ <Path d="M4 14h6v6M20 10h-6V4M14 10l7-7M10 14l-7 7" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
851
+ </Svg>);
@@ -11,6 +11,7 @@ const SegmentedTabs = React.memo(({ tabs, activeKey, onChange, style }) => (<Vie
11
11
  borderWidth: 1,
12
12
  borderColor: AppColors.dividerColor,
13
13
  gap: 3,
14
+ minWidth: 0,
14
15
  },
15
16
  style,
16
17
  ]}>
@@ -1 +1 @@
1
- export declare const LIB_VERSION = "2.3.18";
1
+ export declare const LIB_VERSION = "2.3.19";
@@ -1 +1 @@
1
- export const LIB_VERSION = '2.3.18';
1
+ export const LIB_VERSION = '2.3.19';
@@ -16,6 +16,8 @@
16
16
  "viewOnNpm": "View on NPM",
17
17
  "later": "Later",
18
18
  "source": "SOURCE",
19
+ "minimize": "Minimize",
20
+ "tapToExpand": "Tap to expand",
19
21
  "open": "Open",
20
22
  "openInBrowser": "Open in Browser",
21
23
  "openInBrowserPrompt": "Are you sure you want to open this URL in your external browser?"
@@ -54,7 +56,7 @@
54
56
  "crash": "Crash",
55
57
  "device": "Device",
56
58
  "storage": "Storage",
57
- "media": "Media"
59
+ "media": "Screencast"
58
60
  },
59
61
  "settings": {
60
62
  "mainTitle": "Settings & Modules",
@@ -1109,14 +1111,30 @@
1109
1111
  "allPurged": "All captured media purged",
1110
1112
  "converting": "Converting...",
1111
1113
  "convertToGif": "Convert to GIF",
1114
+ "playVideo": "Play Video",
1115
+ "playError": "Unable to play video",
1112
1116
  "convertedSuccess": "Converted to Animated GIF successfully!",
1113
1117
  "convertFailed": "Failed to convert to GIF",
1114
1118
  "share": "Share",
1115
1119
  "copyUri": "Copy URI",
1116
1120
  "uriCopied": "File URI copied to clipboard",
1117
1121
  "delete": "Delete",
1122
+ "deleteSelected": "Delete ({{count}})",
1123
+ "deleteSelectedTitle": "Delete Selected",
1124
+ "deleteSelectedMessage": "Are you sure you want to delete {{count}} selected media items?",
1125
+ "deletedCount": "Deleted {{count}} media items",
1118
1126
  "deleteTitle": "Delete Media",
1119
1127
  "deleteMessage": "Are you sure you want to delete {{filename}}?",
1120
- "shareUnavailable": "Sharing not available on this device"
1128
+ "shareUnavailable": "Sharing not available on this device",
1129
+ "showingResults": "Showing {{count}} results",
1130
+ "showingFilteredResults": "Showing {{count}} of {{total}} results",
1131
+ "selectedOfResults": "Selected {{selected}} of {{total}} results",
1132
+ "minimize": "Minimize",
1133
+ "expand": "Expand Fullscreen",
1134
+ "collapse": "Exit Fullscreen",
1135
+ "speed": "Speed",
1136
+ "loop": "Loop",
1137
+ "pause": "Pause",
1138
+ "play": "Play"
1121
1139
  }
1122
1140
  }
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, PanResponder, Platform, UIManager, LogBox, } from 'react-native';
2
+ import { Alert, Animated, Easing, PanResponder, Platform, UIManager, LogBox, } from 'react-native';
3
3
  import { NavigationContext } from '@react-navigation/native';
4
4
  import { I18nextProvider, i18n } from './i18n';
5
5
  import ErrorBoundary from './components/ErrorBoundary';
@@ -17,8 +17,9 @@ import { subscribeAnalyticsEvents, clearAnalyticsEvents, autoSetupAnalyticsLogge
17
17
  import { getReduxState, subscribeReduxState, setReduxAutoRefresh, getLastActionForReducer, clearActionHistory, isReduxConnected, setReduxModuleEnabled, } from './customHooks/reduxLogger';
18
18
  import { setPerformanceModuleEnabled } from './customHooks/performanceTracker';
19
19
  import { setBundleModuleEnabled } from './customHooks/bundleAnalyzer';
20
- import { showNativeFloatingButton, hideNativeFloatingButton, setNativeFloatingButtonBadge, subscribeNativeFloatingButtonPress, subscribeNativeDeviceShake, isNativeModuleAvailable, } from './native/NativeInspector';
20
+ import { hideNativeFloatingButton, subscribeNativeFloatingButtonPress, subscribeNativeDeviceShake, isNativeModuleAvailable, } from './native/NativeInspector';
21
21
  import { fetchRemoteConfigModuleStatus, } from './helpers/remoteConfig';
22
+ import { ScreenCapture } from './capture';
22
23
  import { LIB_VERSION } from './constants';
23
24
  import { toggleGlobalTheme } from './styles';
24
25
  const NetworkInspector = ({ enabled = true, storage, navigationRef, appIcon, environment, initialVisible = false, visible: controlledVisible, remoteConfig, }) => {
@@ -30,6 +31,11 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, appIcon, env
30
31
  const [modalAnimationType, setModalAnimationType] = useState('slide');
31
32
  const [logs, setLogs] = useState([]);
32
33
  const [visible, setVisible] = useState(controlledVisible ?? initialVisible ?? false);
34
+ const [isMinimized, setIsMinimized] = useState(false);
35
+ const minimizeInspector = useCallback(() => {
36
+ setIsMinimized(true);
37
+ setVisible(false);
38
+ }, []);
33
39
  useEffect(() => {
34
40
  if (controlledVisible !== undefined) {
35
41
  setVisible(controlledVisible);
@@ -91,6 +97,24 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, appIcon, env
91
97
  const [lastReadLogsCount, setLastReadLogsCount] = useState(0);
92
98
  const [lastReadApisCount, setLastReadApisCount] = useState(0);
93
99
  const [lastReadCrashesCount, setLastReadCrashesCount] = useState(0);
100
+ const [mediaCount, setMediaCount] = useState(0);
101
+ const refreshMediaCount = useCallback(async () => {
102
+ try {
103
+ const items = await ScreenCapture.getMediaList();
104
+ const count = items ? items.length : 0;
105
+ setMediaCount(count);
106
+ return count;
107
+ }
108
+ catch {
109
+ setMediaCount(0);
110
+ return 0;
111
+ }
112
+ }, []);
113
+ useEffect(() => {
114
+ if (visible) {
115
+ refreshMediaCount();
116
+ }
117
+ }, [visible, refreshMediaCount]);
94
118
  const [crashRecords, setCrashRecords] = useState(() => getCrashRecords());
95
119
  const [selectedCrash, setSelectedCrash] = useState(null);
96
120
  const [maxCrashLogs, setMaxCrashLogs] = useState(50);
@@ -171,6 +195,7 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, appIcon, env
171
195
  device: false,
172
196
  storage: false,
173
197
  debugging: false,
198
+ media: true,
174
199
  });
175
200
  const [maxNetworkLogs, setMaxNetworkLogs] = useState(250);
176
201
  const [maxAnalyticsEventsLimit, setMaxAnalyticsEventsLimit] = useState(150);
@@ -225,6 +250,7 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, appIcon, env
225
250
  device: false,
226
251
  storage: false,
227
252
  debugging: false,
253
+ media: true,
228
254
  });
229
255
  setDefaultTab('apis');
230
256
  setIsAutoRamLimitEnabled(true);
@@ -687,10 +713,11 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, appIcon, env
687
713
  const loop = Animated.loop(Animated.sequence([
688
714
  Animated.timing(fabShineAnim, {
689
715
  toValue: 1,
690
- duration: 1100,
716
+ duration: 3200,
717
+ easing: Easing.inOut(Easing.ease),
691
718
  useNativeDriver: false,
692
719
  }),
693
- Animated.delay(1600),
720
+ Animated.delay(3500),
694
721
  Animated.timing(fabShineAnim, {
695
722
  toValue: 0,
696
723
  duration: 0,
@@ -735,31 +762,10 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, appIcon, env
735
762
  const isNativeModule = useMemo(() => isNativeModuleAvailable(), []);
736
763
  const useNativeFab = (Platform.OS === 'ios' || Platform.OS === 'android') && isNativeModule;
737
764
  useEffect(() => {
738
- if (!useNativeFab || !enabled) {
739
- if (useNativeFab) {
740
- hideNativeFloatingButton().catch(() => { });
741
- }
742
- return;
743
- }
744
- if (visible) {
765
+ if (useNativeFab) {
745
766
  hideNativeFloatingButton().catch(() => { });
746
767
  }
747
- else {
748
- showNativeFloatingButton().catch(() => { });
749
- setNativeFloatingButtonBadge(logs.length > 0 || analyticsEvents.length > 0).catch(() => { });
750
- }
751
- return () => {
752
- if (useNativeFab) {
753
- hideNativeFloatingButton().catch(() => { });
754
- }
755
- };
756
- }, [
757
- useNativeFab,
758
- enabled,
759
- visible,
760
- logs.length,
761
- analyticsEvents.length,
762
- ]);
768
+ }, [useNativeFab, visible]);
763
769
  useEffect(() => {
764
770
  if (!useNativeFab)
765
771
  return;
@@ -1486,8 +1492,9 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, appIcon, env
1486
1492
  analytics: `${searchedLogs.filter(l => l.message.toLowerCase().includes('[analytics error]')).length}/${total}`,
1487
1493
  };
1488
1494
  }, [visibleConsoleLogs, logSearch]);
1489
- function closeModal() {
1495
+ const closeModal = useCallback(() => {
1490
1496
  setVisible(false);
1497
+ setIsMinimized(false);
1491
1498
  setTimeout(() => {
1492
1499
  setSelected(null);
1493
1500
  setSelectedEvent(null);
@@ -1496,7 +1503,7 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, appIcon, env
1496
1503
  setSelectedReduxAction(null);
1497
1504
  setSelectedCrash(null);
1498
1505
  }, 300);
1499
- }
1506
+ }, []);
1500
1507
  function handleClearAll() {
1501
1508
  clearNetworkLogs();
1502
1509
  setLogs([]);
@@ -1633,6 +1640,9 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, appIcon, env
1633
1640
  visible,
1634
1641
  setVisible,
1635
1642
  closeModal,
1643
+ isMinimized,
1644
+ setIsMinimized,
1645
+ minimizeInspector,
1636
1646
  isReady,
1637
1647
  enabled,
1638
1648
  isEnabled: enabled,
@@ -1650,6 +1660,8 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, appIcon, env
1650
1660
  toggleTabVisibility,
1651
1661
  lastReadApisCount,
1652
1662
  lastReadLogsCount,
1663
+ mediaCount,
1664
+ refreshMediaCount,
1653
1665
  selected,
1654
1666
  setSelected,
1655
1667
  selectedEvent,
@@ -1815,6 +1827,8 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, appIcon, env
1815
1827
  toggleTabVisibility,
1816
1828
  lastReadApisCount,
1817
1829
  lastReadLogsCount,
1830
+ mediaCount,
1831
+ refreshMediaCount,
1818
1832
  selected,
1819
1833
  selectedEvent,
1820
1834
  selectedLog,
@@ -1905,6 +1919,7 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, appIcon, env
1905
1919
  deviceFreeRamMb,
1906
1920
  reduxAutoRefresh,
1907
1921
  reduxExpandDepth,
1922
+ isMinimized,
1908
1923
  ]);
1909
1924
  return (<InspectorContext.Provider value={contextValue}>
1910
1925
  <MainScreen />
@@ -100,6 +100,7 @@ export interface RecordingOptions {
100
100
  }
101
101
  export interface RecordingResult {
102
102
  uri: string;
103
+ thumbnailUri?: string;
103
104
  format: 'mp4' | 'gif';
104
105
  durationMs: number;
105
106
  hasAudio: boolean;
@@ -118,6 +119,7 @@ export interface CapturedMediaItem {
118
119
  type: 'image' | 'video' | 'gif';
119
120
  format: string;
120
121
  uri: string;
122
+ thumbnailUri?: string;
121
123
  filename: string;
122
124
  sizeBytes: number;
123
125
  timestamp: number;
@@ -130,6 +132,7 @@ export declare const takeNativeScreenshot: (options?: ScreenshotOptions) => Prom
130
132
  export declare const startNativeVideoRecording: (options?: RecordingOptions) => Promise<boolean>;
131
133
  export declare const stopNativeVideoRecording: () => Promise<RecordingResult | null>;
132
134
  export declare const isNativeRecordingActive: () => Promise<boolean>;
135
+ export declare const playNativeVideo: (videoUri: string) => Promise<boolean>;
133
136
  export declare const convertNativeVideoToGif: (videoUri: string, options?: GifConversionOptions) => Promise<RecordingResult | null>;
134
137
  export declare const fetchCapturedMediaList: () => Promise<CapturedMediaItem[]>;
135
138
  export declare const deleteCapturedMediaFile: (uri: string) => Promise<boolean>;
@@ -317,6 +317,18 @@ export const isNativeRecordingActive = async () => {
317
317
  return false;
318
318
  }
319
319
  };
320
+ export const playNativeVideo = async (videoUri) => {
321
+ if (!NativeModule || !NativeModule.playVideo) {
322
+ return false;
323
+ }
324
+ try {
325
+ const result = await NativeModule.playVideo(videoUri);
326
+ return !!result;
327
+ }
328
+ catch {
329
+ return false;
330
+ }
331
+ };
320
332
  export const convertNativeVideoToGif = async (videoUri, options) => {
321
333
  if (!NativeModule || !NativeModule.convertToGif) {
322
334
  return null;
@@ -20,6 +20,7 @@ export interface Spec extends TurboModule {
20
20
  stopVideoRecording(): Promise<Object>;
21
21
  isRecording(): Promise<boolean>;
22
22
  convertToGif(videoUri: string, options: Object): Promise<Object>;
23
+ playVideo(videoUri: string): Promise<boolean>;
23
24
  getCapturedMedia(): Promise<string>;
24
25
  deleteCapturedMedia(uri: string): Promise<boolean>;
25
26
  clearAllCapturedMedia(): Promise<boolean>;