1export interface NativeReviewVisibleRange {
2 readonly firstRowIndex: number;
3 readonly lastRowIndex: number;
4}
5
6export function createReviewDiffHighlightScheduler(
7 request: (range: NativeReviewVisibleRange) => void,
8) {
9 let requestedRange: NativeReviewVisibleRange = { firstRowIndex: 0, lastRowIndex: 80 };
10 let visibleRange = requestedRange;
11 let timer: ReturnType<typeof setTimeout> | undefined;
12
13 const cancel = () => {
14 clearTimeout(timer);
15 timer = undefined;
16 };
17 const flush = () => {
18 cancel();
19 requestedRange = visibleRange;
20 request(visibleRange);
21 };
22
23 return {
24 update(nextRange: NativeReviewVisibleRange) {
25 if (
26 nextRange.firstRowIndex === visibleRange.firstRowIndex &&
27 nextRange.lastRowIndex === visibleRange.lastRowIndex
28 ) {
29 return;
30 }
31 visibleRange = nextRange;
32 cancel();
33 // Accumulate small scroll events relative to the last request, not each other.
34 const movedRows =
35 Math.abs(nextRange.firstRowIndex - requestedRange.firstRowIndex) +
36 Math.abs(nextRange.lastRowIndex - requestedRange.lastRowIndex);
37 if (movedRows >= 20) {
38 flush();
39 } else if (movedRows > 0) {
40 // Cover the final viewport even when scrolling stops below the threshold.
41 timer = setTimeout(flush, 150);
42 }
43 },
44 reset() {
45 cancel();
46 requestedRange = { firstRowIndex: 0, lastRowIndex: 80 };
47 visibleRange = requestedRange;
48 },
49 cancel,
50 };
51}