import { useState } from 'react' import { Timeline } from './components/timeline/Timeline' import { DuplicatesView } from './components/duplicates/DuplicatesView' import { LeftSidebar } from './components/layout/LeftSidebar' import { RightSidebar } from './components/layout/RightSidebar' import { TopBar } from './components/layout/TopBar' import { ScanProgress } from './components/ScanProgress' import { ToastContainer } from './components/ToastContainer' import { KeyboardHints } from './components/KeyboardHints' import { PreviewView } from './components/preview/PreviewView' import { FilterBar } from './components/filter/FilterBar' import { DiscardActionBar } from './components/discard/DiscardActionBar' import { SettingsDialog } from './components/dialogs/SettingsDialog' import { usePhotoStore } from './store/photoStore' import { useFilterStore } from './store/filterStore' import { useKeyboardShortcuts } from './hooks/useKeyboardShortcuts' import { useFilterUrlSync } from './hooks/useFilterUrlSync' import { usePhotosQuery } from './hooks/usePhotosQuery' function App() { const [leftSidebarOpen, setLeftSidebarOpen] = useState(true) const [rightSidebarOpen, setRightSidebarOpen] = useState(true) const [settingsOpen, setSettingsOpen] = useState(false) const viewMode = usePhotoStore((state) => state.viewMode) const currentSection = useFilterStore((s) => s.currentSection) // Bidirectional sync of filter store with URL query params. useFilterUrlSync() // Subscribe to the same photos query the Timeline uses, so the keyboard // "open preview on first photo" path can read from the live cache regardless // of what filter key it's stored under. const { data: allPhotos } = usePhotosQuery() // Set up global keyboard shortcuts useKeyboardShortcuts({ onToggleLeftSidebar: () => setLeftSidebarOpen(!leftSidebarOpen), onToggleRightSidebar: () => setRightSidebarOpen(!rightSidebarOpen), getFirstPhotoId: () => allPhotos?.[0]?.id ?? null, }) // Right sidebar stays open by default and shows whatever's selected // (or an empty state if nothing is). User can still toggle it manually. const showRightSidebar = rightSidebarOpen && viewMode === 'grid' return (
setLeftSidebarOpen(true)} onExpandRight={() => setRightSidebarOpen(true)} />
{/* Left Sidebar */}
setLeftSidebarOpen(false)} onOpenSettings={() => setSettingsOpen(true)} />
{/* Main column — filter bar, discard bar, timeline. Lives to the * right of the left sidebar so the filter row doesn't bleed * across the sidebar. relative so the KeyboardHints overlay * centers against this column, not the viewport. */}
{/* The Duplicates section gets its own grouped grid view — * the regular timeline can't represent groups, and a flat * filtered list of "is_duplicate=true" photos was the old * half-broken UX. */} {currentSection === 'duplicates' ? : }
{/* Floating keyboard hints — bottom-center of the main column, * glassy. Mounted here so it's centered against the timeline, * not the viewport (which would be offset by the sidebars). */}
{/* Right Sidebar */}
setRightSidebarOpen(false)} />
{/* Scan Progress Indicator */} {/* Toast Notifications */} {/* Preview overlay — covers TopBar when active */} {viewMode === 'preview' && } {/* Settings panel — admin/maintenance actions */} setSettingsOpen(false)} />
) } export default App