fix: parent folders clickable; refresh photos when scan completes
Two related sidebar UX bugs. 1. Parent folders weren't clickable renderTreeItem's onClick called toggleExpanded(item.id) for any row with children — so a parent folder only expanded/collapsed, never applied its filter. Restructured: folder rows always call applyLibraryNode (which the photos endpoint already expands to include descendants), and the chevron remains a separate stopPropagation button for expansion. Other group headers (Library, Folders, Tags) still toggle expansion on row click since they have no associated filter. Result: clicking any folder at any depth filters the timeline to that folder + every descendant, matching the Lightroom model the user expects. 2. New files not appearing after Scan all folders scanLibraryMutation.onSettled invalidated ['photos'] when the trigger returned, but POST /library/scan just queues the celery task and returns immediately. By the time the worker finishes walking the directory and inserting new rows, the photos query has already refetched (with no new data) and is sitting on a 30-second staleTime — so newly-indexed photos stayed invisible until the next manual refetch. Fix: ScanProgress already polls /library/scan/status. Track the previous is_scanning value via a ref; when it transitions from true → false, invalidate ['photos'], ['folders'], ['folders', 'tree'], ['heaps'], and ['tags']. That's the actual moment new data is available, regardless of how the scan was triggered (button, watcher, startup). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { FolderOpen, Loader2, Check, AlertCircle, X } from 'lucide-react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { library } from '../services/api'
|
||||
import clsx from 'clsx'
|
||||
|
||||
@@ -15,6 +15,8 @@ interface ScanStatus {
|
||||
export function ScanProgress() {
|
||||
const [isVisible, setIsVisible] = useState(false)
|
||||
const [isMinimized, setIsMinimized] = useState(false)
|
||||
const queryClient = useQueryClient()
|
||||
const wasScanningRef = useRef(false)
|
||||
|
||||
// Poll scan status every 2 seconds when scanning
|
||||
const { data: scanStatus } = useQuery<ScanStatus>({
|
||||
@@ -31,18 +33,34 @@ export function ScanProgress() {
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (scanStatus?.is_scanning) {
|
||||
const isScanning = scanStatus?.is_scanning ?? false
|
||||
|
||||
if (isScanning) {
|
||||
setIsVisible(true)
|
||||
setIsMinimized(false)
|
||||
} else if (isVisible && !scanStatus?.is_scanning && (scanStatus?.processed_files ?? 0) > 0) {
|
||||
// Keep showing for 3 seconds after scan completes
|
||||
setTimeout(() => {
|
||||
if (!scanStatus?.is_scanning) {
|
||||
setIsVisible(false)
|
||||
}
|
||||
}, 3000)
|
||||
wasScanningRef.current = true
|
||||
} else if (wasScanningRef.current) {
|
||||
// Just transitioned from scanning → done. THIS is the right moment
|
||||
// to invalidate caches that might have new data: the photos query
|
||||
// (new files indexed), the folder tree (new folders walked), the
|
||||
// heap counts (in case a heap photo got reattached).
|
||||
wasScanningRef.current = false
|
||||
queryClient.invalidateQueries({ queryKey: ['photos'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['folders'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['folders', 'tree'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['heaps'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['tags'] })
|
||||
|
||||
if (isVisible && (scanStatus?.processed_files ?? 0) > 0) {
|
||||
// Keep showing for 3 seconds after scan completes
|
||||
setTimeout(() => {
|
||||
if (!scanStatus?.is_scanning) {
|
||||
setIsVisible(false)
|
||||
}
|
||||
}, 3000)
|
||||
}
|
||||
}
|
||||
}, [scanStatus?.is_scanning, scanStatus?.processed_files, isVisible])
|
||||
}, [scanStatus?.is_scanning, scanStatus?.processed_files, isVisible, queryClient])
|
||||
|
||||
if (!isVisible || !scanStatus) return null
|
||||
|
||||
|
||||
Reference in New Issue
Block a user