diff --git a/frontend/src/components/timeline/Timeline.tsx b/frontend/src/components/timeline/Timeline.tsx index ae29ded..b6971f9 100644 --- a/frontend/src/components/timeline/Timeline.tsx +++ b/frontend/src/components/timeline/Timeline.tsx @@ -207,9 +207,11 @@ export function Timeline() { const prevViewModeRef = useRef(viewMode) // Auto-focus the first photo on initial grid load so arrow-key nav - // works immediately without a pre-click. One-shot — after the user - // explicitly clears the selection (Escape), we don't re-focus, so - // the metadata sidebar can collapse and stay collapsed. + // works immediately without a pre-click. One-shot per section — after + // the user explicitly clears the selection (Escape), we don't + // re-focus within the same section, so the metadata sidebar can + // collapse and stay collapsed. Reset by the section-change effect + // below so switching views (e.g. All → Discarded) re-focuses. const didAutoFocusRef = useRef(false) // visibleSequence isn't in scope yet (derived below from items). Read // it through a ref so this effect can focus the first *visually* @@ -230,6 +232,23 @@ export function Timeline() { selectPhoto(firstVisible) }, [viewMode, activePhotoId, photos, selectPhoto]) + // On section switch (e.g. All Photos → Discarded) or fresh mount, + // reset the one-shot guard and clear the stale active photo so the + // effect above picks the first photo of the new view once its query + // resolves. The previous section's activePhotoId almost never + // belongs to the new section — carrying it over leaves the metadata + // sidebar showing something that isn't even in the visible grid. + // Ref starts null so the first mount after a Duplicates/Memories + // detour also trips this path. + const clearSelection = usePhotoStore((s) => s.clearSelection) + const prevSectionRef = useRef(null) + useEffect(() => { + if (prevSectionRef.current === currentSection) return + prevSectionRef.current = currentSection + didAutoFocusRef.current = false + clearSelection() + }, [currentSection, clearSelection]) + // Membership in the active heap (drives the green tint on each // thumbnail). Subscribed once at this level so we don't have hundreds // of thumbnails each subscribing to the same query.