A show card's picture opened the show page everywhere but Home, whose shelves carried the
resolved `next`. Now every Plex picture plays: a show asks `GET /api/plex/show/{id}/next`
(the in-progress episode, else the first unwatched, else S1·E1 — the rule Home and the show
page's Resume follow) on the click, fresh each time. A started show shows Continue + Restart;
its title, an ⓘ and the menu's "Open show" row open its page; one with no episodes opens there.
The question is asked per show, never carried on every card: three probes a show would cost a
grid page up to 180 queries. The hover preview asks it once it opens and names the episode
("Next: S6·E16 — Title"). Related shows on a show page play the same way.
42 lines
1.6 KiB
TypeScript
42 lines
1.6 KiB
TypeScript
import { useState, type HTMLAttributes } from "react";
|
|
import { useAnchoredPopover } from "./anchoredPopoverHooks";
|
|
|
|
// The hover wiring of a media card's preview (./MediaPreview has the why).
|
|
|
|
/** How long the pointer must rest on a card: long enough that a sweep across a grid opens nothing. */
|
|
const MEDIA_PREVIEW_DELAY = 900;
|
|
|
|
export function useMediaPreview(enabled = true) {
|
|
const [open, setOpen] = useState(false);
|
|
const pop = useAnchoredPopover({
|
|
open: open && enabled,
|
|
onOpenChange: setOpen,
|
|
mode: "hover",
|
|
placement: "right-start",
|
|
offset: 8,
|
|
hoverDelay: { open: MEDIA_PREVIEW_DELAY, close: 0 },
|
|
pointerOnly: true,
|
|
});
|
|
return {
|
|
popover: pop,
|
|
/** The preview is up: the pointer has rested on the card (what a card may fetch for it waits on). */
|
|
open: open && enabled,
|
|
/** The card root's props: the anchor, the hover wiring, and a close on any press — merged with
|
|
* the root's own (its menu's right-click and long-press). */
|
|
rootProps: (own: HTMLAttributes<HTMLDivElement> & Record<string, unknown> = {}) => ({
|
|
...(pop.getReferenceProps({
|
|
...own,
|
|
onPointerDown: (e: React.PointerEvent<HTMLDivElement>) => {
|
|
setOpen(false);
|
|
(own.onPointerDown as ((e: React.PointerEvent<HTMLDivElement>) => void) | undefined)?.(e);
|
|
},
|
|
onContextMenu: (e: React.MouseEvent<HTMLDivElement>) => {
|
|
setOpen(false);
|
|
(own.onContextMenu as ((e: React.MouseEvent<HTMLDivElement>) => void) | undefined)?.(e);
|
|
},
|
|
}) as HTMLAttributes<HTMLDivElement> & Record<string, unknown>),
|
|
ref: pop.refs.setReference,
|
|
}),
|
|
};
|
|
}
|