Skip to content

Commit 20554c1

Browse files
committed
Make each timeline entry a complex object
Each step can be a group and have an environment separately from the suspense nodes that are part of the group.
1 parent 558ca67 commit 20554c1

5 files changed

Lines changed: 73 additions & 39 deletions

File tree

packages/react-devtools-shared/src/devtools/store.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import type {
5151
ComponentFilter,
5252
ElementType,
5353
SuspenseNode,
54+
SuspenseTimelineStep,
5455
Rect,
5556
} from 'react-devtools-shared/src/frontend/types';
5657
import type {
@@ -895,13 +896,13 @@ export default class Store extends EventEmitter<{
895896
*/
896897
getSuspendableDocumentOrderSuspense(
897898
uniqueSuspendersOnly: boolean,
898-
): $ReadOnlyArray<SuspenseNode['id']> {
899+
): $ReadOnlyArray<SuspenseTimelineStep> {
899900
const roots = this.roots;
900901
if (roots.length === 0) {
901902
return [];
902903
}
903904

904-
const list: SuspenseNode['id'][] = [];
905+
const list: SuspenseTimelineStep[] = [];
905906
for (let i = 0; i < roots.length; i++) {
906907
const rootID = roots[i];
907908
const root = this.getElementByID(rootID);
@@ -914,7 +915,10 @@ export default class Store extends EventEmitter<{
914915
if (suspense !== null) {
915916
if (list.length === 0) {
916917
// start with an arbitrary root that will allow inspection of the Screen
917-
list.push(suspense.id);
918+
list.push({
919+
id: suspense.id,
920+
environment: null,
921+
});
918922
}
919923

920924
const stack = [suspense];
@@ -936,7 +940,10 @@ export default class Store extends EventEmitter<{
936940
// Roots are already included as part of the Screen
937941
current.id !== rootID
938942
) {
939-
list.push(current.id);
943+
list.push({
944+
id: current.id,
945+
environment: null,
946+
});
940947
}
941948
// Add children in reverse order to maintain document order
942949
for (let j = current.children.length - 1; j >= 0; j--) {

packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ function SuspenseRects({
154154
const selected = inspectedElementID === suspenseID;
155155

156156
const hovered =
157-
hoveredTimelineIndex > -1 && timeline[hoveredTimelineIndex] === suspenseID;
157+
hoveredTimelineIndex > -1 &&
158+
timeline[hoveredTimelineIndex].id === suspenseID;
158159

159160
const boundingBox = getBoundingBox(suspense.rects);
160161

packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTimeline.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ function SuspenseTimelineInput() {
3434
const max = timeline.length > 0 ? timeline.length - 1 : 0;
3535

3636
function switchSuspenseNode(nextTimelineIndex: number) {
37-
const nextSelectedSuspenseID = timeline[nextTimelineIndex];
37+
const nextSelectedSuspenseID = timeline[nextTimelineIndex].id;
3838
treeDispatch({
3939
type: 'SELECT_ELEMENT_BY_ID',
4040
payload: nextSelectedSuspenseID,
@@ -54,7 +54,7 @@ function SuspenseTimelineInput() {
5454
}
5555

5656
function handleHoverSegment(hoveredIndex: number) {
57-
const nextSelectedSuspenseID = timeline[hoveredIndex];
57+
const nextSelectedSuspenseID = timeline[hoveredIndex].id;
5858
suspenseTreeDispatch({
5959
type: 'HOVER_TIMELINE_FOR_ID',
6060
payload: nextSelectedSuspenseID,
@@ -68,7 +68,7 @@ function SuspenseTimelineInput() {
6868
}
6969

7070
function skipPrevious() {
71-
const nextSelectedSuspenseID = timeline[timelineIndex - 1];
71+
const nextSelectedSuspenseID = timeline[timelineIndex - 1].id;
7272
treeDispatch({
7373
type: 'SELECT_ELEMENT_BY_ID',
7474
payload: nextSelectedSuspenseID,
@@ -80,7 +80,7 @@ function SuspenseTimelineInput() {
8080
}
8181

8282
function skipForward() {
83-
const nextSelectedSuspenseID = timeline[timelineIndex + 1];
83+
const nextSelectedSuspenseID = timeline[timelineIndex + 1].id;
8484
treeDispatch({
8585
type: 'SELECT_ELEMENT_BY_ID',
8686
payload: nextSelectedSuspenseID,
@@ -106,7 +106,7 @@ function SuspenseTimelineInput() {
106106
// anything suspended in the root. The step after that should have one less
107107
// thing suspended. I.e. the first suspense boundary should be unsuspended
108108
// when it's selected. This also lets you show everything in the last step.
109-
const suspendedSet = timeline.slice(timelineIndex + 1);
109+
const suspendedSet = timeline.slice(timelineIndex + 1).map(step => step.id);
110110
bridge.send('overrideSuspenseMilestone', {
111111
suspendedSet,
112112
});

packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTreeContext.js

Lines changed: 50 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
* @flow
88
*/
99
import type {ReactContext} from 'shared/ReactTypes';
10-
import type {SuspenseNode} from 'react-devtools-shared/src/frontend/types';
10+
import type {
11+
SuspenseNode,
12+
SuspenseTimelineStep,
13+
} from 'react-devtools-shared/src/frontend/types';
1114
import type Store from '../../store';
1215

1316
import * as React from 'react';
@@ -25,7 +28,7 @@ export type SuspenseTreeState = {
2528
lineage: $ReadOnlyArray<SuspenseNode['id']> | null,
2629
roots: $ReadOnlyArray<SuspenseNode['id']>,
2730
selectedSuspenseID: SuspenseNode['id'] | null,
28-
timeline: $ReadOnlyArray<SuspenseNode['id']>,
31+
timeline: $ReadOnlyArray<SuspenseTimelineStep>,
2932
timelineIndex: number | -1,
3033
hoveredTimelineIndex: number | -1,
3134
uniqueSuspendersOnly: boolean,
@@ -49,7 +52,7 @@ type ACTION_SELECT_SUSPENSE_BY_ID = {
4952
type ACTION_SET_SUSPENSE_TIMELINE = {
5053
type: 'SET_SUSPENSE_TIMELINE',
5154
payload: [
52-
$ReadOnlyArray<SuspenseNode['id']>,
55+
$ReadOnlyArray<SuspenseTimelineStep>,
5356
// The next Suspense ID to select in the timeline
5457
SuspenseNode['id'] | null,
5558
// Whether this timeline includes only unique suspenders
@@ -111,7 +114,7 @@ function getInitialState(store: Store): SuspenseTreeState {
111114
store.getSuspendableDocumentOrderSuspense(uniqueSuspendersOnly);
112115
const timelineIndex = timeline.length - 1;
113116
const selectedSuspenseID =
114-
timelineIndex === -1 ? null : timeline[timelineIndex];
117+
timelineIndex === -1 ? null : timeline[timelineIndex].id;
115118
const lineage =
116119
selectedSuspenseID !== null
117120
? store.getSuspenseLineage(selectedSuspenseID)
@@ -164,37 +167,44 @@ function SuspenseTreeContextController({children}: Props): React.Node {
164167
selectedSuspenseID = null;
165168
}
166169

167-
let selectedTimelineID =
168-
state.timeline === null
170+
const selectedTimelineStep =
171+
state.timeline === null || state.timelineIndex === -1
169172
? null
170173
: state.timeline[state.timelineIndex];
171-
while (
172-
selectedTimelineID !== null &&
173-
removedIDs.has(selectedTimelineID)
174-
) {
175-
// $FlowExpectedError[incompatible-type]
176-
selectedTimelineID = removedIDs.get(selectedTimelineID);
174+
let selectedTimelineID: null | number = null;
175+
if (selectedTimelineStep !== null) {
176+
selectedTimelineID = selectedTimelineStep.id;
177+
// $FlowFixMe
178+
while (removedIDs.has(selectedTimelineID)) {
179+
// $FlowFixMe
180+
selectedTimelineID = removedIDs.get(selectedTimelineID);
181+
}
177182
}
178183

179184
// TODO: Handle different timeline modes (e.g. random order)
180185
const nextTimeline = store.getSuspendableDocumentOrderSuspense(
181186
state.uniqueSuspendersOnly,
182187
);
183188

184-
let nextTimelineIndex =
185-
selectedTimelineID === null || nextTimeline.length === 0
186-
? -1
187-
: nextTimeline.indexOf(selectedTimelineID);
189+
let nextTimelineIndex = -1;
190+
if (selectedTimelineID !== null && nextTimeline.length !== 0) {
191+
for (let i = 0; i < nextTimeline.length; i++) {
192+
if (nextTimeline[i].id === selectedTimelineID) {
193+
nextTimelineIndex = i;
194+
break;
195+
}
196+
}
197+
}
188198
if (
189199
nextTimeline.length > 0 &&
190200
(nextTimelineIndex === -1 || state.autoSelect)
191201
) {
192202
nextTimelineIndex = nextTimeline.length - 1;
193-
selectedSuspenseID = nextTimeline[nextTimelineIndex];
203+
selectedSuspenseID = nextTimeline[nextTimelineIndex].id;
194204
}
195205

196206
if (selectedSuspenseID === null && nextTimeline.length > 0) {
197-
selectedSuspenseID = nextTimeline[nextTimeline.length - 1];
207+
selectedSuspenseID = nextTimeline[nextTimeline.length - 1].id;
198208
}
199209

200210
const nextLineage =
@@ -256,12 +266,12 @@ function SuspenseTreeContextController({children}: Props): React.Node {
256266
nextMilestoneIndex = nextTimeline.indexOf(previousMilestoneID);
257267
if (nextMilestoneIndex === -1 && nextTimeline.length > 0) {
258268
nextMilestoneIndex = nextTimeline.length - 1;
259-
nextSelectedSuspenseID = nextTimeline[nextMilestoneIndex];
269+
nextSelectedSuspenseID = nextTimeline[nextMilestoneIndex].id;
260270
nextLineage = store.getSuspenseLineage(nextSelectedSuspenseID);
261271
}
262272
} else if (nextRootID !== null) {
263273
nextMilestoneIndex = nextTimeline.length - 1;
264-
nextSelectedSuspenseID = nextTimeline[nextMilestoneIndex];
274+
nextSelectedSuspenseID = nextTimeline[nextMilestoneIndex].id;
265275
nextLineage = store.getSuspenseLineage(nextSelectedSuspenseID);
266276
}
267277

@@ -276,7 +286,7 @@ function SuspenseTreeContextController({children}: Props): React.Node {
276286
}
277287
case 'SUSPENSE_SET_TIMELINE_INDEX': {
278288
const nextTimelineIndex = action.payload;
279-
const nextSelectedSuspenseID = state.timeline[nextTimelineIndex];
289+
const nextSelectedSuspenseID = state.timeline[nextTimelineIndex].id;
280290
const nextLineage = store.getSuspenseLineage(
281291
nextSelectedSuspenseID,
282292
);
@@ -301,7 +311,7 @@ function SuspenseTreeContextController({children}: Props): React.Node {
301311
) {
302312
return state;
303313
}
304-
const nextSelectedSuspenseID = state.timeline[nextTimelineIndex];
314+
const nextSelectedSuspenseID = state.timeline[nextTimelineIndex].id;
305315
const nextLineage = store.getSuspenseLineage(
306316
nextSelectedSuspenseID,
307317
);
@@ -329,7 +339,7 @@ function SuspenseTreeContextController({children}: Props): React.Node {
329339
) {
330340
// If we're restarting at the end. Then loop around and start again from the beginning.
331341
nextTimelineIndex = 0;
332-
nextSelectedSuspenseID = state.timeline[nextTimelineIndex];
342+
nextSelectedSuspenseID = state.timeline[nextTimelineIndex].id;
333343
nextLineage = store.getSuspenseLineage(nextSelectedSuspenseID);
334344
}
335345

@@ -352,7 +362,7 @@ function SuspenseTreeContextController({children}: Props): React.Node {
352362
if (nextTimelineIndex > state.timeline.length - 1) {
353363
return state;
354364
}
355-
const nextSelectedSuspenseID = state.timeline[nextTimelineIndex];
365+
const nextSelectedSuspenseID = state.timeline[nextTimelineIndex].id;
356366
const nextLineage = store.getSuspenseLineage(
357367
nextSelectedSuspenseID,
358368
);
@@ -369,8 +379,14 @@ function SuspenseTreeContextController({children}: Props): React.Node {
369379
}
370380
case 'TOGGLE_TIMELINE_FOR_ID': {
371381
const suspenseID = action.payload;
372-
const timelineIndexForSuspenseID =
373-
state.timeline.indexOf(suspenseID);
382+
383+
let timelineIndexForSuspenseID = -1;
384+
for (let i = 0; i < state.timeline.length; i++) {
385+
if (state.timeline[i].id === suspenseID) {
386+
timelineIndexForSuspenseID = i;
387+
break;
388+
}
389+
}
374390
if (timelineIndexForSuspenseID === -1) {
375391
// This boundary is no longer in the timeline.
376392
return state;
@@ -387,7 +403,7 @@ function SuspenseTreeContextController({children}: Props): React.Node {
387403
timelineIndexForSuspenseID
388404
: // Otherwise, if we're currently showing it, jump to right before to hide it.
389405
timelineIndexForSuspenseID - 1;
390-
const nextSelectedSuspenseID = state.timeline[nextTimelineIndex];
406+
const nextSelectedSuspenseID = state.timeline[nextTimelineIndex].id;
391407
const nextLineage = store.getSuspenseLineage(
392408
nextSelectedSuspenseID,
393409
);
@@ -403,8 +419,13 @@ function SuspenseTreeContextController({children}: Props): React.Node {
403419
}
404420
case 'HOVER_TIMELINE_FOR_ID': {
405421
const suspenseID = action.payload;
406-
const timelineIndexForSuspenseID =
407-
state.timeline.indexOf(suspenseID);
422+
let timelineIndexForSuspenseID = -1;
423+
for (let i = 0; i < state.timeline.length; i++) {
424+
if (state.timeline[i].id === suspenseID) {
425+
timelineIndexForSuspenseID = i;
426+
break;
427+
}
428+
}
408429
return {
409430
...state,
410431
hoveredTimelineIndex: timelineIndexForSuspenseID,

packages/react-devtools-shared/src/frontend/types.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,11 @@ export type Rect = {
193193
height: number,
194194
};
195195

196+
export type SuspenseTimelineStep = {
197+
id: SuspenseNode['id'], // TODO: Will become a group.
198+
environment: null | string,
199+
};
200+
196201
export type SuspenseNode = {
197202
id: Element['id'],
198203
parentID: SuspenseNode['id'] | 0,

0 commit comments

Comments
 (0)