@buoy-gg/network 4.0.2 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/index.js +14 -0
- package/lib/commonjs/network/components/NetworkEventItemCompact.js +32 -18
- package/lib/commonjs/network/components/NetworkHeaderLive.js +199 -0
- package/lib/commonjs/network/components/NetworkListScreen.js +203 -0
- package/lib/commonjs/network/components/NetworkModal.js +153 -630
- package/lib/commonjs/network/hooks/useNetworkBadgeCounts.js +83 -0
- package/lib/commonjs/network/hooks/useNetworkEventList.js +93 -0
- package/lib/commonjs/network/hooks/useNetworkHasMatches.js +56 -0
- package/lib/commonjs/network/utils/filterNetworkEvents.js +96 -0
- package/lib/commonjs/network/utils/generateNetworkCopyText.js +139 -0
- package/lib/module/index.js +5 -0
- package/lib/module/network/components/NetworkEventItemCompact.js +32 -18
- package/lib/module/network/components/NetworkHeaderLive.js +193 -0
- package/lib/module/network/components/NetworkListScreen.js +199 -0
- package/lib/module/network/components/NetworkModal.js +157 -634
- package/lib/module/network/hooks/useNetworkBadgeCounts.js +79 -0
- package/lib/module/network/hooks/useNetworkEventList.js +89 -0
- package/lib/module/network/hooks/useNetworkHasMatches.js +52 -0
- package/lib/module/network/utils/filterNetworkEvents.js +92 -0
- package/lib/module/network/utils/generateNetworkCopyText.js +135 -0
- package/lib/typescript/index.d.ts +2 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkEventItemCompact.d.ts +0 -4
- package/lib/typescript/network/components/NetworkEventItemCompact.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkHeaderLive.d.ts +52 -0
- package/lib/typescript/network/components/NetworkHeaderLive.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkListScreen.d.ts +27 -0
- package/lib/typescript/network/components/NetworkListScreen.d.ts.map +1 -0
- package/lib/typescript/network/components/NetworkModal.d.ts.map +1 -1
- package/lib/typescript/network/hooks/useNetworkBadgeCounts.d.ts +24 -0
- package/lib/typescript/network/hooks/useNetworkBadgeCounts.d.ts.map +1 -0
- package/lib/typescript/network/hooks/useNetworkEventList.d.ts +34 -0
- package/lib/typescript/network/hooks/useNetworkEventList.d.ts.map +1 -0
- package/lib/typescript/network/hooks/useNetworkHasMatches.d.ts +23 -0
- package/lib/typescript/network/hooks/useNetworkHasMatches.d.ts.map +1 -0
- package/lib/typescript/network/utils/filterNetworkEvents.d.ts +13 -0
- package/lib/typescript/network/utils/filterNetworkEvents.d.ts.map +1 -0
- package/lib/typescript/network/utils/generateNetworkCopyText.d.ts +11 -0
- package/lib/typescript/network/utils/generateNetworkCopyText.d.ts.map +1 -0
- package/package.json +2 -2
package/lib/commonjs/index.js
CHANGED
|
@@ -117,6 +117,18 @@ Object.defineProperty(exports, "useIgnoredPatterns", {
|
|
|
117
117
|
return _sharedUi.useIgnoredPatterns;
|
|
118
118
|
}
|
|
119
119
|
});
|
|
120
|
+
Object.defineProperty(exports, "useNetworkBadgeCounts", {
|
|
121
|
+
enumerable: true,
|
|
122
|
+
get: function () {
|
|
123
|
+
return _useNetworkBadgeCounts.useNetworkBadgeCounts;
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
Object.defineProperty(exports, "useNetworkEventList", {
|
|
127
|
+
enumerable: true,
|
|
128
|
+
get: function () {
|
|
129
|
+
return _useNetworkEventList.useNetworkEventList;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
120
132
|
Object.defineProperty(exports, "useNetworkEvents", {
|
|
121
133
|
enumerable: true,
|
|
122
134
|
get: function () {
|
|
@@ -134,6 +146,8 @@ var _NetworkModal = require("./network/components/NetworkModal");
|
|
|
134
146
|
var _NetworkEventDetailView = require("./network/components/NetworkEventDetailView");
|
|
135
147
|
var _NetworkEventItemCompact = require("./network/components/NetworkEventItemCompact");
|
|
136
148
|
var _useNetworkEvents = require("./network/hooks/useNetworkEvents");
|
|
149
|
+
var _useNetworkEventList = require("./network/hooks/useNetworkEventList");
|
|
150
|
+
var _useNetworkBadgeCounts = require("./network/hooks/useNetworkBadgeCounts");
|
|
137
151
|
var _useTickEveryMinute = require("./network/hooks/useTickEveryMinute");
|
|
138
152
|
var _formatting = require("./network/utils/formatting");
|
|
139
153
|
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
@@ -112,11 +112,33 @@ function SizeIndicators({
|
|
|
112
112
|
* Compact list-row representation of a network event. Optimized for large lists with memoization
|
|
113
113
|
* and periodic refresh via `useTickEveryMinute` to keep relative timestamps accurate.
|
|
114
114
|
*/
|
|
115
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Sole tick-context consumer in the row (rule2: composition over memo) —
|
|
117
|
+
* the periodic relative-timestamp tick re-renders ONLY this Text, not the
|
|
118
|
+
* whole card. Profiling (tool-bench 2026-07-06): the tick accounted for
|
|
119
|
+
* ~30 of 65 row renders (plus their SVG-icon churn) in a 12s recording.
|
|
120
|
+
*/
|
|
121
|
+
function BottomRightTime({
|
|
122
|
+
timestamp,
|
|
123
|
+
duration
|
|
124
|
+
}) {
|
|
125
|
+
const tick = (0, _useTickEveryMinute.useTickEveryMinute)();
|
|
126
|
+
const relativeTime = (0, _sharedUi.formatRelativeTime)(timestamp, tick);
|
|
127
|
+
return duration ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
|
|
128
|
+
style: styles.bottomRightText,
|
|
129
|
+
children: [(0, _formatting.formatDuration)(duration), " ", relativeTime]
|
|
130
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
131
|
+
style: styles.bottomRightText,
|
|
132
|
+
children: relativeTime
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
const NetworkEventItemCompact = exports.NetworkEventItemCompact = /*#__PURE__*/(0, _react.memo)(
|
|
136
|
+
// Named function (not an arrow) so render-capture reports attribute row
|
|
137
|
+
// renders to this component instead of a compiled anonymous name.
|
|
138
|
+
function NetworkEventItemCompact({
|
|
116
139
|
event,
|
|
117
140
|
onPress
|
|
118
|
-
})
|
|
119
|
-
const tick = (0, _useTickEveryMinute.useTickEveryMinute)();
|
|
141
|
+
}) {
|
|
120
142
|
const statusColor = getStatusColor(event.status, event.error);
|
|
121
143
|
const isPending = !event.status && !event.error;
|
|
122
144
|
const contentType = getContentTypeBadge(event.responseHeaders);
|
|
@@ -138,15 +160,6 @@ const NetworkEventItemCompact = exports.NetworkEventItemCompact = /*#__PURE__*/(
|
|
|
138
160
|
// For non-GraphQL requests with operation names (e.g., gRPC)
|
|
139
161
|
displayUrl = `${displayUrl}\n(${event.operationName})`;
|
|
140
162
|
}
|
|
141
|
-
|
|
142
|
-
// Format time with both absolute and relative
|
|
143
|
-
const timeString = new Date(event.timestamp).toLocaleTimeString("en-US", {
|
|
144
|
-
hour: "numeric",
|
|
145
|
-
minute: "2-digit",
|
|
146
|
-
second: "2-digit",
|
|
147
|
-
hour12: true
|
|
148
|
-
});
|
|
149
|
-
const relativeTime = (0, _sharedUi.formatRelativeTime)(event.timestamp, tick);
|
|
150
163
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.DevToolsCard, {
|
|
151
164
|
onPress: () => onPress(event),
|
|
152
165
|
style: styles.cardWrapper,
|
|
@@ -197,16 +210,17 @@ const NetworkEventItemCompact = exports.NetworkEventItemCompact = /*#__PURE__*/(
|
|
|
197
210
|
})]
|
|
198
211
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
199
212
|
style: styles.bottomRightContainer,
|
|
200
|
-
children:
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
204
|
-
style: styles.bottomRightText,
|
|
205
|
-
children: relativeTime
|
|
213
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(BottomRightTime, {
|
|
214
|
+
timestamp: event.timestamp,
|
|
215
|
+
duration: event.duration
|
|
206
216
|
})
|
|
207
217
|
})]
|
|
208
218
|
});
|
|
209
219
|
});
|
|
220
|
+
|
|
221
|
+
// Without this, render-capture reports attribute row renders to the
|
|
222
|
+
// anonymous memo wrapper ("Memo"/"_c4") instead of the row.
|
|
223
|
+
NetworkEventItemCompact.displayName = "NetworkEventItemCompact";
|
|
210
224
|
const styles = _reactNative.StyleSheet.create({
|
|
211
225
|
cardWrapper: {
|
|
212
226
|
marginHorizontal: 8
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.NetworkClearButton = NetworkClearButton;
|
|
7
|
+
exports.NetworkHeaderBadges = NetworkHeaderBadges;
|
|
8
|
+
exports.NetworkHeaderCopyButton = NetworkHeaderCopyButton;
|
|
9
|
+
var _reactNative = require("react-native");
|
|
10
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
11
|
+
var _networkEventStore = require("../utils/networkEventStore");
|
|
12
|
+
var _useNetworkBadgeCounts = require("../hooks/useNetworkBadgeCounts");
|
|
13
|
+
var _useNetworkHasMatches = require("../hooks/useNetworkHasMatches");
|
|
14
|
+
var _generateNetworkCopyText = require("../utils/generateNetworkCopyText");
|
|
15
|
+
var _filterNetworkEvents = require("../utils/filterNetworkEvents");
|
|
16
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
17
|
+
/**
|
|
18
|
+
* The header pieces that consume LIVE event data, decomposed out of
|
|
19
|
+
* NetworkModal per rule3 (decompose by responsibility) so each one is its
|
|
20
|
+
* own narrow store subscriber and the modal root/header never re-render
|
|
21
|
+
* per network event:
|
|
22
|
+
*
|
|
23
|
+
* - `NetworkHeaderBadges` — success/error/pending count chips
|
|
24
|
+
* - `NetworkClearButton` — trash button (enabled = store has events;
|
|
25
|
+
* clear handler co-located here per rule2)
|
|
26
|
+
* - `NetworkHeaderCopyButton` — copy-all button; the copy TEXT is
|
|
27
|
+
* resolved lazily at press time (CopyButton accepts a function value —
|
|
28
|
+
* rule2's latest-ref pattern), so no string is built per event
|
|
29
|
+
*
|
|
30
|
+
* Profiling evidence for this decomposition: tool-bench 2026-07-06 —
|
|
31
|
+
* Trash2/CopyButton re-rendered 15× for 15 network events when the
|
|
32
|
+
* subscription lived at the modal root.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/** Success / error / pending chips — tap to toggle the status filter. */
|
|
36
|
+
function NetworkHeaderBadges({
|
|
37
|
+
activeStatus,
|
|
38
|
+
onToggleStatus,
|
|
39
|
+
ignoredPatterns,
|
|
40
|
+
paused
|
|
41
|
+
}) {
|
|
42
|
+
const counts = (0, _useNetworkBadgeCounts.useNetworkBadgeCounts)({
|
|
43
|
+
ignoredPatterns,
|
|
44
|
+
paused
|
|
45
|
+
});
|
|
46
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
47
|
+
style: styles.headerChipRow,
|
|
48
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
49
|
+
style: [styles.headerChip, activeStatus === "success" && styles.headerChipActive],
|
|
50
|
+
onPress: () => onToggleStatus("success"),
|
|
51
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CheckCircle, {
|
|
52
|
+
size: 12,
|
|
53
|
+
color: _sharedUi.macOSColors.semantic.success
|
|
54
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
55
|
+
style: [styles.headerChipValue, {
|
|
56
|
+
color: _sharedUi.macOSColors.semantic.success
|
|
57
|
+
}],
|
|
58
|
+
children: counts.successful
|
|
59
|
+
})]
|
|
60
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
61
|
+
style: [styles.headerChip, activeStatus === "error" && styles.headerChipActive],
|
|
62
|
+
onPress: () => onToggleStatus("error"),
|
|
63
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.XCircle, {
|
|
64
|
+
size: 12,
|
|
65
|
+
color: _sharedUi.macOSColors.semantic.error
|
|
66
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
67
|
+
style: [styles.headerChipValue, {
|
|
68
|
+
color: _sharedUi.macOSColors.semantic.error
|
|
69
|
+
}],
|
|
70
|
+
children: counts.failed
|
|
71
|
+
})]
|
|
72
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
73
|
+
style: [styles.headerChip, activeStatus === "pending" && styles.headerChipActive],
|
|
74
|
+
onPress: () => onToggleStatus("pending"),
|
|
75
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Clock, {
|
|
76
|
+
size: 12,
|
|
77
|
+
color: _sharedUi.macOSColors.semantic.warning
|
|
78
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
79
|
+
style: [styles.headerChipValue, {
|
|
80
|
+
color: _sharedUi.macOSColors.semantic.warning
|
|
81
|
+
}],
|
|
82
|
+
children: counts.pending
|
|
83
|
+
})]
|
|
84
|
+
})]
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/** Trash button — handler co-located; enabled while the store has events.
|
|
88
|
+
* Subscribes to a flip-guarded boolean, so it renders when the store goes
|
|
89
|
+
* empty↔non-empty, not on every count change. */
|
|
90
|
+
function NetworkClearButton({
|
|
91
|
+
paused
|
|
92
|
+
}) {
|
|
93
|
+
const hasEvents = (0, _useNetworkHasMatches.useNetworkHasEvents)({
|
|
94
|
+
paused
|
|
95
|
+
});
|
|
96
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
97
|
+
"sentry-label": "ignore clear events",
|
|
98
|
+
onPress: () => _networkEventStore.networkEventStore.clearEvents(),
|
|
99
|
+
style: [styles.headerActionButton, !hasEvents && styles.headerActionButtonDisabled],
|
|
100
|
+
disabled: !hasEvents,
|
|
101
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Trash2, {
|
|
102
|
+
size: 14,
|
|
103
|
+
color: hasEvents ? _sharedUi.macOSColors.text.muted : _sharedUi.macOSColors.text.disabled
|
|
104
|
+
})
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Copy-all-requests button. Subscribes to a flip-guarded boolean (its
|
|
109
|
+
* enabled state); the copy text is generated at PRESS time from a live
|
|
110
|
+
* store read, so there's no per-event work and no stale closure.
|
|
111
|
+
*/
|
|
112
|
+
function NetworkHeaderCopyButton({
|
|
113
|
+
isPro,
|
|
114
|
+
filter,
|
|
115
|
+
ignoredPatterns,
|
|
116
|
+
copySettings,
|
|
117
|
+
paused,
|
|
118
|
+
onUpgradePress
|
|
119
|
+
}) {
|
|
120
|
+
const hasEvents = (0, _useNetworkHasMatches.useNetworkHasMatches)({
|
|
121
|
+
filter,
|
|
122
|
+
ignoredPatterns,
|
|
123
|
+
paused
|
|
124
|
+
});
|
|
125
|
+
if (!isPro) {
|
|
126
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
127
|
+
"sentry-label": "ignore copy pro",
|
|
128
|
+
onPress: onUpgradePress,
|
|
129
|
+
style: [styles.headerActionButton, !hasEvents && styles.headerActionButtonDisabled],
|
|
130
|
+
disabled: !hasEvents,
|
|
131
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Copy, {
|
|
132
|
+
size: 14,
|
|
133
|
+
color: hasEvents ? _sharedUi.macOSColors.text.secondary : _sharedUi.macOSColors.text.disabled
|
|
134
|
+
})
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CopyButton
|
|
138
|
+
// Lazy value: resolved at copy time by CopyButton's latest-ref
|
|
139
|
+
// pattern, reading the LIVE store — no per-event work, no staleness.
|
|
140
|
+
, {
|
|
141
|
+
value: () => (0, _generateNetworkCopyText.generateNetworkCopyText)((0, _filterNetworkEvents.filterNetworkEvents)((0, _filterNetworkEvents.applyIgnoredPatterns)(_networkEventStore.networkEventStore.getEvents(), ignoredPatterns), filter), copySettings),
|
|
142
|
+
size: 14,
|
|
143
|
+
buttonStyle: styles.headerActionButton,
|
|
144
|
+
colors: {
|
|
145
|
+
idle: hasEvents ? _sharedUi.macOSColors.text.secondary : _sharedUi.macOSColors.text.disabled,
|
|
146
|
+
success: _sharedUi.macOSColors.semantic.success,
|
|
147
|
+
error: _sharedUi.macOSColors.semantic.error
|
|
148
|
+
},
|
|
149
|
+
disabled: !hasEvents
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
const styles = _reactNative.StyleSheet.create({
|
|
153
|
+
headerChipRow: {
|
|
154
|
+
flexDirection: "row",
|
|
155
|
+
alignItems: "center",
|
|
156
|
+
gap: 8
|
|
157
|
+
},
|
|
158
|
+
headerChip: {
|
|
159
|
+
flexDirection: "row",
|
|
160
|
+
alignItems: "center",
|
|
161
|
+
gap: 4,
|
|
162
|
+
backgroundColor: _sharedUi.macOSColors.background.hover,
|
|
163
|
+
paddingHorizontal: 10,
|
|
164
|
+
paddingVertical: 5,
|
|
165
|
+
borderRadius: 12,
|
|
166
|
+
borderWidth: 1,
|
|
167
|
+
borderColor: _sharedUi.macOSColors.border.default
|
|
168
|
+
},
|
|
169
|
+
headerChipActive: {
|
|
170
|
+
backgroundColor: _sharedUi.macOSColors.semantic.infoBackground,
|
|
171
|
+
borderColor: _sharedUi.macOSColors.semantic.info + "50",
|
|
172
|
+
shadowColor: _sharedUi.macOSColors.semantic.info,
|
|
173
|
+
shadowOffset: {
|
|
174
|
+
width: 0,
|
|
175
|
+
height: 1
|
|
176
|
+
},
|
|
177
|
+
shadowOpacity: 0.08,
|
|
178
|
+
shadowRadius: 2,
|
|
179
|
+
elevation: 1
|
|
180
|
+
},
|
|
181
|
+
headerChipValue: {
|
|
182
|
+
fontSize: 12,
|
|
183
|
+
fontWeight: "600",
|
|
184
|
+
fontFamily: "monospace"
|
|
185
|
+
},
|
|
186
|
+
headerActionButton: {
|
|
187
|
+
width: 32,
|
|
188
|
+
height: 32,
|
|
189
|
+
borderRadius: 8,
|
|
190
|
+
backgroundColor: _sharedUi.macOSColors.background.hover,
|
|
191
|
+
borderWidth: 1,
|
|
192
|
+
borderColor: _sharedUi.macOSColors.border.default,
|
|
193
|
+
alignItems: "center",
|
|
194
|
+
justifyContent: "center"
|
|
195
|
+
},
|
|
196
|
+
headerActionButtonDisabled: {
|
|
197
|
+
opacity: 0.55
|
|
198
|
+
}
|
|
199
|
+
});
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.NetworkListScreen = NetworkListScreen;
|
|
7
|
+
var _reactNative = require("react-native");
|
|
8
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
9
|
+
var _NetworkEventItemCompact = require("./NetworkEventItemCompact");
|
|
10
|
+
var _useNetworkEventList = require("../hooks/useNetworkEventList");
|
|
11
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
12
|
+
/**
|
|
13
|
+
* The network tool's LIST screen — owns the event-store subscription, the
|
|
14
|
+
* FlatList, empty state, disabled banner, and the free-tier locked-history
|
|
15
|
+
* footer. Decomposed from NetworkModal per rule3 so per-event re-renders
|
|
16
|
+
* are confined to this subtree (plus the header's live leaves) instead of
|
|
17
|
+
* the whole modal.
|
|
18
|
+
*
|
|
19
|
+
* Stable-tree contract (rule4): hooks run unconditionally, then the
|
|
20
|
+
* component returns null when not visible. The subscription is gated with
|
|
21
|
+
* `paused || !visible` so a hidden list doesn't subscribe at all.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
function EmptyState({
|
|
25
|
+
isEnabled
|
|
26
|
+
}) {
|
|
27
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
28
|
+
style: styles.emptyState,
|
|
29
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Globe, {
|
|
30
|
+
size: 32,
|
|
31
|
+
color: _sharedUi.macOSColors.text.muted
|
|
32
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
33
|
+
style: styles.emptyTitle,
|
|
34
|
+
children: "No network events"
|
|
35
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
36
|
+
style: styles.emptyText,
|
|
37
|
+
children: isEnabled ? "Network requests will appear here" : "Enable interception to start capturing"
|
|
38
|
+
})]
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const keyExtractor = item => item.id;
|
|
42
|
+
function NetworkListScreen({
|
|
43
|
+
visible,
|
|
44
|
+
paused,
|
|
45
|
+
filter,
|
|
46
|
+
ignoredPatterns,
|
|
47
|
+
isPro,
|
|
48
|
+
searchText,
|
|
49
|
+
onEventPress,
|
|
50
|
+
onUpgradePress
|
|
51
|
+
}) {
|
|
52
|
+
const {
|
|
53
|
+
visibleEvents,
|
|
54
|
+
hasLockedEvents,
|
|
55
|
+
lockedEventCount
|
|
56
|
+
} = (0, _useNetworkEventList.useNetworkEventList)({
|
|
57
|
+
filter,
|
|
58
|
+
ignoredPatterns,
|
|
59
|
+
paused: paused || !visible,
|
|
60
|
+
isPro
|
|
61
|
+
});
|
|
62
|
+
if (!visible) return null;
|
|
63
|
+
const isEnabled = !paused;
|
|
64
|
+
const renderItem = ({
|
|
65
|
+
item
|
|
66
|
+
}) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_NetworkEventItemCompact.NetworkEventItemCompact, {
|
|
67
|
+
event: item,
|
|
68
|
+
onPress: onEventPress
|
|
69
|
+
});
|
|
70
|
+
const renderListFooter = () => {
|
|
71
|
+
if (!hasLockedEvents) return null;
|
|
72
|
+
const isSearching = searchText.length > 0;
|
|
73
|
+
const bannerText = isSearching ? `${lockedEventCount} matching ${lockedEventCount === 1 ? "request" : "requests"} in locked history` : `${lockedEventCount} requests locked`;
|
|
74
|
+
const subtextMessage = isSearching ? "Upgrade to Pro to search your full request history" : "Upgrade to Pro to unlock full request history";
|
|
75
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
|
|
76
|
+
style: styles.limitBanner,
|
|
77
|
+
onPress: onUpgradePress,
|
|
78
|
+
activeOpacity: 0.8,
|
|
79
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
80
|
+
style: styles.limitBannerContent,
|
|
81
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
82
|
+
style: styles.limitBannerLeft,
|
|
83
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Zap, {
|
|
84
|
+
size: 16,
|
|
85
|
+
color: "#20C997"
|
|
86
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
87
|
+
style: styles.limitBannerText,
|
|
88
|
+
children: bannerText
|
|
89
|
+
})]
|
|
90
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ProBadge, {})]
|
|
91
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
92
|
+
style: styles.limitBannerSubtext,
|
|
93
|
+
children: subtextMessage
|
|
94
|
+
})]
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
98
|
+
children: [!isEnabled ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
99
|
+
style: styles.disabledBanner,
|
|
100
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Power, {
|
|
101
|
+
size: 14,
|
|
102
|
+
color: _sharedUi.macOSColors.semantic.warning
|
|
103
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
104
|
+
style: styles.disabledText,
|
|
105
|
+
children: "Network interception is disabled"
|
|
106
|
+
})]
|
|
107
|
+
}) : null, visibleEvents.length > 0 || hasLockedEvents ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.FlatList, {
|
|
108
|
+
data: visibleEvents,
|
|
109
|
+
renderItem: renderItem,
|
|
110
|
+
keyExtractor: keyExtractor,
|
|
111
|
+
contentContainerStyle: styles.listContent,
|
|
112
|
+
showsVerticalScrollIndicator: true,
|
|
113
|
+
removeClippedSubviews: true,
|
|
114
|
+
onEndReachedThreshold: 0.8
|
|
115
|
+
// The list OWNS scrolling (NetworkModal passes
|
|
116
|
+
// disableScrollWrapper while this screen is visible) so FlatList
|
|
117
|
+
// virtualization actually works. The previous shipped config —
|
|
118
|
+
// scrollEnabled=false inside JsModal's ScrollView — mounted ALL
|
|
119
|
+
// rows: at the store's 500-event cap that measured 17 JS FPS /
|
|
120
|
+
// 133% CPU / 1.4GB vs ~53 JS FPS / 52% / 360MB virtualized
|
|
121
|
+
// (buoy-optimize batch 2026-07-06, batch-1783396983529-6l7w).
|
|
122
|
+
// Window/batch values are the batch's "tuned" winner.
|
|
123
|
+
,
|
|
124
|
+
scrollEnabled: true,
|
|
125
|
+
initialNumToRender: 12,
|
|
126
|
+
maxToRenderPerBatch: 8,
|
|
127
|
+
windowSize: 5,
|
|
128
|
+
updateCellsBatchingPeriod: 50,
|
|
129
|
+
"sentry-label": "ignore network events list",
|
|
130
|
+
ListFooterComponent: renderListFooter
|
|
131
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(EmptyState, {
|
|
132
|
+
isEnabled: isEnabled
|
|
133
|
+
})]
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
const styles = _reactNative.StyleSheet.create({
|
|
137
|
+
disabledBanner: {
|
|
138
|
+
flexDirection: "row",
|
|
139
|
+
alignItems: "center",
|
|
140
|
+
gap: 8,
|
|
141
|
+
padding: 10,
|
|
142
|
+
marginHorizontal: 12,
|
|
143
|
+
marginTop: 8,
|
|
144
|
+
backgroundColor: _sharedUi.macOSColors.semantic.warningBackground,
|
|
145
|
+
borderRadius: 8,
|
|
146
|
+
borderWidth: 1,
|
|
147
|
+
borderColor: _sharedUi.macOSColors.semantic.warning + "20"
|
|
148
|
+
},
|
|
149
|
+
disabledText: {
|
|
150
|
+
color: _sharedUi.macOSColors.semantic.warning,
|
|
151
|
+
fontSize: 11,
|
|
152
|
+
flex: 1
|
|
153
|
+
},
|
|
154
|
+
listContent: {
|
|
155
|
+
paddingTop: 8
|
|
156
|
+
},
|
|
157
|
+
emptyState: {
|
|
158
|
+
alignItems: "center",
|
|
159
|
+
paddingVertical: 40
|
|
160
|
+
},
|
|
161
|
+
emptyTitle: {
|
|
162
|
+
color: _sharedUi.macOSColors.text.primary,
|
|
163
|
+
fontSize: 14,
|
|
164
|
+
fontWeight: "600",
|
|
165
|
+
marginTop: 12,
|
|
166
|
+
marginBottom: 6
|
|
167
|
+
},
|
|
168
|
+
emptyText: {
|
|
169
|
+
color: _sharedUi.macOSColors.text.muted,
|
|
170
|
+
fontSize: 12,
|
|
171
|
+
textAlign: "center"
|
|
172
|
+
},
|
|
173
|
+
limitBanner: {
|
|
174
|
+
backgroundColor: "#1A1A1A",
|
|
175
|
+
borderRadius: 10,
|
|
176
|
+
borderWidth: 1,
|
|
177
|
+
borderColor: "#20C997" + "40",
|
|
178
|
+
padding: 12,
|
|
179
|
+
marginHorizontal: 12,
|
|
180
|
+
marginTop: 12,
|
|
181
|
+
marginBottom: 34
|
|
182
|
+
},
|
|
183
|
+
limitBannerContent: {
|
|
184
|
+
flexDirection: "row",
|
|
185
|
+
alignItems: "center",
|
|
186
|
+
justifyContent: "space-between",
|
|
187
|
+
marginBottom: 4
|
|
188
|
+
},
|
|
189
|
+
limitBannerLeft: {
|
|
190
|
+
flexDirection: "row",
|
|
191
|
+
alignItems: "center",
|
|
192
|
+
gap: 8
|
|
193
|
+
},
|
|
194
|
+
limitBannerText: {
|
|
195
|
+
color: "#E0E0E0",
|
|
196
|
+
fontSize: 13,
|
|
197
|
+
fontWeight: "600"
|
|
198
|
+
},
|
|
199
|
+
limitBannerSubtext: {
|
|
200
|
+
color: "#888888",
|
|
201
|
+
fontSize: 11
|
|
202
|
+
}
|
|
203
|
+
});
|