Skip to content

Commit cb7fcb1

Browse files
committed
fix: maintainFocusOnPartInstance race condition
1 parent de7f162 commit cb7fcb1

File tree

1 file changed

+39
-19
lines changed

1 file changed

+39
-19
lines changed

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

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,41 +9,60 @@ import { logger } from './logging'
99
const HEADER_MARGIN = 24 // TODOSYNC: TV2 uses 15. If it's needed to be different, it needs to be made generic somehow..
1010
const FALLBACK_HEADER_HEIGHT = 65
1111

12-
let focusInterval: NodeJS.Timeout | undefined
13-
let _dontClearInterval = false
12+
// Replace the global variable with a more structured approach
13+
const focusState = {
14+
interval: undefined as NodeJS.Timeout | undefined,
15+
isScrolling: false,
16+
startTime: 0,
17+
}
1418

1519
export function maintainFocusOnPartInstance(
1620
partInstanceId: PartInstanceId,
1721
timeWindow: number,
1822
forceScroll?: boolean,
1923
noAnimation?: boolean
2024
): void {
21-
const startTime = Date.now()
22-
const focus = () => {
23-
if (Date.now() - startTime < timeWindow) {
24-
_dontClearInterval = true
25-
scrollToPartInstance(partInstanceId, forceScroll, noAnimation)
26-
.then(() => {
27-
_dontClearInterval = false
28-
})
29-
.catch(() => {
30-
_dontClearInterval = false
31-
})
32-
} else {
25+
focusState.startTime = Date.now()
26+
27+
const focus = async () => {
28+
// Only proceed if we're not already scrolling and within the time window
29+
if (!focusState.isScrolling && Date.now() - focusState.startTime < timeWindow) {
30+
focusState.isScrolling = true
31+
32+
try {
33+
await scrollToPartInstance(partInstanceId, forceScroll, noAnimation)
34+
} catch (error) {
35+
// Handle error if needed
36+
} finally {
37+
focusState.isScrolling = false
38+
}
39+
} else if (Date.now() - focusState.startTime >= timeWindow) {
3340
quitFocusOnPart()
3441
}
3542
}
43+
3644
document.addEventListener('wheel', onWheelWhenMaintainingFocus, {
3745
once: true,
3846
capture: true,
3947
passive: true,
4048
})
41-
focusInterval = setInterval(focus, 500)
49+
50+
// Clear any existing interval before creating a new one
51+
if (focusState.interval) {
52+
clearInterval(focusState.interval)
53+
}
54+
4255
focus()
56+
.then(() => {
57+
focusState.interval = setInterval(focus, 500)
58+
})
59+
.catch(() => {
60+
// Handle error if needed
61+
})
4362
}
4463

4564
export function isMaintainingFocus(): boolean {
46-
return !!focusInterval
65+
return !!focusState.interval
4766
}
4867

4968
function onWheelWhenMaintainingFocus() {
@@ -54,9 +73,10 @@ function quitFocusOnPart() {
5473
document.removeEventListener('wheel', onWheelWhenMaintainingFocus, {
5574
capture: true,
5675
})
57-
if (!_dontClearInterval && focusInterval) {
58-
clearInterval(focusInterval)
59-
focusInterval = undefined
76+
77+
if (focusState.interval) {
78+
clearInterval(focusState.interval)
79+
focusState.interval = undefined
6080
}
6181
}
6282

0 commit comments

Comments
 (0)