ui(timeline): auto-focus first photo on every section switch
The previous auto-focus guard was one-shot for the lifetime of the component, so switching from All Photos → Discarded (or any other filter-based section) carried over the old activePhotoId — and if it wasn't in the new view, nothing was focused at all. A new effect watches currentSection and, on any change (or fresh mount after a Duplicates/Memories detour), resets the guard and clears the stale selection so the existing auto-focus picks the first visible photo of the new view. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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<string | null>(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.
|
||||
|
||||
Reference in New Issue
Block a user