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:
@@ -20,6 +20,7 @@ const KEYS = [
|
||||
"tagmode_topic",
|
||||
"tagmode_other",
|
||||
"tagmode", // legacy global (topic-only) — still recognised so old shared links resolve
|
||||
"notag",
|
||||
"channel",
|
||||
"maxage",
|
||||
"from",
|
||||
@@ -44,6 +45,7 @@ export function filtersToParams(f: FeedFilters): URLSearchParams {
|
||||
for (const cat of TAG_CATEGORIES) {
|
||||
if (f.tagModes[cat] === "and") p.set(`tagmode_${cat}`, "and");
|
||||
}
|
||||
if (f.untagged) p.set("notag", "1");
|
||||
if (f.channelIds.length) p.set("channel", f.channelIds.join(","));
|
||||
if (f.maxAgeDays) p.set("maxage", String(f.maxAgeDays));
|
||||
if (f.dateFrom) p.set("from", f.dateFrom);
|
||||
@@ -95,6 +97,7 @@ export function paramsToFilters(params: URLSearchParams, base: FeedFilters): Fee
|
||||
topic: params.get("tagmode_topic") === "and" || params.get("tagmode") === "and" ? "and" : "or",
|
||||
other: params.get("tagmode_other") === "and" ? "and" : "or",
|
||||
};
|
||||
if (params.has("notag")) f.untagged = params.get("notag") === "1";
|
||||
f.channelIds = (params.get("channel") ?? "").split(",").filter(Boolean);
|
||||
f.maxAgeDays = num(params.get("maxage"));
|
||||
f.dateFrom = params.get("from") || undefined;
|
||||
|
||||
Reference in New Issue
Block a user