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:
@@ -140,4 +140,12 @@ describe("filterParamUpdates", () => {
|
||||
const back = paramsToFilters(new URLSearchParams("tagmode=and"), DEFAULTS);
|
||||
expect(back.tagModes).toEqual({ language: "or", topic: "and", other: "or" });
|
||||
});
|
||||
|
||||
it("round-trips the 'No tag' (untagged) flag; emitted only when on", () => {
|
||||
expect(filtersToParams({ ...DEFAULTS, untagged: true }).get("notag")).toBe("1");
|
||||
expect(filtersToParams({ ...DEFAULTS, untagged: false }).has("notag")).toBe(false);
|
||||
expect(paramsToFilters(new URLSearchParams("notag=1"), DEFAULTS).untagged).toBe(true);
|
||||
// Absent → the base value is left untouched (undefined here, i.e. off).
|
||||
expect(paramsToFilters(new URLSearchParams(""), DEFAULTS).untagged).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user