perf+ux: cheaper sidebar toggle, virtualised filmstrip, compact toasts
Timeline's items array used to rebuild on every sub-pixel cellSize tick during the sidebar CSS transition, causing visible jank with thousands of photos. Row heights now resolve off a ref at virtualizer-measure time, so items only rebuild when the column count actually changes. PreviewFilmstrip is horizontally virtualised (~15 cells in the DOM instead of N), cutting preview open latency on large libraries. Also honor the user's explicit right-sidebar collapse (don't auto-reopen on photo selection) and shrink the sonner toasts to a tighter form factor. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,11 @@ import { TooltipProvider } from '@/components/ui/tooltip'
|
||||
function MainApp() {
|
||||
const [leftSidebarOpen, setLeftSidebarOpen] = useState(true)
|
||||
const [rightSidebarOpen, setRightSidebarOpen] = useState(true)
|
||||
// Respect the user's manual collapse of the metadata panel. Once they
|
||||
// close it explicitly (via `i` hotkey or the sidebar toggle button),
|
||||
// selecting a new photo should NOT force it back open. Cleared when
|
||||
// they open it manually again.
|
||||
const rightCollapsedByUser = useRef(false)
|
||||
const viewMode = usePhotoStore((state) => state.viewMode)
|
||||
const activePhotoId = usePhotoStore((state) => state.activePhotoId)
|
||||
const currentSection = useFilterStore((s) => s.currentSection)
|
||||
@@ -45,9 +50,24 @@ function MainApp() {
|
||||
}, [currentSection])
|
||||
|
||||
useEffect(() => {
|
||||
setRightSidebarOpen(!!activePhotoId)
|
||||
if (!activePhotoId) {
|
||||
setRightSidebarOpen(false)
|
||||
return
|
||||
}
|
||||
// User explicitly collapsed the panel — don't undo that just because
|
||||
// they picked a different photo.
|
||||
if (rightCollapsedByUser.current) return
|
||||
setRightSidebarOpen(true)
|
||||
}, [activePhotoId])
|
||||
|
||||
const toggleRightSidebar = () => {
|
||||
setRightSidebarOpen((prev) => {
|
||||
const next = !prev
|
||||
rightCollapsedByUser.current = !next
|
||||
return next
|
||||
})
|
||||
}
|
||||
|
||||
// Bidirectional sync of filter store with URL query params.
|
||||
useFilterUrlSync()
|
||||
|
||||
@@ -59,7 +79,7 @@ function MainApp() {
|
||||
// Set up global keyboard shortcuts
|
||||
useKeyboardShortcuts({
|
||||
onToggleLeftSidebar: () => setLeftSidebarOpen(!leftSidebarOpen),
|
||||
onToggleRightSidebar: () => setRightSidebarOpen(!rightSidebarOpen),
|
||||
onToggleRightSidebar: toggleRightSidebar,
|
||||
getFirstPhotoId: () => allPhotos?.[0]?.id ?? null,
|
||||
})
|
||||
|
||||
@@ -96,7 +116,7 @@ function MainApp() {
|
||||
leftSidebarOpen={leftSidebarOpen}
|
||||
rightSidebarOpen={showRightSidebar}
|
||||
onToggleLeftSidebar={() => setLeftSidebarOpen(!leftSidebarOpen)}
|
||||
onToggleRightSidebar={() => setRightSidebarOpen(!rightSidebarOpen)}
|
||||
onToggleRightSidebar={toggleRightSidebar}
|
||||
/>
|
||||
)}
|
||||
{!isSettings && <DiscardActionBar />}
|
||||
|
||||
Reference in New Issue
Block a user