feat(feed,channels): "No tag" filter for untagged channels (E8·S1)
Filter to channels you haven't personally tagged in the channel manager (no user-owned ChannelTag; auto language/topic tags don't count): - Feed: a "No tag" sentinel in the "Your tags" sidebar combobox (exclusive with real tag picks) + a removable "Untagged" chip; new `untagged` bool feed param / `notag` URL key, persisted + coerced on every filter path. - Channel manager: a virtual "No tag" chip in the tag-chip row filters the table to untagged channels (client-side, shared NO_TAG_SENTINEL). Backend `untagged` is a plain channel-level narrowing, so feed_summary keeps it applied in the base pass — the count/facet lockstep holds with no mirror. Note: item E (per-tag counts) and item D (Any/All toggle) were already shipped; S1 delivers only item B.
This commit is contained in:
@@ -71,6 +71,9 @@ export interface FeedFilters {
|
||||
// Library (scope "all") only — provenance filter: "organic" (default) hides search-
|
||||
// discovered videos, "all" mixes them in, "search" shows only them. Ignored for "my".
|
||||
librarySource?: "organic" | "all" | "search" | "plex";
|
||||
/** "No tag" filter (item B): keep only channels the user has NOT personally tagged in the channel
|
||||
* manager (no user-owned ChannelTag). Auto/system tags don't count. */
|
||||
untagged?: boolean;
|
||||
/** OR-ed together: a video from ANY of these passes, and every other filter still applies. */
|
||||
channelIds: string[];
|
||||
maxAgeDays?: number;
|
||||
@@ -89,6 +92,7 @@ function filterParams(f: FeedFilters): URLSearchParams {
|
||||
for (const cat of TAG_CATEGORIES) {
|
||||
if (f.tagModes[cat] === "and") p.set(`tag_mode_${cat}`, "and");
|
||||
}
|
||||
if (f.untagged) p.set("untagged", "true");
|
||||
if (f.q) p.set("q", f.q);
|
||||
p.set("scope", f.scope);
|
||||
p.set("show_normal", String(f.includeNormal));
|
||||
|
||||
Reference in New Issue
Block a user