refactor(pins): drop the unused ComfortPinMenu and the comment that described it

Every surface composes the pin row into its object's one action list (E28 C1); the pin-only menu
had no caller left. Also un-exports isStandInFocus, which only its own file reads since H1.
This commit is contained in:
2026-09-27 15:03:28 +02:00
parent dddf79ec2f
commit 6ffb5bd8c1
3 changed files with 5 additions and 44 deletions
@@ -1,38 +0,0 @@
import { useTranslation } from "react-i18next";
import { useComfortPin, type PinTarget } from "./comfortPin";
import { ContextMenu } from "./ui/ContextMenu";
import { type ContextMenuController } from "./ui/useContextMenu";
// The menu SURFACE for the comfort-shelf pin (E24 S3). Split from `comfortPin.tsx` for the same
// reason `ContextMenu.tsx` is split from `useContextMenu.ts`: a file that exports components and
// nothing else keeps react-refresh working for every surface that imports it.
/**
* The whole right-click menu for a surface whose only context action is the pin — the card grids and
* rails. Render ONE per list, outside the rows (see `ui/ContextMenu`), and spread
* `menu.triggerProps` on each row.
*
* `target` maps the row the user right-clicked to its pin; `title` names it in the confirmation
* toast. Both come from the call site because only it knows what its rows are.
*/
export function ComfortPinMenu<T>({
menu,
target,
title,
}: {
menu: ContextMenuController<T>;
target: (row: T) => PinTarget | null;
title: (row: T) => string;
}) {
const { t } = useTranslation();
const pin = useComfortPin(menu.open);
const row = menu.target;
return (
<ContextMenu
menu={menu}
ariaLabel={t("home.pin.menu")}
items={row ? pin.item(target(row), title(row)) : []}
/>
);
}
+4 -5
View File
@@ -15,13 +15,12 @@ import { type ActionMenuItem } from "./ui/ActionMenu";
// shown — a video, a channel, a playlist, a Plex show or movie: you decide something is comfort media
// while you are watching it. (Locked decision 3: an episode, a season and a person are not pin kinds,
// so their cards and strips carry no pin.) This file is the one place the pin lives; every surface
// composes the same `ActionMenuItem` into the menu it already has (`Playlists`, `PlaylistsRail`) or
// mounts the `ComfortPinMenu` wrapper when it has none.
// composes the same `ActionMenuItem` into its object's ONE action list (E28 C1): the video, Plex and
// channel actions (`useVideoActions`, `usePlexCardActions`, `Channels`), the playlist rail and page,
// and Home's shelves. There is no pin-only menu any more.
//
// Feature layer, not `components/ui/`: it speaks a domain API slice, which the platform boundary
// (eslint `no-restricted-imports`) forbids a `ui/` primitive from doing. The MENU SURFACE that
// renders this item lives in `ComfortPinMenu.tsx` — the same hook/component split, for the same
// react-refresh reason, as `useContextMenu.ts` beside `ContextMenu.tsx`.
// (eslint `no-restricted-imports`) forbids a `ui/` primitive from doing.
/** What the API needs to pin something: the kind, and the id the rest of the API already speaks for
* that kind (a video id, a channel id, a Plex `rating_key`, a playlist id). Mirrors
+1 -1
View File
@@ -52,7 +52,7 @@ export function focusNeighbour(el: HTMLElement): boolean {
let standIn: HTMLElement | null = null;
/** Whether `el` holds the focus only because the keeper put it there. */
export function isStandInFocus(el: Element | null): boolean {
function isStandInFocus(el: Element | null): boolean {
return el !== null && el === standIn;
}