From fc5f30fad15944e92236ab5fa9b3907b78c3ab94 Mon Sep 17 00:00:00 2001 From: dtoro Date: Wed, 20 May 2026 06:31:17 +0000 Subject: [PATCH] fix(sidebar): use count.labels for Tags badge, drop bogus `label:*` query MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PhotoPrism's q-DSL has no "has any label" predicate: `label:*` matches every photo regardless of label, `label:` only matches that one slug, and `keywords:*` behaves the same way. The prior `all:true label:*` returned a 400 (and the earlier "drop all:true" follow-up made it return the unfiltered library size, which then fed into tagsTotal and inflated the parent Tags badge to ~library_size on admin sessions). Switch labelsBadge to the precomputed `configQuery.count.labels` — the same source the Tags sub-row's `tagCategoryCount('labels')` already uses. The parent Tags badge now sums the exact same numbers the sub-rows display: labels, keywords, people (distinct slugs/keywords/subjects) plus ratings/colors (photos carrying each mark). Drop the wantScoped short-circuit since the values are all library-wide now anyway. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../lib/components/layout/LeftSidebar.svelte | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/web/src/lib/components/layout/LeftSidebar.svelte b/web/src/lib/components/layout/LeftSidebar.svelte index 4f55131..d2a60ff 100644 --- a/web/src/lib/components/layout/LeftSidebar.svelte +++ b/web/src/lib/components/layout/LeftSidebar.svelte @@ -141,17 +141,12 @@ const reviewCountQuery = scopedCountQuery('review', 'review:true'); const hiddenCountQuery = scopedCountQuery('hidden', 'hidden:true'); const archivedCountQuery = scopedCountQuery('archived', 'archived:true'); - // Labels is special: `configQuery.count.labels` is the number of distinct - // label categories (PhotoPrism's roll-up), not the number of photos that - // carry a label. The Tags surface wants picture counts everywhere, so we - // always run a `countPhotos('label:*')` query regardless of the admin/ - // BasePath shape and never fall back to the category-count. - const labelsCountQuery = createQuery(() => ({ - queryKey: ['photos', 'scoped-count', 'labels', userBasePath(), isAdminUser], - queryFn: () => countPhotos(scoped('label:*')), - enabled: isAuthenticated(), - staleTime: 60_000 - })); + // Labels: PhotoPrism's q-DSL has no "has any label" predicate + // (`label:*` matches every photo, `label:` only matches the + // named label), so the sidebar uses `configQuery.count.labels` — the + // precomputed count of distinct label slugs. Same source the + // `tagCategoryCount('labels')` sub-row already uses, so the parent + // Tags badge sums the same numbers the sub-rows display. function bucketCount( key: 'favorites' | 'review' | 'hidden' | 'archived', @@ -303,7 +298,7 @@ const hiddenBadge = $derived(bucketCount('hidden', hiddenCountQuery)); const archivedBadge = $derived(bucketCount('archived', archivedCountQuery)); const labelsBadge = $derived( - labelsCountQuery.isPending ? undefined : labelsCountQuery.data + configQuery.data?.count?.labels ); const createMut = createMutation(() => ({ @@ -586,13 +581,13 @@ // section/route ViewItem shape. ]; - // Total badge for the "Tags" header row. Rolls up labels + keywords + - // people + ratings + colors. Labels flows through countPhotos (scoped); - // keywords/people/ratings/colors are library-wide and only contribute - // when we're in admin-without-BasePath mode (their sources don't scope). + // Total badge for the "Tags" header row. Sum of the same per-category + // counts the sub-rows render (labels/people: distinct slugs/subjects, + // keywords: distinct keywords, ratings/colors: photos carrying each + // mark). Library-wide numbers — the badge is a quick "how many tags + // exist?" rollup, not a per-user-visible count. const tagsTotal = $derived.by(() => { if (labelsBadge === undefined) return undefined; - if (wantScoped) return labelsBadge; const keywords = keywordsQuery.data?.length ?? 0; const people = configQuery.data?.count?.people ?? 0; return labelsBadge + keywords + people + ratingsCount + colorsCount;