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.
This commit is contained in:
2026-10-03 02:43:52 +02:00
parent b40440b825
commit a4775e1cd0
3 changed files with 38 additions and 6 deletions
+5 -6
View File
@@ -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[]) {
@@ -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();
});
});
+14
View File
@@ -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<string, number> | undefined,
): ComboboxOption => ({
value: String(tag.id),
label: tag.name,
count: facets ? (facets[String(tag.id)] ?? 0) : undefined,
});