@buoy-gg/network 7.0.12 → 7.0.13
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 +13 -0
- package/lib/commonjs/network/components/NetworkDetailStepper.js +26 -14
- package/lib/commonjs/network/components/NetworkEventDetailView.js +56 -11
- package/lib/commonjs/network/components/NetworkHeaderLive.js +6 -1
- package/lib/commonjs/network/components/NetworkListScreen.js +9 -4
- package/lib/commonjs/network/components/NetworkModal.js +64 -24
- package/lib/commonjs/network/hooks/useNetworkSelection.js +53 -0
- package/lib/commonjs/network/utils/networkSelectionStore.js +193 -0
- package/lib/module/index.js +4 -0
- package/lib/module/network/components/NetworkDetailStepper.js +27 -15
- package/lib/module/network/components/NetworkEventDetailView.js +56 -11
- package/lib/module/network/components/NetworkHeaderLive.js +6 -1
- package/lib/module/network/components/NetworkListScreen.js +9 -4
- package/lib/module/network/components/NetworkModal.js +64 -24
- package/lib/module/network/hooks/useNetworkSelection.js +46 -0
- package/lib/module/network/utils/networkSelectionStore.js +188 -0
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkDetailStepper.d.ts +8 -5
- package/lib/typescript/network/components/NetworkDetailStepper.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkEventDetailView.d.ts +36 -1
- package/lib/typescript/network/components/NetworkEventDetailView.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkHeaderLive.d.ts +5 -2
- package/lib/typescript/network/components/NetworkHeaderLive.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkListScreen.d.ts.map +1 -1
- package/lib/typescript/network/components/NetworkModal.d.ts.map +1 -1
- package/lib/typescript/network/hooks/useNetworkSelection.d.ts +26 -0
- package/lib/typescript/network/hooks/useNetworkSelection.d.ts.map +1 -0
- package/lib/typescript/network/utils/networkSelectionStore.d.ts +117 -0
- package/lib/typescript/network/utils/networkSelectionStore.d.ts.map +1 -0
- package/package.json +2 -2
|
@@ -13,6 +13,8 @@ import { NetworkSavedScreen } from "./NetworkSavedScreen";
|
|
|
13
13
|
import { NetworkDetailStepper } from "./NetworkDetailStepper";
|
|
14
14
|
import { useNetworkEventList } from "../hooks/useNetworkEventList";
|
|
15
15
|
import { useLiveNetworkEvent } from "../hooks/useLiveNetworkEvent";
|
|
16
|
+
import { useHasNetworkSelection, useNetworkBodyEventId, useSelectedNetworkEventId } from "../hooks/useNetworkSelection";
|
|
17
|
+
import { networkSelectionStore } from "../utils/networkSelectionStore";
|
|
16
18
|
import { FREE_TIER_SAVED_LIMIT, MAX_SAVED } from "../utils/networkSavedStore";
|
|
17
19
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
18
20
|
// ============================================================================
|
|
@@ -24,7 +26,6 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
24
26
|
|
|
25
27
|
function NetworkDetailScreen({
|
|
26
28
|
visible,
|
|
27
|
-
eventId,
|
|
28
29
|
paused,
|
|
29
30
|
ignoredPatternValues,
|
|
30
31
|
onTogglePattern,
|
|
@@ -33,34 +34,66 @@ function NetworkDetailScreen({
|
|
|
33
34
|
ignoredPatterns,
|
|
34
35
|
fromSaved,
|
|
35
36
|
savedSearch,
|
|
36
|
-
onSelectEvent,
|
|
37
37
|
applySafeAreaInset
|
|
38
38
|
}) {
|
|
39
|
+
/**
|
|
40
|
+
* The detail renders at the IMMEDIATE selection — every step, no delay.
|
|
41
|
+
* Measured, 100 steps at a 150ms tap interval over 64KB payloads:
|
|
42
|
+
*
|
|
43
|
+
* whole detail, bodies included ... 647ms/step
|
|
44
|
+
* detail with no bodies at all .... 43ms/step (over the 150ms tap gap)
|
|
45
|
+
*
|
|
46
|
+
* So ~93% of a step is the request/response BODY trees, and everything else
|
|
47
|
+
* — status, URL breakdown, timing, header counts — is affordable at any tap
|
|
48
|
+
* rate. Deferring the whole detail (which is what this used to do) therefore
|
|
49
|
+
* bought nothing a user could feel: it made the cheap 43ms wait alongside
|
|
50
|
+
* the expensive 450ms instead of splitting them.
|
|
51
|
+
*
|
|
52
|
+
* So only the expensive part waits. `bodyId` trails `selectedId` by the
|
|
53
|
+
* settle window, so `heavyReady` is false exactly while the cursor is still
|
|
54
|
+
* moving — the JSON trees are skipped during a burst and mount once you land.
|
|
55
|
+
*/
|
|
56
|
+
const eventId = useSelectedNetworkEventId();
|
|
57
|
+
const settledId = useNetworkBodyEventId();
|
|
58
|
+
const heavyReady = eventId != null && eventId === settledId;
|
|
39
59
|
// Resolve the ID to the live event so a request opened while pending picks
|
|
40
60
|
// up its response/error in place instead of freezing on the snapshot taken
|
|
41
61
|
// when the row was tapped.
|
|
42
62
|
const event = useLiveNetworkEvent(eventId, {
|
|
43
63
|
paused: paused || !visible
|
|
44
64
|
});
|
|
45
|
-
|
|
65
|
+
|
|
66
|
+
// Instrumentation for the benchmark harness. Gated on `heavyReady` on
|
|
67
|
+
// purpose: the cheap chrome now renders on every step, so reporting here
|
|
68
|
+
// unconditionally would make `bodyMs` a duplicate of `cursorMs`. What the
|
|
69
|
+
// harness needs to know is when the expensive TREES landed.
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (visible && event && heavyReady) {
|
|
72
|
+
networkSelectionStore.reportRendered(event.id);
|
|
73
|
+
}
|
|
74
|
+
}, [visible, event, heavyReady]);
|
|
75
|
+
if (!visible) return null;
|
|
46
76
|
// Column: the detail body scrolls, the stepper stays pinned under it. The
|
|
47
77
|
// body owns its scroll here (see `bodyOwnsScroll`) — inside JsModal's
|
|
48
|
-
// wrapper the footer would scroll away with the content.
|
|
78
|
+
// wrapper the footer would scroll away with the content. The spacer keeps
|
|
79
|
+
// the footer at the bottom on the rare frame where the ID resolves to
|
|
80
|
+
// nothing (cleared, aged out, and not saved).
|
|
49
81
|
return /*#__PURE__*/_jsxs(View, {
|
|
50
82
|
style: styles.detailRoot,
|
|
51
|
-
children: [/*#__PURE__*/_jsx(NetworkEventDetailView, {
|
|
83
|
+
children: [event ? /*#__PURE__*/_jsx(NetworkEventDetailView, {
|
|
52
84
|
event: event,
|
|
53
85
|
ignoredPatterns: ignoredPatternValues,
|
|
54
|
-
onTogglePattern: onTogglePattern
|
|
86
|
+
onTogglePattern: onTogglePattern,
|
|
87
|
+
showBodies: heavyReady
|
|
88
|
+
}) : /*#__PURE__*/_jsx(View, {
|
|
89
|
+
style: styles.detailBodyFallback
|
|
55
90
|
}), /*#__PURE__*/_jsx(NetworkDetailStepper, {
|
|
56
|
-
eventId: event.id,
|
|
57
91
|
paused: paused,
|
|
58
92
|
filter: filter,
|
|
59
93
|
ignoredPatterns: ignoredPatterns,
|
|
60
94
|
isPro: isPro,
|
|
61
95
|
fromSaved: fromSaved,
|
|
62
96
|
savedSearch: savedSearch,
|
|
63
|
-
onSelect: onSelectEvent,
|
|
64
97
|
applySafeAreaInset: applySafeAreaInset
|
|
65
98
|
})]
|
|
66
99
|
});
|
|
@@ -155,9 +188,15 @@ function NetworkModalInner({
|
|
|
155
188
|
if (controlledPause) onPausedChange(value);else setPausedState(value);
|
|
156
189
|
};
|
|
157
190
|
const [filter, setFilter] = useState({});
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
191
|
+
/**
|
|
192
|
+
* The cursor lives in `networkSelectionStore`, and the root subscribes to
|
|
193
|
+
* the BOOLEAN only. That is the whole point: this component re-renders
|
|
194
|
+
* `JsModal` (which can never bail — its `header` prop is a fresh object
|
|
195
|
+
* every render), the entire header, and all five screens. None of them care
|
|
196
|
+
* WHICH request is selected, only THAT one is, and this flips twice a
|
|
197
|
+
* session instead of once per tap.
|
|
198
|
+
*/
|
|
199
|
+
const hasSelection = useHasNetworkSelection();
|
|
161
200
|
const [showFilterView, setShowFilterView] = useState(false);
|
|
162
201
|
const [showSavedView, setShowSavedView] = useState(false);
|
|
163
202
|
const [activeTab, setActiveTab] = useState("filters");
|
|
@@ -231,12 +270,12 @@ function NetworkModalInner({
|
|
|
231
270
|
|
|
232
271
|
// Simple handlers - no useCallback needed per rule2
|
|
233
272
|
const handleBack = () => {
|
|
234
|
-
|
|
273
|
+
networkSelectionStore.clear();
|
|
235
274
|
};
|
|
236
275
|
|
|
237
276
|
// Stable identity: this is passed down to the memoized list rows.
|
|
238
277
|
const handleEventPress = useCallback(event => {
|
|
239
|
-
|
|
278
|
+
networkSelectionStore.select(event.id);
|
|
240
279
|
}, []);
|
|
241
280
|
const handleSearch = text => {
|
|
242
281
|
setSearchText(text);
|
|
@@ -294,7 +333,7 @@ function NetworkModalInner({
|
|
|
294
333
|
}
|
|
295
334
|
|
|
296
335
|
// Event detail view header
|
|
297
|
-
if (
|
|
336
|
+
if (hasSelection) {
|
|
298
337
|
return /*#__PURE__*/_jsxs(ModalHeader, {
|
|
299
338
|
children: [/*#__PURE__*/_jsx(ModalHeader.Navigation, {
|
|
300
339
|
onBack: handleBack
|
|
@@ -303,7 +342,6 @@ function NetworkModalInner({
|
|
|
303
342
|
centered: true
|
|
304
343
|
}), /*#__PURE__*/_jsx(ModalHeader.Actions, {
|
|
305
344
|
children: /*#__PURE__*/_jsx(NetworkDetailPinActions, {
|
|
306
|
-
eventId: selectedEventId,
|
|
307
345
|
isPro: isPro,
|
|
308
346
|
onUpgradePress: () => showUpgrade("saved")
|
|
309
347
|
})
|
|
@@ -417,13 +455,12 @@ function NetworkModalInner({
|
|
|
417
455
|
// same deal. The DETAIL screen owns it as well, so its prev/next footer
|
|
418
456
|
// stays pinned instead of scrolling away with the body. Filter/copy still
|
|
419
457
|
// use the wrapper.
|
|
420
|
-
const bodyOwnsScroll =
|
|
458
|
+
const bodyOwnsScroll = hasSelection || !showFilterView;
|
|
421
459
|
const body = /*#__PURE__*/_jsxs(_Fragment, {
|
|
422
460
|
children: [/*#__PURE__*/_jsxs(View, {
|
|
423
461
|
style: styles.container,
|
|
424
462
|
children: [/*#__PURE__*/_jsx(NetworkDetailScreen, {
|
|
425
|
-
visible:
|
|
426
|
-
eventId: selectedEventId,
|
|
463
|
+
visible: hasSelection,
|
|
427
464
|
paused: paused,
|
|
428
465
|
ignoredPatternValues: ignoredPatternValues,
|
|
429
466
|
onTogglePattern: toggleIgnoredPattern,
|
|
@@ -434,13 +471,12 @@ function NetworkModalInner({
|
|
|
434
471
|
// the user came from — and therefore which one to step through.
|
|
435
472
|
,
|
|
436
473
|
fromSaved: showSavedView,
|
|
437
|
-
savedSearch: savedSearch
|
|
438
|
-
onSelectEvent: setSelectedEventId
|
|
474
|
+
savedSearch: savedSearch
|
|
439
475
|
// Inline (desktop panel) has no home indicator to clear.
|
|
440
476
|
,
|
|
441
477
|
applySafeAreaInset: !inline && modalMode === "bottomSheet"
|
|
442
478
|
}), /*#__PURE__*/_jsx(NetworkFilterScreen, {
|
|
443
|
-
visible: showFilterView && activeTab === "filters" && !
|
|
479
|
+
visible: showFilterView && activeTab === "filters" && !hasSelection,
|
|
444
480
|
paused: paused,
|
|
445
481
|
isPro: isPro,
|
|
446
482
|
filter: filter,
|
|
@@ -450,7 +486,7 @@ function NetworkModalInner({
|
|
|
450
486
|
onAddPattern: addIgnoredPattern,
|
|
451
487
|
onTogglePatternMode: toggleIgnoredPatternMode
|
|
452
488
|
}), /*#__PURE__*/_jsx(NetworkCopyScreen, {
|
|
453
|
-
visible: showFilterView && activeTab === "copy" && !
|
|
489
|
+
visible: showFilterView && activeTab === "copy" && !hasSelection,
|
|
454
490
|
paused: paused,
|
|
455
491
|
isPro: isPro,
|
|
456
492
|
filter: filter,
|
|
@@ -458,7 +494,7 @@ function NetworkModalInner({
|
|
|
458
494
|
settings: copySettings,
|
|
459
495
|
onSettingsChange: setCopySettings
|
|
460
496
|
}), /*#__PURE__*/_jsx(NetworkSavedScreen, {
|
|
461
|
-
visible: showSavedView &&
|
|
497
|
+
visible: showSavedView && !hasSelection && !showFilterView,
|
|
462
498
|
isPro: isPro,
|
|
463
499
|
copySettings: copySettings,
|
|
464
500
|
search: savedSearch,
|
|
@@ -466,7 +502,7 @@ function NetworkModalInner({
|
|
|
466
502
|
onEventPress: handleEventPress,
|
|
467
503
|
onUpgradePress: () => showUpgrade("saved")
|
|
468
504
|
}), /*#__PURE__*/_jsx(NetworkListScreen, {
|
|
469
|
-
visible:
|
|
505
|
+
visible: !hasSelection && !showFilterView && !showSavedView,
|
|
470
506
|
paused: paused,
|
|
471
507
|
filter: filter,
|
|
472
508
|
ignoredPatterns: ignoredPatterns,
|
|
@@ -524,6 +560,10 @@ const styles = StyleSheet.create({
|
|
|
524
560
|
flex: 1,
|
|
525
561
|
minHeight: 0
|
|
526
562
|
},
|
|
563
|
+
/** Holds the footer down when the selected ID resolves to no event. */
|
|
564
|
+
detailBodyFallback: {
|
|
565
|
+
flex: 1
|
|
566
|
+
},
|
|
527
567
|
/** Inline shell — fills the host panel; `minHeight: 0` so the list's own
|
|
528
568
|
* FlatList can scroll instead of stretching the column. */
|
|
529
569
|
inlineRoot: {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Narrow subscriptions to the prev/next cursor (`networkSelectionStore`).
|
|
5
|
+
*
|
|
6
|
+
* One hook per consumer, each returning the SMALLEST value that consumer
|
|
7
|
+
* renders — that is the whole point of moving the cursor into a store. The
|
|
8
|
+
* modal root takes `useHasNetworkSelection()` (a boolean that flips twice a
|
|
9
|
+
* session), so a step no longer re-renders `JsModal`, the header and all five
|
|
10
|
+
* screens; the footer and the body take the parts that actually change.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { useSyncExternalStore } from "react";
|
|
14
|
+
import { networkSelectionStore } from "../utils/networkSelectionStore";
|
|
15
|
+
const subscribe = onChange => networkSelectionStore.subscribe(onChange);
|
|
16
|
+
const getState = () => networkSelectionStore.getState();
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Is ANYTHING selected — i.e. is the detail screen up?
|
|
20
|
+
*
|
|
21
|
+
* `useSyncExternalStore` compares the selector's result, so the root only
|
|
22
|
+
* re-renders when this boolean flips, not on every step.
|
|
23
|
+
*/
|
|
24
|
+
export function useHasNetworkSelection() {
|
|
25
|
+
return useSyncExternalStore(subscribe, () => getState().selectedId != null, () => getState().selectedId != null);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Where the USER is. Moves synchronously on tap — footer, header pin actions. */
|
|
29
|
+
export function useSelectedNetworkEventId() {
|
|
30
|
+
return useSyncExternalStore(subscribe, () => getState().selectedId, () => getState().selectedId);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** What the BODY may render. Lags `selectedId` — see networkSelectionStore. */
|
|
34
|
+
export function useNetworkBodyEventId() {
|
|
35
|
+
return useSyncExternalStore(subscribe, () => getState().bodyId, () => getState().bodyId);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Cursor position + list length, for the "REQUEST n OF m" counter. */
|
|
39
|
+
export function useNetworkCursor() {
|
|
40
|
+
const index = useSyncExternalStore(subscribe, () => getState().index, () => getState().index);
|
|
41
|
+
const total = useSyncExternalStore(subscribe, () => getState().ids.length, () => getState().ids.length);
|
|
42
|
+
return {
|
|
43
|
+
index,
|
|
44
|
+
total
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Which request the detail view is showing, and where it sits in the list —
|
|
5
|
+
* the prev/next CURSOR.
|
|
6
|
+
*
|
|
7
|
+
* ## Why this is a store and not `useState` in the modal
|
|
8
|
+
*
|
|
9
|
+
* It used to be `useState` in `NetworkModalInner`. Every tap on Previous/Next
|
|
10
|
+
* therefore dirtied the modal ROOT, which re-rendered `JsModal` (1833 lines,
|
|
11
|
+
* nine useStates, animations/gestures/persistence — and it can never bail out,
|
|
12
|
+
* because its `header` prop is a fresh object literal every render), the whole
|
|
13
|
+
* header, and all five screens. None of them care which request is selected;
|
|
14
|
+
* they only care THAT one is.
|
|
15
|
+
*
|
|
16
|
+
* This is the same argument `NetworkModal`'s own header comment already makes
|
|
17
|
+
* about network events ("with the subscription here, 15 events re-rendered
|
|
18
|
+
* JsModal + every header button 15×"). Stepping is that same class of
|
|
19
|
+
* high-frequency churn, and it was sitting in the same wrong place. Now the
|
|
20
|
+
* root subscribes to `hasSelection` — a boolean that flips twice a session —
|
|
21
|
+
* while the footer, the body and the header's pin actions subscribe to the
|
|
22
|
+
* parts they actually render.
|
|
23
|
+
*
|
|
24
|
+
* ## The three cursors
|
|
25
|
+
*
|
|
26
|
+
* `selectedId` is where the USER is. It moves on the tap, synchronously.
|
|
27
|
+
*
|
|
28
|
+
* `bodyId` is what the detail body is allowed to render, and it deliberately
|
|
29
|
+
* LAGS: drawing a request means handing its payload to the virtualized data
|
|
30
|
+
* viewer to flatten, and that has no business blocking the next tap. It
|
|
31
|
+
* catches up only once the selection has held still for `settleMs`, so a
|
|
32
|
+
* burst of taps renders the request you land on and none of the ones you flew
|
|
33
|
+
* past. (`useDeferredValue` was tried here first and is the wrong tool — it
|
|
34
|
+
* lowers the priority of that work without preventing it, so every settled
|
|
35
|
+
* step still queued a flatten and each one delayed the touch behind it.)
|
|
36
|
+
*
|
|
37
|
+
* `renderedId` is what actually made it to the screen, reported by the detail
|
|
38
|
+
* screen after commit. Nothing in the product reads it; it exists so the
|
|
39
|
+
* benchmark harness can measure "time until request N was really painted"
|
|
40
|
+
* separately from "time until the cursor reached N".
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* How long the selection must hold still before the body renders it. Short
|
|
45
|
+
* enough that a single step reads as immediate (the footer and header have
|
|
46
|
+
* already moved), long enough that a burst collapses to one render.
|
|
47
|
+
*/
|
|
48
|
+
export const DEFAULT_SETTLE_MS = 120;
|
|
49
|
+
class NetworkSelectionStore {
|
|
50
|
+
ids = [];
|
|
51
|
+
index = -1;
|
|
52
|
+
selectedId = null;
|
|
53
|
+
bodyId = null;
|
|
54
|
+
renderedId = null;
|
|
55
|
+
settleMs = DEFAULT_SETTLE_MS;
|
|
56
|
+
settleTimer = null;
|
|
57
|
+
subscribers = new Set();
|
|
58
|
+
snapshot = this.buildSnapshot();
|
|
59
|
+
buildSnapshot() {
|
|
60
|
+
return {
|
|
61
|
+
ids: this.ids,
|
|
62
|
+
index: this.index,
|
|
63
|
+
selectedId: this.selectedId,
|
|
64
|
+
bodyId: this.bodyId,
|
|
65
|
+
renderedId: this.renderedId
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* One immutable snapshot per mutation, so subscribers can identity-compare
|
|
71
|
+
* and `useSyncExternalStore` stays happy.
|
|
72
|
+
*/
|
|
73
|
+
emit() {
|
|
74
|
+
this.snapshot = this.buildSnapshot();
|
|
75
|
+
for (const subscriber of this.subscribers) subscriber(this.snapshot);
|
|
76
|
+
}
|
|
77
|
+
getState() {
|
|
78
|
+
return this.snapshot;
|
|
79
|
+
}
|
|
80
|
+
subscribe(subscriber) {
|
|
81
|
+
this.subscribers.add(subscriber);
|
|
82
|
+
return () => {
|
|
83
|
+
this.subscribers.delete(subscriber);
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Publish the navigable order. Called by the stepper whenever its derived
|
|
89
|
+
* list changes (filter, search, pins, new requests arriving).
|
|
90
|
+
*
|
|
91
|
+
* The cursor is re-anchored BY ID, not by position: the live list is
|
|
92
|
+
* newest-first and new requests are prepended, so holding an index would
|
|
93
|
+
* silently slide the selection onto a different request every time one
|
|
94
|
+
* landed.
|
|
95
|
+
*/
|
|
96
|
+
setIds(ids) {
|
|
97
|
+
const sameIds = ids.length === this.ids.length && ids.every((id, i) => id === this.ids[i]);
|
|
98
|
+
if (sameIds) return;
|
|
99
|
+
this.ids = ids;
|
|
100
|
+
this.index = this.selectedId == null ? -1 : ids.indexOf(this.selectedId);
|
|
101
|
+
this.emit();
|
|
102
|
+
}
|
|
103
|
+
select(id) {
|
|
104
|
+
if (id === this.selectedId) return;
|
|
105
|
+
this.selectedId = id;
|
|
106
|
+
this.index = id == null ? -1 : this.ids.indexOf(id);
|
|
107
|
+
this.scheduleBody(id);
|
|
108
|
+
this.emit();
|
|
109
|
+
}
|
|
110
|
+
clear() {
|
|
111
|
+
this.select(null);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Move the cursor. O(1) — the whole reason `index` is stored rather than
|
|
116
|
+
* re-derived with a `findIndex` on every render.
|
|
117
|
+
*
|
|
118
|
+
* Returns the id it landed on, or null if the move was out of bounds (the
|
|
119
|
+
* footer disables its arrows at the ends, but the benchmark driver calls
|
|
120
|
+
* this directly and needs to know when it has run off the end).
|
|
121
|
+
*/
|
|
122
|
+
step(delta) {
|
|
123
|
+
if (this.index < 0) return null;
|
|
124
|
+
const next = this.index + delta;
|
|
125
|
+
if (next < 0 || next >= this.ids.length) return null;
|
|
126
|
+
const id = this.ids[next];
|
|
127
|
+
this.selectedId = id;
|
|
128
|
+
this.index = next;
|
|
129
|
+
this.scheduleBody(id);
|
|
130
|
+
this.emit();
|
|
131
|
+
return id;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Opening and closing skip the settle delay: there is no in-flight render to
|
|
136
|
+
* protect on a null↔id transition, and waiting there just reads as a hang.
|
|
137
|
+
* Only id → id steps debounce.
|
|
138
|
+
*/
|
|
139
|
+
scheduleBody(id) {
|
|
140
|
+
if (this.settleTimer != null) {
|
|
141
|
+
clearTimeout(this.settleTimer);
|
|
142
|
+
this.settleTimer = null;
|
|
143
|
+
}
|
|
144
|
+
if (id == null || this.bodyId == null || this.settleMs <= 0) {
|
|
145
|
+
this.bodyId = id;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
this.settleTimer = setTimeout(() => {
|
|
149
|
+
this.settleTimer = null;
|
|
150
|
+
if (this.bodyId === this.selectedId) return;
|
|
151
|
+
this.bodyId = this.selectedId;
|
|
152
|
+
this.emit();
|
|
153
|
+
}, this.settleMs);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Benchmark toggle — `?settle=0` measures the gate's contribution. */
|
|
157
|
+
setSettleMs(ms) {
|
|
158
|
+
this.settleMs = Math.max(0, ms);
|
|
159
|
+
}
|
|
160
|
+
getSettleMs() {
|
|
161
|
+
return this.settleMs;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Reported by the detail screen after commit. Instrumentation only. */
|
|
165
|
+
reportRendered(id) {
|
|
166
|
+
if (id === this.renderedId) return;
|
|
167
|
+
this.renderedId = id;
|
|
168
|
+
this.emit();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Test/bench helper — drop all cursor state without touching the events. */
|
|
172
|
+
reset() {
|
|
173
|
+
if (this.settleTimer != null) clearTimeout(this.settleTimer);
|
|
174
|
+
this.settleTimer = null;
|
|
175
|
+
this.ids = [];
|
|
176
|
+
this.index = -1;
|
|
177
|
+
this.selectedId = null;
|
|
178
|
+
this.bodyId = null;
|
|
179
|
+
this.renderedId = null;
|
|
180
|
+
this.emit();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
export const networkSelectionStore = new NetworkSelectionStore();
|
|
184
|
+
|
|
185
|
+
/** Convenience for publishing an event list as the navigable order. */
|
|
186
|
+
export function toSelectionIds(events) {
|
|
187
|
+
return events.map(event => event.id);
|
|
188
|
+
}
|
|
@@ -20,6 +20,7 @@ export { useLiveNetworkEvent } from "./network/hooks/useLiveNetworkEvent";
|
|
|
20
20
|
export { useNetworkBadgeCounts } from "./network/hooks/useNetworkBadgeCounts";
|
|
21
21
|
export { useNetworkSaved, type UseNetworkSavedResult, } from "./network/hooks/useNetworkSaved";
|
|
22
22
|
export { splitPinned, type PinnedSplit } from "./network/utils/withPinnedFirst";
|
|
23
|
+
export { networkSelectionStore, DEFAULT_SETTLE_MS, type NetworkSelectionState, } from "./network/utils/networkSelectionStore";
|
|
23
24
|
export { TickProvider, useTickEveryMinute } from "./network/hooks/useTickEveryMinute";
|
|
24
25
|
export { formatBytes, formatDuration, formatHttpStatus, } from "./network/utils/formatting";
|
|
25
26
|
export type { NetworkEvent, NetworkStats, NetworkFilter, NetworkEventStatus, NetworkInsight, IgnoredPattern, IgnoredPatternMatchMode, } from "./network/types";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAOhE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAClF,YAAY,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AACvF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EACL,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,wCAAwC,CAAC;AAKhD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAIpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAI9E,OAAO,EACL,eAAe,EACf,KAAK,qBAAqB,GAC3B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAOhE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAClF,YAAY,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AACvF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EACL,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,wCAAwC,CAAC;AAKhD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAIpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAI9E,OAAO,EACL,eAAe,EACf,KAAK,qBAAqB,GAC3B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAIhF,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,KAAK,qBAAqB,GAC3B,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAKtF,OAAO,EACL,WAAW,EACX,cAAc,EACd,gBAAgB,GACjB,MAAM,4BAA4B,CAAC;AAKpC,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AAQzB,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACxB,YAAY,GACb,MAAM,oBAAoB,CAAC;AAK5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EACL,2BAA2B,EAC3B,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GACzB,MAAM,oCAAoC,CAAC;AAO5C,gBAAgB;AAChB,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE;;;GAGG;AACH,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,UAAU,EACV,SAAS,EACT,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,GACvB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,gBAAgB;AAChB,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,iCAAiC,CAAC"}
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* It is its OWN leaf (rule3) because it is the only part of the detail screen
|
|
6
6
|
* that needs the event LIST. Subscribing here keeps the request stream out of
|
|
7
7
|
* the detail body's render path, which is the expensive subtree (headers,
|
|
8
|
-
* bodies, JSON viewers
|
|
9
|
-
*
|
|
8
|
+
* bodies, JSON viewers): a request arriving re-renders this ~30-node footer
|
|
9
|
+
* and nothing else.
|
|
10
10
|
*
|
|
11
11
|
* The order it steps through is EXACTLY what the list screen renders —
|
|
12
12
|
* `[...pinned, ...rest]` off the same `useNetworkEventList` + `splitPinned`
|
|
@@ -16,12 +16,16 @@
|
|
|
16
16
|
* "request N of <what the list is showing>", never "N of everything ever
|
|
17
17
|
* captured". Detail opened from the Saved screen steps through the SAVED
|
|
18
18
|
* list instead (with its own search applied), for the same reason.
|
|
19
|
+
*
|
|
20
|
+
* This component OWNS the navigable order but not the cursor: it publishes
|
|
21
|
+
* the ordered ids into `networkSelectionStore` and reads the cursor back.
|
|
22
|
+
* That split is what makes stepping O(1) (the store keeps the index, so
|
|
23
|
+
* there is no `findIndex` per render) and what lets the benchmark harness
|
|
24
|
+
* drive `step(+1)` directly without going through the UI.
|
|
19
25
|
*/
|
|
20
26
|
import { type IgnoredPattern } from "@buoy-gg/shared-ui";
|
|
21
27
|
import type { NetworkFilter } from "../types";
|
|
22
28
|
interface NetworkDetailStepperProps {
|
|
23
|
-
/** The request currently on screen. */
|
|
24
|
-
eventId: string;
|
|
25
29
|
paused: boolean;
|
|
26
30
|
filter: NetworkFilter;
|
|
27
31
|
ignoredPatterns: readonly IgnoredPattern[];
|
|
@@ -30,7 +34,6 @@ interface NetworkDetailStepperProps {
|
|
|
30
34
|
fromSaved: boolean;
|
|
31
35
|
/** The Saved screen's search box (lifted to the modal root so both agree). */
|
|
32
36
|
savedSearch: string;
|
|
33
|
-
onSelect: (eventId: string) => void;
|
|
34
37
|
/** Only a bottom sheet sits on the home indicator — see NetworkModal. */
|
|
35
38
|
applySafeAreaInset: boolean;
|
|
36
39
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NetworkDetailStepper.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkDetailStepper.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"NetworkDetailStepper.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkDetailStepper.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,KAAK,EAAgB,aAAa,EAAE,MAAM,UAAU,CAAC;AAW5D,UAAU,yBAAyB;IACjC,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,aAAa,CAAC;IACtB,eAAe,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C,KAAK,EAAE,OAAO,CAAC;IACf,wEAAwE;IACxE,SAAS,EAAE,OAAO,CAAC;IACnB,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAMD,eAAO,MAAM,oBAAoB,iEAiE/B,CAAC"}
|
|
@@ -3,11 +3,46 @@ interface NetworkEventDetailViewProps {
|
|
|
3
3
|
event: NetworkEvent;
|
|
4
4
|
ignoredPatterns?: Set<string>;
|
|
5
5
|
onTogglePattern?: (pattern: string) => void;
|
|
6
|
+
/**
|
|
7
|
+
* Mount the request/response BODY trees.
|
|
8
|
+
*
|
|
9
|
+
* False while the prev/next cursor is still moving. The body trees are ~93%
|
|
10
|
+
* of a step's cost (measured: 647ms/step with them, 43ms without, at a 150ms
|
|
11
|
+
* tap interval over 64KB payloads), and they are the only part of this view
|
|
12
|
+
* that can't keep up with a thumb. Everything else renders on every step.
|
|
13
|
+
*
|
|
14
|
+
* Note the mechanism: this doesn't skip the tree so much as ensure it gets
|
|
15
|
+
* unmounted by the next tap before its chunked flatten completes. Stepping
|
|
16
|
+
* therefore costs ~14ms/step instead of ~500ms, and exactly one tree
|
|
17
|
+
* flattens for real — the one you stop on.
|
|
18
|
+
*
|
|
19
|
+
* Defaults true so the Events tool — which renders this same view without a
|
|
20
|
+
* cursor — is unaffected.
|
|
21
|
+
*/
|
|
22
|
+
showBodies?: boolean;
|
|
6
23
|
}
|
|
7
24
|
/**
|
|
8
25
|
* Detailed inspector rendering request/response metadata for a single network event. Designed to
|
|
9
26
|
* mimic Sentry’s layout with collapsible sections, diff views, and quick copy affordances.
|
|
27
|
+
*
|
|
28
|
+
* MEMOIZED with profiling evidence, not by reflex. Two render paths hit this
|
|
29
|
+
* subtree with unchanged props:
|
|
30
|
+
*
|
|
31
|
+
* 1. `useLiveNetworkEvent` re-subscribes when the selected ID changes, and
|
|
32
|
+
* `subscribeToEvents` replays immediately — so every step through the
|
|
33
|
+
* prev/next footer committed the SAME event object twice, rendering this
|
|
34
|
+
* whole tree (expanded DataViewer included) a second time for nothing.
|
|
35
|
+
* 2. Any modal-root state change (header search, mode toggle) re-renders the
|
|
36
|
+
* shell, and the detail body has nothing to do with it.
|
|
37
|
+
*
|
|
38
|
+
* Both props besides `event` are stable by construction — `values` is memoized
|
|
39
|
+
* in `useIgnoredPatterns` and `toggle` is module-scope there — so the bail-out
|
|
40
|
+
* actually fires.
|
|
41
|
+
*
|
|
42
|
+
* Measured baseline (js-top, example-dev-build, 5 steps over 109–324KB
|
|
43
|
+
* pokeapi responses): 307ms of React work across 5 steps ≈ 61ms of JS per
|
|
44
|
+
* step. Case 1 alone is half of that, since it re-ran the whole body render.
|
|
10
45
|
*/
|
|
11
|
-
export declare
|
|
46
|
+
export declare const NetworkEventDetailView: import("react").NamedExoticComponent<NetworkEventDetailViewProps>;
|
|
12
47
|
export {};
|
|
13
48
|
//# sourceMappingURL=NetworkEventDetailView.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NetworkEventDetailView.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkEventDetailView.tsx"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"NetworkEventDetailView.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkEventDetailView.tsx"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,UAAU,CAAC;AA0B7D,UAAU,2BAA2B;IACnC,KAAK,EAAE,YAAY,CAAC;IACpB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C;;;;;;;;;;;;;;;OAeG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AA4QD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,sBAAsB,mEA6fjC,CAAC"}
|
|
@@ -43,7 +43,6 @@ interface NetworkSavedButtonProps {
|
|
|
43
43
|
*/
|
|
44
44
|
export declare function NetworkSavedButton({ onPress }: NetworkSavedButtonProps): import("react").JSX.Element;
|
|
45
45
|
interface NetworkDetailPinActionsProps {
|
|
46
|
-
eventId: string | null;
|
|
47
46
|
isPro: boolean;
|
|
48
47
|
onUpgradePress: () => void;
|
|
49
48
|
}
|
|
@@ -60,8 +59,12 @@ interface NetworkDetailPinActionsProps {
|
|
|
60
59
|
* The event object is resolved at PRESS time rather than held as a prop: the
|
|
61
60
|
* live store has the fresh copy while the request is in flight, and the saved
|
|
62
61
|
* store is the only copy once it has been cleared or aged out.
|
|
62
|
+
*
|
|
63
|
+
* The selected id is SUBSCRIBED here rather than passed down, so stepping
|
|
64
|
+
* through requests doesn't have to re-render the modal root just to rebuild
|
|
65
|
+
* the header around a new prop.
|
|
63
66
|
*/
|
|
64
|
-
export declare function NetworkDetailPinActions({
|
|
67
|
+
export declare function NetworkDetailPinActions({ isPro, onUpgradePress, }: NetworkDetailPinActionsProps): import("react").JSX.Element;
|
|
65
68
|
interface NetworkHeaderCopyButtonProps {
|
|
66
69
|
isPro: boolean;
|
|
67
70
|
filter: NetworkFilter;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NetworkHeaderLive.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkHeaderLive.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAUL,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"NetworkHeaderLive.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkHeaderLive.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAUL,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAsB9D,KAAK,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAEpD,UAAU,wBAAwB;IAChC,YAAY,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;IACtC,cAAc,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IAC/C,eAAe,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,yEAAyE;AACzE,wBAAgB,mBAAmB,CAAC,EAClC,YAAY,EACZ,cAAc,EACd,eAAe,EACf,MAAM,GACP,EAAE,wBAAwB,+BA4D1B;AAED,UAAU,uBAAuB;IAC/B,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;iDAEiD;AACjD,wBAAgB,kBAAkB,CAAC,EAAE,MAAM,EAAE,EAAE,uBAAuB,+BAqBrE;AAED,UAAU,uBAAuB;IAC/B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,OAAO,EAAE,EAAE,uBAAuB,+BAwBtE;AAED,UAAU,4BAA4B;IACpC,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,KAAK,EACL,cAAc,GACf,EAAE,4BAA4B,+BAuF9B;AAED,UAAU,4BAA4B;IACpC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,aAAa,CAAC;IACtB,eAAe,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C,YAAY,EAAE,YAAY,CAAC;IAC3B,MAAM,EAAE,OAAO,CAAC;IAChB,cAAc,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,KAAK,EACL,MAAM,EACN,eAAe,EACf,YAAY,EACZ,MAAM,EACN,cAAc,GACf,EAAE,4BAA4B,+BAiD9B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NetworkListScreen.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkListScreen.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AA4E5D,UAAU,sBAAsB;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,aAAa,CAAC;IACtB,eAAe,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,YAAY,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAC5C,cAAc,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,MAAM,EACN,MAAM,EACN,eAAe,EACf,KAAK,EACL,UAAU,EACV,YAAY,EACZ,cAAc,GACf,EAAE,sBAAsB,
|
|
1
|
+
{"version":3,"file":"NetworkListScreen.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkListScreen.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AA4E5D,UAAU,sBAAsB;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,aAAa,CAAC;IACtB,eAAe,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,YAAY,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAC5C,cAAc,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,MAAM,EACN,MAAM,EACN,eAAe,EACf,KAAK,EACL,UAAU,EACV,YAAY,EACZ,cAAc,GACf,EAAE,sBAAsB,sCAuHxB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NetworkModal.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkModal.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"NetworkModal.d.ts","sourceRoot":"","sources":["../../../../src/network/components/NetworkModal.tsx"],"names":[],"mappings":"AAwDA,UAAU,iBAAiB;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,KAAK,IAAI,CAAC;IACvC,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CAC5C;AA6rBD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,+BAMpD;AAED,MAAM,WAAW,oBAAoB;IACnC,+EAA+E;IAC/E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CAC5C;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,KAAK,GAAE,oBAAyB,+BAa/D"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Narrow subscriptions to the prev/next cursor (`networkSelectionStore`).
|
|
3
|
+
*
|
|
4
|
+
* One hook per consumer, each returning the SMALLEST value that consumer
|
|
5
|
+
* renders — that is the whole point of moving the cursor into a store. The
|
|
6
|
+
* modal root takes `useHasNetworkSelection()` (a boolean that flips twice a
|
|
7
|
+
* session), so a step no longer re-renders `JsModal`, the header and all five
|
|
8
|
+
* screens; the footer and the body take the parts that actually change.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Is ANYTHING selected — i.e. is the detail screen up?
|
|
12
|
+
*
|
|
13
|
+
* `useSyncExternalStore` compares the selector's result, so the root only
|
|
14
|
+
* re-renders when this boolean flips, not on every step.
|
|
15
|
+
*/
|
|
16
|
+
export declare function useHasNetworkSelection(): boolean;
|
|
17
|
+
/** Where the USER is. Moves synchronously on tap — footer, header pin actions. */
|
|
18
|
+
export declare function useSelectedNetworkEventId(): string | null;
|
|
19
|
+
/** What the BODY may render. Lags `selectedId` — see networkSelectionStore. */
|
|
20
|
+
export declare function useNetworkBodyEventId(): string | null;
|
|
21
|
+
/** Cursor position + list length, for the "REQUEST n OF m" counter. */
|
|
22
|
+
export declare function useNetworkCursor(): {
|
|
23
|
+
index: number;
|
|
24
|
+
total: number;
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=useNetworkSelection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useNetworkSelection.d.ts","sourceRoot":"","sources":["../../../../src/network/hooks/useNetworkSelection.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH;;;;;GAKG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAMhD;AAED,kFAAkF;AAClF,wBAAgB,yBAAyB,IAAI,MAAM,GAAG,IAAI,CAMzD;AAED,+EAA+E;AAC/E,wBAAgB,qBAAqB,IAAI,MAAM,GAAG,IAAI,CAMrD;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,IAAI;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAYnE"}
|