import { useLayoutEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { useQueryClient } from "@tanstack/react-query"; import { SlidersHorizontal } from "lucide-react"; import { useDismiss } from "./useDismiss"; import { api } from "./api"; // Shared "detail page" UI reused by the movie info page (PlexInfo) and the series show/season pages, // so they look consistent (the standing glassy/HTPC brief): the faint fixed art backdrop, the two // per-user display prefs (art background / cast row — same keys as the movie info page), and the // customize menu that toggles them. export function useDetailPrefs() { const qc = useQueryClient(); const prefs = (qc.getQueryData<{ preferences?: Record }>(["me"])?.preferences ?? {}) as { plexInfoArtBg?: boolean; plexInfoCast?: boolean; }; const [artBg, setArtBg] = useState(prefs.plexInfoArtBg !== false); const [showCast, setShowCast] = useState(prefs.plexInfoCast !== false); const save = (patch: Record) => { api.savePrefs(patch).catch(() => {}); qc.setQueryData<{ preferences?: Record } | undefined>(["me"], (m) => m ? { ...m, preferences: { ...(m.preferences || {}), ...patch } } : m, ); }; return { artBg, showCast, toggleArtBg: () => { const next = !artBg; setArtBg(next); save({ plexInfoArtBg: next }); }, toggleCast: () => { const next = !showCast; setShowCast(next); save({ plexInfoCast: next }); }, }; } // Paint the item's art as a faint FIXED backdrop on the page's
scroll container (HTPC-style), // so the glass panels float over it. Cleared on unmount / when disabled / when there's no art. export function useArtBackdrop(art: string | null | undefined, enabled: boolean) { useLayoutEffect(() => { const main = document.querySelector("main"); if (!main) return; const s = main.style; const clear = () => { s.backgroundImage = ""; s.backgroundSize = ""; s.backgroundPosition = ""; s.backgroundRepeat = ""; s.backgroundAttachment = ""; }; if (enabled && art) { s.backgroundImage = `linear-gradient(color-mix(in srgb, var(--bg) 60%, transparent), ` + `color-mix(in srgb, var(--bg) 64%, transparent)), url("${art}")`; s.backgroundSize = "cover"; s.backgroundPosition = "center 20%"; s.backgroundRepeat = "no-repeat"; s.backgroundAttachment = "fixed"; } else { clear(); } return clear; }, [art, enabled]); } export function DetailCustomizeMenu({ artBg, onToggleArtBg, showCast, onToggleCast, hasCast, }: { artBg: boolean; onToggleArtBg: () => void; showCast: boolean; onToggleCast: () => void; hasCast: boolean; }) { const { t } = useTranslation(); const [open, setOpen] = useState(false); const btnRef = useRef(null); const menuRef = useRef(null); useDismiss(open, () => setOpen(false), [menuRef, btnRef]); return (
{open && (
{hasCast && }
)}
); } function PrefToggle({ label, on, onClick }: { label: string; on: boolean; onClick: () => void }) { return ( ); }