diff --git a/web/src/lib/components/layout/LeftSidebar.svelte b/web/src/lib/components/layout/LeftSidebar.svelte index 092a789..822cda3 100644 --- a/web/src/lib/components/layout/LeftSidebar.svelte +++ b/web/src/lib/components/layout/LeftSidebar.svelte @@ -41,6 +41,7 @@ } from '$lib/services/adapters/review'; import { filters, + navigateToFolder, setFolderPath, setSection, TAG_CATEGORIES, @@ -223,23 +224,40 @@ })); const folderCounts = $derived(folderCountsQuery.data ?? {}); - // Root entry shows "the user's library" — for admins without a - // BasePath that's still the whole library, served cheaply from - // /api/v1/config's `count.all`. For any user with a non-empty - // BasePath the precomputed total is wrong (it's library-wide), so we - // ask the sidecar for a recursive count rooted at the user's - // BasePath — listFolderCounts maps `""` through toOriginalsPath, which - // resolves to the BasePath itself, and the sidecar fan-out recurses. + // Root entry shows "the user's library" using the same filter the + // timeline applies at folderPath=='/' — empty q, which PhotoPrism + // resolves to the visible listing (no archived / hidden / review). + // Earlier this used /config's `count.all`, but that aggregate + // includes those buckets and didn't match what the user can actually + // click "select all" on; the discrepancy was confusing + // (LeftSidebar said 357, the action bar said ~329). + // + // `scopedRootCountQuery` retains the sidecar fan-out for users with + // a BasePath — `listFolderCounts(['''])` resolves `''` through + // `toOriginalsPath` to the user's BasePath and recurses, so it picks + // up the same subset PhotoPrism would. Empty BasePath admins use the + // PhotoPrism count-via-X-Count path so both surfaces agree. const scopedRootCountQuery = createQuery>(() => ({ queryKey: ['photos', 'root-count', userBasePath()], queryFn: () => listFolderCounts(['']), enabled: isAuthenticated() && userBasePath() !== '', staleTime: 60_000 })); + const visibleRootCountQuery = createQuery(() => ({ + queryKey: ['photos', 'visible-root-count', userBasePath()], + // `merged: true` so the count matches the timeline's photo entries + // (one per logical photo) rather than its file-row total. Without + // it, sidecar/companion files inflate the badge — e.g. a HEIC + JPG + // pair counts twice — and "select all" in the timeline never + // reaches the badge's number. + queryFn: () => countPhotos(scoped(''), { merged: true }), + enabled: isAuthenticated() && userBasePath() === '' && isAdminUser, + staleTime: 60_000 + })); const rootCount = $derived( userBasePath() === '' ? isAdminUser - ? (configQuery.data?.count?.all ?? 0) + ? (visibleRootCountQuery.data ?? 0) : 0 : (scopedRootCountQuery.data?.[''] ?? 0) ); @@ -490,14 +508,7 @@ } async function pickFolder(folderPath: string) { - // Folder selection works on top of the All Photos section; clearing - // the heap/section context mirrors mule-image's "drill into folder" - // behaviour. The URL sync $effect on the timeline picks this up. - setSection('all-photos'); - setFolderPath(folderPath); - const params = new URLSearchParams(); - params.set('folder', folderPath); - await goto(`/?${params.toString()}`, { keepFocus: true, noScroll: true }); + await navigateToFolder(folderPath); } function onCreateHeap() { diff --git a/web/src/lib/components/sidebar/RightSidebar.svelte b/web/src/lib/components/sidebar/RightSidebar.svelte index 23699d5..fa1a805 100644 --- a/web/src/lib/components/sidebar/RightSidebar.svelte +++ b/web/src/lib/components/sidebar/RightSidebar.svelte @@ -6,18 +6,20 @@ PUT (Details fields need the full body). -->