feat(plex): add a movie or show to a collection from its card menu
"Add to collection…" opens a panel beside the card: the title's library's manual collections tick it in or out, and a name + Enter creates a new one with the title in it. It used to take the Collections page, a new collection, its name and a title search, then another search per title. A lean endpoint lists the collections with whether each holds the title. Admin only, as on the title's page. Also: a collection seeded with a show is made as a show collection (Plex type 2). Measured: as a movie one it came out empty and refused the show after.
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Check, Info, ListPlus, Play, SkipBack, Tv2, Undo2 } from "lucide-react";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { Check, Info, Layers, ListPlus, Play, SkipBack, Tv2, Undo2 } from "lucide-react";
|
||||
|
||||
import { type PlexCard } from "../lib/api";
|
||||
import { qk } from "../lib/queryKeys";
|
||||
import PlexCollectionAddAt, { type PlexCollectionTarget } from "./PlexCollectionAdd";
|
||||
import { pinPlex, useComfortPin } from "./comfortPin";
|
||||
import { useCopyItems } from "./copyItems";
|
||||
import PlexPlaylistAdd, { type PlexAddTarget } from "./PlexPlaylistAdd";
|
||||
@@ -46,15 +49,26 @@ export function usePlexCardActions(handlers: PlexActionHandlers, open: boolean)
|
||||
const copy = useCopyItems();
|
||||
const addLabel = (c: PlexCard) => t(plexAddLabelKey(c));
|
||||
const [adding, setAdding] = useState<PlexAddTarget | null>(null);
|
||||
const [collecting, setCollecting] = useState<PlexCollectionTarget | null>(null);
|
||||
// Collections are shared and written to Plex itself, so only an admin edits them (as on the title's
|
||||
// own page).
|
||||
const qc = useQueryClient();
|
||||
const isAdmin = qc.getQueryData<{ role?: string }>(qk.me())?.role === "admin";
|
||||
// A whole show needs its episode keys, which only the show page has ("Add whole show" there), so
|
||||
// only a leaf is added from a card.
|
||||
const add = (c: PlexCard) => setAdding({ kind: "single", ratingKey: c.id, title: c.title });
|
||||
|
||||
/** `play: false` where the host menu leads with its own Play row (Home's shelf resolves `next`);
|
||||
* `pin: false` where the host carries its own shelf rows or the object is not a pin kind. */
|
||||
* `pin: false` where the host carries its own shelf rows or the object is not a pin kind;
|
||||
* `anchor`: the element the menu came from, which "Add to collection…" opens its panel beside —
|
||||
* without one the row is left out rather than opening the panel at a corner. */
|
||||
const items = (
|
||||
c: PlexCard,
|
||||
{ play = true, pin: withPin = true }: { play?: boolean; pin?: boolean } = {},
|
||||
{
|
||||
play = true,
|
||||
pin: withPin = true,
|
||||
anchor = null,
|
||||
}: { play?: boolean; pin?: boolean; anchor?: Element | null } = {},
|
||||
): ActionMenuItem[] => {
|
||||
const { onOpen, onInfo, onToggleWatched, onRestart } = handlers;
|
||||
const playable = isPlexPlayable(c);
|
||||
@@ -104,6 +118,18 @@ export function usePlexCardActions(handlers: PlexActionHandlers, open: boolean)
|
||||
},
|
||||
]
|
||||
: []),
|
||||
// A movie or a show can be a collection member; an episode cannot.
|
||||
...(isAdmin && anchor && c.type !== "episode"
|
||||
? [
|
||||
{
|
||||
id: "collection",
|
||||
label: t("plex.collectionAdd.row"),
|
||||
icon: <Layers className={icon} />,
|
||||
separatorBefore: !playable,
|
||||
onSelect: () => setCollecting({ ratingKey: c.id, anchor }),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(playable && onInfo
|
||||
? [
|
||||
{
|
||||
@@ -120,7 +146,14 @@ export function usePlexCardActions(handlers: PlexActionHandlers, open: boolean)
|
||||
];
|
||||
};
|
||||
|
||||
const panel = adding && <PlexPlaylistAdd target={adding} onClose={() => setAdding(null)} />;
|
||||
const panel = (
|
||||
<>
|
||||
{adding && <PlexPlaylistAdd target={adding} onClose={() => setAdding(null)} />}
|
||||
{collecting && (
|
||||
<PlexCollectionAddAt target={collecting} onClose={() => setCollecting(null)} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
return { items, add, panel };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user