Sidebar: suppress global count badges for non-admin users
PhotoPrism's /api/v1/config.count returns library-wide aggregates to any authenticated session, with no per-user scoping. The timeline itself IS scoped (a guest sees zero photos), but the sidebar was rendering admin-side totals next to Review / Hidden / Archive / Tags / Map / root for non-admins — including a freshly-registered "test" user with role=guest and BasePath="". Until PhotoPrism gains per-user counters, the SPA now derives an `isAdminUser` flag and gates every count that's drawn from configQuery on it. Non-admin users see the labels without badges; counts re-appear automatically when promoted. Per-folder counts from the sidecar (which DO scope to BasePath) are unaffected.
This commit is contained in:
@@ -92,6 +92,16 @@
|
|||||||
enabled: isAuthenticated()
|
enabled: isAuthenticated()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// PhotoPrism's /api/v1/config.count returns library-wide aggregates
|
||||||
|
// to any authenticated session regardless of role — the timeline
|
||||||
|
// itself IS scoped per-user, but the precomputed counters aren't.
|
||||||
|
// Showing those numbers in a non-admin's sidebar is misleading
|
||||||
|
// (e.g. the `test` user with role=guest saw the admin's library
|
||||||
|
// totals next to Review / Hidden / Archive). Until PhotoPrism gains
|
||||||
|
// per-user count scoping, just suppress the global-derived badges
|
||||||
|
// for anyone who isn't the admin.
|
||||||
|
const isAdminUser = $derived(session.user?.Role === 'admin');
|
||||||
|
|
||||||
const marksQuery = createQuery<PhotoMarksMap>(() => ({
|
const marksQuery = createQuery<PhotoMarksMap>(() => ({
|
||||||
queryKey: ['marks'],
|
queryKey: ['marks'],
|
||||||
queryFn: getAllMarks,
|
queryFn: getAllMarks,
|
||||||
@@ -213,7 +223,9 @@
|
|||||||
}));
|
}));
|
||||||
const rootCount = $derived(
|
const rootCount = $derived(
|
||||||
userBasePath() === ''
|
userBasePath() === ''
|
||||||
? (configQuery.data?.count?.all ?? 0)
|
? isAdminUser
|
||||||
|
? (configQuery.data?.count?.all ?? 0)
|
||||||
|
: 0
|
||||||
: (scopedRootCountQuery.data?.[''] ?? 0)
|
: (scopedRootCountQuery.data?.[''] ?? 0)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -425,16 +437,19 @@
|
|||||||
// separate "everything regardless of folder" destination would just
|
// separate "everything regardless of folder" destination would just
|
||||||
// duplicate it for users whose photos live under the root.
|
// duplicate it for users whose photos live under the root.
|
||||||
const views: ViewItem[] = [
|
const views: ViewItem[] = [
|
||||||
{ kind: 'route', href: '/map', label: 'Map', getCount: () => geoQuery.data?.features?.length },
|
{ kind: 'route', href: '/map', label: 'Map', getCount: () => (isAdminUser ? geoQuery.data?.features?.length : undefined) },
|
||||||
// Tags hosts four tabs (Labels (auto) / Keywords / Ratings / Colors);
|
// Tags hosts four tabs (Labels (auto) / Keywords / Ratings / Colors);
|
||||||
// the badge sums each tab's badge so the sidebar number is the
|
// the badge sums each tab's badge so the sidebar number is the
|
||||||
// total of what the inner tabs show. Keywords is lazy — it only
|
// total of what the inner tabs show. Keywords is lazy — it only
|
||||||
// contributes after /tags?tab=keywords has been visited once.
|
// contributes after /tags?tab=keywords has been visited once.
|
||||||
|
// Suppressed for non-admins because every contributing query is
|
||||||
|
// library-wide rather than per-user.
|
||||||
{
|
{
|
||||||
kind: 'route',
|
kind: 'route',
|
||||||
href: '/tags',
|
href: '/tags',
|
||||||
label: 'Tags',
|
label: 'Tags',
|
||||||
getCount: () => {
|
getCount: () => {
|
||||||
|
if (!isAdminUser) return undefined;
|
||||||
const labels = configQuery.data?.count?.labels;
|
const labels = configQuery.data?.count?.labels;
|
||||||
if (labels === undefined) return undefined;
|
if (labels === undefined) return undefined;
|
||||||
const keywords = keywordsQuery.data?.length ?? 0;
|
const keywords = keywordsQuery.data?.length ?? 0;
|
||||||
@@ -449,13 +464,14 @@
|
|||||||
href: '/review',
|
href: '/review',
|
||||||
label: 'Review',
|
label: 'Review',
|
||||||
getCount: () => {
|
getCount: () => {
|
||||||
|
if (!isAdminUser) return undefined;
|
||||||
const review = configQuery.data?.count?.review;
|
const review = configQuery.data?.count?.review;
|
||||||
if (review === undefined) return undefined;
|
if (review === undefined) return undefined;
|
||||||
return review + (stacksQuery.data?.length ?? 0) + (crossFolderQuery.data?.groups.length ?? 0);
|
return review + (stacksQuery.data?.length ?? 0) + (crossFolderQuery.data?.groups.length ?? 0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ kind: 'section', id: 'hidden', label: 'Hidden', getCount: () => configQuery.data?.count?.hidden },
|
{ kind: 'section', id: 'hidden', label: 'Hidden', getCount: () => (isAdminUser ? configQuery.data?.count?.hidden : undefined) },
|
||||||
{ kind: 'section', id: 'archive', label: 'Archive', getCount: () => configQuery.data?.count?.archived }
|
{ kind: 'section', id: 'archive', label: 'Archive', getCount: () => (isAdminUser ? configQuery.data?.count?.archived : undefined) }
|
||||||
];
|
];
|
||||||
|
|
||||||
function isRouteActive(href: string): boolean {
|
function isRouteActive(href: string): boolean {
|
||||||
|
|||||||
Reference in New Issue
Block a user