diff --git a/client/src/App.jsx b/client/src/App.jsx index e2c6f32..41bc190 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -1,5 +1,5 @@ import { useState, useEffect, useRef, useCallback } from 'react'; -import { fetchPhotos, fetchLibraryStats, fetchVideoStats } from './api/photos.js'; +import { fetchPhotos, fetchLibraryStats, fetchVideoStats, bulkStar, bulkAddToCollection } from './api/photos.js'; import Timeline from './components/Timeline.jsx'; import Lightbox from './components/Lightbox.jsx'; import FilterSidebar from './components/FilterSidebar.jsx'; @@ -11,6 +11,7 @@ import { lazy, Suspense } from 'react'; const MapView = lazy(() => import('./components/MapView.jsx')); const MemoriesView = lazy(() => import('./components/MemoriesView.jsx')); const VideosView = lazy(() => import('./components/VideosView.jsx')); +const AlbumsView = lazy(() => import('./components/AlbumsView.jsx')); const EMPTY_FILTERS = { q: '', from: '', to: '', tagId: '', collectionId: '', starred: '', city: '' }; const EMPTY_VIDEO_FILTERS = { q: '', from: '', to: '', tagId: '', collectionId: '', starred: '', city: '' }; @@ -30,9 +31,10 @@ export default function App() { const [videoFilters, setVideoFilters] = useState(EMPTY_VIDEO_FILTERS); const [videoStats, setVideoStats] = useState(null); - const [lbPhotos, setLbPhotos] = useState(null); - const [lbIndex, setLbIndex] = useState(null); + const [lbPhotos, setLbPhotos] = useState(null); + const [lbIndex, setLbIndex] = useState(null); const [showSettings, setShowSettings] = useState(false); + const [selected, setSelected] = useState(new Set()); const loadingRef = useRef(false); const sentinelRef = useRef(null); @@ -90,6 +92,46 @@ export default function App() { return () => obs.disconnect(); }, [nextCursor, view, filters]); + function toggleSelect(photoId) { + setSelected(prev => { + const next = new Set(prev); + next.has(photoId) ? next.delete(photoId) : next.add(photoId); + return next; + }); + } + + function clearSelection() { setSelected(new Set()); } + + async function handleBulkStar(starred) { + const ids = [...selected]; + try { + await bulkStar(ids, starred); + const idSet = new Set(ids); + setPhotos(prev => prev.map(p => idSet.has(p.id) ? { ...p, starred } : p)); + clearSelection(); + loadStats(); + } catch {} + } + + async function handleBulkAddToCollection(collectionId) { + if (!collectionId) return; + try { + await bulkAddToCollection([...selected], Number(collectionId)); + clearSelection(); + } catch {} + } + + function handleSelectAlbum({ type, collectionId, year, city, camera }) { + const f = { q: '', from: '', to: '', tagId: '', collectionId: '', starred: '', city: '' }; + if (type === 'collection') f.collectionId = String(collectionId); + else if (type === 'year') { f.from = `${year}-01-01`; f.to = `${year}-12-31`; } + else if (type === 'city') f.city = city; + else if (type === 'camera') f.q = camera; + setFilters(f); + setView('library'); + clearSelection(); + } + function openLightbox(photosArr, idx) { setLbPhotos(photosArr); setLbIndex(idx); @@ -128,7 +170,7 @@ export default function App() {