react-native-inapp-inspector 1.0.14 β 1.0.16
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 +203 -44
- package/dist/commonjs/components/AnimatedEntrance.d.ts +12 -0
- package/dist/commonjs/components/AnimatedEntrance.js +72 -0
- package/dist/commonjs/components/ConsoleLogCard.js +114 -29
- package/dist/commonjs/components/EmptyState.js +56 -5
- package/dist/commonjs/components/ReduxTreeView.js +96 -27
- package/dist/commonjs/components/TouchableScale.js +18 -2
- package/dist/commonjs/customHooks/analyticsLogger.d.ts +1 -0
- package/dist/commonjs/customHooks/analyticsLogger.js +57 -17
- package/dist/commonjs/customHooks/networkLogger.d.ts +1 -1
- package/dist/commonjs/customHooks/networkLogger.js +54 -46
- package/dist/commonjs/index.js +1595 -822
- package/dist/esm/components/AnimatedEntrance.d.ts +12 -0
- package/dist/esm/components/AnimatedEntrance.js +37 -0
- package/dist/esm/components/ConsoleLogCard.js +116 -31
- package/dist/esm/components/EmptyState.js +24 -6
- package/dist/esm/components/ReduxTreeView.js +95 -29
- package/dist/esm/components/TouchableScale.js +18 -2
- package/dist/esm/customHooks/analyticsLogger.d.ts +1 -0
- package/dist/esm/customHooks/analyticsLogger.js +55 -16
- package/dist/esm/customHooks/networkLogger.d.ts +1 -1
- package/dist/esm/customHooks/networkLogger.js +56 -48
- package/dist/esm/index.js +1597 -824
- package/example/App.tsx +1 -3
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -8,66 +8,75 @@
|
|
|
8
8
|
</picture>
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/react-native-inapp-inspector"><img src="https://img.shields.io/npm/v/react-native-inapp-inspector?color=6366f1&label=npm" alt="npm version" /></a>
|
|
13
|
+
<a href="https://github.com/vengatmacuser/react-native-inapp-inspector/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="license" /></a>
|
|
14
|
+
<a href="https://github.com/vengatmacuser/react-native-inapp-inspector"><img src="https://img.shields.io/badge/platform-iOS%20%7C%20Android-blue" alt="platform" /></a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
A self-contained in-app debugging overlay for React Native. Inspect network traffic, console output, analytics events, Redux state, and WebView activity directly inside your app.
|
|
18
|
+
|
|
19
|
+
---
|
|
12
20
|
|
|
13
21
|
## Features
|
|
14
22
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
| Feature | Description |
|
|
24
|
+
|---|---|
|
|
25
|
+
| Network inspector | Captures `fetch` and axios `GET`, `POST`, `PUT`, `PATCH`, and `DELETE` calls with URL, method, status, headers, body, response, duration, caller, cURL, and fetch snippets. |
|
|
26
|
+
| Insights dashboard | Shows request totals, status breakdowns, latency, payload size, slow requests, and recent activity charts. |
|
|
27
|
+
| Console logger | Captures `console.log`, `console.info`, `console.warn`, and `console.error` with source method and caller details. |
|
|
28
|
+
| Analytics tracker | Captures manual analytics events and patched `@react-native-firebase/analytics` calls including `logEvent`, `logScreenView`, user properties, and user id. |
|
|
29
|
+
| Redux inspector | Connects to a Redux store, displays the live state tree, tracks dispatched actions, affected slices, and action history. |
|
|
30
|
+
| WebView inspector | Provides an instrumented `WebView` with console capture, navigation history, HTML/CSS/JS snapshots, and optional loading overlay. |
|
|
31
|
+
| Error boundary | Exports an `ErrorBoundary` for catching React errors and wrapping the inspector safely. |
|
|
21
32
|
|
|
22
33
|
---
|
|
23
34
|
|
|
24
35
|
## Video Walkthrough
|
|
25
36
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
[π¬ Download or watch the Video Walkthrough](https://raw.githubusercontent.com/vengatmacuser/react-native-inapp-inspector/main/example/guidance/Video-WalkThrough.mp4)
|
|
37
|
+
[Download or watch the Video Walkthrough](https://raw.githubusercontent.com/vengatmacuser/react-native-inapp-inspector/main/example/guidance/Video-WalkThrough.mp4)
|
|
29
38
|
|
|
30
39
|
---
|
|
31
40
|
|
|
32
41
|
## Installation
|
|
33
42
|
|
|
34
|
-
|
|
43
|
+
```bash
|
|
44
|
+
npm install --save-dev react-native-inapp-inspector axios
|
|
45
|
+
```
|
|
35
46
|
|
|
36
47
|
```bash
|
|
37
|
-
|
|
38
|
-
# OR
|
|
39
|
-
yarn add -D react-native-inapp-inspector
|
|
48
|
+
yarn add -D react-native-inapp-inspector axios
|
|
40
49
|
```
|
|
41
50
|
|
|
42
|
-
The package
|
|
51
|
+
The package has React and React Native as peer dependencies. It depends on `@react-navigation/native`, `react-native-linear-gradient`, and `react-native-svg`. The current network logger imports `axios` for axios interception, so install `axios` even if most of your requests use `fetch`.
|
|
52
|
+
|
|
53
|
+
Install optional integrations when you use WebView or Firebase Analytics capture:
|
|
43
54
|
|
|
44
|
-
For iOS projects, don't forget to run pod install:
|
|
45
55
|
```bash
|
|
46
|
-
|
|
56
|
+
npm install react-native-webview @react-native-firebase/analytics
|
|
47
57
|
```
|
|
48
58
|
|
|
49
|
-
|
|
59
|
+
For iOS, install pods after adding native dependencies:
|
|
50
60
|
|
|
51
|
-
|
|
61
|
+
```bash
|
|
62
|
+
cd ios && pod install
|
|
63
|
+
```
|
|
52
64
|
|
|
53
65
|
---
|
|
54
66
|
|
|
55
|
-
##
|
|
67
|
+
## Basic Setup
|
|
56
68
|
|
|
57
|
-
|
|
58
|
-
Place the `<NetworkInspector />` component at the root level of your application (usually in `App.tsx` or your root navigation container):
|
|
69
|
+
Mount the inspector once near the root of your app.
|
|
59
70
|
|
|
60
71
|
```tsx
|
|
61
72
|
import React from 'react';
|
|
62
|
-
import {
|
|
73
|
+
import {SafeAreaView} from 'react-native';
|
|
63
74
|
import NetworkInspector from 'react-native-inapp-inspector';
|
|
64
75
|
|
|
65
76
|
const App = () => {
|
|
66
77
|
return (
|
|
67
|
-
<SafeAreaView style={{
|
|
68
|
-
{/* Your
|
|
69
|
-
|
|
70
|
-
{/* Floating Inspector overlay */}
|
|
78
|
+
<SafeAreaView style={{flex: 1}}>
|
|
79
|
+
{/* Your app */}
|
|
71
80
|
<NetworkInspector />
|
|
72
81
|
</SafeAreaView>
|
|
73
82
|
);
|
|
@@ -76,38 +85,188 @@ const App = () => {
|
|
|
76
85
|
export default App;
|
|
77
86
|
```
|
|
78
87
|
|
|
79
|
-
|
|
80
|
-
|
|
88
|
+
When mounted, the inspector sets up network logging, clears previous network logs, patches console logging, and attempts Firebase Analytics auto-setup if `@react-native-firebase/analytics` is installed.
|
|
89
|
+
|
|
90
|
+
If you need to capture requests that happen before the component mounts, call `setupNetworkLogger()` at module level in your app entry file.
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
import NetworkInspector, {setupNetworkLogger} from 'react-native-inapp-inspector';
|
|
94
|
+
|
|
95
|
+
setupNetworkLogger();
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
You can disable the overlay without removing it from your tree:
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
<NetworkInspector enabled={false} />
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
81
105
|
|
|
82
|
-
|
|
106
|
+
## Network Logging
|
|
107
|
+
|
|
108
|
+
`setupNetworkLogger()` patches global `fetch`, the default axios instance, and future axios instances created with `axios.create()`.
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
83
111
|
import axios from 'axios';
|
|
84
|
-
import {
|
|
112
|
+
import {setupNetworkLogger} from 'react-native-inapp-inspector';
|
|
85
113
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
});
|
|
114
|
+
setupNetworkLogger();
|
|
115
|
+
|
|
116
|
+
const api = axios.create({baseURL: 'https://api.example.com'});
|
|
117
|
+
|
|
118
|
+
await fetch('https://api.example.com/users');
|
|
119
|
+
await api.post('/login', {email, password});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
If an axios instance was created before `setupNetworkLogger()` ran, attach interceptors manually:
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
import {addAxiosInterceptors} from 'react-native-inapp-inspector';
|
|
89
126
|
|
|
90
|
-
// Register the interceptor
|
|
91
127
|
addAxiosInterceptors(api);
|
|
92
128
|
```
|
|
93
129
|
|
|
94
|
-
|
|
95
|
-
|
|
130
|
+
Network logs are capped at 100 entries and ignore React Native symbolication requests.
|
|
131
|
+
|
|
132
|
+
---
|
|
96
133
|
|
|
97
|
-
|
|
98
|
-
import { subscribeAnalyticsEvents } from 'react-native-inapp-inspector';
|
|
134
|
+
## Console Logging
|
|
99
135
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
136
|
+
The inspector captures console output automatically when mounted. You can also set it up manually:
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
import {setupConsoleLogger} from 'react-native-inapp-inspector';
|
|
140
|
+
|
|
141
|
+
setupConsoleLogger();
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Captured levels are `log`, `info`, `warn`, and `error`.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Analytics Logging
|
|
149
|
+
|
|
150
|
+
Manual events can be pushed into the Analytics tab:
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
import {logAnalyticsEvent} from 'react-native-inapp-inspector';
|
|
154
|
+
|
|
155
|
+
logAnalyticsEvent('purchase_completed', {
|
|
156
|
+
item_id: 'SKU-42',
|
|
157
|
+
value: 29.99,
|
|
158
|
+
currency: 'USD',
|
|
103
159
|
});
|
|
104
160
|
```
|
|
105
161
|
|
|
162
|
+
For Firebase Analytics, pass the result of `analytics()` to `setupAnalyticsLogger()` before analytics calls begin:
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
import analytics from '@react-native-firebase/analytics';
|
|
166
|
+
import {setupAnalyticsLogger} from 'react-native-inapp-inspector';
|
|
167
|
+
|
|
168
|
+
setupAnalyticsLogger(analytics());
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The logger patches `logEvent`, `logScreenView`, `setUserProperty`, `setUserProperties`, and `setUserId`. Analytics events are capped at 200 entries.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Redux Inspection
|
|
176
|
+
|
|
177
|
+
Connect your Redux store once during app startup.
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
import {connectReduxStore} from 'react-native-inapp-inspector';
|
|
181
|
+
import store from './store';
|
|
182
|
+
|
|
183
|
+
connectReduxStore(store);
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The Redux tab shows the latest state tree, recent dispatches, payloads, and changed top-level state slices. Action history is capped at 50 entries.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## WebView Inspection
|
|
191
|
+
|
|
192
|
+
Use the exported `WebView` as a drop-in wrapper around `react-native-webview`.
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
import {WebView} from 'react-native-inapp-inspector';
|
|
196
|
+
|
|
197
|
+
<WebView source={{uri: 'https://example.com'}} />;
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
The wrapper forwards your props and ref, preserves your `onMessage`, `onNavigationStateChange`, `onLoadStart`, and `onLoadEnd` handlers, and injects scripts for WebView console logs, navigation history, and source snapshots.
|
|
201
|
+
|
|
202
|
+
Disable the loading overlay with `showLoader={false}`:
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
<WebView source={{uri: 'https://example.com'}} showLoader={false} />
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Error Boundary
|
|
211
|
+
|
|
212
|
+
```tsx
|
|
213
|
+
import {ErrorBoundary} from 'react-native-inapp-inspector';
|
|
214
|
+
|
|
215
|
+
<ErrorBoundary>
|
|
216
|
+
<YourComponent />
|
|
217
|
+
</ErrorBoundary>
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Public API
|
|
223
|
+
|
|
224
|
+
| Export | Type | Description |
|
|
225
|
+
|---|---|---|
|
|
226
|
+
| `NetworkInspector` | default component | Floating inspector overlay. Mount once near the root of the app. |
|
|
227
|
+
| `setupNetworkLogger()` | function | Patches `fetch`, default axios, and future `axios.create()` instances. |
|
|
228
|
+
| `addAxiosInterceptors(instance)` | function | Manually attaches axios interceptors to an existing instance. |
|
|
229
|
+
| `clearNetworkLogs()` | function | Clears captured network logs. |
|
|
230
|
+
| `subscribeNetworkLogs(callback)` | function | Subscribes to network log updates and returns an unsubscribe function. |
|
|
231
|
+
| `setupConsoleLogger()` | function | Patches console methods. |
|
|
232
|
+
| `clearConsoleLogs()` | function | Clears captured console logs. |
|
|
233
|
+
| `subscribeConsoleLogs(callback)` | function | Subscribes to console log updates and returns an unsubscribe function. |
|
|
234
|
+
| `setupAnalyticsLogger(instance)` | function | Patches a Firebase Analytics instance. |
|
|
235
|
+
| `logAnalyticsEvent(name, params?, userProperties?)` | function | Adds a manual analytics event to the inspector. |
|
|
236
|
+
| `clearAnalyticsEvents()` | function | Clears captured analytics events. |
|
|
237
|
+
| `subscribeAnalyticsEvents(callback)` | function | Subscribes to analytics event updates and returns an unsubscribe function. |
|
|
238
|
+
| `connectReduxStore(store)` | function | Connects a Redux store for live state and action inspection. |
|
|
239
|
+
| `getReduxState()` | function | Returns the latest captured Redux state. |
|
|
240
|
+
| `subscribeReduxState(callback)` | function | Subscribes to Redux state updates and returns an unsubscribe function. |
|
|
241
|
+
| `WebView` | component | Instrumented WebView wrapper. |
|
|
242
|
+
| `getWebViewLogs()` | function | Returns captured WebView console logs. |
|
|
243
|
+
| `getWebViewNavHistory()` | function | Returns captured WebView navigation history. |
|
|
244
|
+
| `getWebViewHtml()` | function | Returns the latest captured WebView HTML. |
|
|
245
|
+
| `getWebViewCss()` | function | Returns the latest captured WebView CSS. |
|
|
246
|
+
| `getWebViewJs()` | function | Returns the latest captured WebView JavaScript. |
|
|
247
|
+
| `getWebViewHtmlUrl()` | function | Returns the URL for the latest captured WebView source snapshot. |
|
|
248
|
+
| `clearWebViewData()` | function | Clears captured WebView logs, navigation, and source data. |
|
|
249
|
+
| `subscribeWebView(callback)` | function | Subscribes to WebView data changes and returns an unsubscribe function. |
|
|
250
|
+
| `ErrorBoundary` | component | React error boundary component. |
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Example App
|
|
255
|
+
|
|
256
|
+
The `example` directory contains a React Native sample app and video walkthrough.
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
cd example
|
|
260
|
+
npm install
|
|
261
|
+
cd ios && pod install && cd ..
|
|
262
|
+
npm run ios
|
|
263
|
+
```
|
|
264
|
+
|
|
106
265
|
---
|
|
107
266
|
|
|
108
267
|
## Support
|
|
109
268
|
|
|
110
|
-
If you find this project useful, consider sponsoring its development
|
|
269
|
+
If you find this project useful, consider sponsoring its development:
|
|
111
270
|
|
|
112
271
|
[](https://github.com/sponsors/vengatmacuser)
|
|
113
272
|
|
|
@@ -115,4 +274,4 @@ If you find this project useful, consider sponsoring its development on GitHub S
|
|
|
115
274
|
|
|
116
275
|
## License
|
|
117
276
|
|
|
118
|
-
|
|
277
|
+
MIT - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ViewStyle } from 'react-native';
|
|
3
|
+
interface AnimatedEntranceProps {
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
delay?: number;
|
|
6
|
+
distance?: number;
|
|
7
|
+
duration?: number;
|
|
8
|
+
index?: number;
|
|
9
|
+
style?: ViewStyle | ViewStyle[];
|
|
10
|
+
}
|
|
11
|
+
declare const AnimatedEntrance: ({ children, delay, distance, duration, index, style, }: AnimatedEntranceProps) => React.JSX.Element;
|
|
12
|
+
export default AnimatedEntrance;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const react_1 = __importStar(require("react"));
|
|
37
|
+
const react_native_1 = require("react-native");
|
|
38
|
+
const AnimatedEntrance = ({ children, delay = 0, distance = 10, duration = 220, index = 0, style, }) => {
|
|
39
|
+
const progress = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
|
|
40
|
+
(0, react_1.useEffect)(() => {
|
|
41
|
+
progress.setValue(0);
|
|
42
|
+
react_native_1.Animated.timing(progress, {
|
|
43
|
+
toValue: 1,
|
|
44
|
+
duration,
|
|
45
|
+
delay: delay + Math.min(index, 12) * 18,
|
|
46
|
+
useNativeDriver: true,
|
|
47
|
+
}).start();
|
|
48
|
+
}, [delay, duration, index, progress]);
|
|
49
|
+
return (<react_native_1.Animated.View style={[
|
|
50
|
+
style,
|
|
51
|
+
{
|
|
52
|
+
opacity: progress,
|
|
53
|
+
transform: [
|
|
54
|
+
{
|
|
55
|
+
translateY: progress.interpolate({
|
|
56
|
+
inputRange: [0, 1],
|
|
57
|
+
outputRange: [distance, 0],
|
|
58
|
+
}),
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
scale: progress.interpolate({
|
|
62
|
+
inputRange: [0, 1],
|
|
63
|
+
outputRange: [0.985, 1],
|
|
64
|
+
}),
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
]}>
|
|
69
|
+
{children}
|
|
70
|
+
</react_native_1.Animated.View>);
|
|
71
|
+
};
|
|
72
|
+
exports.default = AnimatedEntrance;
|
|
@@ -109,7 +109,10 @@ const getLogMessageWithBadges = (message, searchStr, textStyle, highlightStyle,
|
|
|
109
109
|
const remainingText = message.substring(fullPrefix.length);
|
|
110
110
|
const tags = fullPrefix.match(/\[[^\]]+\]/g) || [];
|
|
111
111
|
const getTagColor = (tag) => {
|
|
112
|
-
const cleanTag = tag
|
|
112
|
+
const cleanTag = tag
|
|
113
|
+
.replace(/[\[\]]/g, '')
|
|
114
|
+
.trim()
|
|
115
|
+
.toUpperCase();
|
|
113
116
|
if (cleanTag === 'API')
|
|
114
117
|
return '#0284C7';
|
|
115
118
|
if (cleanTag === 'TEST')
|
|
@@ -126,7 +129,14 @@ const getLogMessageWithBadges = (message, searchStr, textStyle, highlightStyle,
|
|
|
126
129
|
for (let i = 0; i < cleanTag.length; i++) {
|
|
127
130
|
hash = cleanTag.charCodeAt(i) + ((hash << 5) - hash);
|
|
128
131
|
}
|
|
129
|
-
const colors = [
|
|
132
|
+
const colors = [
|
|
133
|
+
'#0891B2',
|
|
134
|
+
'#0D9488',
|
|
135
|
+
'#2563EB',
|
|
136
|
+
'#D97706',
|
|
137
|
+
'#E11D48',
|
|
138
|
+
'#8B5CF6',
|
|
139
|
+
];
|
|
130
140
|
return colors[Math.abs(hash) % colors.length];
|
|
131
141
|
};
|
|
132
142
|
return (<react_native_1.Text style={textStyle} numberOfLines={numberOfLines}>
|
|
@@ -147,6 +157,7 @@ const getLogMessageWithBadges = (message, searchStr, textStyle, highlightStyle,
|
|
|
147
157
|
};
|
|
148
158
|
exports.ConsoleLogCard = react_1.default.memo(function ConsoleLogCard({ item, searchStr = '', isWebView = false, }) {
|
|
149
159
|
const [expanded, setExpanded] = (0, react_1.useState)(false);
|
|
160
|
+
const chevronAnim = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
|
|
150
161
|
const jsonContent = getJsonContent(item.message);
|
|
151
162
|
const isAnalyticsError = item.message
|
|
152
163
|
.toLowerCase()
|
|
@@ -230,6 +241,26 @@ exports.ConsoleLogCard = react_1.default.memo(function ConsoleLogCard({ item, se
|
|
|
230
241
|
}
|
|
231
242
|
};
|
|
232
243
|
const colors = getLogColors();
|
|
244
|
+
(0, react_1.useEffect)(() => {
|
|
245
|
+
if (react_native_1.Platform.OS === 'android') {
|
|
246
|
+
react_native_1.UIManager.setLayoutAnimationEnabledExperimental?.(true);
|
|
247
|
+
}
|
|
248
|
+
}, []);
|
|
249
|
+
(0, react_1.useEffect)(() => {
|
|
250
|
+
react_native_1.Animated.timing(chevronAnim, {
|
|
251
|
+
toValue: expanded ? 1 : 0,
|
|
252
|
+
duration: 180,
|
|
253
|
+
useNativeDriver: true,
|
|
254
|
+
}).start();
|
|
255
|
+
}, [chevronAnim, expanded]);
|
|
256
|
+
const toggleExpanded = () => {
|
|
257
|
+
react_native_1.LayoutAnimation.configureNext(react_native_1.LayoutAnimation.Presets.easeInEaseOut);
|
|
258
|
+
setExpanded(prev => !prev);
|
|
259
|
+
};
|
|
260
|
+
const chevronRotate = chevronAnim.interpolate({
|
|
261
|
+
inputRange: [0, 1],
|
|
262
|
+
outputRange: ['0deg', '180deg'],
|
|
263
|
+
});
|
|
233
264
|
// Show limited lines unless expanded
|
|
234
265
|
const numLines = expanded ? undefined : 5;
|
|
235
266
|
const hasLongMessage = jsonContent
|
|
@@ -250,10 +281,8 @@ exports.ConsoleLogCard = react_1.default.memo(function ConsoleLogCard({ item, se
|
|
|
250
281
|
paddingRight: 4,
|
|
251
282
|
},
|
|
252
283
|
]}>
|
|
253
|
-
|
|
254
284
|
{/* Left Content Area */}
|
|
255
|
-
<react_native_1.Pressable onPress={
|
|
256
|
-
|
|
285
|
+
<react_native_1.Pressable onPress={toggleExpanded} style={{ flex: 1, paddingRight: 6 }}>
|
|
257
286
|
<react_native_1.View style={styles.cardHeader}>
|
|
258
287
|
<react_native_1.View style={styles.headerLeft}>
|
|
259
288
|
<CopyButton_1.default value={item.message} label="Log message"/>
|
|
@@ -271,7 +300,10 @@ exports.ConsoleLogCard = react_1.default.memo(function ConsoleLogCard({ item, se
|
|
|
271
300
|
},
|
|
272
301
|
]}>
|
|
273
302
|
<react_native_1.Text style={[styles.badgeText, { color: '#6B4EFF' }]}>
|
|
274
|
-
console.
|
|
303
|
+
console.
|
|
304
|
+
{('sourceMethod' in item ? item.sourceMethod : undefined) ||
|
|
305
|
+
item.type ||
|
|
306
|
+
'log'}
|
|
275
307
|
</react_native_1.Text>
|
|
276
308
|
</react_native_1.View>
|
|
277
309
|
{jsonContent && (<react_native_1.View style={[
|
|
@@ -283,7 +315,9 @@ exports.ConsoleLogCard = react_1.default.memo(function ConsoleLogCard({ item, se
|
|
|
283
315
|
},
|
|
284
316
|
]}>
|
|
285
317
|
<react_native_1.Text style={[styles.badgeText, { color: '#0D9488' }]}>
|
|
286
|
-
{Array.isArray(jsonContent.data)
|
|
318
|
+
{Array.isArray(jsonContent.data)
|
|
319
|
+
? `Array[${jsonContent.data.length}]`
|
|
320
|
+
: `Object{${Object.keys(jsonContent.data).length}}`}
|
|
287
321
|
</react_native_1.Text>
|
|
288
322
|
</react_native_1.View>)}
|
|
289
323
|
{isAnalyticsError && (<react_native_1.View style={[
|
|
@@ -302,7 +336,10 @@ exports.ConsoleLogCard = react_1.default.memo(function ConsoleLogCard({ item, se
|
|
|
302
336
|
borderWidth: 1,
|
|
303
337
|
},
|
|
304
338
|
]}>
|
|
305
|
-
<react_native_1.Text style={[
|
|
339
|
+
<react_native_1.Text style={[
|
|
340
|
+
styles.badgeText,
|
|
341
|
+
{ color: AppColors_1.AppColors.grayTextStrong },
|
|
342
|
+
]}>
|
|
306
343
|
user-log
|
|
307
344
|
</react_native_1.Text>
|
|
308
345
|
</react_native_1.View>)}
|
|
@@ -318,43 +355,92 @@ exports.ConsoleLogCard = react_1.default.memo(function ConsoleLogCard({ item, se
|
|
|
318
355
|
webview
|
|
319
356
|
</react_native_1.Text>
|
|
320
357
|
</react_native_1.View>)}
|
|
321
|
-
<react_native_1.Text style={[styles.serialNumber, { color: AppColors_1.AppColors.grayTextWeak }]}
|
|
322
|
-
|
|
358
|
+
<react_native_1.Text style={[styles.serialNumber, { color: AppColors_1.AppColors.grayTextWeak }]}>
|
|
359
|
+
#{item.id + 1}
|
|
360
|
+
</react_native_1.Text>
|
|
361
|
+
<react_native_1.Text style={[styles.timestamp, { color: AppColors_1.AppColors.grayTextWeak }]}>
|
|
362
|
+
{formatTime(item.timestamp)}
|
|
363
|
+
</react_native_1.Text>
|
|
323
364
|
</react_native_1.View>
|
|
324
|
-
|
|
325
|
-
{caller && caller !== 'Unknown' && (<react_native_1.Text style={[
|
|
365
|
+
|
|
366
|
+
{caller && caller !== 'Unknown' && (<react_native_1.Text style={[
|
|
367
|
+
styles.callerText,
|
|
368
|
+
{ color: AppColors_1.AppColors.grayTextWeak, marginRight: 4 },
|
|
369
|
+
]} numberOfLines={1} ellipsizeMode="middle">
|
|
326
370
|
{caller.split('/').pop() || caller}
|
|
327
371
|
</react_native_1.Text>)}
|
|
328
372
|
</react_native_1.View>
|
|
329
373
|
|
|
330
|
-
<react_native_1.View style={[
|
|
374
|
+
<react_native_1.View style={[
|
|
375
|
+
styles.cardBody,
|
|
376
|
+
{
|
|
377
|
+
backgroundColor: AppColors_1.AppColors.primaryLight,
|
|
378
|
+
borderColor: AppColors_1.AppColors.dividerColor,
|
|
379
|
+
},
|
|
380
|
+
]}>
|
|
331
381
|
{jsonContent ? (<>
|
|
332
|
-
{jsonContent.header ? (<react_native_1.Pressable onPress={
|
|
382
|
+
{jsonContent.header ? (<react_native_1.Pressable onPress={toggleExpanded}>
|
|
333
383
|
{getLogMessageWithBadges(jsonContent.header, searchStr, [styles.messageText, { color: AppColors_1.AppColors.primaryBlack }], styles.highlight, numLines)}
|
|
334
384
|
</react_native_1.Pressable>) : null}
|
|
335
|
-
{expanded ? (<react_native_1.View style={[
|
|
385
|
+
{expanded ? (<react_native_1.View style={[
|
|
386
|
+
styles.jsonContainer,
|
|
387
|
+
{
|
|
388
|
+
backgroundColor: AppColors_1.AppColors.grayBackground,
|
|
389
|
+
borderColor: AppColors_1.AppColors.dividerColor,
|
|
390
|
+
},
|
|
391
|
+
]}>
|
|
336
392
|
<JsonViewer_1.default data={jsonContent.data} search={searchStr} forceOpen={expanded}/>
|
|
337
|
-
</react_native_1.View>) : (<react_native_1.Pressable onPress={
|
|
338
|
-
|
|
393
|
+
</react_native_1.View>) : (<react_native_1.Pressable onPress={toggleExpanded} style={[
|
|
394
|
+
styles.jsonPreviewContainer,
|
|
395
|
+
{
|
|
396
|
+
backgroundColor: AppColors_1.AppColors.grayBackground,
|
|
397
|
+
borderColor: AppColors_1.AppColors.dividerColor,
|
|
398
|
+
},
|
|
399
|
+
]}>
|
|
400
|
+
<HighlightText_1.default text={getJsonPreviewText(jsonContent.data).text} search={searchStr} style={[
|
|
401
|
+
styles.jsonPreviewText,
|
|
402
|
+
{ color: AppColors_1.AppColors.primaryBlack },
|
|
403
|
+
]} highlightStyle={styles.highlight} detectLinks={true}/>
|
|
339
404
|
</react_native_1.Pressable>)}
|
|
340
|
-
</>) : (<react_native_1.Pressable onPress={
|
|
405
|
+
</>) : (<react_native_1.Pressable onPress={toggleExpanded}>
|
|
341
406
|
{getLogMessageWithBadges(item.message, searchStr, [styles.messageText, { color: AppColors_1.AppColors.primaryBlack }], styles.highlight, numLines)}
|
|
342
407
|
</react_native_1.Pressable>)}
|
|
343
|
-
{hasLongMessage && (<react_native_1.Pressable onPress={
|
|
408
|
+
{hasLongMessage && (<react_native_1.Pressable onPress={toggleExpanded} style={styles.seeMoreBtn} hitSlop={8}>
|
|
344
409
|
<react_native_1.Text style={styles.seeMoreText}>
|
|
345
410
|
{expanded ? 'See Less' : 'See More'}
|
|
346
411
|
</react_native_1.Text>
|
|
347
412
|
</react_native_1.Pressable>)}
|
|
348
413
|
</react_native_1.View>
|
|
349
414
|
|
|
350
|
-
{expanded && (<react_native_1.View style={[
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
415
|
+
{expanded && (<react_native_1.View style={[
|
|
416
|
+
styles.cardFooter,
|
|
417
|
+
{ borderTopColor: AppColors_1.AppColors.dividerColor, gap: 6 },
|
|
418
|
+
]}>
|
|
419
|
+
<react_native_1.View style={{
|
|
420
|
+
flexDirection: 'row',
|
|
421
|
+
justifyContent: 'space-between',
|
|
422
|
+
alignItems: 'center',
|
|
423
|
+
}}>
|
|
424
|
+
<react_native_1.Text style={{
|
|
425
|
+
fontFamily: AppFonts_1.AppFonts.interRegular,
|
|
426
|
+
fontSize: 10.5,
|
|
427
|
+
color: AppColors_1.AppColors.grayTextWeak,
|
|
428
|
+
}}>
|
|
429
|
+
Length: {item.message.length} chars β’ Size:{' '}
|
|
430
|
+
{encodeURIComponent(item.message).replace(/%[0-9A-F]{2}/g, 'a').length}{' '}
|
|
431
|
+
bytes
|
|
354
432
|
</react_native_1.Text>
|
|
355
433
|
</react_native_1.View>
|
|
356
|
-
{caller && caller !== 'Unknown' && (<react_native_1.View style={{
|
|
357
|
-
|
|
434
|
+
{caller && caller !== 'Unknown' && (<react_native_1.View style={{
|
|
435
|
+
flexDirection: 'row',
|
|
436
|
+
alignItems: 'center',
|
|
437
|
+
justifyContent: 'space-between',
|
|
438
|
+
marginTop: 4,
|
|
439
|
+
}}>
|
|
440
|
+
<react_native_1.Text style={[
|
|
441
|
+
styles.fullCallerText,
|
|
442
|
+
{ color: AppColors_1.AppColors.grayText, flex: 1, marginRight: 8 },
|
|
443
|
+
]} numberOfLines={2}>
|
|
358
444
|
Caller: {caller}
|
|
359
445
|
</react_native_1.Text>
|
|
360
446
|
<CopyButton_1.default value={caller} label="Caller stack frame"/>
|
|
@@ -363,7 +449,7 @@ exports.ConsoleLogCard = react_1.default.memo(function ConsoleLogCard({ item, se
|
|
|
363
449
|
</react_native_1.Pressable>
|
|
364
450
|
|
|
365
451
|
{/* Right Isolated Chevron Area */}
|
|
366
|
-
<react_native_1.Pressable onPress={
|
|
452
|
+
<react_native_1.Pressable onPress={toggleExpanded} style={{
|
|
367
453
|
width: 28,
|
|
368
454
|
alignItems: 'center',
|
|
369
455
|
justifyContent: 'center',
|
|
@@ -371,11 +457,10 @@ exports.ConsoleLogCard = react_1.default.memo(function ConsoleLogCard({ item, se
|
|
|
371
457
|
marginTop: expanded ? 8 : 0,
|
|
372
458
|
height: expanded ? 32 : undefined,
|
|
373
459
|
}} hitSlop={12}>
|
|
374
|
-
<react_native_1.View style={{ transform: [{ rotate:
|
|
460
|
+
<react_native_1.Animated.View style={{ transform: [{ rotate: chevronRotate }] }}>
|
|
375
461
|
<NetworkIcons_1.ChevronIcon size={16} color={AppColors_1.AppColors.grayTextWeak}/>
|
|
376
|
-
</react_native_1.View>
|
|
462
|
+
</react_native_1.Animated.View>
|
|
377
463
|
</react_native_1.Pressable>
|
|
378
|
-
|
|
379
464
|
</react_native_1.View>
|
|
380
465
|
</react_native_1.View>);
|
|
381
466
|
});
|