feat(filters): dynamic faceted chips driven by /api/facets

Topic and language chips now show live channel counts for the current filter
context instead of the static global count, and chips that match nothing are
hidden (selected chips stay so they can be cleared). Selecting a channel (or any
filter) drops the now-irrelevant chips and updates the rest. Extract a shared
filterParams() so the feed and facets queries see identical filters; the facets
query is keyed on filters so it refetches as they change. Trilingual empty-state
string when a category has no matching tags.
This commit is contained in:
npeter83 2026-06-15 12:06:02 +02:00
parent 79e7694b24
commit 79f53ccf59
5 changed files with 63 additions and 17 deletions

View file

@ -152,13 +152,13 @@ async function req(url: string, opts: RequestInit = {}): Promise<any> {
return r.status === 204 ? null : r.json();
}
function feedQuery(f: FeedFilters, offset: number, limit: number): string {
// The filter half of the query (everything except paging/sort), shared by the feed and
// the facet-count endpoint so both see exactly the same filter context.
function filterParams(f: FeedFilters): URLSearchParams {
const p = new URLSearchParams();
f.tags.forEach((t) => p.append("tags", String(t)));
p.set("tag_mode", f.tagMode);
if (f.q) p.set("q", f.q);
p.set("sort", f.sort);
if (f.sort === "shuffle" && f.seed) p.set("seed", String(f.seed));
p.set("scope", f.scope);
p.set("show_normal", String(f.includeNormal));
p.set("include_shorts", String(f.includeShorts));
@ -170,6 +170,13 @@ function feedQuery(f: FeedFilters, offset: number, limit: number): string {
if (f.dateTo) p.set("published_before", f.dateTo);
if (f.minDuration != null) p.set("min_duration", String(f.minDuration));
if (f.maxDuration != null) p.set("max_duration", String(f.maxDuration));
return p;
}
function feedQuery(f: FeedFilters, offset: number, limit: number): string {
const p = filterParams(f);
p.set("sort", f.sort);
if (f.sort === "shuffle" && f.seed) p.set("seed", String(f.seed));
p.set("offset", String(offset));
p.set("limit", String(limit));
return p.toString();
@ -252,6 +259,8 @@ export const api = {
req(`/api/feed?${feedQuery(f, offset, limit)}`),
feedCount: (f: FeedFilters): Promise<{ count: number }> =>
req(`/api/feed/count?${feedQuery(f, 0, 0)}`),
facets: (f: FeedFilters): Promise<{ counts: Record<string, number> }> =>
req(`/api/facets?${filterParams(f).toString()}`),
setState: (id: string, status: string) =>
req(`/api/videos/${id}/state`, { method: "POST", body: JSON.stringify({ status }) }),
saveProgress: (id: string, positionSeconds: number, durationSeconds: number) =>