31
selectedSuspenseID: SuspenseNode['id'] | null,
32
timeline: $ReadOnlyArray<SuspenseNode['id']>,
33
timelineIndex: number | -1,
34
+ hoveredTimelineIndex: number | -1,
35
uniqueSuspendersOnly: boolean,
36
playing: boolean,
37
};
73
type ACTION_SUSPENSE_PLAY_TICK = {
74
type: 'SUSPENSE_PLAY_TICK',
75
};
76
+type ACTION_TOGGLE_TIMELINE_FOR_ID = {
77
+ type: 'TOGGLE_TIMELINE_FOR_ID',
78
+ payload: SuspenseNode['id'],
79
+};
80
+type ACTION_HOVER_TIMELINE_FOR_ID = {
81
+ type: 'HOVER_TIMELINE_FOR_ID',
82
+ payload: SuspenseNode['id'],
83
+};
84
85
export type SuspenseTreeAction =
86
| ACTION_SUSPENSE_TREE_MUTATION
90
| ACTION_SUSPENSE_SET_TIMELINE_INDEX
91
| ACTION_SUSPENSE_SKIP_TIMELINE_INDEX
92
| ACTION_SUSPENSE_PLAY_PAUSE
84
- | ACTION_SUSPENSE_PLAY_TICK;
93
+ | ACTION_SUSPENSE_PLAY_TICK
94
+ | ACTION_TOGGLE_TIMELINE_FOR_ID
95
+ | ACTION_HOVER_TIMELINE_FOR_ID;
96
export type SuspenseTreeDispatch = (action: SuspenseTreeAction) => void;
97
98
const SuspenseTreeStateContext: ReactContext<SuspenseTreeState> =
133
selectedRootID,
134
timeline: [],
135
timelineIndex: -1,
136
+ hoveredTimelineIndex: -1,
137
uniqueSuspendersOnly,
138
playing: false,
139
};
156
selectedRootID,
157
timeline,
158
timelineIndex,
159
+ hoveredTimelineIndex: -1,
160
uniqueSuspendersOnly,
161
playing: false,
162
};
410
playing: nextPlaying,
411
};
412
}
413
+ case 'TOGGLE_TIMELINE_FOR_ID': {
414
+ const suspenseID = action.payload;
415
+ const timelineIndexForSuspenseID =
416
+ state.timeline.indexOf(suspenseID);
417
+ if (timelineIndexForSuspenseID === -1) {
418
+ // This boundary is no longer in the timeline.
419
+ return state;
420
+ }
421
+ const nextTimelineIndex =
422
+ timelineIndexForSuspenseID === 0
423
+ ? // For roots, there's no toggling. It's always just jump to beginning.
424
+ 0
425
+ : // For boundaries, we'll either jump to before or after its reveal depending
426
+ // on if we're currently displaying it or not according to the timeline.
427
+ state.timelineIndex < timelineIndexForSuspenseID
428
+ ? // We're currently before this suspense boundary has been revealed so we
429
+ // should jump ahead to reveal it.
430
+ timelineIndexForSuspenseID
431
+ : // Otherwise, if we're currently showing it, jump to right before to hide it.
432
+ timelineIndexForSuspenseID - 1;
433
+ const nextSelectedSuspenseID = state.timeline[nextTimelineIndex];
434
+ const nextLineage = store.getSuspenseLineage(
435
+ nextSelectedSuspenseID,
436
+ );
437
+ return {
438
+ ...state,
439
+ lineage: nextLineage,
440
+ selectedSuspenseID: nextSelectedSuspenseID,
441
+ timelineIndex: nextTimelineIndex,
442
+ playing: false, // pause
443
+ };
444
+ }
445
+ case 'HOVER_TIMELINE_FOR_ID': {
446
+ const suspenseID = action.payload;
447
+ const timelineIndexForSuspenseID =
448
+ state.timeline.indexOf(suspenseID);
449
+ return {
450
+ ...state,
451
+ hoveredTimelineIndex: timelineIndexForSuspenseID,
452
+ };
453
+ }
454
default:
455
throw new Error(`Unrecognized action "${action.type}"`);
456
}