diff --git a/frontend/src/components/PlexDiscover.dom.test.tsx b/frontend/src/components/PlexDiscover.dom.test.tsx
index b1966a85..4845ebea 100644
--- a/frontend/src/components/PlexDiscover.dom.test.tsx
+++ b/frontend/src/components/PlexDiscover.dom.test.tsx
@@ -114,7 +114,48 @@ describe("PlexDiscover", () => {
expect(screen.getAllByText("Random Pick").length).toBeGreaterThan(0);
expect(screen.getAllByText("Rec One").length).toBeGreaterThan(0);
// Now called with the active filters too (empty default here, no provider mounted).
- expect(plexDiscover).toHaveBeenCalledWith("both", "en", expect.objectContaining({ genres: [] }));
+ expect(plexDiscover).toHaveBeenCalledWith(
+ "both",
+ "en",
+ expect.objectContaining({ genres: [] }),
+ );
+ });
+
+ it("registers the discover + surprise queries keyed by the active lang", async () => {
+ // The real cache-separation guard (the queryKeys factory test only pins that a lang ARG survives the
+ // spread): inspect the actual keys the component registered. A regression dropping lang from either
+ // qk call would collide HU/EN in the cache even while the queryFn still fetches the right language.
+ plexDiscover.mockResolvedValue(fullPayload);
+ plexSurprise.mockResolvedValue({ scope: "both", items: [] });
+ const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } });
+ render(
+
+
+ ,
+ );
+ await screen.findByText("plex.discover.continueWatching");
+ const keys = qc
+ .getQueryCache()
+ .getAll()
+ .map((q) => q.queryKey);
+ expect(keys).toContainEqual([
+ "plex-discover",
+ "both",
+ "en",
+ expect.objectContaining({ genres: [] }),
+ ]);
+ expect(keys).toContainEqual([
+ "plex-surprise",
+ "both",
+ "en",
+ expect.objectContaining({ genres: [] }),
+ ]);
});
it("fires onGenre with the tile's value", async () => {
diff --git a/frontend/src/components/PlexSidebar.tsx b/frontend/src/components/PlexSidebar.tsx
index a12cf31c..64d665cd 100644
--- a/frontend/src/components/PlexSidebar.tsx
+++ b/frontend/src/components/PlexSidebar.tsx
@@ -413,7 +413,7 @@ function CollectionPicker({
values: string[];
titles: Record | undefined;
mode: "any" | "all";
- onChange: (values: string[], titles: Record) => void;
+ onChange: (values: string[], titles: Record | undefined) => void;
onModeChange: (mode: "any" | "all") => void;
}) {
const { t } = useTranslation();
@@ -451,10 +451,12 @@ function CollectionPicker({
matchMode={mode === "all" ? "and" : "or"}
onMatchModeChange={(m) => onModeChange(m === "and" ? "all" : "any")}
onChange={(next) => {
- // Carry only the selected ids' titles forward (drop titles for de-selected ones).
+ // Carry only the selected ids' titles forward (drop titles for de-selected ones). Collapse an
+ // empty map to undefined so a clear-all key-matches the pristine state (an empty {} still hashes
+ // differently from an absent key), mirroring the chip-row remove handler.
const nextTitles: Record = {};
for (const id of next) nextTitles[id] = labelMap[id] ?? titles?.[id] ?? id;
- onChange(next, nextTitles);
+ onChange(next, next.length ? nextTitles : undefined);
}}
/>
);
diff --git a/frontend/src/lib/queryKeys.test.ts b/frontend/src/lib/queryKeys.test.ts
index 91bf121d..88b035d2 100644
--- a/frontend/src/lib/queryKeys.test.ts
+++ b/frontend/src/lib/queryKeys.test.ts
@@ -85,14 +85,22 @@ describe("qk — variadic composite plex keys", () => {
]);
});
- it("keys the Discover/Surprise rails by lang so a language switch caches separately", () => {
- // Both surfaces filter against the language-specific genre column, so HU and EN must not collide.
- expect(qk.plexDiscover("movie", "hu", FILTERS)).not.toEqual(
- qk.plexDiscover("movie", "en", FILTERS),
- );
- expect(qk.plexSurprise("movie", "hu", FILTERS)).not.toEqual(
- qk.plexSurprise("movie", "en", FILTERS),
- );
+ it("preserves scope+lang+filters as distinct Discover/Surprise key segments", () => {
+ // Shape-pinning only: these lock that the variadic factory carries every arg through (a "tidy-up"
+ // that drops a param would break cache separation). That the CALL SITE actually passes lang is
+ // guarded separately in PlexDiscover.dom.test.tsx (it inspects the registered query keys).
+ expect(qk.plexDiscover("movie", "hu", FILTERS)).toEqual([
+ "plex-discover",
+ "movie",
+ "hu",
+ FILTERS,
+ ]);
+ expect(qk.plexSurprise("movie", "hu", FILTERS)).toEqual([
+ "plex-surprise",
+ "movie",
+ "hu",
+ FILTERS,
+ ]);
// A bare call stays a prefix for the reshuffle/watched-toggle invalidations.
expect(qk.plexSurprise()).toEqual(["plex-surprise"]);
});