Skip to content

Commit de7f162

Browse files
committed
fox: remove requestIdleCallback implementation as scrollToPosition is no longer called 10 times on a take
1 parent f47b911 commit de7f162

File tree

1 file changed

+70
-128
lines changed

1 file changed

+70
-128
lines changed

packages/webui/src/client/lib/viewPort.ts

Lines changed: 70 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,7 @@ export async function scrollToPartInstance(
6868
quitFocusOnPart()
6969
const partInstance = UIPartInstances.findOne(partInstanceId)
7070
if (partInstance) {
71-
console.log('scrollToPartInstance - Emitting GO_TO_PART_INSTANCE', partInstance.segmentId, partInstanceId)
72-
// RundownViewEventBus.emit(RundownViewEvents.GO_TO_PART_INSTANCE, {
73-
// segmentId: partInstance.segmentId,
74-
// partInstanceId: partInstanceId,
75-
// })
76-
return scrollToSegment(partInstance.segmentId, forceScroll, noAnimation, partInstanceId)
71+
return scrollToSegment(partInstance.segmentId, forceScroll, noAnimation)
7772
}
7873
throw new Error('Could not find PartInstance')
7974
}
@@ -120,39 +115,10 @@ let currentScrollingElement: HTMLElement | undefined
120115
export async function scrollToSegment(
121116
elementToScrollToOrSegmentId: HTMLElement | SegmentId,
122117
forceScroll?: boolean,
123-
noAnimation?: boolean,
124-
partInstanceId?: PartInstanceId | undefined
118+
noAnimation?: boolean
125119
): Promise<boolean> {
126-
const getElementToScrollTo = (showHistory: boolean): HTMLElement | null => {
127-
if (isProtectedString(elementToScrollToOrSegmentId)) {
128-
let targetElement = document.querySelector<HTMLElement>(
129-
`#${SEGMENT_TIMELINE_ELEMENT_ID}${elementToScrollToOrSegmentId}`
130-
)
131-
132-
if (showHistory && Settings.followOnAirSegmentsHistory && targetElement) {
133-
let i = Settings.followOnAirSegmentsHistory
134-
while (i > 0) {
135-
// Segment timeline is wrapped by <div><div>...</div></div> when rendered
136-
const next: any = targetElement?.parentElement?.parentElement?.previousElementSibling?.children
137-
.item(0)
138-
?.children.item(0)
139-
if (next) {
140-
targetElement = next
141-
i--
142-
} else {
143-
i = 0
144-
}
145-
}
146-
}
147-
148-
return targetElement
149-
}
150-
151-
return elementToScrollToOrSegmentId
152-
}
153-
154-
const elementToScrollTo: HTMLElement | null = getElementToScrollTo(false)
155-
const historyTarget: HTMLElement | null = getElementToScrollTo(true)
120+
const elementToScrollTo: HTMLElement | null = getElementToScrollTo(elementToScrollToOrSegmentId, false)
121+
const historyTarget: HTMLElement | null = getElementToScrollTo(elementToScrollToOrSegmentId, true)
156122

157123
// historyTarget will be === to elementToScrollTo if history is not used / not found
158124
if (!elementToScrollTo || !historyTarget) {
@@ -163,24 +129,66 @@ export async function scrollToSegment(
163129
historyTarget,
164130
forceScroll || !regionInViewport(historyTarget, elementToScrollTo),
165131
noAnimation,
166-
false,
167-
partInstanceId
132+
false
168133
)
169134
}
170135

136+
function getElementToScrollTo(
137+
elementToScrollToOrSegmentId: HTMLElement | SegmentId,
138+
showHistory: boolean
139+
): HTMLElement | null {
140+
if (isProtectedString(elementToScrollToOrSegmentId)) {
141+
// Get the current segment element
142+
let targetElement = document.querySelector<HTMLElement>(
143+
`#${SEGMENT_TIMELINE_ELEMENT_ID}${elementToScrollToOrSegmentId}`
144+
)
145+
if (showHistory && Settings.followOnAirSegmentsHistory && targetElement) {
146+
let i = Settings.followOnAirSegmentsHistory
147+
148+
// Find previous segments
149+
while (i > 0 && targetElement) {
150+
const currentSegmentId = targetElement.id
151+
const allSegments = Array.from(document.querySelectorAll(`[id^="${SEGMENT_TIMELINE_ELEMENT_ID}"]`))
152+
153+
// Find current segment's index in the array of all segments
154+
const currentIndex = allSegments.findIndex((el) => el.id === currentSegmentId)
155+
156+
// Find the previous segment
157+
if (currentIndex > 0) {
158+
targetElement = allSegments[currentIndex - 1] as HTMLElement
159+
i--
160+
} else {
161+
// No more previous segments
162+
break
163+
}
164+
}
165+
}
166+
167+
return targetElement
168+
}
169+
170+
return elementToScrollToOrSegmentId
171+
}
172+
171173
async function innerScrollToSegment(
172174
elementToScrollTo: HTMLElement,
173175
forceScroll?: boolean,
174176
noAnimation?: boolean,
175-
secondStage?: boolean,
176-
partInstanceId?: PartInstanceId | undefined
177+
secondStage?: boolean
177178
): Promise<boolean> {
178179
if (!secondStage) {
179180
currentScrollingElement = elementToScrollTo
180181
} else if (secondStage && elementToScrollTo !== currentScrollingElement) {
181182
throw new Error('Scroll overriden by another scroll')
182183
}
183184

185+
// Ensure that the element is ready to be scrolled:
186+
if (!secondStage) {
187+
await new Promise((resolve) => setTimeout(resolve, 250))
188+
}
189+
console.log('innerScrollToSegment', elementToScrollTo)
190+
await new Promise((resolve) => requestAnimationFrame(resolve))
191+
184192
let { top, bottom } = elementToScrollTo.getBoundingClientRect()
185193
top = Math.floor(top)
186194
bottom = Math.floor(bottom)
@@ -193,36 +201,24 @@ async function innerScrollToSegment(
193201

194202
return scrollToPosition(top + window.scrollY, noAnimation).then(
195203
async () => {
196-
// retry scroll in case we have to load some data
197-
if (pendingSecondStageScroll) window.cancelIdleCallback(pendingSecondStageScroll)
198204
return new Promise<boolean>((resolve, reject) => {
199-
// scrollToPosition will resolve after some time, at which point a new pendingSecondStageScroll may have been created
200-
201-
pendingSecondStageScroll = window.requestIdleCallback(
202-
() => {
203-
if (!secondStage) {
204-
let { top, bottom } = elementToScrollTo.getBoundingClientRect()
205-
top = Math.floor(top)
206-
bottom = Math.floor(bottom)
207-
208-
if (bottom > Math.floor(window.innerHeight) || top < headerHeight) {
209-
innerScrollToSegment(
210-
elementToScrollTo,
211-
forceScroll,
212-
true,
213-
true,
214-
partInstanceId
215-
).then(resolve, reject)
216-
} else {
217-
resolve(true)
218-
}
205+
if (!secondStage) {
206+
// Wait to settle 1 atemt to scroll
207+
setTimeout(() => {
208+
let { top, bottom } = elementToScrollTo.getBoundingClientRect()
209+
top = Math.floor(top)
210+
bottom = Math.floor(bottom)
211+
if (bottom > Math.floor(window.innerHeight) || top < headerHeight) {
212+
// If not in place atempt to scroll again
213+
innerScrollToSegment(elementToScrollTo, forceScroll, true, true).then(resolve, reject)
219214
} else {
220-
currentScrollingElement = undefined
221215
resolve(true)
222216
}
223-
},
224-
{ timeout: 250 }
225-
)
217+
}, 600)
218+
} else {
219+
currentScrollingElement = undefined
220+
resolve(true)
221+
}
226222
})
227223
},
228224
(error) => {
@@ -252,9 +248,6 @@ function getRegionPosition(topElement: HTMLElement, bottomElement: HTMLElement):
252248
return { top, bottom }
253249
}
254250

255-
let scrollToPositionRequest: number | undefined
256-
let scrollToPositionRequestReject: ((reason?: any) => void) | undefined
257-
258251
export async function scrollToPosition(scrollPosition: number, noAnimation?: boolean): Promise<void> {
259252
// Calculate the exact position
260253
const headerOffset = getHeaderHeight() + HEADER_MARGIN
@@ -264,66 +257,15 @@ export async function scrollToPosition(scrollPosition: number, noAnimation?: boo
264257
window.scroll({
265258
top: targetTop,
266259
left: 0,
260+
behavior: 'instant',
267261
})
262+
console.log(`scrollToPosition: immediate scroll complete, position=${window.scrollY}`)
268263
return Promise.resolve()
269264
} else {
270-
return new Promise((resolve, reject) => {
271-
if (scrollToPositionRequest !== undefined) window.cancelIdleCallback(scrollToPositionRequest)
272-
if (scrollToPositionRequestReject !== undefined)
273-
scrollToPositionRequestReject('Prevented by another scroll')
274-
275-
scrollToPositionRequestReject = reject
276-
277-
scrollToPositionRequest = window.requestIdleCallback(
278-
() => {
279-
window.scroll({
280-
top: targetTop,
281-
left: 0,
282-
behavior: 'smooth',
283-
})
284-
285-
const checkScrollPosition = () => {
286-
const currentPosition = window.scrollY
287-
// Check if target position is reached
288-
if (
289-
Math.abs(currentPosition - targetTop) < 2 ||
290-
Math.abs(currentPosition - lastCheckedPosition) < 1
291-
) {
292-
// Fine adjust the position
293-
if (Math.abs(currentPosition - targetTop) > 2) {
294-
window.scroll({
295-
top: targetTop,
296-
left: 0,
297-
})
298-
}
299-
300-
// Clean up and resolve
301-
clearInterval(checkInterval)
302-
clearTimeout(timeoutTimer)
303-
resolve()
304-
scrollToPositionRequestReject = undefined
305-
return
306-
}
307-
308-
lastCheckedPosition = currentPosition
309-
}
310-
311-
// Keep track of the last position to detect when scrolling stops
312-
let lastCheckedPosition = window.scrollY
313-
314-
// Check every 100ms
315-
const checkInterval = setInterval(checkScrollPosition, 100)
316-
317-
// Fallback timeout - resolve after a reasonable time even if not at target
318-
const timeoutTimer = setTimeout(() => {
319-
clearInterval(checkInterval)
320-
resolve()
321-
scrollToPositionRequestReject = undefined
322-
}, 1500)
323-
},
324-
// Make sure we wait to ensure the scroll is started
325-
{ timeout: 250 }
326-
)
265+
window.scroll({
266+
top: targetTop,
267+
left: 0,
268+
behavior: 'smooth',
327269
})
328270
}
329271
}

0 commit comments

Comments
 (0)