react-native-xenon 1.0.1 → 1.1.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.
- package/README.md +9 -9
- package/lib/commonjs/core/utils.js +33 -10
- package/lib/commonjs/core/utils.js.map +1 -1
- package/lib/commonjs/hooks/useNetworkInterceptor.js +7 -2
- package/lib/commonjs/hooks/useNetworkInterceptor.js.map +1 -1
- package/lib/commonjs/interceptors/FetchInterceptor.js +1 -1
- package/lib/commonjs/interceptors/FetchInterceptor.js.map +1 -1
- package/lib/commonjs/ui/Xenon.js +14 -10
- package/lib/commonjs/ui/Xenon.js.map +1 -1
- package/lib/commonjs/ui/components/bubble/Bubble.js +2 -2
- package/lib/commonjs/ui/components/bubble/Bubble.js.map +1 -1
- package/lib/commonjs/ui/components/common/ShareableText.js +24 -0
- package/lib/commonjs/ui/components/common/ShareableText.js.map +1 -0
- package/lib/commonjs/ui/components/details/NetworkRequestDetails.js +43 -25
- package/lib/commonjs/ui/components/details/NetworkRequestDetails.js.map +1 -1
- package/lib/commonjs/ui/components/headers/NetworkHeader.js +9 -24
- package/lib/commonjs/ui/components/headers/NetworkHeader.js.map +1 -1
- package/lib/commonjs/ui/components/index.js +9 -9
- package/lib/commonjs/ui/components/index.js.map +1 -1
- package/lib/commonjs/ui/components/items/NetworkRequestDetailsItem.js +38 -12
- package/lib/commonjs/ui/components/items/NetworkRequestDetailsItem.js.map +1 -1
- package/lib/module/core/utils.js +33 -10
- package/lib/module/core/utils.js.map +1 -1
- package/lib/module/hooks/useNetworkInterceptor.js +7 -2
- package/lib/module/hooks/useNetworkInterceptor.js.map +1 -1
- package/lib/module/interceptors/FetchInterceptor.js +1 -1
- package/lib/module/interceptors/FetchInterceptor.js.map +1 -1
- package/lib/module/ui/Xenon.js +14 -10
- package/lib/module/ui/Xenon.js.map +1 -1
- package/lib/module/ui/components/bubble/Bubble.js +2 -2
- package/lib/module/ui/components/bubble/Bubble.js.map +1 -1
- package/lib/module/ui/components/common/ShareableText.js +24 -0
- package/lib/module/ui/components/common/ShareableText.js.map +1 -0
- package/lib/module/ui/components/details/NetworkRequestDetails.js +43 -25
- package/lib/module/ui/components/details/NetworkRequestDetails.js.map +1 -1
- package/lib/module/ui/components/headers/NetworkHeader.js +9 -24
- package/lib/module/ui/components/headers/NetworkHeader.js.map +1 -1
- package/lib/module/ui/components/index.js +9 -9
- package/lib/module/ui/components/index.js.map +1 -1
- package/lib/module/ui/components/items/NetworkRequestDetailsItem.js +38 -12
- package/lib/module/ui/components/items/NetworkRequestDetailsItem.js.map +1 -1
- package/lib/typescript/commonjs/src/core/utils.d.ts +4 -2
- package/lib/typescript/commonjs/src/core/utils.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/hooks/useNetworkInterceptor.d.ts +2 -1
- package/lib/typescript/commonjs/src/hooks/useNetworkInterceptor.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/ui/Xenon.d.ts +44 -4
- package/lib/typescript/commonjs/src/ui/Xenon.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/ui/components/common/ShareableText.d.ts +7 -0
- package/lib/typescript/commonjs/src/ui/components/common/ShareableText.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/ui/components/details/NetworkRequestDetails.d.ts +2 -2
- package/lib/typescript/commonjs/src/ui/components/details/NetworkRequestDetails.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/ui/components/headers/NetworkHeader.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/ui/components/index.d.ts +3 -3
- package/lib/typescript/commonjs/src/ui/components/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/ui/components/items/NetworkRequestDetailsItem.d.ts.map +1 -1
- package/lib/typescript/module/src/core/utils.d.ts +4 -2
- package/lib/typescript/module/src/core/utils.d.ts.map +1 -1
- package/lib/typescript/module/src/hooks/useNetworkInterceptor.d.ts +2 -1
- package/lib/typescript/module/src/hooks/useNetworkInterceptor.d.ts.map +1 -1
- package/lib/typescript/module/src/ui/Xenon.d.ts +44 -4
- package/lib/typescript/module/src/ui/Xenon.d.ts.map +1 -1
- package/lib/typescript/module/src/ui/components/common/ShareableText.d.ts +7 -0
- package/lib/typescript/module/src/ui/components/common/ShareableText.d.ts.map +1 -0
- package/lib/typescript/module/src/ui/components/details/NetworkRequestDetails.d.ts +2 -2
- package/lib/typescript/module/src/ui/components/details/NetworkRequestDetails.d.ts.map +1 -1
- package/lib/typescript/module/src/ui/components/headers/NetworkHeader.d.ts.map +1 -1
- package/lib/typescript/module/src/ui/components/index.d.ts +3 -3
- package/lib/typescript/module/src/ui/components/index.d.ts.map +1 -1
- package/lib/typescript/module/src/ui/components/items/NetworkRequestDetailsItem.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/utils.ts +33 -9
- package/src/hooks/useNetworkInterceptor.ts +13 -2
- package/src/interceptors/FetchInterceptor.ts +1 -1
- package/src/ui/Xenon.tsx +52 -10
- package/src/ui/components/bubble/Bubble.tsx +2 -2
- package/src/ui/components/common/ShareableText.tsx +21 -0
- package/src/ui/components/details/NetworkRequestDetails.tsx +42 -34
- package/src/ui/components/headers/NetworkHeader.tsx +14 -26
- package/src/ui/components/index.ts +3 -3
- package/src/ui/components/items/NetworkRequestDetailsItem.tsx +45 -18
package/src/core/utils.ts
CHANGED
@@ -1,23 +1,45 @@
|
|
1
1
|
import { URL } from 'react-native-url-polyfill';
|
2
2
|
import { NetworkType, type HttpRequest, type LogMessage, type WebSocketRequest } from '../types';
|
3
3
|
import colors from '../theme/colors';
|
4
|
+
import { Share } from 'react-native';
|
5
|
+
import refs, { DebuggerVisibility } from './refs';
|
6
|
+
|
7
|
+
let isSharing = false;
|
8
|
+
|
9
|
+
export const shareText = async (text: string) => {
|
10
|
+
if (isSharing) return;
|
11
|
+
|
12
|
+
try {
|
13
|
+
isSharing = true;
|
14
|
+
refs.debugger.current?.setCurrentIndex(DebuggerVisibility.Bubble);
|
15
|
+
|
16
|
+
await Share.share({
|
17
|
+
message: text.trim(),
|
18
|
+
});
|
19
|
+
} catch (error) {
|
20
|
+
// Handle error
|
21
|
+
} finally {
|
22
|
+
refs.debugger.current?.setCurrentIndex(DebuggerVisibility.Panel);
|
23
|
+
isSharing = false;
|
24
|
+
}
|
25
|
+
};
|
4
26
|
|
5
27
|
export const getNetworkUtils = (data?: HttpRequest | WebSocketRequest) => {
|
6
28
|
if (!data || !data.url) return {};
|
7
29
|
|
8
|
-
const
|
30
|
+
const isWS = data?.type === NetworkType.WS;
|
9
31
|
const requestUrl = new URL(data.url);
|
10
32
|
|
11
33
|
const overviewShown = !!data.url;
|
12
|
-
const httpHeadersShown =
|
13
|
-
const websocketHeadersShown =
|
34
|
+
const httpHeadersShown = !isWS && (!!data.requestHeaders?.size || !!data.responseHeaders?.size);
|
35
|
+
const websocketHeadersShown = isWS && !!Object.keys(data.options?.headers ?? {}).length;
|
14
36
|
const headersShown = httpHeadersShown || websocketHeadersShown;
|
15
|
-
const requestShown =
|
16
|
-
const responseShown =
|
17
|
-
const messagesShown =
|
37
|
+
const requestShown = !isWS && (!!requestUrl.search || !!data.body);
|
38
|
+
const responseShown = !isWS && !!data.response;
|
39
|
+
const messagesShown = isWS && !!data.messages;
|
18
40
|
|
19
41
|
return {
|
20
|
-
|
42
|
+
isWS,
|
21
43
|
requestUrl,
|
22
44
|
overviewShown,
|
23
45
|
headersShown,
|
@@ -68,6 +90,8 @@ export const getHttpInterceptorId = () => {
|
|
68
90
|
//#endregion
|
69
91
|
|
70
92
|
//#region formatters
|
93
|
+
export const showNewLine = (when: boolean) => (when ? '\n' : '');
|
94
|
+
|
71
95
|
const limitChar = (value: any, limit = 5000) => {
|
72
96
|
const stringValue = typeof value === 'string' ? value : JSON.stringify(value ?? '');
|
73
97
|
|
@@ -81,7 +105,7 @@ export const keyValueToString = (
|
|
81
105
|
value: any,
|
82
106
|
newLine: 'leading' | 'trailing' | null = 'trailing',
|
83
107
|
): string =>
|
84
|
-
`${newLine === 'leading' ? '\n' : ''}${key}: ${limitChar(value)}${newLine === 'trailing'
|
108
|
+
`${newLine === 'leading' ? '\n' : ''}${key}: ${limitChar(value)}${showNewLine(newLine === 'trailing')}`;
|
85
109
|
|
86
110
|
export const formatRequestMethod = (method?: string) => method ?? 'GET';
|
87
111
|
|
@@ -116,7 +140,7 @@ export const convertToCurl = (
|
|
116
140
|
let curlCommand = `curl -X ${method.toUpperCase()} "${url}"`;
|
117
141
|
|
118
142
|
if (headers) {
|
119
|
-
for (const [key, value] of
|
143
|
+
for (const [key, value] of headers.entries()) {
|
120
144
|
curlCommand += ` -H "${key}: ${value}"`;
|
121
145
|
}
|
122
146
|
}
|
@@ -14,6 +14,7 @@ import { keyValueToString } from '../core/utils';
|
|
14
14
|
|
15
15
|
interface NetworkInterceptorParams {
|
16
16
|
autoEnabled: boolean;
|
17
|
+
includeDomains?: string[];
|
17
18
|
}
|
18
19
|
|
19
20
|
type NetworkRequests<T> = Map<NonNullable<ID>, T>;
|
@@ -24,7 +25,11 @@ const xhrInterceptor = new XHRInterceptor();
|
|
24
25
|
const fetchInterceptor = new FetchInterceptor();
|
25
26
|
const webSocketInterceptor = new WebSocketInterceptor();
|
26
27
|
|
27
|
-
export default function useNetworkInterceptor({
|
28
|
+
export default function useNetworkInterceptor({
|
29
|
+
autoEnabled,
|
30
|
+
includeDomains,
|
31
|
+
}: NetworkInterceptorParams) {
|
32
|
+
const joinedIncludeDomains = includeDomains?.join(',') ?? '';
|
28
33
|
const [isInterceptorEnabled, setIsInterceptorEnabled] = useState(autoEnabled);
|
29
34
|
|
30
35
|
const [networkRequests, setNetworkRequests] = useImmer(initRequests);
|
@@ -42,6 +47,11 @@ export default function useNetworkInterceptor({ autoEnabled }: NetworkIntercepto
|
|
42
47
|
const openCallback: HttpHandlers['open'] = (id, type, method, url) => {
|
43
48
|
if (!id) return;
|
44
49
|
|
50
|
+
const isNotMatchedDomain =
|
51
|
+
!!joinedIncludeDomains.length && !includeDomains?.some(domain => url.includes(domain));
|
52
|
+
|
53
|
+
if (isNotMatchedDomain) return;
|
54
|
+
|
45
55
|
setNetworkRequests((draft: NetworkRequests<HttpRequest>) => {
|
46
56
|
draft.set(id, { type, method, url });
|
47
57
|
});
|
@@ -139,7 +149,8 @@ export default function useNetworkInterceptor({ autoEnabled }: NetworkIntercepto
|
|
139
149
|
.set('headerReceived', headerReceivedCallback)
|
140
150
|
.set('response', responseCallback)
|
141
151
|
.enableInterception();
|
142
|
-
|
152
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
153
|
+
}, [joinedIncludeDomains, setNetworkRequests]);
|
143
154
|
|
144
155
|
const enableWebSocketInterception = useCallback(() => {
|
145
156
|
const connectCallback: WebSocketHandlers['connect'] = (
|
@@ -86,7 +86,7 @@ export default class FetchInterceptor extends HttpInterceptor {
|
|
86
86
|
const responseContentType = contentTypeString ? contentTypeString.split(';')[0] : undefined;
|
87
87
|
const responseSize = contentLengthString ? parseInt(contentLengthString, 10) : undefined;
|
88
88
|
|
89
|
-
|
89
|
+
const responseHeaders: Map<string, string> = new Map();
|
90
90
|
|
91
91
|
clonedResponseHeaders.forEach((headerValue: string, headerKey: string) => {
|
92
92
|
responseHeaders.set(headerKey, headerValue);
|
package/src/ui/Xenon.tsx
CHANGED
@@ -1,18 +1,15 @@
|
|
1
1
|
import { enableMapSet } from 'immer';
|
2
|
-
import { createRef, memo, useImperativeHandle, useMemo, type ReactNode } from 'react';
|
2
|
+
import { createRef, memo, useImperativeHandle, useMemo, type JSX, type ReactNode } from 'react';
|
3
3
|
import { Platform, StyleSheet, useWindowDimensions, View } from 'react-native';
|
4
4
|
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
|
5
5
|
import { FullWindowOverlay } from 'react-native-screens';
|
6
6
|
import { useImmer } from 'use-immer';
|
7
|
-
import MainContext from '../contexts
|
7
|
+
import { MainContext } from '../contexts';
|
8
8
|
import refs, { DebuggerVisibility } from '../core/refs';
|
9
9
|
import { useConsoleInterceptor, useNetworkInterceptor } from '../hooks';
|
10
10
|
import colors from '../theme/colors';
|
11
11
|
import { type DebuggerState } from '../types';
|
12
|
-
import { Bubble } from './components';
|
13
|
-
import IndexedStack from './components/common/IndexedStack';
|
14
|
-
import Header from './components/headers/Header';
|
15
|
-
import Panel from './components/panels/Panel';
|
12
|
+
import { Bubble, Header, IndexedStack, Panel } from './components';
|
16
13
|
|
17
14
|
namespace Xenon {
|
18
15
|
interface Methods {
|
@@ -22,12 +19,41 @@ namespace Xenon {
|
|
22
19
|
}
|
23
20
|
|
24
21
|
interface Props {
|
25
|
-
|
22
|
+
/**
|
23
|
+
* Determines whether the network inspector is automatically enabled upon initialization.
|
24
|
+
* @default true
|
25
|
+
*/
|
26
26
|
autoInspectNetworkEnabled?: boolean;
|
27
|
+
/**
|
28
|
+
* Determines whether the console inspector is automatically enabled upon initialization.
|
29
|
+
* @default true
|
30
|
+
*/
|
27
31
|
autoInspectConsoleEnabled?: boolean;
|
32
|
+
/**
|
33
|
+
* Defines the size of the interactive bubble used in the UI.
|
34
|
+
* @default 40
|
35
|
+
*/
|
28
36
|
bubbleSize?: number;
|
37
|
+
/**
|
38
|
+
* Defines the opacity level of the bubble when it is idle.
|
39
|
+
* @default 0.5
|
40
|
+
*/
|
29
41
|
idleBubbleOpacity?: number;
|
30
|
-
|
42
|
+
/**
|
43
|
+
* Domains to include in network interception. Defaults to all domains.
|
44
|
+
* @default undefined
|
45
|
+
* @example ['example1.com', 'api.example2.com']
|
46
|
+
*/
|
47
|
+
includeDomains?: string[];
|
48
|
+
}
|
49
|
+
|
50
|
+
interface WrapperProps extends Props {
|
51
|
+
/**
|
52
|
+
* If true, completely disables the debugger by rendering only the children components without any debugging functionality.
|
53
|
+
* @default false
|
54
|
+
*/
|
55
|
+
disabled?: boolean;
|
56
|
+
children: ReactNode;
|
31
57
|
}
|
32
58
|
|
33
59
|
enableMapSet();
|
@@ -50,8 +76,22 @@ namespace Xenon {
|
|
50
76
|
},
|
51
77
|
});
|
52
78
|
|
79
|
+
/**
|
80
|
+
* Checks whether the debugger is currently visible.
|
81
|
+
* @returns `true` if the debugger is currently visible, otherwise `false`.
|
82
|
+
*/
|
53
83
|
export const isVisible = () => ref.current?.isVisible() ?? false;
|
84
|
+
|
85
|
+
/**
|
86
|
+
* Makes the debugger visible. If it is already visible, this method has no additional effect.
|
87
|
+
* @returns `void`
|
88
|
+
*/
|
54
89
|
export const show = (): void => ref.current?.show();
|
90
|
+
|
91
|
+
/**
|
92
|
+
* Hides the debugger. If it is already hidden, this method has no additional effect.
|
93
|
+
* @returns `void`
|
94
|
+
*/
|
55
95
|
export const hide = (): void => ref.current?.hide();
|
56
96
|
|
57
97
|
const Debugger = memo(
|
@@ -60,6 +100,7 @@ namespace Xenon {
|
|
60
100
|
autoInspectConsoleEnabled = true,
|
61
101
|
bubbleSize = 40,
|
62
102
|
idleBubbleOpacity = 0.5,
|
103
|
+
includeDomains,
|
63
104
|
}: Props) => {
|
64
105
|
const { width, height } = useWindowDimensions();
|
65
106
|
|
@@ -78,6 +119,7 @@ namespace Xenon {
|
|
78
119
|
|
79
120
|
const networkInterceptor = useNetworkInterceptor({
|
80
121
|
autoEnabled: autoInspectNetworkEnabled,
|
122
|
+
includeDomains,
|
81
123
|
});
|
82
124
|
|
83
125
|
const consoleInterceptor = useConsoleInterceptor({
|
@@ -132,8 +174,8 @@ namespace Xenon {
|
|
132
174
|
},
|
133
175
|
);
|
134
176
|
|
135
|
-
export function Wrapper({ disabled = false, children, ...props }:
|
136
|
-
if (disabled) return children;
|
177
|
+
export function Wrapper({ disabled = false, children, ...props }: WrapperProps): JSX.Element {
|
178
|
+
if (disabled) return children as JSX.Element;
|
137
179
|
|
138
180
|
return (
|
139
181
|
<>
|
@@ -36,7 +36,7 @@ const Bubble = forwardRef<View, BubbleProps>(
|
|
36
36
|
|
37
37
|
const blur = () => {
|
38
38
|
opacityTimer.current = setTimeout(() => {
|
39
|
-
setIdleOpacity(
|
39
|
+
setIdleOpacity(idleBubbleOpacity);
|
40
40
|
clearTimer();
|
41
41
|
}, 1000);
|
42
42
|
};
|
@@ -94,7 +94,7 @@ const Bubble = forwardRef<View, BubbleProps>(
|
|
94
94
|
});
|
95
95
|
},
|
96
96
|
});
|
97
|
-
}, [bubbleSize,
|
97
|
+
}, [bubbleSize, idleBubbleOpacity, screenHeight, screenWidth]);
|
98
98
|
|
99
99
|
return (
|
100
100
|
<View ref={ref} style={[styles.bubbleBackdrop, style]}>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { Text, type TextProps } from 'react-native';
|
2
|
+
import { shareText } from '../../../core/utils';
|
3
|
+
|
4
|
+
interface ShareableTextProps extends TextProps {
|
5
|
+
children: string | string[];
|
6
|
+
}
|
7
|
+
|
8
|
+
export default function ShareableText({ children, ...props }: ShareableTextProps) {
|
9
|
+
return (
|
10
|
+
<Text
|
11
|
+
selectionColor={'transparent'}
|
12
|
+
suppressHighlighting={true}
|
13
|
+
onLongPress={async () => {
|
14
|
+
shareText(Array.isArray(children) ? children.join('') : children);
|
15
|
+
}}
|
16
|
+
{...props}
|
17
|
+
>
|
18
|
+
{children}
|
19
|
+
</Text>
|
20
|
+
);
|
21
|
+
}
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import { forwardRef, useContext, useRef, type JSX } from 'react';
|
2
|
-
import { ScrollView, StyleSheet, type StyleProp, type ViewStyle } from 'react-native';
|
1
|
+
import { forwardRef, useContext, useRef, type JSX, type ReactNode } from 'react';
|
2
|
+
import { ScrollView, StyleSheet, View, type StyleProp, type ViewStyle } from 'react-native';
|
3
3
|
import { MainContext } from '../../../contexts';
|
4
4
|
import {
|
5
5
|
beautify,
|
@@ -12,7 +12,13 @@ import colors from '../../../theme/colors';
|
|
12
12
|
import { type DetailTab, type HttpRequest, type WebSocketRequest } from '../../../types';
|
13
13
|
import NetworkRequestDetailsItem from '../items/NetworkRequestDetailsItem';
|
14
14
|
|
15
|
-
const
|
15
|
+
const TabScrollView = ({ id, children }: { id: string; children: ReactNode }) => (
|
16
|
+
<ScrollView key={id} contentContainerStyle={styles.contentContainer}>
|
17
|
+
{children}
|
18
|
+
</ScrollView>
|
19
|
+
);
|
20
|
+
|
21
|
+
const NetworkRequestDetails = forwardRef<View, { style?: StyleProp<ViewStyle> }>(
|
16
22
|
({ style }, ref) => {
|
17
23
|
const {
|
18
24
|
debuggerState: { detailsData },
|
@@ -26,7 +32,7 @@ const NetworkRequestDetails = forwardRef<ScrollView, { style?: StyleProp<ViewSty
|
|
26
32
|
typeof detailsData?.beautified === 'boolean';
|
27
33
|
|
28
34
|
const {
|
29
|
-
|
35
|
+
isWS,
|
30
36
|
requestUrl,
|
31
37
|
overviewShown,
|
32
38
|
headersShown,
|
@@ -54,14 +60,14 @@ const NetworkRequestDetails = forwardRef<ScrollView, { style?: StyleProp<ViewSty
|
|
54
60
|
|
55
61
|
if (overviewShown && !content.current.overview && item) {
|
56
62
|
content.current.overview = (
|
57
|
-
|
63
|
+
<TabScrollView id="overview">
|
58
64
|
<NetworkRequestDetailsItem label="Request Type" content={item.type} />
|
59
65
|
|
60
66
|
<NetworkRequestDetailsItem label="Request URL" content={item.url} />
|
61
67
|
|
62
68
|
<NetworkRequestDetailsItem
|
63
69
|
label="Request Method"
|
64
|
-
content={formatRequestMethod(
|
70
|
+
content={formatRequestMethod(isWS ? undefined : (item as HttpRequest).method)}
|
65
71
|
/>
|
66
72
|
|
67
73
|
<NetworkRequestDetailsItem
|
@@ -71,32 +77,30 @@ const NetworkRequestDetails = forwardRef<ScrollView, { style?: StyleProp<ViewSty
|
|
71
77
|
|
72
78
|
<NetworkRequestDetailsItem
|
73
79
|
label="Start Time"
|
74
|
-
content={new Date(item.startTime ?? 0).
|
80
|
+
content={new Date(item.startTime ?? 0).toISOString()}
|
75
81
|
/>
|
76
82
|
|
77
83
|
<NetworkRequestDetailsItem
|
78
84
|
label="End Time"
|
79
|
-
content={new Date(item.endTime ?? 0).
|
85
|
+
content={new Date(item.endTime ?? 0).toISOString()}
|
80
86
|
/>
|
81
87
|
|
82
88
|
<NetworkRequestDetailsItem
|
83
89
|
label="Duration"
|
84
90
|
content={formatRequestDuration(item.startTime, item.endTime)}
|
85
91
|
/>
|
86
|
-
|
92
|
+
</TabScrollView>
|
87
93
|
);
|
88
94
|
}
|
89
95
|
|
90
96
|
if (headersShown && !content.current.headers && item) {
|
91
97
|
let headers: [string, string][] = [];
|
92
|
-
|
93
|
-
|
98
|
+
const requestHeaders: [string, string][] = [];
|
99
|
+
const responseHeaders: [string, string][] = [];
|
94
100
|
|
95
|
-
if (
|
101
|
+
if (isWS) {
|
96
102
|
headers = Object.entries((item as WebSocketRequest).options?.headers ?? {});
|
97
|
-
}
|
98
|
-
|
99
|
-
if (isHttp) {
|
103
|
+
} else {
|
100
104
|
for (const [key, value] of ((item as HttpRequest).requestHeaders ?? new Map()).entries()) {
|
101
105
|
requestHeaders.push([key, value]);
|
102
106
|
}
|
@@ -106,24 +110,24 @@ const NetworkRequestDetails = forwardRef<ScrollView, { style?: StyleProp<ViewSty
|
|
106
110
|
}
|
107
111
|
|
108
112
|
content.current.headers = (
|
109
|
-
|
110
|
-
{
|
113
|
+
<TabScrollView id="headers">
|
114
|
+
{isWS && !!headers.length && (
|
111
115
|
<NetworkRequestDetailsItem label="Headers" content={headers} />
|
112
116
|
)}
|
113
117
|
|
114
|
-
{
|
118
|
+
{!isWS && !!requestHeaders.length && (
|
115
119
|
<NetworkRequestDetailsItem label="Request Headers" content={requestHeaders} />
|
116
120
|
)}
|
117
121
|
|
118
|
-
{
|
122
|
+
{!isWS && !!responseHeaders.length && (
|
119
123
|
<NetworkRequestDetailsItem label="Response Headers" content={responseHeaders} />
|
120
124
|
)}
|
121
|
-
|
125
|
+
</TabScrollView>
|
122
126
|
);
|
123
127
|
}
|
124
128
|
|
125
|
-
if (requestShown && shouldBeautifiedRefUpdate && item
|
126
|
-
|
129
|
+
if (requestShown && shouldBeautifiedRefUpdate && item) {
|
130
|
+
const queryStringParameters: [string, string][] = [];
|
127
131
|
|
128
132
|
requestUrl.searchParams.forEach((value, name) => {
|
129
133
|
queryStringParameters.push([name, value]);
|
@@ -132,31 +136,33 @@ const NetworkRequestDetails = forwardRef<ScrollView, { style?: StyleProp<ViewSty
|
|
132
136
|
const body = beautify((item as HttpRequest).body, detailsData?.beautified ?? false);
|
133
137
|
|
134
138
|
content.current.request = (
|
135
|
-
|
139
|
+
<TabScrollView id="request">
|
136
140
|
{!!queryStringParameters.length && (
|
137
141
|
<NetworkRequestDetailsItem label="Query String" content={queryStringParameters} />
|
138
142
|
)}
|
139
143
|
|
140
144
|
{!!body && <NetworkRequestDetailsItem label="Body" content={body} />}
|
141
|
-
|
145
|
+
</TabScrollView>
|
142
146
|
);
|
143
147
|
}
|
144
148
|
|
145
149
|
if (responseShown && shouldBeautifiedRefUpdate && item) {
|
150
|
+
const response = beautify((item as HttpRequest).response, detailsData?.beautified ?? false);
|
146
151
|
content.current.response = (
|
147
|
-
<
|
148
|
-
label="Response"
|
149
|
-
|
150
|
-
/>
|
152
|
+
<TabScrollView id="response">
|
153
|
+
<NetworkRequestDetailsItem label="Response" content={response} />
|
154
|
+
</TabScrollView>
|
151
155
|
);
|
152
156
|
}
|
153
157
|
|
154
158
|
if (messagesShown && !content.current.messages && item) {
|
155
159
|
content.current.messages = (
|
156
|
-
<
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
+
<TabScrollView id="messages">
|
161
|
+
<NetworkRequestDetailsItem
|
162
|
+
label="Messages"
|
163
|
+
content={(item as WebSocketRequest).messages!}
|
164
|
+
/>
|
165
|
+
</TabScrollView>
|
160
166
|
);
|
161
167
|
}
|
162
168
|
|
@@ -165,9 +171,9 @@ const NetworkRequestDetails = forwardRef<ScrollView, { style?: StyleProp<ViewSty
|
|
165
171
|
}
|
166
172
|
|
167
173
|
return (
|
168
|
-
<
|
174
|
+
<View ref={ref} style={[styles.container, style]}>
|
169
175
|
{content.current[detailsData?.selectedTab as keyof typeof content.current]}
|
170
|
-
</
|
176
|
+
</View>
|
171
177
|
);
|
172
178
|
},
|
173
179
|
);
|
@@ -175,6 +181,8 @@ const NetworkRequestDetails = forwardRef<ScrollView, { style?: StyleProp<ViewSty
|
|
175
181
|
const styles = StyleSheet.create({
|
176
182
|
container: {
|
177
183
|
flex: 1,
|
184
|
+
},
|
185
|
+
contentContainer: {
|
178
186
|
padding: 8,
|
179
187
|
},
|
180
188
|
text: {
|
@@ -1,8 +1,8 @@
|
|
1
1
|
import { forwardRef, useContext } from 'react';
|
2
|
-
import {
|
2
|
+
import { type ScrollView, type StyleProp, type ViewStyle } from 'react-native';
|
3
3
|
import { MainContext } from '../../../contexts';
|
4
|
-
import
|
5
|
-
import { convertToCurl, getNetworkUtils } from '../../../core/utils';
|
4
|
+
import { type PanelState } from '../../../core/refs';
|
5
|
+
import { convertToCurl, getNetworkUtils, shareText } from '../../../core/utils';
|
6
6
|
import colors from '../../../theme/colors';
|
7
7
|
import icons from '../../../theme/icons';
|
8
8
|
import { NetworkType, type HttpRequest, type WebSocketRequest } from '../../../types';
|
@@ -15,8 +15,6 @@ interface NetworkHeaderProps {
|
|
15
15
|
style?: StyleProp<ViewStyle>;
|
16
16
|
}
|
17
17
|
|
18
|
-
let isSharing = false;
|
19
|
-
|
20
18
|
const NetworkHeader = forwardRef<ScrollView, NetworkHeaderProps>(
|
21
19
|
({ selectedPanel, style }, ref) => {
|
22
20
|
const {
|
@@ -26,7 +24,7 @@ const NetworkHeader = forwardRef<ScrollView, NetworkHeaderProps>(
|
|
26
24
|
|
27
25
|
const data = detailsData?.data as HttpRequest | WebSocketRequest | undefined;
|
28
26
|
|
29
|
-
const {
|
27
|
+
const { isWS, overviewShown, headersShown, requestShown, responseShown, messagesShown } =
|
30
28
|
getNetworkUtils(data);
|
31
29
|
|
32
30
|
return (
|
@@ -36,13 +34,13 @@ const NetworkHeader = forwardRef<ScrollView, NetworkHeaderProps>(
|
|
36
34
|
|
37
35
|
<Divider type="vertical" />
|
38
36
|
|
39
|
-
{overviewShown && <HeaderComponents.TabItem tab="overview" label="Overview" />}
|
40
|
-
{headersShown && <HeaderComponents.TabItem tab="headers" label="Headers" />}
|
41
|
-
{requestShown && <HeaderComponents.TabItem tab="request" label="Request" />}
|
42
|
-
{responseShown && <HeaderComponents.TabItem tab="response" label="Response" />}
|
43
|
-
{messagesShown && <HeaderComponents.TabItem tab="messages" label="Messages" />}
|
37
|
+
{!!overviewShown && <HeaderComponents.TabItem tab="overview" label="Overview" />}
|
38
|
+
{!!headersShown && <HeaderComponents.TabItem tab="headers" label="Headers" />}
|
39
|
+
{!!requestShown && <HeaderComponents.TabItem tab="request" label="Request" />}
|
40
|
+
{!!responseShown && <HeaderComponents.TabItem tab="response" label="Response" />}
|
41
|
+
{!!messagesShown && <HeaderComponents.TabItem tab="messages" label="Messages" />}
|
44
42
|
|
45
|
-
{
|
43
|
+
{!isWS && (
|
46
44
|
<>
|
47
45
|
<Divider type="vertical" />
|
48
46
|
|
@@ -59,20 +57,10 @@ const NetworkHeader = forwardRef<ScrollView, NetworkHeaderProps>(
|
|
59
57
|
<DebuggerHeaderItem
|
60
58
|
content={icons.share}
|
61
59
|
onPress={async () => {
|
62
|
-
if (
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
refs.debugger.current?.setCurrentIndex(DebuggerVisibility.Bubble);
|
67
|
-
|
68
|
-
await Share.share({
|
69
|
-
message: convertToCurl(data.method, data.url, data.requestHeaders, data.body),
|
70
|
-
});
|
71
|
-
} catch (error) {
|
72
|
-
// Handle error
|
73
|
-
} finally {
|
74
|
-
isSharing = false;
|
75
|
-
}
|
60
|
+
if (data?.type === NetworkType.WS) return;
|
61
|
+
await shareText(
|
62
|
+
convertToCurl(data!.method, data!.url, data!.requestHeaders, data!.body),
|
63
|
+
);
|
76
64
|
}}
|
77
65
|
/>
|
78
66
|
</>
|
@@ -1,4 +1,4 @@
|
|
1
1
|
export { default as Bubble } from './bubble/Bubble';
|
2
|
-
export { default as
|
3
|
-
export { default as
|
4
|
-
export { default as
|
2
|
+
export { default as Header } from './headers/Header';
|
3
|
+
export { default as Panel } from './panels/Panel';
|
4
|
+
export { default as IndexedStack } from './common/IndexedStack';
|
@@ -1,5 +1,7 @@
|
|
1
|
-
import { StyleSheet, Text } from 'react-native';
|
1
|
+
import { StyleSheet, Text, View } from 'react-native';
|
2
2
|
import colors from '../../../theme/colors';
|
3
|
+
import { showNewLine } from '../../../core/utils';
|
4
|
+
import ShareableText from '../common/ShareableText';
|
3
5
|
|
4
6
|
interface NetworkRequestDetailsItemProps {
|
5
7
|
label: string;
|
@@ -10,32 +12,57 @@ export default function NetworkRequestDetailsItem({
|
|
10
12
|
label,
|
11
13
|
content,
|
12
14
|
}: NetworkRequestDetailsItemProps) {
|
15
|
+
const renderContent = () => {
|
16
|
+
switch (true) {
|
17
|
+
case typeof content === 'string':
|
18
|
+
return (
|
19
|
+
<ShareableText style={styles.text}>
|
20
|
+
{content}
|
21
|
+
{showNewLine(!content.endsWith('\n'))}
|
22
|
+
</ShareableText>
|
23
|
+
);
|
24
|
+
case Array.isArray(content):
|
25
|
+
return (
|
26
|
+
<View>
|
27
|
+
{content.map(([key, value], index) => (
|
28
|
+
<View style={styles.pairContainer} key={`${key}-${index}`}>
|
29
|
+
<Text style={[styles.label, styles.subLabel]}>
|
30
|
+
{key}
|
31
|
+
{':'}
|
32
|
+
</Text>
|
33
|
+
|
34
|
+
<ShareableText style={styles.text}>
|
35
|
+
{value}
|
36
|
+
{showNewLine(index === content.length - 1)}
|
37
|
+
</ShareableText>
|
38
|
+
</View>
|
39
|
+
))}
|
40
|
+
</View>
|
41
|
+
);
|
42
|
+
}
|
43
|
+
};
|
44
|
+
|
13
45
|
return (
|
14
|
-
<
|
15
|
-
<Text style={styles.label}>
|
16
|
-
|
17
|
-
|
18
|
-
</Text>
|
19
|
-
{typeof content === 'string'
|
20
|
-
? content
|
21
|
-
: content.map(([key, value]) => (
|
22
|
-
<Text key={key} style={[styles.label, styles.subLabel]}>
|
23
|
-
{key}
|
24
|
-
{': '}
|
25
|
-
<Text style={styles.text}>{value}</Text>
|
26
|
-
{'\n'}
|
27
|
-
</Text>
|
28
|
-
))}
|
29
|
-
{Array.isArray(content) || content?.endsWith('\n') ? '' : '\n'}
|
30
|
-
</Text>
|
46
|
+
<View style={styles.container}>
|
47
|
+
<Text style={styles.label}>{label}</Text>
|
48
|
+
{renderContent()}
|
49
|
+
</View>
|
31
50
|
);
|
32
51
|
}
|
33
52
|
|
34
53
|
const styles = StyleSheet.create({
|
54
|
+
container: {
|
55
|
+
rowGap: 2,
|
56
|
+
},
|
57
|
+
pairContainer: {
|
58
|
+
flexDirection: 'row',
|
59
|
+
columnGap: 4,
|
60
|
+
},
|
35
61
|
text: {
|
36
62
|
fontSize: 14,
|
37
63
|
fontWeight: 'normal',
|
38
64
|
color: colors.black,
|
65
|
+
flexShrink: 1,
|
39
66
|
},
|
40
67
|
label: {
|
41
68
|
fontSize: 16,
|