fix(sidebar): use count.labels for Tags badge, drop bogus label:* query
PhotoPrism's q-DSL has no "has any label" predicate: `label:*` matches
every photo regardless of label, `label:<slug>` 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) <noreply@anthropic.com>
This commit is contained in:
@@ -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<number>(() => ({
|
||||
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:<slug>` 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<number | undefined>(
|
||||
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<number | undefined>(() => {
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user