import { useTranslation } from "react-i18next"; import { Copy, Link2 } from "lucide-react"; import { copyText } from "../../lib/clipboard"; import { msg, type NotifText, notify } from "../../lib/notifications"; import { type ActionMenuItem } from "./ActionMenu"; // "Copy title" and "Copy link", the two rows every object's menu ends its middle group with // (DESIGN.md C1, "Text stays selectable"): on touch the long-press belongs to the context menu, so the text a // finger cannot select is copied from here. One builder, so every object says it the same way and // the clipboard write always runs inside the gesture (`inGesture`, which Safari insists on). // // Domain-free: it speaks through lib/notifications, the platform's toast service, so any module's menu // can end with it. type Named = "title" | "name"; export function useCopyItems() { const { t } = useTranslation(); const copy = (text: string, done: NotifText) => { void copyText(text).then((ok) => notify( ok ? { level: "success", message: done } : { level: "error", message: msg("common.copy.failed") }, ), ); }; /** The rows for one object. `text` is its title (a video, a show) or its name (a channel, a * playlist); a row whose value is missing is left out rather than copying an empty string. * `linkLabel` names the link row where "Copy link" would be ambiguous (a library file's menu also * copies its share link). */ return ( text: string | null | undefined, link: string | null | undefined, named: Named = "title", linkLabel?: string, ): ActionMenuItem[] => [ ...(text ? [ { id: "copy-text", label: t(named === "title" ? "common.copy.title" : "common.copy.name"), icon: , inGesture: true, onSelect: () => copy(text, msg("common.copy.done")), }, ] : []), ...(link ? [ { id: "copy-link", label: linkLabel ?? t("common.copy.link"), icon: , inGesture: true, onSelect: () => copy(link, msg("common.copy.linkDone")), }, ] : []), ]; }