react-native-inapp-inspector 1.1.35 → 2.0.1

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 (104) 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 +890 -0
  4. package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.kt +17 -0
  5. package/dist/commonjs/components/ConsoleLogCard.js +5 -5
  6. package/dist/commonjs/components/CopyButton.js +3 -0
  7. package/dist/commonjs/components/EndOfListFooter.js +1 -1
  8. package/dist/commonjs/components/ErrorBoundary.js +3 -0
  9. package/dist/commonjs/components/Inspector/AnalyticsTab.js +25 -73
  10. package/dist/commonjs/components/Inspector/BundleTab.js +671 -189
  11. package/dist/commonjs/components/Inspector/ConsoleTab.js +110 -99
  12. package/dist/commonjs/components/Inspector/CrashTab.js +99 -91
  13. package/dist/commonjs/components/Inspector/InspectorHeader.js +107 -20
  14. package/dist/commonjs/components/Inspector/MainScreen.js +111 -68
  15. package/dist/commonjs/components/Inspector/NetworkTab.js +151 -30
  16. package/dist/commonjs/components/Inspector/PerformanceTab.js +96 -3
  17. package/dist/commonjs/components/Inspector/ReduxTab.js +18 -10
  18. package/dist/commonjs/components/Inspector/SettingsPanel.js +1218 -1238
  19. package/dist/commonjs/components/Inspector/TabBar.js +4 -2
  20. package/dist/commonjs/components/JsonViewer.js +9 -4
  21. package/dist/commonjs/components/LogCard.js +31 -1
  22. package/dist/commonjs/components/LogSyntaxHighlighter.d.ts +16 -0
  23. package/dist/commonjs/components/LogSyntaxHighlighter.js +130 -0
  24. package/dist/commonjs/components/NetworkIcons.d.ts +3 -0
  25. package/dist/commonjs/components/NetworkIcons.js +15 -1
  26. package/dist/commonjs/components/Slider.d.ts +13 -0
  27. package/dist/commonjs/components/Slider.js +261 -0
  28. package/dist/commonjs/components/TouchableScale.d.ts +1 -0
  29. package/dist/commonjs/components/TouchableScale.js +30 -32
  30. package/dist/commonjs/constants/index.d.ts +1 -2
  31. package/dist/commonjs/constants/index.js +4 -0
  32. package/dist/commonjs/constants/version.d.ts +1 -1
  33. package/dist/commonjs/constants/version.js +1 -1
  34. package/dist/commonjs/customHooks/analyticsLogger.d.ts +4 -0
  35. package/dist/commonjs/customHooks/analyticsLogger.js +8 -6
  36. package/dist/commonjs/customHooks/crashHandler.js +8 -1
  37. package/dist/commonjs/helpers/index.d.ts +1 -0
  38. package/dist/commonjs/helpers/index.js +25 -1
  39. package/dist/commonjs/helpers/searchQueryParser.d.ts +6 -0
  40. package/dist/commonjs/helpers/searchQueryParser.js +236 -0
  41. package/dist/commonjs/helpers/settingsStore.d.ts +9 -0
  42. package/dist/commonjs/helpers/settingsStore.js +59 -3
  43. package/dist/commonjs/index.d.ts +1 -0
  44. package/dist/commonjs/index.js +220 -43
  45. package/dist/commonjs/native/NativeInspector.d.ts +125 -0
  46. package/dist/commonjs/native/NativeInspector.js +313 -0
  47. package/dist/commonjs/native/NativeNetworkInspector.d.ts +22 -0
  48. package/dist/commonjs/native/NativeNetworkInspector.js +4 -0
  49. package/dist/commonjs/styles/index.js +41 -41
  50. package/dist/commonjs/types/enums.d.ts +1 -1
  51. package/dist/commonjs/types/interfaces.d.ts +9 -0
  52. package/dist/esm/components/ConsoleLogCard.js +5 -5
  53. package/dist/esm/components/CopyButton.js +3 -0
  54. package/dist/esm/components/EndOfListFooter.js +2 -2
  55. package/dist/esm/components/ErrorBoundary.js +3 -0
  56. package/dist/esm/components/Inspector/AnalyticsTab.js +27 -75
  57. package/dist/esm/components/Inspector/BundleTab.js +671 -189
  58. package/dist/esm/components/Inspector/ConsoleTab.js +111 -100
  59. package/dist/esm/components/Inspector/CrashTab.js +99 -91
  60. package/dist/esm/components/Inspector/InspectorHeader.js +108 -21
  61. package/dist/esm/components/Inspector/MainScreen.js +113 -70
  62. package/dist/esm/components/Inspector/NetworkTab.js +152 -31
  63. package/dist/esm/components/Inspector/PerformanceTab.js +96 -3
  64. package/dist/esm/components/Inspector/ReduxTab.js +18 -10
  65. package/dist/esm/components/Inspector/SettingsPanel.js +1222 -1242
  66. package/dist/esm/components/Inspector/TabBar.js +4 -2
  67. package/dist/esm/components/JsonViewer.js +9 -4
  68. package/dist/esm/components/LogCard.js +32 -2
  69. package/dist/esm/components/LogSyntaxHighlighter.d.ts +16 -0
  70. package/dist/esm/components/LogSyntaxHighlighter.js +123 -0
  71. package/dist/esm/components/NetworkIcons.d.ts +3 -0
  72. package/dist/esm/components/NetworkIcons.js +11 -0
  73. package/dist/esm/components/Slider.d.ts +13 -0
  74. package/dist/esm/components/Slider.js +222 -0
  75. package/dist/esm/components/TouchableScale.d.ts +1 -0
  76. package/dist/esm/components/TouchableScale.js +31 -33
  77. package/dist/esm/constants/index.d.ts +1 -2
  78. package/dist/esm/constants/index.js +4 -0
  79. package/dist/esm/constants/version.d.ts +1 -1
  80. package/dist/esm/constants/version.js +1 -1
  81. package/dist/esm/customHooks/analyticsLogger.d.ts +4 -0
  82. package/dist/esm/customHooks/analyticsLogger.js +6 -5
  83. package/dist/esm/customHooks/crashHandler.js +8 -1
  84. package/dist/esm/helpers/index.d.ts +1 -0
  85. package/dist/esm/helpers/index.js +11 -1
  86. package/dist/esm/helpers/searchQueryParser.d.ts +6 -0
  87. package/dist/esm/helpers/searchQueryParser.js +233 -0
  88. package/dist/esm/helpers/settingsStore.d.ts +9 -0
  89. package/dist/esm/helpers/settingsStore.js +58 -3
  90. package/dist/esm/index.d.ts +1 -0
  91. package/dist/esm/index.js +207 -44
  92. package/dist/esm/native/NativeInspector.d.ts +125 -0
  93. package/dist/esm/native/NativeInspector.js +289 -0
  94. package/dist/esm/native/NativeNetworkInspector.d.ts +22 -0
  95. package/dist/esm/native/NativeNetworkInspector.js +2 -0
  96. package/dist/esm/styles/index.js +41 -41
  97. package/dist/esm/types/enums.d.ts +1 -1
  98. package/dist/esm/types/interfaces.d.ts +9 -0
  99. package/ios/NetworkInspectorModule.h +6 -0
  100. package/ios/NetworkInspectorModule.m +760 -5
  101. package/package.json +13 -2
  102. package/react-native-inapp-inspector.podspec +7 -2
  103. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +0 -97
  104. package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.java +0 -28
@@ -6,6 +6,305 @@
6
6
  static NetworkInspectorModule *sharedInstance = nil;
7
7
  static NSUncaughtExceptionHandler *previousUncaughtExceptionHandler = NULL;
8
8
 
9
+ @interface InAppInspectorFloatingView : UIView
10
+ @property (nonatomic, copy) void (^onTapBlock)(void);
11
+ @property (nonatomic, strong) UIView *badgeDot;
12
+ - (void)updateBadgeVisible:(BOOL)visible;
13
+ @end
14
+
15
+ @interface InAppInspectorOwlView : UIView
16
+ @end
17
+
18
+ @implementation InAppInspectorOwlView
19
+
20
+ - (instancetype)initWithFrame:(CGRect)frame {
21
+ if (self = [super initWithFrame:frame]) {
22
+ self.backgroundColor = [UIColor clearColor];
23
+ self.userInteractionEnabled = NO;
24
+ }
25
+ return self;
26
+ }
27
+
28
+ - (void)drawRect:(CGRect)rect {
29
+ CGContextRef ctx = UIGraphicsGetCurrentContext();
30
+ if (!ctx) return;
31
+
32
+ CGFloat w = rect.size.width;
33
+ CGFloat scale = w / 256.0;
34
+
35
+ CGContextSaveGState(ctx);
36
+ CGContextScaleCTM(ctx, scale, scale);
37
+
38
+ // 1. Owl Body with Ears
39
+ UIBezierPath *bodyPath = [UIBezierPath bezierPath];
40
+ [bodyPath moveToPoint:CGPointMake(62, 150)];
41
+ [bodyPath addCurveToPoint:CGPointMake(90, 58) controlPoint1:CGPointMake(58, 104) controlPoint2:CGPointMake(70, 70)];
42
+ [bodyPath addLineToPoint:CGPointMake(98, 42)]; // Left ear
43
+ [bodyPath addLineToPoint:CGPointMake(116, 62)];
44
+ [bodyPath addQuadCurveToPoint:CGPointMake(140, 62) controlPoint:CGPointMake(128, 57)];
45
+ [bodyPath addLineToPoint:CGPointMake(158, 42)]; // Right ear
46
+ [bodyPath addLineToPoint:CGPointMake(166, 58)];
47
+ [bodyPath addCurveToPoint:CGPointMake(194, 150) controlPoint1:CGPointMake(186, 70) controlPoint2:CGPointMake(198, 104)];
48
+ [bodyPath addCurveToPoint:CGPointMake(152, 212) controlPoint1:CGPointMake(198, 180) controlPoint2:CGPointMake(184, 204)];
49
+ [bodyPath addCurveToPoint:CGPointMake(104, 212) controlPoint1:CGPointMake(140, 216) controlPoint2:CGPointMake(116, 216)];
50
+ [bodyPath addCurveToPoint:CGPointMake(62, 150) controlPoint1:CGPointMake(72, 204) controlPoint2:CGPointMake(58, 180)];
51
+ [bodyPath closePath];
52
+
53
+ [[UIColor colorWithRed:32.0/255.0 green:46.0/255.0 blue:85.0/255.0 alpha:1.0] setFill];
54
+ [bodyPath fill];
55
+
56
+ [[UIColor colorWithRed:56.0/255.0 green:189.0/255.0 blue:248.0/255.0 alpha:1.0] setStroke];
57
+ bodyPath.lineWidth = 4.0;
58
+ [bodyPath stroke];
59
+
60
+ // 2. Wings
61
+ UIBezierPath *leftWing = [UIBezierPath bezierPath];
62
+ [leftWing moveToPoint:CGPointMake(74, 124)];
63
+ [leftWing addCurveToPoint:CGPointMake(86, 204) controlPoint1:CGPointMake(58, 154) controlPoint2:CGPointMake(60, 190)];
64
+ [leftWing addCurveToPoint:CGPointMake(88, 126) controlPoint1:CGPointMake(79, 176) controlPoint2:CGPointMake(77, 148)];
65
+ [leftWing closePath];
66
+ [[UIColor colorWithRed:26.0/255.0 green:37.0/255.0 blue:69.0/255.0 alpha:1.0] setFill];
67
+ [leftWing fill];
68
+
69
+ UIBezierPath *rightWing = [UIBezierPath bezierPath];
70
+ [rightWing moveToPoint:CGPointMake(182, 124)];
71
+ [rightWing addCurveToPoint:CGPointMake(170, 204) controlPoint1:CGPointMake(198, 154) controlPoint2:CGPointMake(196, 190)];
72
+ [rightWing addCurveToPoint:CGPointMake(168, 126) controlPoint1:CGPointMake(177, 176) controlPoint2:CGPointMake(179, 148)];
73
+ [rightWing closePath];
74
+ [[UIColor colorWithRed:26.0/255.0 green:37.0/255.0 blue:69.0/255.0 alpha:1.0] setFill];
75
+ [rightWing fill];
76
+
77
+ // 3. Belly Plate
78
+ UIBezierPath *belly = [UIBezierPath bezierPath];
79
+ [belly moveToPoint:CGPointMake(128, 126)];
80
+ [belly addCurveToPoint:CGPointMake(162, 174) controlPoint1:CGPointMake(151, 126) controlPoint2:CGPointMake(164, 148)];
81
+ [belly addCurveToPoint:CGPointMake(128, 212) controlPoint1:CGPointMake(160, 198) controlPoint2:CGPointMake(146, 212)];
82
+ [belly addCurveToPoint:CGPointMake(94, 174) controlPoint1:CGPointMake(110, 212) controlPoint2:CGPointMake(96, 198)];
83
+ [belly addCurveToPoint:CGPointMake(128, 126) controlPoint1:CGPointMake(92, 148) controlPoint2:CGPointMake(105, 126)];
84
+ [belly closePath];
85
+ [[UIColor colorWithRed:51.0/255.0 green:71.0/255.0 blue:122.0/255.0 alpha:1.0] setFill];
86
+ [belly fill];
87
+
88
+ // 4. Chest Screen `< / >`
89
+ UIBezierPath *screen = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(107, 161, 42, 32) cornerRadius:8];
90
+ [[UIColor colorWithRed:12.0/255.0 green:20.0/255.0 blue:38.0/255.0 alpha:1.0] setFill];
91
+ [screen fill];
92
+ [[UIColor colorWithRed:56.0/255.0 green:189.0/255.0 blue:248.0/255.0 alpha:0.8] setStroke];
93
+ screen.lineWidth = 2.0;
94
+ [screen stroke];
95
+
96
+ UIBezierPath *codeLines = [UIBezierPath bezierPath];
97
+ [codeLines moveToPoint:CGPointMake(122, 170)];
98
+ [codeLines addLineToPoint:CGPointMake(115, 177)];
99
+ [codeLines addLineToPoint:CGPointMake(122, 184)];
100
+ [codeLines moveToPoint:CGPointMake(134, 170)];
101
+ [codeLines addLineToPoint:CGPointMake(141, 177)];
102
+ [codeLines addLineToPoint:CGPointMake(134, 184)];
103
+ [codeLines moveToPoint:CGPointMake(130, 168)];
104
+ [codeLines addLineToPoint:CGPointMake(126, 186)];
105
+ [[UIColor colorWithRed:143.0/255.0 green:208.0/255.0 blue:236.0/255.0 alpha:1.0] setStroke];
106
+ codeLines.lineWidth = 3.0;
107
+ codeLines.lineCapStyle = kCGLineCapRound;
108
+ codeLines.lineJoinStyle = kCGLineJoinRound;
109
+ [codeLines stroke];
110
+
111
+ // 5. Normal Right Eye
112
+ UIBezierPath *rightEye = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(136, 90, 34, 34)];
113
+ [[UIColor colorWithRed:251.0/255.0 green:191.0/255.0 blue:36.0/255.0 alpha:1.0] setFill];
114
+ [rightEye fill];
115
+ [[UIColor colorWithRed:26.0/255.0 green:18.0/255.0 blue:5.0/255.0 alpha:1.0] setStroke];
116
+ rightEye.lineWidth = 2.2;
117
+ [rightEye stroke];
118
+
119
+ UIBezierPath *rightPupil = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(145, 100, 16, 16)];
120
+ [[UIColor colorWithRed:10.0/255.0 green:14.0/255.0 blue:24.0/255.0 alpha:1.0] setFill];
121
+ [rightPupil fill];
122
+
123
+ UIBezierPath *rightGlint = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(153, 98, 6, 6)];
124
+ [[UIColor whiteColor] setFill];
125
+ [rightGlint fill];
126
+
127
+ // 6. Beak
128
+ UIBezierPath *beak = [UIBezierPath bezierPath];
129
+ [beak moveToPoint:CGPointMake(123.5, 123)];
130
+ [beak addQuadCurveToPoint:CGPointMake(132.5, 123) controlPoint:CGPointMake(128, 121)];
131
+ [beak addQuadCurveToPoint:CGPointMake(128, 134.5) controlPoint:CGPointMake(131, 132)];
132
+ [beak addQuadCurveToPoint:CGPointMake(123.5, 123) controlPoint:CGPointMake(125, 132)];
133
+ [beak closePath];
134
+ [[UIColor colorWithRed:251.0/255.0 green:146.0/255.0 blue:60.0/255.0 alpha:1.0] setFill];
135
+ [beak fill];
136
+
137
+ // 7. Magnifier Debug Left Eye
138
+ UIBezierPath *leftEye = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(75, 83, 40, 40)];
139
+ [[UIColor colorWithRed:251.0/255.0 green:191.0/255.0 blue:36.0/255.0 alpha:1.0] setFill];
140
+ [leftEye fill];
141
+ [[UIColor colorWithRed:26.0/255.0 green:18.0/255.0 blue:5.0/255.0 alpha:1.0] setStroke];
142
+ leftEye.lineWidth = 2.6;
143
+ [leftEye stroke];
144
+
145
+ UIBezierPath *leftPupil = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(85, 94, 20, 20)];
146
+ [[UIColor colorWithRed:10.0/255.0 green:14.0/255.0 blue:24.0/255.0 alpha:1.0] setFill];
147
+ [leftPupil fill];
148
+
149
+ UIBezierPath *leftGlint = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(95, 92, 7, 7)];
150
+ [[UIColor whiteColor] setFill];
151
+ [leftGlint fill];
152
+
153
+ // Magnifier Lens Ring & Handle
154
+ UIBezierPath *handle = [UIBezierPath bezierPath];
155
+ [handle moveToPoint:CGPointMake(75, 123)];
156
+ [handle addLineToPoint:CGPointMake(54, 147)];
157
+ [[UIColor colorWithRed:56.0/255.0 green:189.0/255.0 blue:248.0/255.0 alpha:1.0] setStroke];
158
+ handle.lineWidth = 8.5;
159
+ handle.lineCapStyle = kCGLineCapRound;
160
+ [handle stroke];
161
+
162
+ UIBezierPath *lensRing = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(67, 75, 56, 56)];
163
+ lensRing.lineWidth = 7.0;
164
+ [lensRing stroke];
165
+
166
+ // Rosy Cheeks
167
+ UIBezierPath *leftCheek = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(74, 121, 18, 12)];
168
+ [[UIColor colorWithRed:251.0/255.0 green:113.0/255.0 blue:133.0/255.0 alpha:0.5] setFill];
169
+ [leftCheek fill];
170
+
171
+ UIBezierPath *rightCheek = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(158, 116, 18, 12)];
172
+ [[UIColor colorWithRed:251.0/255.0 green:113.0/255.0 blue:133.0/255.0 alpha:0.5] setFill];
173
+ [rightCheek fill];
174
+
175
+ CGContextRestoreGState(ctx);
176
+ }
177
+
178
+ @end
179
+
180
+ @interface InAppInspectorFloatingView ()
181
+ @property (nonatomic, assign) CGPoint touchDownPoint;
182
+ @property (nonatomic, assign) CGPoint initialCenter;
183
+ @property (nonatomic, assign) NSTimeInterval touchDownTime;
184
+ @property (nonatomic, assign) BOOL isDragging;
185
+ @end
186
+
187
+ @implementation InAppInspectorFloatingView
188
+
189
+ - (instancetype)initWithFrame:(CGRect)frame {
190
+ self = [super initWithFrame:frame];
191
+ if (self) {
192
+ self.userInteractionEnabled = YES;
193
+ self.multipleTouchEnabled = NO;
194
+ self.layer.cornerRadius = frame.size.width / 2.0;
195
+ self.layer.masksToBounds = NO;
196
+ self.backgroundColor = [UIColor colorWithRed:15.0/255.0 green:23.0/255.0 blue:42.0/255.0 alpha:0.95];
197
+
198
+ // Glow shadow
199
+ self.layer.shadowColor = [UIColor colorWithRed:56.0/255.0 green:189.0/255.0 blue:248.0/255.0 alpha:0.65].CGColor;
200
+ self.layer.shadowOffset = CGSizeMake(0, 5);
201
+ self.layer.shadowRadius = 10;
202
+ self.layer.shadowOpacity = 1.0;
203
+ self.layer.borderWidth = 2.2;
204
+ self.layer.borderColor = [UIColor colorWithRed:56.0/255.0 green:189.0/255.0 blue:248.0/255.0 alpha:0.9].CGColor;
205
+
206
+ // Native drawn Inspector Owl icon (Enhanced & Larger)
207
+ CGFloat iconSize = frame.size.width * 0.94;
208
+ CGFloat iconOffset = (frame.size.width - iconSize) / 2.0;
209
+ InAppInspectorOwlView *owlView = [[InAppInspectorOwlView alloc] initWithFrame:CGRectMake(iconOffset, iconOffset, iconSize, iconSize)];
210
+ owlView.userInteractionEnabled = NO;
211
+ [self addSubview:owlView];
212
+ }
213
+ return self;
214
+ }
215
+
216
+ - (void)updateBadgeVisible:(BOOL)visible {
217
+ // Active badge dot removed
218
+ }
219
+
220
+ - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
221
+ UITouch *touch = [touches anyObject];
222
+ if (!touch) return;
223
+
224
+ if (self.superview) {
225
+ [self.superview bringSubviewToFront:self];
226
+ }
227
+
228
+ self.touchDownPoint = [touch locationInView:self.window];
229
+ self.initialCenter = self.center;
230
+ self.touchDownTime = CACurrentMediaTime();
231
+ self.isDragging = NO;
232
+
233
+ [UIView animateWithDuration:0.08 animations:^{
234
+ self.transform = CGAffineTransformMakeScale(0.92, 0.92);
235
+ }];
236
+ }
237
+
238
+ - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
239
+ UITouch *touch = [touches anyObject];
240
+ UIView *superview = self.superview;
241
+ if (!touch || !superview) return;
242
+
243
+ CGPoint currentPoint = [touch locationInView:self.window];
244
+ CGFloat dx = currentPoint.x - self.touchDownPoint.x;
245
+ CGFloat dy = currentPoint.y - self.touchDownPoint.y;
246
+ CGFloat distance = hypot(dx, dy);
247
+
248
+ if (!self.isDragging && distance > 14.0) {
249
+ self.isDragging = YES;
250
+ }
251
+
252
+ if (self.isDragging) {
253
+ CGFloat halfW = self.bounds.size.width / 2.0;
254
+ CGFloat halfH = self.bounds.size.height / 2.0;
255
+ CGFloat minX = halfW + 10.0;
256
+ CGFloat maxX = superview.bounds.size.width - halfW - 10.0;
257
+ CGFloat minY = halfH + 44.0;
258
+ CGFloat maxY = superview.bounds.size.height - halfH - 44.0;
259
+
260
+ CGPoint newCenter = CGPointMake(self.initialCenter.x + dx, self.initialCenter.y + dy);
261
+ newCenter.x = MAX(minX, MIN(maxX, newCenter.x));
262
+ newCenter.y = MAX(minY, MIN(maxY, newCenter.y));
263
+ self.center = newCenter;
264
+ }
265
+ }
266
+
267
+ - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
268
+ UITouch *touch = [touches anyObject];
269
+ UIView *superview = self.superview;
270
+
271
+ [UIView animateWithDuration:0.12 animations:^{
272
+ self.transform = CGAffineTransformIdentity;
273
+ }];
274
+
275
+ if (!touch || !superview) return;
276
+
277
+ CGPoint currentPoint = [touch locationInView:self.window];
278
+ CGFloat dx = currentPoint.x - self.touchDownPoint.x;
279
+ CGFloat dy = currentPoint.y - self.touchDownPoint.y;
280
+ CGFloat distance = hypot(dx, dy);
281
+ NSTimeInterval elapsed = CACurrentMediaTime() - self.touchDownTime;
282
+
283
+ if (!self.isDragging && distance < 24.0 && elapsed < 0.85) {
284
+ if (self.onTapBlock) {
285
+ self.onTapBlock();
286
+ }
287
+ } else if (self.isDragging) {
288
+ CGFloat halfW = self.bounds.size.width / 2.0;
289
+ CGFloat minX = halfW + 10.0;
290
+ CGFloat maxX = superview.bounds.size.width - halfW - 10.0;
291
+ CGFloat targetX = (self.center.x < superview.bounds.size.width / 2.0) ? minX : maxX;
292
+ [UIView animateWithDuration:0.25 delay:0 usingSpringWithDamping:0.75 initialSpringVelocity:0.5 options:UIViewAnimationOptionCurveEaseOut animations:^{
293
+ self.center = CGPointMake(targetX, self.center.y);
294
+ } completion:nil];
295
+ }
296
+ }
297
+
298
+ - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
299
+ [UIView animateWithDuration:0.12 animations:^{
300
+ self.transform = CGAffineTransformIdentity;
301
+ }];
302
+ }
303
+
304
+ @end
305
+
306
+ static InAppInspectorFloatingView *floatingButtonView = nil;
307
+
9
308
  static void NativeSignalHandler(int signalNumber) {
10
309
  void* callstack[128];
11
310
  int frames = backtrace(callstack, 128);
@@ -58,22 +357,57 @@ static void NativeExceptionHandler(NSException *exception) {
58
357
 
59
358
  @implementation NetworkInspectorModule {
60
359
  bool hasListeners;
360
+ CADisplayLink *displayLink;
361
+ CFTimeInterval lastFpsTimestamp;
362
+ NSInteger frameCount;
363
+ double currentCalculatedFps;
364
+ dispatch_queue_t _networkQueue;
365
+ dispatch_queue_t _consoleQueue;
366
+ dispatch_queue_t _analyticsQueue;
367
+ dispatch_queue_t _reduxQueue;
368
+ dispatch_queue_t _crashQueue;
369
+ dispatch_queue_t _metricsQueue;
370
+ NSMutableArray *_nativeNetworkLogs;
371
+ NSMutableArray *_nativeConsoleLogs;
372
+ NSMutableArray *_nativeAnalyticsEvents;
373
+ NSMutableArray *_nativeCrashRecords;
61
374
  }
62
375
 
63
376
  RCT_EXPORT_MODULE(NetworkInspectorModule);
64
377
 
65
- + (BOOL)requiresMainQueueSetup {
66
- return YES;
67
- }
68
-
69
378
  - (instancetype)init {
70
379
  if (self = [super init]) {
71
380
  sharedInstance = self;
381
+ self->currentCalculatedFps = 60.0;
382
+ self->_networkQueue = dispatch_queue_create("com.inappinspector.network", DISPATCH_QUEUE_SERIAL);
383
+ self->_consoleQueue = dispatch_queue_create("com.inappinspector.console", DISPATCH_QUEUE_SERIAL);
384
+ self->_analyticsQueue = dispatch_queue_create("com.inappinspector.analytics", DISPATCH_QUEUE_SERIAL);
385
+ self->_reduxQueue = dispatch_queue_create("com.inappinspector.redux", DISPATCH_QUEUE_SERIAL);
386
+ self->_crashQueue = dispatch_queue_create("com.inappinspector.crash", DISPATCH_QUEUE_SERIAL);
387
+ self->_metricsQueue = dispatch_queue_create("com.inappinspector.metrics", DISPATCH_QUEUE_SERIAL);
388
+ self->_nativeNetworkLogs = [NSMutableArray array];
389
+ self->_nativeConsoleLogs = [NSMutableArray array];
390
+ self->_nativeAnalyticsEvents = [NSMutableArray array];
391
+ self->_nativeCrashRecords = [NSMutableArray array];
72
392
  [self installHandlers];
393
+ [[NSNotificationCenter defaultCenter] addObserver:self
394
+ selector:@selector(handleMotionShakeNotification:)
395
+ name:@"RCTShowDevMenuNotification"
396
+ object:nil];
73
397
  }
74
398
  return self;
75
399
  }
76
400
 
401
+ + (BOOL)requiresMainQueueSetup {
402
+ return YES;
403
+ }
404
+
405
+ - (void)handleMotionShakeNotification:(NSNotification *)notification {
406
+ if (hasListeners) {
407
+ [self sendEventWithName:@"onDeviceShake" body:@{}];
408
+ }
409
+ }
410
+
77
411
  - (void)startObserving {
78
412
  hasListeners = YES;
79
413
  }
@@ -83,7 +417,7 @@ RCT_EXPORT_MODULE(NetworkInspectorModule);
83
417
  }
84
418
 
85
419
  - (NSArray<NSString *> *)supportedEvents {
86
- return @[@"onNativeCrash"];
420
+ return @[@"onNativeCrash", @"onFloatingButtonPress", @"onDeviceShake"];
87
421
  }
88
422
 
89
423
  - (void)installHandlers {
@@ -122,4 +456,425 @@ RCT_EXPORT_METHOD(enableNativeCrashProtection:(RCTPromiseResolveBlock)resolve
122
456
  resolve(@(YES));
123
457
  }
124
458
 
459
+ RCT_EXPORT_METHOD(getDeviceMetrics:(RCTPromiseResolveBlock)resolve
460
+ rejecter:(RCTPromiseRejectBlock)reject) {
461
+ NSMutableDictionary *metrics = [NSMutableDictionary dictionary];
462
+
463
+ // 1. Memory / RAM Metrics
464
+ unsigned long long physicalMemory = [NSProcessInfo processInfo].physicalMemory;
465
+ [metrics setObject:@(physicalMemory) forKey:@"totalRAM"];
466
+
467
+ // App Resident Memory via mach task
468
+ struct mach_task_basic_info info;
469
+ mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT;
470
+ if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &count) == KERN_SUCCESS) {
471
+ [metrics setObject:@(info.resident_size) forKey:@"residentMemory"];
472
+ [metrics setObject:@(info.virtual_size) forKey:@"virtualMemory"];
473
+ }
474
+
475
+ // Free Memory
476
+ mach_port_t host_port = mach_host_self();
477
+ mach_msg_type_number_t host_size = sizeof(vm_statistics64_data_t) / sizeof(integer_t);
478
+ vm_size_t pagesize;
479
+ vm_statistics64_data_t vm_stat;
480
+ host_page_size(host_port, &pagesize);
481
+ if (host_statistics64(host_port, HOST_VM_INFO64, (host_info64_t)&vm_stat, &host_size) == KERN_SUCCESS) {
482
+ unsigned long long freeMem = (vm_stat.free_count + vm_stat.inactive_count) * pagesize;
483
+ [metrics setObject:@(freeMem) forKey:@"freeRAM"];
484
+ [metrics setObject:@(physicalMemory - freeMem) forKey:@"usedRAM"];
485
+ }
486
+
487
+ // 2. Storage Metrics
488
+ NSError *error = nil;
489
+ NSDictionary *fsAttrs = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:&error];
490
+ if (fsAttrs) {
491
+ NSNumber *freeSize = [fsAttrs objectForKey:NSFileSystemFreeSize];
492
+ NSNumber *totalSize = [fsAttrs objectForKey:NSFileSystemSize];
493
+ if (freeSize) [metrics setObject:freeSize forKey:@"freeStorage"];
494
+ if (totalSize) [metrics setObject:totalSize forKey:@"totalStorage"];
495
+ }
496
+
497
+ // 3. Battery Level & State
498
+ [UIDevice currentDevice].batteryMonitoringEnabled = YES;
499
+ float batteryLevel = [UIDevice currentDevice].batteryLevel;
500
+ if (batteryLevel >= 0.0f) {
501
+ [metrics setObject:@(batteryLevel * 100.0f) forKey:@"batteryPercent"];
502
+ }
503
+ UIDeviceBatteryState bState = [UIDevice currentDevice].batteryState;
504
+ BOOL isCharging = (bState == UIDeviceBatteryStateCharging || bState == UIDeviceBatteryStateFull);
505
+ [metrics setObject:@(isCharging) forKey:@"isCharging"];
506
+
507
+ // 4. Device & Hardware Identifiers
508
+ [metrics setObject:[UIDevice currentDevice].model forKey:@"deviceModel"];
509
+ [metrics setObject:[UIDevice currentDevice].systemName forKey:@"deviceBrand"];
510
+ [metrics setObject:[UIDevice currentDevice].systemVersion forKey:@"osVersion"];
511
+ [metrics setObject:@"arm64" forKey:@"cpuAbi"];
512
+
513
+ resolve(metrics);
514
+ }
515
+
516
+ RCT_EXPORT_METHOD(showFloatingButton:(NSDictionary *)options
517
+ resolver:(RCTPromiseResolveBlock)resolve
518
+ rejecter:(RCTPromiseRejectBlock)reject) {
519
+ dispatch_async(dispatch_get_main_queue(), ^{
520
+ UIWindow *targetWindow = nil;
521
+ if (@available(iOS 13.0, *)) {
522
+ for (UIWindowScene *scene in [UIApplication sharedApplication].connectedScenes) {
523
+ if (scene.activationState == UISceneActivationStateForegroundActive && [scene isKindOfClass:[UIWindowScene class]]) {
524
+ for (UIWindow *w in scene.windows) {
525
+ if (w.isKeyWindow) {
526
+ targetWindow = w;
527
+ break;
528
+ }
529
+ }
530
+ }
531
+ if (targetWindow) break;
532
+ }
533
+ }
534
+ if (!targetWindow) {
535
+ targetWindow = [UIApplication sharedApplication].keyWindow;
536
+ if (!targetWindow && [UIApplication sharedApplication].windows.count > 0) {
537
+ targetWindow = [UIApplication sharedApplication].windows.firstObject;
538
+ }
539
+ }
540
+
541
+ if (!targetWindow) {
542
+ resolve(@(NO));
543
+ return;
544
+ }
545
+
546
+ CGFloat size = 64.0;
547
+ if (options && options[@"size"]) {
548
+ size = [options[@"size"] doubleValue];
549
+ }
550
+
551
+ CGFloat initialX = targetWindow.bounds.size.width - size - 20.0;
552
+ CGFloat initialY = targetWindow.bounds.size.height - size - 110.0;
553
+ if (options && options[@"x"]) {
554
+ initialX = [options[@"x"] doubleValue];
555
+ }
556
+ if (options && options[@"y"]) {
557
+ initialY = [options[@"y"] doubleValue];
558
+ }
559
+
560
+ if (floatingButtonView == nil) {
561
+ floatingButtonView = [[InAppInspectorFloatingView alloc] initWithFrame:CGRectMake(initialX, initialY, size, size)];
562
+ __weak NetworkInspectorModule *weakSelf = self;
563
+ floatingButtonView.onTapBlock = ^{
564
+ NetworkInspectorModule *strongSelf = weakSelf ?: sharedInstance;
565
+ if (strongSelf) {
566
+ [strongSelf sendEventWithName:@"onFloatingButtonPress" body:@{}];
567
+ }
568
+ };
569
+ }
570
+
571
+ if (floatingButtonView.superview == nil) {
572
+ [targetWindow addSubview:floatingButtonView];
573
+ } else {
574
+ [targetWindow bringSubviewToFront:floatingButtonView];
575
+ }
576
+
577
+ floatingButtonView.hidden = NO;
578
+ floatingButtonView.alpha = 0.0;
579
+ [UIView animateWithDuration:0.2 animations:^{
580
+ floatingButtonView.alpha = 1.0;
581
+ }];
582
+
583
+ resolve(@(YES));
584
+ });
585
+ }
586
+
587
+ RCT_EXPORT_METHOD(hideFloatingButton:(RCTPromiseResolveBlock)resolve
588
+ rejecter:(RCTPromiseRejectBlock)reject) {
589
+ dispatch_async(dispatch_get_main_queue(), ^{
590
+ if (floatingButtonView != nil) {
591
+ [UIView animateWithDuration:0.15 animations:^{
592
+ floatingButtonView.alpha = 0.0;
593
+ } completion:^(BOOL finished) {
594
+ floatingButtonView.hidden = YES;
595
+ }];
596
+ }
597
+ resolve(@(YES));
598
+ });
599
+ }
600
+
601
+ RCT_EXPORT_METHOD(setFloatingButtonBadge:(BOOL)hasBadge
602
+ resolver:(RCTPromiseResolveBlock)resolve
603
+ rejecter:(RCTPromiseRejectBlock)reject) {
604
+ dispatch_async(dispatch_get_main_queue(), ^{
605
+ if (floatingButtonView != nil) {
606
+ [floatingButtonView updateBadgeVisible:hasBadge];
607
+ }
608
+ resolve(@(YES));
609
+ });
610
+ }
611
+
612
+ RCT_EXPORT_METHOD(setFloatingButtonPosition:(double)x y:(double)y
613
+ resolver:(RCTPromiseResolveBlock)resolve
614
+ rejecter:(RCTPromiseRejectBlock)reject) {
615
+ dispatch_async(dispatch_get_main_queue(), ^{
616
+ if (floatingButtonView != nil && floatingButtonView.superview != nil) {
617
+ floatingButtonView.frame = CGRectMake(x, y, floatingButtonView.frame.size.width, floatingButtonView.frame.size.height);
618
+ }
619
+ resolve(@(YES));
620
+ });
621
+ }
622
+
623
+ RCT_EXPORT_METHOD(startFpsMonitoring:(RCTPromiseResolveBlock)resolve
624
+ rejecter:(RCTPromiseRejectBlock)reject) {
625
+ dispatch_async(dispatch_get_main_queue(), ^{
626
+ if (self->displayLink == nil) {
627
+ self->lastFpsTimestamp = 0;
628
+ self->frameCount = 0;
629
+ self->currentCalculatedFps = 60.0;
630
+ self->displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleFpsTick:)];
631
+ [self->displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
632
+ }
633
+ resolve(@(YES));
634
+ });
635
+ }
636
+
637
+ - (void)handleFpsTick:(CADisplayLink *)link {
638
+ if (self->lastFpsTimestamp == 0) {
639
+ self->lastFpsTimestamp = link.timestamp;
640
+ return;
641
+ }
642
+ self->frameCount++;
643
+ CFTimeInterval elapsed = link.timestamp - self->lastFpsTimestamp;
644
+ if (elapsed >= 0.5) {
645
+ self->currentCalculatedFps = (double)self->frameCount / elapsed;
646
+ self->frameCount = 0;
647
+ self->lastFpsTimestamp = link.timestamp;
648
+ }
649
+ }
650
+
651
+ RCT_EXPORT_METHOD(stopFpsMonitoring:(RCTPromiseResolveBlock)resolve
652
+ rejecter:(RCTPromiseRejectBlock)reject) {
653
+ dispatch_async(dispatch_get_main_queue(), ^{
654
+ if (self->displayLink != nil) {
655
+ [self->displayLink invalidate];
656
+ self->displayLink = nil;
657
+ }
658
+ resolve(@(YES));
659
+ });
660
+ }
661
+
662
+ RCT_EXPORT_METHOD(getFpsMetrics:(RCTPromiseResolveBlock)resolve
663
+ rejecter:(RCTPromiseRejectBlock)reject) {
664
+ NSMutableDictionary *dict = [NSMutableDictionary dictionary];
665
+ [dict setObject:@(self->currentCalculatedFps > 0 ? self->currentCalculatedFps : 60.0) forKey:@"fps"];
666
+ [dict setObject:@(60.0) forKey:@"targetFps"];
667
+ resolve(dict);
668
+ }
669
+
670
+ RCT_EXPORT_METHOD(getNativeStorageItem:(NSString *)key
671
+ resolver:(RCTPromiseResolveBlock)resolve
672
+ rejecter:(RCTPromiseRejectBlock)reject) {
673
+ NSString *prefKey = [NSString stringWithFormat:@"inapp_inspector_%@", key ?: @""];
674
+ NSString *val = [[NSUserDefaults standardUserDefaults] stringForKey:prefKey];
675
+ resolve(val ?: [NSNull null]);
676
+ }
677
+
678
+ RCT_EXPORT_METHOD(setNativeStorageItem:(NSString *)key
679
+ value:(NSString *)value
680
+ resolver:(RCTPromiseResolveBlock)resolve
681
+ rejecter:(RCTPromiseRejectBlock)reject) {
682
+ NSString *prefKey = [NSString stringWithFormat:@"inapp_inspector_%@", key ?: @""];
683
+ if (value == nil || [value isKindOfClass:[NSNull class]]) {
684
+ [[NSUserDefaults standardUserDefaults] removeObjectForKey:prefKey];
685
+ } else {
686
+ [[NSUserDefaults standardUserDefaults] setObject:value forKey:prefKey];
687
+ }
688
+ [[NSUserDefaults standardUserDefaults] synchronize];
689
+ resolve(@(YES));
690
+ }
691
+
692
+ RCT_EXPORT_METHOD(triggerHaptic:(NSString *)style
693
+ resolver:(RCTPromiseResolveBlock)resolve
694
+ rejecter:(RCTPromiseRejectBlock)reject) {
695
+ dispatch_async(dispatch_get_main_queue(), ^{
696
+ if (@available(iOS 10.0, *)) {
697
+ NSString *s = [style lowercaseString] ?: @"light";
698
+ if ([s isEqualToString:@"medium"]) {
699
+ UIImpactFeedbackGenerator *gen = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleMedium];
700
+ [gen prepare];
701
+ [gen impactOccurred];
702
+ } else if ([s isEqualToString:@"heavy"]) {
703
+ UIImpactFeedbackGenerator *gen = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleHeavy];
704
+ [gen prepare];
705
+ [gen impactOccurred];
706
+ } else if ([s isEqualToString:@"success"]) {
707
+ UINotificationFeedbackGenerator *gen = [[UINotificationFeedbackGenerator alloc] init];
708
+ [gen prepare];
709
+ [gen notificationOccurred:UINotificationFeedbackTypeSuccess];
710
+ } else if ([s isEqualToString:@"warning"]) {
711
+ UINotificationFeedbackGenerator *gen = [[UINotificationFeedbackGenerator alloc] init];
712
+ [gen prepare];
713
+ [gen notificationOccurred:UINotificationFeedbackTypeWarning];
714
+ } else if ([s isEqualToString:@"error"]) {
715
+ UINotificationFeedbackGenerator *gen = [[UINotificationFeedbackGenerator alloc] init];
716
+ [gen prepare];
717
+ [gen notificationOccurred:UINotificationFeedbackTypeError];
718
+ } else {
719
+ UIImpactFeedbackGenerator *gen = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
720
+ [gen prepare];
721
+ [gen impactOccurred];
722
+ }
723
+ }
724
+ resolve(@(YES));
725
+ });
726
+ }
727
+
728
+ RCT_EXPORT_METHOD(getNativeSystemMetrics:(RCTPromiseResolveBlock)resolve
729
+ rejecter:(RCTPromiseRejectBlock)reject) {
730
+ dispatch_async(self->_metricsQueue, ^{
731
+ NSMutableDictionary *dict = [NSMutableDictionary dictionary];
732
+
733
+ if (@available(iOS 11.0, *)) {
734
+ NSProcessInfoThermalState thermal = [[NSProcessInfo processInfo] thermalState];
735
+ NSString *thermalStr = @"nominal";
736
+ if (thermal == NSProcessInfoThermalStateFair) thermalStr = @"fair";
737
+ else if (thermal == NSProcessInfoThermalStateSerious) thermalStr = @"serious";
738
+ else if (thermal == NSProcessInfoThermalStateCritical) thermalStr = @"critical";
739
+ [dict setObject:thermalStr forKey:@"thermalState"];
740
+ } else {
741
+ [dict setObject:@"nominal" forKey:@"thermalState"];
742
+ }
743
+
744
+ struct mach_task_basic_info info;
745
+ mach_msg_type_number_t size = MACH_TASK_BASIC_INFO_COUNT;
746
+ kern_return_t kerr = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &size);
747
+ if (kerr == KERN_SUCCESS) {
748
+ double ramMB = (double)info.resident_size / (1024.0 * 1024.0);
749
+ [dict setObject:@(ramMB) forKey:@"residentRamMb"];
750
+ }
751
+
752
+ [dict setObject:@(self->currentCalculatedFps > 0 ? self->currentCalculatedFps : 60.0) forKey:@"fps"];
753
+ [dict setObject:@([[NSProcessInfo processInfo] activeProcessorCount]) forKey:@"activeCpuCores"];
754
+ [dict setObject:@([[NSProcessInfo processInfo] physicalMemory] / (1024.0 * 1024.0)) forKey:@"totalPhysicalRamMb"];
755
+
756
+ resolve(dict);
757
+ });
758
+ }
759
+
760
+ RCT_EXPORT_METHOD(pushNativeLogRecord:(NSString *)pageKey
761
+ jsonPayload:(NSString *)jsonPayload
762
+ resolve:(RCTPromiseResolveBlock)resolve
763
+ reject:(RCTPromiseRejectBlock)reject) {
764
+ if (!jsonPayload || jsonPayload.length == 0) {
765
+ resolve(@(NO));
766
+ return;
767
+ }
768
+
769
+ dispatch_queue_t targetQueue = self->_networkQueue;
770
+ NSMutableArray *targetStore = self->_nativeNetworkLogs;
771
+ if ([pageKey isEqualToString:@"logs"]) {
772
+ targetQueue = self->_consoleQueue;
773
+ targetStore = self->_nativeConsoleLogs;
774
+ } else if ([pageKey isEqualToString:@"analytics"]) {
775
+ targetQueue = self->_analyticsQueue;
776
+ targetStore = self->_nativeAnalyticsEvents;
777
+ } else if ([pageKey isEqualToString:@"crash"]) {
778
+ targetQueue = self->_crashQueue;
779
+ targetStore = self->_nativeCrashRecords;
780
+ }
781
+
782
+ dispatch_async(targetQueue, ^{
783
+ NSData *data = [jsonPayload dataUsingEncoding:NSUTF8StringEncoding];
784
+ NSDictionary *dict = nil;
785
+ if (data) {
786
+ dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
787
+ }
788
+
789
+ id itemId = dict ? dict[@"id"] : nil;
790
+ BOOL updated = NO;
791
+
792
+ if (itemId != nil) {
793
+ NSString *idPattern = [NSString stringWithFormat:@"\"id\":%@", itemId];
794
+ NSString *idStrPattern = [NSString stringWithFormat:@"\"id\":\"%@\"", itemId];
795
+ for (NSUInteger i = 0; i < targetStore.count; i++) {
796
+ NSString *existingJson = targetStore[i];
797
+ if ([existingJson containsString:idPattern] || [existingJson containsString:idStrPattern]) {
798
+ [targetStore replaceObjectAtIndex:i withObject:jsonPayload];
799
+ updated = YES;
800
+ break;
801
+ }
802
+ }
803
+ }
804
+
805
+ if (!updated) {
806
+ [targetStore insertObject:jsonPayload atIndex:0];
807
+ if (targetStore.count > 2000) {
808
+ [targetStore removeLastObject];
809
+ }
810
+ }
811
+ resolve(@(YES));
812
+ });
813
+ }
814
+
815
+ RCT_EXPORT_METHOD(getNativeCachedPage:(NSString *)pageKey
816
+ offset:(NSInteger)offset
817
+ limit:(NSInteger)limit
818
+ query:(NSString *)query
819
+ resolve:(RCTPromiseResolveBlock)resolve
820
+ reject:(RCTPromiseRejectBlock)reject) {
821
+ dispatch_queue_t targetQueue = self->_networkQueue;
822
+ NSMutableArray *targetStore = self->_nativeNetworkLogs;
823
+ if ([pageKey isEqualToString:@"logs"]) {
824
+ targetQueue = self->_consoleQueue;
825
+ targetStore = self->_nativeConsoleLogs;
826
+ } else if ([pageKey isEqualToString:@"analytics"]) {
827
+ targetQueue = self->_analyticsQueue;
828
+ targetStore = self->_nativeAnalyticsEvents;
829
+ } else if ([pageKey isEqualToString:@"crash"]) {
830
+ targetQueue = self->_crashQueue;
831
+ targetStore = self->_nativeCrashRecords;
832
+ }
833
+
834
+ dispatch_async(targetQueue, ^{
835
+ NSMutableArray *results = [NSMutableArray array];
836
+ NSString *cleanQuery = query ? [query stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].lowercaseString : @"";
837
+
838
+ NSInteger startIndex = MAX(0, offset);
839
+ NSInteger itemsCollected = 0;
840
+ NSInteger actualLimit = limit > 0 ? limit : 50;
841
+
842
+ for (NSInteger i = startIndex; i < targetStore.count && itemsCollected < actualLimit; i++) {
843
+ NSString *itemJson = targetStore[i];
844
+ if (cleanQuery.length > 0) {
845
+ if ([itemJson.lowercaseString containsString:cleanQuery]) {
846
+ [results addObject:itemJson];
847
+ itemsCollected++;
848
+ }
849
+ } else {
850
+ [results addObject:itemJson];
851
+ itemsCollected++;
852
+ }
853
+ }
854
+
855
+ NSError *error = nil;
856
+ NSData *jsonData = [NSJSONSerialization dataWithJSONObject:@{
857
+ @"pageKey": pageKey ?: @"apis",
858
+ @"total": @(targetStore.count),
859
+ @"offset": @(offset),
860
+ @"items": results
861
+ } options:0 error:&error];
862
+
863
+ if (error || !jsonData) {
864
+ resolve(@"{\"items\":[],\"total\":0}");
865
+ } else {
866
+ NSString *jsonStr = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
867
+ resolve(jsonStr);
868
+ }
869
+ });
870
+ }
871
+
872
+ #ifdef RCT_NEW_ARCH_ENABLED
873
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
874
+ (const facebook::react::ObjCTurboModule::InitParams &)params
875
+ {
876
+ return std::make_shared<facebook::react::NativeNetworkInspectorSpecJSI>(params);
877
+ }
878
+ #endif
879
+
125
880
  @end