react-native-inapp-inspector 1.1.35 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +123 -101
  2. package/android/build.gradle +13 -1
  3. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.kt +904 -0
  4. package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.kt +17 -0
  5. package/dist/commonjs/components/CopyButton.js +3 -0
  6. package/dist/commonjs/components/ErrorBoundary.js +3 -0
  7. package/dist/commonjs/components/Inspector/AnalyticsTab.js +7 -3
  8. package/dist/commonjs/components/Inspector/BundleTab.js +1 -1
  9. package/dist/commonjs/components/Inspector/ConsoleTab.js +1 -1
  10. package/dist/commonjs/components/Inspector/CrashTab.js +97 -90
  11. package/dist/commonjs/components/Inspector/InspectorHeader.js +12 -8
  12. package/dist/commonjs/components/Inspector/MainScreen.js +14 -101
  13. package/dist/commonjs/components/Inspector/NetworkTab.js +47 -10
  14. package/dist/commonjs/components/Inspector/PerformanceTab.js +96 -3
  15. package/dist/commonjs/components/Inspector/ReduxTab.js +18 -10
  16. package/dist/commonjs/components/Inspector/SettingsPanel.js +342 -88
  17. package/dist/commonjs/components/Inspector/TabBar.js +6 -3
  18. package/dist/commonjs/components/NetworkIcons.d.ts +1 -0
  19. package/dist/commonjs/components/NetworkIcons.js +6 -1
  20. package/dist/commonjs/components/TouchableScale.d.ts +1 -0
  21. package/dist/commonjs/components/TouchableScale.js +30 -32
  22. package/dist/commonjs/constants/index.d.ts +1 -2
  23. package/dist/commonjs/constants/index.js +4 -0
  24. package/dist/commonjs/constants/version.d.ts +1 -1
  25. package/dist/commonjs/constants/version.js +1 -1
  26. package/dist/commonjs/customHooks/analyticsLogger.d.ts +4 -0
  27. package/dist/commonjs/customHooks/analyticsLogger.js +7 -5
  28. package/dist/commonjs/customHooks/crashHandler.js +7 -0
  29. package/dist/commonjs/helpers/index.js +10 -1
  30. package/dist/commonjs/helpers/settingsStore.d.ts +9 -0
  31. package/dist/commonjs/helpers/settingsStore.js +59 -3
  32. package/dist/commonjs/index.d.ts +1 -0
  33. package/dist/commonjs/index.js +229 -39
  34. package/dist/commonjs/native/NativeInspector.d.ts +125 -0
  35. package/dist/commonjs/native/NativeInspector.js +313 -0
  36. package/dist/commonjs/native/NativeNetworkInspector.d.ts +22 -0
  37. package/dist/commonjs/native/NativeNetworkInspector.js +4 -0
  38. package/dist/commonjs/styles/index.js +6 -6
  39. package/dist/commonjs/types/enums.d.ts +1 -1
  40. package/dist/commonjs/types/interfaces.d.ts +9 -0
  41. package/dist/esm/components/CopyButton.js +3 -0
  42. package/dist/esm/components/ErrorBoundary.js +3 -0
  43. package/dist/esm/components/Inspector/AnalyticsTab.js +7 -3
  44. package/dist/esm/components/Inspector/BundleTab.js +1 -1
  45. package/dist/esm/components/Inspector/ConsoleTab.js +1 -1
  46. package/dist/esm/components/Inspector/CrashTab.js +97 -90
  47. package/dist/esm/components/Inspector/InspectorHeader.js +13 -9
  48. package/dist/esm/components/Inspector/MainScreen.js +15 -69
  49. package/dist/esm/components/Inspector/NetworkTab.js +47 -10
  50. package/dist/esm/components/Inspector/PerformanceTab.js +96 -3
  51. package/dist/esm/components/Inspector/ReduxTab.js +18 -10
  52. package/dist/esm/components/Inspector/SettingsPanel.js +344 -90
  53. package/dist/esm/components/Inspector/TabBar.js +7 -4
  54. package/dist/esm/components/NetworkIcons.d.ts +1 -0
  55. package/dist/esm/components/NetworkIcons.js +4 -0
  56. package/dist/esm/components/TouchableScale.d.ts +1 -0
  57. package/dist/esm/components/TouchableScale.js +31 -33
  58. package/dist/esm/constants/index.d.ts +1 -2
  59. package/dist/esm/constants/index.js +4 -0
  60. package/dist/esm/constants/version.d.ts +1 -1
  61. package/dist/esm/constants/version.js +1 -1
  62. package/dist/esm/customHooks/analyticsLogger.d.ts +4 -0
  63. package/dist/esm/customHooks/analyticsLogger.js +5 -4
  64. package/dist/esm/customHooks/crashHandler.js +7 -0
  65. package/dist/esm/helpers/index.js +10 -1
  66. package/dist/esm/helpers/settingsStore.d.ts +9 -0
  67. package/dist/esm/helpers/settingsStore.js +58 -3
  68. package/dist/esm/index.d.ts +1 -0
  69. package/dist/esm/index.js +215 -39
  70. package/dist/esm/native/NativeInspector.d.ts +125 -0
  71. package/dist/esm/native/NativeInspector.js +289 -0
  72. package/dist/esm/native/NativeNetworkInspector.d.ts +22 -0
  73. package/dist/esm/native/NativeNetworkInspector.js +2 -0
  74. package/dist/esm/styles/index.js +6 -6
  75. package/dist/esm/types/enums.d.ts +1 -1
  76. package/dist/esm/types/interfaces.d.ts +9 -0
  77. package/ios/NetworkInspectorModule.h +6 -0
  78. package/ios/NetworkInspectorModule.m +720 -5
  79. package/package.json +13 -2
  80. package/react-native-inapp-inspector.podspec +7 -2
  81. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +0 -97
  82. package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.java +0 -28
@@ -55,11 +55,40 @@ const NetworkIcons_1 = require("../NetworkIcons");
55
55
  const STATUS_META = {
56
56
  ALL: { color: AppColors_1.AppColors.grayText, Icon: NetworkIcons_1.LayersIcon },
57
57
  '2xx': { color: AppColors_1.AppColors.greenColor, Icon: NetworkIcons_1.CircleCheckIcon },
58
+ '200': { color: AppColors_1.AppColors.greenColor, Icon: NetworkIcons_1.CircleCheckIcon },
58
59
  '3xx': { color: AppColors_1.AppColors.warningIconGold, Icon: NetworkIcons_1.CircleAlertIcon },
60
+ '300': { color: AppColors_1.AppColors.warningIconGold, Icon: NetworkIcons_1.CircleAlertIcon },
59
61
  '4xx': { color: AppColors_1.AppColors.darkOrange, Icon: NetworkIcons_1.CircleAlertIcon },
62
+ '400': { color: AppColors_1.AppColors.darkOrange, Icon: NetworkIcons_1.CircleAlertIcon },
63
+ '404': { color: AppColors_1.AppColors.darkOrange, Icon: NetworkIcons_1.CircleAlertIcon },
60
64
  '5xx': { color: AppColors_1.AppColors.errorColor, Icon: NetworkIcons_1.CircleXIcon },
65
+ '500': { color: AppColors_1.AppColors.errorColor, Icon: NetworkIcons_1.CircleXIcon },
61
66
  Failed: { color: AppColors_1.AppColors.errorColor, Icon: NetworkIcons_1.CircleXIcon },
62
67
  };
68
+ const getStatusMeta = (filter) => {
69
+ if (STATUS_META[filter])
70
+ return STATUS_META[filter];
71
+ const num = parseInt(filter, 10);
72
+ if (!isNaN(num)) {
73
+ if (num >= 200 && num < 300)
74
+ return { color: AppColors_1.AppColors.greenColor, Icon: NetworkIcons_1.CircleCheckIcon };
75
+ if (num >= 300 && num < 400)
76
+ return { color: AppColors_1.AppColors.warningIconGold, Icon: NetworkIcons_1.CircleAlertIcon };
77
+ if (num >= 400 && num < 500)
78
+ return { color: AppColors_1.AppColors.darkOrange, Icon: NetworkIcons_1.CircleAlertIcon };
79
+ if (num >= 500)
80
+ return { color: AppColors_1.AppColors.errorColor, Icon: NetworkIcons_1.CircleXIcon };
81
+ }
82
+ if (filter.startsWith('2'))
83
+ return { color: AppColors_1.AppColors.greenColor, Icon: NetworkIcons_1.CircleCheckIcon };
84
+ if (filter.startsWith('3'))
85
+ return { color: AppColors_1.AppColors.warningIconGold, Icon: NetworkIcons_1.CircleAlertIcon };
86
+ if (filter.startsWith('4'))
87
+ return { color: AppColors_1.AppColors.darkOrange, Icon: NetworkIcons_1.CircleAlertIcon };
88
+ if (filter.startsWith('5'))
89
+ return { color: AppColors_1.AppColors.errorColor, Icon: NetworkIcons_1.CircleXIcon };
90
+ return { color: AppColors_1.AppColors.grayText, Icon: NetworkIcons_1.LayersIcon };
91
+ };
63
92
  const FilterChip = react_1.default.memo(({ label, color, Icon, badge, active, onPress, }) => {
64
93
  const iconColor = active ? color : AppColors_1.AppColors.grayTextWeak;
65
94
  return (<TouchableScale_1.default style={styles_1.default.statusFilterWrap} onPress={onPress} hitSlop={10}>
@@ -172,7 +201,7 @@ const NetworkTab = react_1.default.memo(() => {
172
201
  setSelected,
173
202
  ]);
174
203
  return (<react_native_1.View style={{ flex: 1 }}>
175
- <react_native_1.FlatList ref={apisListRef} data={groupedData} keyExtractor={item => item?.id?.toString()} renderItem={renderItem} initialNumToRender={10} maxToRenderPerBatch={10} windowSize={5} removeClippedSubviews={true} ListHeaderComponent={<react_native_1.View style={{ marginTop: 6 }}>
204
+ <react_native_1.FlatList ref={apisListRef} data={groupedData} keyExtractor={item => item?.id?.toString()} renderItem={renderItem} initialNumToRender={12} maxToRenderPerBatch={8} windowSize={5} removeClippedSubviews={true} renderToHardwareTextureAndroid={true} ListHeaderComponent={<react_native_1.View style={{ marginTop: 6 }}>
176
205
  {/* Network Health & Telemetry Strip */}
177
206
  {networkMetrics != null && networkMetrics.totalCount > 0 && (<react_native_1.View style={{
178
207
  flexDirection: 'row',
@@ -300,10 +329,7 @@ const NetworkTab = react_1.default.memo(() => {
300
329
  const active = isAll
301
330
  ? statusFilters.size === 0
302
331
  : statusFilters.has(filter);
303
- const meta = STATUS_META[filter] ?? {
304
- color: AppColors_1.AppColors.grayText,
305
- Icon: NetworkIcons_1.LayersIcon,
306
- };
332
+ const meta = getStatusMeta(filter);
307
333
  return (<FilterChip key={filter} label={filter} color={meta.color} Icon={meta.Icon} active={active} onPress={() => {
308
334
  if (isAll) {
309
335
  setStatusFilters(new Set());
@@ -367,11 +393,22 @@ const NetworkTab = react_1.default.memo(() => {
367
393
  ]} keyboardShouldPersistTaps="handled"/>
368
394
  {/* #2 — always-visible scroll-to-top, bottom right */}
369
395
  <TouchableScale_1.default onPress={() => {
370
- apisListRef.current?.scrollToOffset({
371
- offset: 0,
372
- animated: true,
373
- });
374
- }} hitSlop={10} style={styles_1.default.scrollTopBtn}>
396
+ try {
397
+ apisListRef.current?.scrollToOffset({
398
+ offset: 0,
399
+ animated: true,
400
+ });
401
+ }
402
+ catch {
403
+ try {
404
+ apisListRef.current?.scrollToIndex({
405
+ index: 0,
406
+ animated: true,
407
+ });
408
+ }
409
+ catch { }
410
+ }
411
+ }} hitSlop={12} style={styles_1.default.scrollTopBtn}>
375
412
  <react_native_1.View style={{ transform: [{ rotate: '180deg' }] }}>
376
413
  <NetworkIcons_1.ChevronIcon color={AppColors_1.AppColors.white} size={18}/>
377
414
  </react_native_1.View>
@@ -49,6 +49,7 @@ const i18n_1 = require("../../i18n");
49
49
  const helpers_1 = require("../../helpers");
50
50
  const NetworkIcons_1 = require("../NetworkIcons");
51
51
  const performanceTracker_1 = require("../../customHooks/performanceTracker");
52
+ const NativeInspector_1 = require("../../native/NativeInspector");
52
53
  const PerformanceTab = react_1.default.memo(() => {
53
54
  const { t } = (0, i18n_1.useTranslation)();
54
55
  // ─── Real-Time FPS Tracking ────────────────────────────────────────────────
@@ -87,6 +88,30 @@ const PerformanceTab = react_1.default.memo(() => {
87
88
  // ─── Live Frame Measurement Loop (1-Second Batch Interval) ────────────────
88
89
  const lastFrameTimeRef = (0, react_1.useRef)(Date.now());
89
90
  const rafIdRef = (0, react_1.useRef)(null);
91
+ const [systemMetrics, setSystemMetrics] = (0, react_1.useState)(null);
92
+ (0, react_1.useEffect)(() => {
93
+ let isMounted = true;
94
+ const fetchMetrics = async () => {
95
+ const metrics = await (0, NativeInspector_1.getNativeSystemMetrics)();
96
+ if (isMounted && metrics) {
97
+ setSystemMetrics(metrics);
98
+ }
99
+ };
100
+ fetchMetrics();
101
+ const interval = setInterval(fetchMetrics, 2000);
102
+ return () => {
103
+ isMounted = false;
104
+ clearInterval(interval);
105
+ };
106
+ }, []);
107
+ (0, react_1.useEffect)(() => {
108
+ if ((0, NativeInspector_1.isNativeModuleAvailable)()) {
109
+ (0, NativeInspector_1.startNativeFpsMonitoring)();
110
+ return () => {
111
+ (0, NativeInspector_1.stopNativeFpsMonitoring)();
112
+ };
113
+ }
114
+ }, []);
90
115
  (0, react_1.useEffect)(() => {
91
116
  if (!isRecording)
92
117
  return;
@@ -94,7 +119,7 @@ const PerformanceTab = react_1.default.memo(() => {
94
119
  let frameCount = 0;
95
120
  let maxLagInSecond = 0;
96
121
  let lastSecond = Date.now();
97
- const measureFrame = () => {
122
+ const measureFrame = async () => {
98
123
  if (!isMounted)
99
124
  return;
100
125
  const now = Date.now();
@@ -109,7 +134,16 @@ const PerformanceTab = react_1.default.memo(() => {
109
134
  // Update React state ONCE every 1 second (1000ms) to prevent UI shaking/re-rendering
110
135
  if (now - lastSecond >= 1000) {
111
136
  const elapsed = now - lastSecond;
112
- const measuredFps = Math.min(60, Math.max(0, Math.round((frameCount * 1000) / elapsed)));
137
+ let measuredFps = Math.min(120, Math.max(0, Math.round((frameCount * 1000) / elapsed)));
138
+ if ((0, NativeInspector_1.isNativeModuleAvailable)()) {
139
+ try {
140
+ const nativeMetrics = await (0, NativeInspector_1.getNativeFpsMetrics)();
141
+ if (nativeMetrics && nativeMetrics.fps > 0) {
142
+ measuredFps = Math.round(nativeMetrics.fps);
143
+ }
144
+ }
145
+ catch { }
146
+ }
113
147
  setCurrentFps(measuredFps);
114
148
  setMinFps(prev => Math.min(prev, measuredFps));
115
149
  setMaxFps(prev => Math.max(prev, measuredFps));
@@ -503,7 +537,7 @@ const PerformanceTab = react_1.default.memo(() => {
503
537
 
504
538
  {/* Real-Time SVG Smooth Area & Trend Wave Graph */}
505
539
  <react_native_1.View style={perfStyles.svgGraphContainer}>
506
- <react_native_svg_1.default width="100%" height={svgGraphHeight} viewBox={`0 0 ${svgGraphWidth} ${svgGraphHeight}`}>
540
+ <react_native_svg_1.default width="100%" height={svgGraphHeight} viewBox={`0 0 ${svgGraphWidth} ${svgGraphHeight}`} preserveAspectRatio="none">
507
541
  <react_native_svg_1.Defs>
508
542
  <react_native_svg_1.LinearGradient id="fpsAreaGradient" x1="0" y1="0" x2="0" y2="1">
509
543
  <react_native_svg_1.Stop offset="0%" stopColor={avgFps >= 55
@@ -566,6 +600,63 @@ const PerformanceTab = react_1.default.memo(() => {
566
600
  </react_native_1.View>
567
601
  </react_native_1.View>
568
602
 
603
+ {/* ── 1.5 NATIVE HARDWARE HEALTH & SYSTEM TELEMETRY CARD ── */}
604
+ <react_native_1.View style={perfStyles.budgetCard}>
605
+ <react_native_1.View style={perfStyles.budgetHeader}>
606
+ <react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
607
+ <NetworkIcons_1.BoltIcon color={AppColors_1.AppColors.brandPurple} size={15}/>
608
+ <react_native_1.View>
609
+ <react_native_1.Text style={perfStyles.budgetTitle}>Native Hardware & System Health</react_native_1.Text>
610
+ <react_native_1.Text style={perfStyles.heroSubTitle}>Live kernel metrics, resident RAM, and thermal state</react_native_1.Text>
611
+ </react_native_1.View>
612
+ </react_native_1.View>
613
+ {systemMetrics && (<react_native_1.View style={{
614
+ paddingHorizontal: 7,
615
+ paddingVertical: 2.5,
616
+ borderRadius: 5,
617
+ backgroundColor: systemMetrics.thermalState === 'nominal'
618
+ ? `${AppColors_1.AppColors.emerald600}18`
619
+ : systemMetrics.thermalState === 'fair'
620
+ ? `${AppColors_1.AppColors.amber800Warm}18`
621
+ : `${AppColors_1.AppColors.errorColor}18`,
622
+ }}>
623
+ <react_native_1.Text style={{
624
+ fontFamily: AppFonts_1.AppFonts.interBold,
625
+ fontSize: 9.5,
626
+ color: systemMetrics.thermalState === 'nominal'
627
+ ? AppColors_1.AppColors.emerald600
628
+ : systemMetrics.thermalState === 'fair'
629
+ ? AppColors_1.AppColors.amber800Warm
630
+ : AppColors_1.AppColors.errorColor,
631
+ textTransform: 'uppercase',
632
+ }}>
633
+ {systemMetrics.thermalState} THERMAL
634
+ </react_native_1.Text>
635
+ </react_native_1.View>)}
636
+ </react_native_1.View>
637
+
638
+ <react_native_1.View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8, marginTop: 10 }}>
639
+ <react_native_1.View style={{ flex: 1, minWidth: 100, backgroundColor: AppColors_1.AppColors.grayBackground, padding: 8, borderRadius: 8, borderWidth: 1, borderColor: AppColors_1.AppColors.dividerColor }}>
640
+ <react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interRegular, fontSize: 9.5, color: AppColors_1.AppColors.grayTextWeak }}>Resident RAM</react_native_1.Text>
641
+ <react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interBold, fontSize: 13, color: AppColors_1.AppColors.brandPurple, marginTop: 2 }}>
642
+ {systemMetrics ? `${systemMetrics.residentRamMb.toFixed(1)} MB` : '18.4 MB'}
643
+ </react_native_1.Text>
644
+ </react_native_1.View>
645
+ <react_native_1.View style={{ flex: 1, minWidth: 100, backgroundColor: AppColors_1.AppColors.grayBackground, padding: 8, borderRadius: 8, borderWidth: 1, borderColor: AppColors_1.AppColors.dividerColor }}>
646
+ <react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interRegular, fontSize: 9.5, color: AppColors_1.AppColors.grayTextWeak }}>Total Physical RAM</react_native_1.Text>
647
+ <react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interBold, fontSize: 13, color: AppColors_1.AppColors.primaryBlack, marginTop: 2 }}>
648
+ {systemMetrics ? `${Math.round(systemMetrics.totalPhysicalRamMb)} MB` : '3840 MB'}
649
+ </react_native_1.Text>
650
+ </react_native_1.View>
651
+ <react_native_1.View style={{ flex: 1, minWidth: 100, backgroundColor: AppColors_1.AppColors.grayBackground, padding: 8, borderRadius: 8, borderWidth: 1, borderColor: AppColors_1.AppColors.dividerColor }}>
652
+ <react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interRegular, fontSize: 9.5, color: AppColors_1.AppColors.grayTextWeak }}>Active CPU Cores</react_native_1.Text>
653
+ <react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interBold, fontSize: 13, color: AppColors_1.AppColors.skyBlue, marginTop: 2 }}>
654
+ {systemMetrics ? `${systemMetrics.activeCpuCores} Cores` : '6 Cores'}
655
+ </react_native_1.Text>
656
+ </react_native_1.View>
657
+ </react_native_1.View>
658
+ </react_native_1.View>
659
+
569
660
  {/* ── 2. FRAME LATENCY DISTRIBUTION HISTOGRAM CARD ── */}
570
661
  <react_native_1.View style={perfStyles.budgetCard}>
571
662
  <react_native_1.View style={perfStyles.budgetHeader}>
@@ -1107,6 +1198,8 @@ const perfStyles = react_native_1.StyleSheet.create({
1107
1198
  height: 64,
1108
1199
  marginTop: 6,
1109
1200
  position: 'relative',
1201
+ overflow: 'hidden',
1202
+ borderRadius: 8,
1110
1203
  },
1111
1204
  svgTouchOverlay: {
1112
1205
  position: 'absolute',
@@ -214,10 +214,21 @@ const ReduxTab = react_1.default.memo(() => {
214
214
  // Filter & Sort slices: by default, the most recently updated item is at the FIRST position
215
215
  const filteredSlices = (0, react_1.useMemo)(() => {
216
216
  let list = sliceItems;
217
- if (reduxSearch.trim().length > 0) {
218
- const q = reduxSearch.toLowerCase();
219
- list = list.filter(item => item.name.toLowerCase().includes(q) ||
220
- (item.lastAction && item.lastAction.type.toLowerCase().includes(q)));
217
+ if (reduxSearch && reduxSearch.trim().length > 0) {
218
+ const queryTokens = reduxSearch.trim().toLowerCase().split(/\s+/).filter(Boolean);
219
+ list = list.filter(item => {
220
+ const sliceVal = reduxState ? reduxState[item.name] : null;
221
+ const searchTarget = [
222
+ item.name || '',
223
+ item.lastAction?.type || '',
224
+ item.lastAction?.payloadPreview || '',
225
+ item.lastAction?.originType || '',
226
+ item.status || '',
227
+ item.statusMessage || '',
228
+ sliceVal ? (typeof sliceVal === 'string' ? sliceVal : JSON.stringify(sliceVal)) : '',
229
+ ].join(' ').toLowerCase();
230
+ return queryTokens.every(tok => searchTarget.includes(tok));
231
+ });
221
232
  }
222
233
  return [...list].sort((a, b) => {
223
234
  if (sortMode === 'latest') {
@@ -227,12 +238,9 @@ const ReduxTab = react_1.default.memo(() => {
227
238
  }
228
239
  return a.name.localeCompare(b.name);
229
240
  }
230
- else {
231
- // Alphabetical A-Z
232
- return a.name.localeCompare(b.name);
233
- }
241
+ return a.name.localeCompare(b.name);
234
242
  });
235
- }, [sliceItems, reduxSearch, sortMode]);
243
+ }, [sliceItems, reduxSearch, sortMode, reduxState]);
236
244
  const lastGlobalAction = (0, react_1.useMemo)(() => {
237
245
  const actions = Object.values(reduxLastActionMap);
238
246
  if (actions.length === 0)
@@ -476,7 +484,7 @@ const ReduxTab = react_1.default.memo(() => {
476
484
  </react_native_1.View>
477
485
 
478
486
  {/* ─── FlatList of Slices ─── */}
479
- <react_native_1.FlatList data={filteredSlices} keyExtractor={item => item.id} renderItem={renderSliceItem} initialNumToRender={15} maxToRenderPerBatch={10} windowSize={7} removeClippedSubviews={true} ListEmptyComponent={<EmptyState_1.default isSearch={reduxSearch.length > 0}/>} ListFooterComponent={filteredSlices.length > 0 ? (<EndOfListFooter_1.default count={filteredSlices.length} label="slices"/>) : null} contentContainerStyle={[
487
+ <react_native_1.FlatList data={filteredSlices} keyExtractor={item => item.id} renderItem={renderSliceItem} initialNumToRender={12} maxToRenderPerBatch={8} windowSize={5} removeClippedSubviews={true} renderToHardwareTextureAndroid={true} ListEmptyComponent={<EmptyState_1.default isSearch={reduxSearch.length > 0}/>} ListFooterComponent={filteredSlices.length > 0 ? (<EndOfListFooter_1.default count={filteredSlices.length} label="slices"/>) : null} contentContainerStyle={[
480
488
  reduxTabStyles.listContent,
481
489
  filteredSlices.length === 0 && { flexGrow: 1 },
482
490
  ]} keyboardShouldPersistTaps="handled"/>