From a4775e1cd067338b09f046de293e49701f898604 Mon Sep 17 00:00:00 2001 From: npeter83 Date: Sat, 3 Oct 2026 02:43:52 +0200 Subject: [PATCH] fix(ui): the filter rail shows no tag counts until the facets land It printed the tag's whole-library count as the current filter's while the facets loaded, and for good when they failed: live, "Music 238" beside a search that matched 3 of its channels. Ordering still falls back to it. --- frontend/src/components/Sidebar.tsx | 11 +++++------ frontend/src/components/facetOption.test.ts | 19 +++++++++++++++++++ frontend/src/components/facetOption.ts | 14 ++++++++++++++ 3 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 frontend/src/components/facetOption.test.ts create mode 100644 frontend/src/components/facetOption.ts diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 1de2d9a6..8d374ac3 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -14,9 +14,10 @@ import FilterRail from "./FilterRail"; import { type PanelItem } from "./ui/PanelGroups"; import { type MediaSourceKind } from "../lib/mediaSource"; import ChannelPicker from "./ChannelPicker"; +import { facetOption } from "./facetOption"; import DateFilterSelect from "./DateFilterSelect"; import YearFilter from "./YearFilter"; -import Combobox, { type ComboboxOption } from "./ui/Combobox"; +import Combobox from "./ui/Combobox"; import MatchToggle from "./ui/MatchToggle"; import { useFeedFilters, useFeedFiltersActions } from "./feedFiltersContext"; import { useChannelsActions } from "./channelsContext"; @@ -72,6 +73,8 @@ export default function Sidebar({ }); const facetsReady = !!summaryQuery.data; const facetCounts = summaryQuery.data?.facets ?? {}; + // Ordering falls back to the tag's whole-library count until the facets land; the number shown does + // not (facetOption). const chipCount = (tag: Tag): number => facetsReady ? (facetCounts[String(tag.id)] ?? 0) : tag.channel_count; const visibleChips = (list: Tag[]): Tag[] => { @@ -93,11 +96,7 @@ export default function Sidebar({ // The language/topic/tags sections all write into the one `filters.tags` list, so each combobox owns // only its own slice: its selection replaces the ids that belong to its list, leaving the others. - const tagOption = (tg: Tag): ComboboxOption => ({ - value: String(tg.id), - label: tg.name, - count: chipCount(tg), - }); + const tagOption = (tg: Tag) => facetOption(tg, summaryQuery.data?.facets); const selectedIn = (list: Tag[]): string[] => filters.tags.filter((id) => list.some((tg) => tg.id === id)).map(String); function setTagSelection(list: Tag[], next: string[]) { diff --git a/frontend/src/components/facetOption.test.ts b/frontend/src/components/facetOption.test.ts new file mode 100644 index 00000000..1a052638 --- /dev/null +++ b/frontend/src/components/facetOption.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from "vitest"; + +import type { Tag } from "../lib/api"; +import { facetOption } from "./facetOption"; + +const music = { id: 7, name: "Music", category: "topic", channel_count: 238 } as Tag; + +describe("a tag as a filter row", () => { + it("counts the current filter's channels", () => { + expect(facetOption(music, { "7": 3 }).count).toBe(3); + expect(facetOption(music, {}).count).toBe(0); + }); + + // E31 review 2, live: with the summary failed, the rail read "Music 238" beside a search that matched + // 3 Music channels — the whole library's count printed as the filter's. + it("shows no count until the facets land, rather than the whole library's", () => { + expect(facetOption(music, undefined).count).toBeUndefined(); + }); +}); diff --git a/frontend/src/components/facetOption.ts b/frontend/src/components/facetOption.ts new file mode 100644 index 00000000..ec888623 --- /dev/null +++ b/frontend/src/components/facetOption.ts @@ -0,0 +1,14 @@ +import type { Tag } from "../lib/api"; +import type { ComboboxOption } from "./ui/Combobox"; + +// A tag as a filter row: its count is the CURRENT filter's (the summary's facets), or none at all while +// the facets load or after they failed. The rail printed the tag's whole-library count in that gap, as +// if it were the filter's: "Music 238" beside a search that matched 3 of its channels (E31 review 2). +export const facetOption = ( + tag: Tag, + facets: Record | undefined, +): ComboboxOption => ({ + value: String(tag.id), + label: tag.name, + count: facets ? (facets[String(tag.id)] ?? 0) : undefined, +});