1
Fork 0
mirror of https://github.com/thegeneralist01/archivr synced 2026-10-09 12:55:00 +02:00

fix: scope summary callbacks to selected entry

This commit is contained in:
archivr-qa 2026-08-24 16:04:19 +02:00
parent c541ca8032
commit 8c9d032156
No known key found for this signature in database

View file

@ -77,10 +77,10 @@ export default function ContextRail({ archiveId, selectedEntry, selectedUids, se
const summaryPollAbortRef = useRef(null) const summaryPollAbortRef = useRef(null)
const summaryGenerateAbortRef = useRef(null) const summaryGenerateAbortRef = useRef(null)
const summarySelectionRef = useRef(null) const summarySelectionRef = useRef(null)
// Update before effects run so a request settled during a new selection's // Update before effects run from the list selection, not detail: detail can
// render can never apply state or refresh the newly selected entry. // briefly describe the previously selected entry while its replacement loads.
const summarySelectionKey = archiveId && detail?.summary?.entry_uid const summarySelectionKey = archiveId && selectedEntry?.entry_uid
? `${archiveId}:${detail.summary.entry_uid}` ? `${archiveId}:${selectedEntry.entry_uid}`
: null : null
summarySelectionRef.current = summarySelectionKey summarySelectionRef.current = summarySelectionKey
@ -143,11 +143,12 @@ export default function ContextRail({ archiveId, selectedEntry, selectedUids, se
summaryPollAbortRef.current = null summaryPollAbortRef.current = null
summaryGenerateAbortRef.current?.abort() summaryGenerateAbortRef.current?.abort()
summaryGenerateAbortRef.current = null summaryGenerateAbortRef.current = null
setSummary(detail?.latest_summary ?? null) const detailMatchesSelection = detail?.summary?.entry_uid === selectedEntry?.entry_uid
setSummary(detailMatchesSelection ? detail.latest_summary ?? null : null)
setSummaryError('') setSummaryError('')
setSummaryBusy(false) setSummaryBusy(false)
setIncludeSummaryImages(false) setIncludeSummaryImages(false)
}, [archiveId, detail?.summary?.entry_uid]) }, [archiveId, selectedEntry?.entry_uid, detail?.summary?.entry_uid])
// Poll only while the latest summary is non-terminal. Anchoring the effect on // Poll only while the latest summary is non-terminal. Anchoring the effect on
// the status (rather than starting a timer inside the click handler) means a // the status (rather than starting a timer inside the click handler) means a
@ -160,6 +161,7 @@ export default function ContextRail({ archiveId, selectedEntry, selectedUids, se
if (!archiveId || !detail?.summary?.entry_uid) return if (!archiveId || !detail?.summary?.entry_uid) return
const entryUid = detail.summary.entry_uid const entryUid = detail.summary.entry_uid
const selectionKey = `${archiveId}:${entryUid}` const selectionKey = `${archiveId}:${entryUid}`
if (summarySelectionKey !== selectionKey) return
const controller = new AbortController() const controller = new AbortController()
summaryPollAbortRef.current = controller summaryPollAbortRef.current = controller
const poll = async () => { const poll = async () => {
@ -172,7 +174,7 @@ export default function ContextRail({ archiveId, selectedEntry, selectedUids, se
clearInterval(intervalId) clearInterval(intervalId)
if (summaryPollRef.current === intervalId) summaryPollRef.current = null if (summaryPollRef.current === intervalId) summaryPollRef.current = null
setSummaryBusy(false) setSummaryBusy(false)
if (st === 'completed') onDetailRefresh?.() if (st === 'completed' && summarySelectionRef.current === selectionKey) onDetailRefresh?.()
} }
} catch (e) { } catch (e) {
if (controller.signal.aborted || summarySelectionRef.current !== selectionKey) return if (controller.signal.aborted || summarySelectionRef.current !== selectionKey) return
@ -188,7 +190,7 @@ export default function ContextRail({ archiveId, selectedEntry, selectedUids, se
controller.abort() controller.abort()
if (summaryPollAbortRef.current === controller) summaryPollAbortRef.current = null if (summaryPollAbortRef.current === controller) summaryPollAbortRef.current = null
} }
}, [summaryStatus, archiveId, detail?.summary?.entry_uid]) }, [summaryStatus, archiveId, selectedEntry?.entry_uid, detail?.summary?.entry_uid, summarySelectionKey])
useEffect(() => () => { useEffect(() => () => {
clearInterval(summaryPollRef.current) clearInterval(summaryPollRef.current)
@ -200,6 +202,7 @@ export default function ContextRail({ archiveId, selectedEntry, selectedUids, se
if (!archiveId || !detail?.summary?.entry_uid || summaryBusy) return if (!archiveId || !detail?.summary?.entry_uid || summaryBusy) return
const entryUid = detail.summary.entry_uid const entryUid = detail.summary.entry_uid
const selectionKey = `${archiveId}:${entryUid}` const selectionKey = `${archiveId}:${entryUid}`
if (summarySelectionRef.current !== selectionKey) return
const controller = new AbortController() const controller = new AbortController()
summaryGenerateAbortRef.current?.abort() summaryGenerateAbortRef.current?.abort()
summaryGenerateAbortRef.current = controller summaryGenerateAbortRef.current = controller
@ -217,7 +220,7 @@ export default function ContextRail({ archiveId, selectedEntry, selectedUids, se
// 200 cache hit: the response *is* the row, no polling needed. // 200 cache hit: the response *is* the row, no polling needed.
setSummary(res) setSummary(res)
setSummaryBusy(false) setSummaryBusy(false)
onDetailRefresh?.() if (summarySelectionRef.current === selectionKey) onDetailRefresh?.()
} else { } else {
// 202: seed a local pending row so the poll effect starts immediately // 202: seed a local pending row so the poll effect starts immediately
// rather than waiting a tick for the first GET. // rather than waiting a tick for the first GET.