Wait for the viewer's table before re-applying row depth on refocus
The instrumentation showed the re-apply mostly throwing "No table set": the viewer is detached from its table for a while after a refocus, the fixed 60ms delay fired inside that window, set_depth threw, and the tree rendered fully expanded. The handful of times the delay happened to be long enough, the log reads "re-apply done depth=0" and the collapse survived. So poll for the table instead of guessing, up to 5s. queued now clears in a finally after the work rather than at the top of the callback, so the focus and visibilitychange that both fire for one window switch no longer each run a re-apply -- that was the doubled applyDepth in the log. Does not address per-node +/- collapse, which is still not recorded at all: expandDepthRef stays null because only the toolbar buttons set it, and the view exposes expand()/collapse() with no getter to read the state back. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
db67f6eede
commit
0520e5e542
@ -236,6 +236,26 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio
|
|||||||
lastViewIdRef.current = id
|
lastViewIdRef.current = id
|
||||||
} catch (err) { dbg(`probe(${where}) threw`, err) }
|
} catch (err) { dbg(`probe(${where}) threw`, err) }
|
||||||
}
|
}
|
||||||
|
// The viewer is briefly detached from its table around a refocus, and anything
|
||||||
|
// touching the view in that window throws "No table set". Poll until it is back
|
||||||
|
// rather than guessing at a delay.
|
||||||
|
async function waitForTable(deadlineMs = 5000, stepMs = 60) {
|
||||||
|
const started = Date.now()
|
||||||
|
let attempts = 0
|
||||||
|
while (Date.now() - started < deadlineMs) {
|
||||||
|
attempts++
|
||||||
|
try {
|
||||||
|
const viewer = viewerRef.current
|
||||||
|
if (viewer && await viewer.getTable()) {
|
||||||
|
if (attempts > 1) dbg(`table re-attached after ${attempts} polls / ${Date.now() - started}ms`)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} catch { /* not yet */ }
|
||||||
|
await new Promise(r => setTimeout(r, stepMs))
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------- END DEBUG
|
// -------------------------------------------------------------- END DEBUG
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -256,16 +276,25 @@ export default function Forecast({ sources = [], sourceId, versions = [], versio
|
|||||||
if (expandDepthRef.current == null) return dbg('bail: no depth recorded (toolbar EXPAND never used, or per-node +/- only)')
|
if (expandDepthRef.current == null) return dbg('bail: no depth recorded (toolbar EXPAND never used, or per-node +/- only)')
|
||||||
if (queued) return dbg('bail: already queued')
|
if (queued) return dbg('bail: already queued')
|
||||||
queued = true
|
queued = true
|
||||||
// let the viewer finish its own redraw first, or it will draw over us
|
// Let the viewer finish its own redraw first, then wait for it to actually
|
||||||
requestAnimationFrame(() => setTimeout(async () => {
|
// have a table again. A fixed delay loses this race most of the time -- the
|
||||||
queued = false
|
// viewer is detached from its table for a while after a refocus, and
|
||||||
const d = expandDepthRef.current
|
// set_depth on a detached viewer throws "No table set" and the tree renders
|
||||||
if (d == null) return dbg('re-apply aborted: depth became null')
|
// fully expanded. queued stays set until the work is done, so a focus and a
|
||||||
|
// visibilitychange for the same switch don't both re-apply.
|
||||||
|
requestAnimationFrame(async () => {
|
||||||
try {
|
try {
|
||||||
|
const d = expandDepthRef.current
|
||||||
|
if (d == null) return dbg('re-apply aborted: depth became null')
|
||||||
|
if (!(await waitForTable())) return dbg('re-apply gave up: viewer never re-attached a table')
|
||||||
await applyDepth(d)
|
await applyDepth(d)
|
||||||
dbg(`re-apply done depth=${d}`)
|
dbg(`re-apply done depth=${d}`)
|
||||||
} catch (err) { dbg('re-apply THREW', err) }
|
} catch (err) {
|
||||||
}, 60))
|
dbg('re-apply THREW', err)
|
||||||
|
} finally {
|
||||||
|
queued = false
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
document.addEventListener('visibilitychange', reapply)
|
document.addEventListener('visibilitychange', reapply)
|
||||||
window.addEventListener('focus', reapply)
|
window.addEventListener('focus', reapply)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user