react-native-xenon 1.0.2 → 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.
Files changed (80) hide show
  1. package/README.md +9 -9
  2. package/lib/commonjs/core/utils.js +32 -9
  3. package/lib/commonjs/core/utils.js.map +1 -1
  4. package/lib/commonjs/hooks/useNetworkInterceptor.js +7 -2
  5. package/lib/commonjs/hooks/useNetworkInterceptor.js.map +1 -1
  6. package/lib/commonjs/interceptors/FetchInterceptor.js +1 -1
  7. package/lib/commonjs/interceptors/FetchInterceptor.js.map +1 -1
  8. package/lib/commonjs/ui/Xenon.js +14 -10
  9. package/lib/commonjs/ui/Xenon.js.map +1 -1
  10. package/lib/commonjs/ui/components/bubble/Bubble.js +2 -2
  11. package/lib/commonjs/ui/components/bubble/Bubble.js.map +1 -1
  12. package/lib/commonjs/ui/components/common/ShareableText.js +24 -0
  13. package/lib/commonjs/ui/components/common/ShareableText.js.map +1 -0
  14. package/lib/commonjs/ui/components/details/NetworkRequestDetails.js +43 -25
  15. package/lib/commonjs/ui/components/details/NetworkRequestDetails.js.map +1 -1
  16. package/lib/commonjs/ui/components/headers/NetworkHeader.js +9 -24
  17. package/lib/commonjs/ui/components/headers/NetworkHeader.js.map +1 -1
  18. package/lib/commonjs/ui/components/index.js +9 -9
  19. package/lib/commonjs/ui/components/index.js.map +1 -1
  20. package/lib/commonjs/ui/components/items/NetworkRequestDetailsItem.js +38 -12
  21. package/lib/commonjs/ui/components/items/NetworkRequestDetailsItem.js.map +1 -1
  22. package/lib/module/core/utils.js +32 -9
  23. package/lib/module/core/utils.js.map +1 -1
  24. package/lib/module/hooks/useNetworkInterceptor.js +7 -2
  25. package/lib/module/hooks/useNetworkInterceptor.js.map +1 -1
  26. package/lib/module/interceptors/FetchInterceptor.js +1 -1
  27. package/lib/module/interceptors/FetchInterceptor.js.map +1 -1
  28. package/lib/module/ui/Xenon.js +14 -10
  29. package/lib/module/ui/Xenon.js.map +1 -1
  30. package/lib/module/ui/components/bubble/Bubble.js +2 -2
  31. package/lib/module/ui/components/bubble/Bubble.js.map +1 -1
  32. package/lib/module/ui/components/common/ShareableText.js +24 -0
  33. package/lib/module/ui/components/common/ShareableText.js.map +1 -0
  34. package/lib/module/ui/components/details/NetworkRequestDetails.js +43 -25
  35. package/lib/module/ui/components/details/NetworkRequestDetails.js.map +1 -1
  36. package/lib/module/ui/components/headers/NetworkHeader.js +9 -24
  37. package/lib/module/ui/components/headers/NetworkHeader.js.map +1 -1
  38. package/lib/module/ui/components/index.js +9 -9
  39. package/lib/module/ui/components/index.js.map +1 -1
  40. package/lib/module/ui/components/items/NetworkRequestDetailsItem.js +38 -12
  41. package/lib/module/ui/components/items/NetworkRequestDetailsItem.js.map +1 -1
  42. package/lib/typescript/commonjs/src/core/utils.d.ts +4 -2
  43. package/lib/typescript/commonjs/src/core/utils.d.ts.map +1 -1
  44. package/lib/typescript/commonjs/src/hooks/useNetworkInterceptor.d.ts +2 -1
  45. package/lib/typescript/commonjs/src/hooks/useNetworkInterceptor.d.ts.map +1 -1
  46. package/lib/typescript/commonjs/src/ui/Xenon.d.ts +44 -4
  47. package/lib/typescript/commonjs/src/ui/Xenon.d.ts.map +1 -1
  48. package/lib/typescript/commonjs/src/ui/components/common/ShareableText.d.ts +7 -0
  49. package/lib/typescript/commonjs/src/ui/components/common/ShareableText.d.ts.map +1 -0
  50. package/lib/typescript/commonjs/src/ui/components/details/NetworkRequestDetails.d.ts +2 -2
  51. package/lib/typescript/commonjs/src/ui/components/details/NetworkRequestDetails.d.ts.map +1 -1
  52. package/lib/typescript/commonjs/src/ui/components/headers/NetworkHeader.d.ts.map +1 -1
  53. package/lib/typescript/commonjs/src/ui/components/index.d.ts +3 -3
  54. package/lib/typescript/commonjs/src/ui/components/index.d.ts.map +1 -1
  55. package/lib/typescript/commonjs/src/ui/components/items/NetworkRequestDetailsItem.d.ts.map +1 -1
  56. package/lib/typescript/module/src/core/utils.d.ts +4 -2
  57. package/lib/typescript/module/src/core/utils.d.ts.map +1 -1
  58. package/lib/typescript/module/src/hooks/useNetworkInterceptor.d.ts +2 -1
  59. package/lib/typescript/module/src/hooks/useNetworkInterceptor.d.ts.map +1 -1
  60. package/lib/typescript/module/src/ui/Xenon.d.ts +44 -4
  61. package/lib/typescript/module/src/ui/Xenon.d.ts.map +1 -1
  62. package/lib/typescript/module/src/ui/components/common/ShareableText.d.ts +7 -0
  63. package/lib/typescript/module/src/ui/components/common/ShareableText.d.ts.map +1 -0
  64. package/lib/typescript/module/src/ui/components/details/NetworkRequestDetails.d.ts +2 -2
  65. package/lib/typescript/module/src/ui/components/details/NetworkRequestDetails.d.ts.map +1 -1
  66. package/lib/typescript/module/src/ui/components/headers/NetworkHeader.d.ts.map +1 -1
  67. package/lib/typescript/module/src/ui/components/index.d.ts +3 -3
  68. package/lib/typescript/module/src/ui/components/index.d.ts.map +1 -1
  69. package/lib/typescript/module/src/ui/components/items/NetworkRequestDetailsItem.d.ts.map +1 -1
  70. package/package.json +1 -1
  71. package/src/core/utils.ts +32 -8
  72. package/src/hooks/useNetworkInterceptor.ts +13 -2
  73. package/src/interceptors/FetchInterceptor.ts +1 -1
  74. package/src/ui/Xenon.tsx +52 -10
  75. package/src/ui/components/bubble/Bubble.tsx +2 -2
  76. package/src/ui/components/common/ShareableText.tsx +21 -0
  77. package/src/ui/components/details/NetworkRequestDetails.tsx +42 -34
  78. package/src/ui/components/headers/NetworkHeader.tsx +14 -26
  79. package/src/ui/components/index.ts +3 -3
  80. package/src/ui/components/items/NetworkRequestDetailsItem.tsx +45 -18
@@ -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({ autoEnabled }: NetworkInterceptorParams) {
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
- }, [setNetworkRequests]);
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
- let responseHeaders: Map<string, string> = new Map();
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/MainContext';
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
- disabled?: boolean;
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
- children?: ReactNode;
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 }: 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(0.5);
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, pan, screenHeight, screenWidth]);
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 NetworkRequestDetails = forwardRef<ScrollView, { style?: StyleProp<ViewStyle> }>(
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
- isHttp,
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(isHttp ? (item as HttpRequest).method : undefined)}
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).toUTCString()}
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).toUTCString()}
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
- let requestHeaders: [string, string][] = [];
93
- let responseHeaders: [string, string][] = [];
98
+ const requestHeaders: [string, string][] = [];
99
+ const responseHeaders: [string, string][] = [];
94
100
 
95
- if (!isHttp) {
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
- {!isHttp && !!headers.length && (
113
+ <TabScrollView id="headers">
114
+ {isWS && !!headers.length && (
111
115
  <NetworkRequestDetailsItem label="Headers" content={headers} />
112
116
  )}
113
117
 
114
- {isHttp && !!requestHeaders.length && (
118
+ {!isWS && !!requestHeaders.length && (
115
119
  <NetworkRequestDetailsItem label="Request Headers" content={requestHeaders} />
116
120
  )}
117
121
 
118
- {isHttp && !!responseHeaders.length && (
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 && requestUrl.searchParams) {
126
- let queryStringParameters: [string, string][] = [];
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
- <NetworkRequestDetailsItem
148
- label="Response"
149
- content={beautify((item as HttpRequest).response, detailsData?.beautified ?? false)}
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
- <NetworkRequestDetailsItem
157
- label="Messages"
158
- content={(item as WebSocketRequest).messages!}
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
- <ScrollView ref={ref} style={[styles.container, style]}>
174
+ <View ref={ref} style={[styles.container, style]}>
169
175
  {content.current[detailsData?.selectedTab as keyof typeof content.current]}
170
- </ScrollView>
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 { Share, type ScrollView, type StyleProp, type ViewStyle } from 'react-native';
2
+ import { type ScrollView, type StyleProp, type ViewStyle } from 'react-native';
3
3
  import { MainContext } from '../../../contexts';
4
- import refs, { DebuggerVisibility, type PanelState } from '../../../core/refs';
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 { isHttp, overviewShown, headersShown, requestShown, responseShown, messagesShown } =
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
- {isHttp && (
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 (isSharing || !data || data.type === NetworkType.WS) return;
63
-
64
- try {
65
- isSharing = true;
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 DebuggerHeader } from './headers/DebuggerHeader';
3
- export { default as ConsolePanel } from './panels/ConsolePanel';
4
- export { default as NetworkPanel } from './panels/NetworkPanel';
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
- <Text style={styles.text}>
15
- <Text style={styles.label}>
16
- {label}
17
- {'\n'}
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,