| 1 | export interface NativeReviewVisibleRange { |
| 2 | readonly firstRowIndex: number; |
| 3 | readonly lastRowIndex: number; |
| 4 | } |
| 5 | |
| 6 | export 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 | } |