release: 0.66.0 — E5 Plex epic (scoped search, HU localization, toolbar/header)
Ships the E5 sprint stack (S1–S7): field-scoped Plex search + typeahead, cast & crew pages, the HU-overlay Hungarian metadata + language-aware serving, the IMDb/audience rating split, the toolbar relocation, and the narrow/mobile header fix — plus the underlying Plex-module refactors (feedquery/plex.cards/plex.query extraction, db.query→2.0 select). - VERSION 0.65.2 → 0.66.0 - releaseNotes.ts: 0.66.0 user-facing entry - prettier-format the E5-touched frontend files (release hygiene) Merged main (0.65.2 download-gc hotfix) in first; no code conflicts (E5 is Plex-only, the hotfix was notifications/downloads).
This commit is contained in:
@@ -1327,7 +1327,10 @@ function PlexShowView({
|
||||
const qc = useQueryClient();
|
||||
const isAdmin = qc.getQueryData<{ role?: string }>(qk.me())?.role === "admin";
|
||||
const lang = plexLang();
|
||||
const q = useQuery({ queryKey: qk.plexShow(showId, lang), queryFn: () => api.plexShow(showId, lang) });
|
||||
const q = useQuery({
|
||||
queryKey: qk.plexShow(showId, lang),
|
||||
queryFn: () => api.plexShow(showId, lang),
|
||||
});
|
||||
const d = q.data;
|
||||
const [addTarget, setAddTarget] = useState<PlexAddTarget | null>(null);
|
||||
const [collOpen, setCollOpen] = useState(false);
|
||||
@@ -1565,7 +1568,10 @@ function PlexSeasonView({
|
||||
const { t } = useTranslation();
|
||||
const qc = useQueryClient();
|
||||
const lang = plexLang();
|
||||
const q = useQuery({ queryKey: qk.plexShow(showId, lang), queryFn: () => api.plexShow(showId, lang) });
|
||||
const q = useQuery({
|
||||
queryKey: qk.plexShow(showId, lang),
|
||||
queryFn: () => api.plexShow(showId, lang),
|
||||
});
|
||||
const d = q.data;
|
||||
const se = d?.seasons.find((s) => s.id === seasonId);
|
||||
const [addTarget, setAddTarget] = useState<PlexAddTarget | null>(null);
|
||||
|
||||
@@ -19,7 +19,11 @@ vi.mock("react-i18next", async (importOriginal) => {
|
||||
vi.mock("../lib/useDebounced", () => ({ useDebounced: (v: unknown) => v }));
|
||||
|
||||
// Controllable provider — PlexSearchBox reads q/scope/field and calls setQ/setField/setJumpTo.
|
||||
const plexState = { scope: "both", field: "all", q: "" } as { scope: string; field: string; q: string };
|
||||
const plexState = { scope: "both", field: "all", q: "" } as {
|
||||
scope: string;
|
||||
field: string;
|
||||
q: string;
|
||||
};
|
||||
// setQ writes back to plexState.q so a typed value flows through to `dq` on the next render (the real
|
||||
// provider persists it) — lets the reopen test assert a fresh query fires on retype, not just that
|
||||
// the panel re-appears from cache.
|
||||
|
||||
@@ -8,11 +8,7 @@ import { usePlex, usePlexActions } from "./PlexProvider";
|
||||
import { api } from "../lib/api";
|
||||
import { qk } from "../lib/queryKeys";
|
||||
import { useDebounced } from "../lib/useDebounced";
|
||||
import {
|
||||
availablePlexFields,
|
||||
healPlexField,
|
||||
plexFieldLabelKey,
|
||||
} from "../lib/plexSearch";
|
||||
import { availablePlexFields, healPlexField, plexFieldLabelKey } from "../lib/plexSearch";
|
||||
|
||||
// The Plex header search box: the shared SearchBar enriched with the S2 search scope-pill (a
|
||||
// GitHub-style field selector inside the box) and an instant typeahead of the top matches attached
|
||||
@@ -46,8 +42,7 @@ export default function PlexSearchBox() {
|
||||
const suggestQ = useQuery({
|
||||
queryKey: qk.plexSuggest(scope, dq, activeField),
|
||||
enabled: suggestEnabled,
|
||||
queryFn: () =>
|
||||
api.plexLibrary({ scope, q: dq, field: activeField, limit: SUGGEST_LIMIT }),
|
||||
queryFn: () => api.plexLibrary({ scope, q: dq, field: activeField, limit: SUGGEST_LIMIT }),
|
||||
staleTime: 30_000,
|
||||
});
|
||||
const suggestions = suggestEnabled ? (suggestQ.data?.items ?? []) : [];
|
||||
@@ -58,9 +53,7 @@ export default function PlexSearchBox() {
|
||||
// Keep the highlighted option scrolled into view during arrow-key navigation.
|
||||
useEffect(() => {
|
||||
if (hi < 0) return;
|
||||
listRef.current
|
||||
?.querySelector(`[data-idx="${hi}"]`)
|
||||
?.scrollIntoView({ block: "nearest" });
|
||||
listRef.current?.querySelector(`[data-idx="${hi}"]`)?.scrollIntoView({ block: "nearest" });
|
||||
}, [hi]);
|
||||
|
||||
const pick = (i: number) => {
|
||||
|
||||
@@ -15,9 +15,7 @@ vi.mock("react-i18next", async (importOriginal) => {
|
||||
|
||||
describe("SearchBar responsive wrapper (E5 S7)", () => {
|
||||
it("carries the container-query classes that make it wrap instead of collapsing", () => {
|
||||
const { container } = render(
|
||||
<SearchBar value="" onChange={() => {}} placeholder="Search…" />,
|
||||
);
|
||||
const { container } = render(<SearchBar value="" onChange={() => {}} placeholder="Search…" />);
|
||||
const cls = (container.firstElementChild as HTMLElement).className;
|
||||
// A usable min-width floor once the column has room → the box wraps via flex-wrap, never ~0:
|
||||
expect(cls).toContain("@min-[420px]/appmain:min-w-[15rem]");
|
||||
|
||||
Reference in New Issue
Block a user