import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { copyText } from "../lib/clipboard"; import { notify } from "../lib/notifications"; // How long a Copy button reads "Copied" after it worked. const COPIED_MS = 2000; /** The feedback of a Copy button that sits beside the value it copies (a token, a share link, a * set-password link), the same at every such button: it reads "Copied" for a moment, and a notice * says it too — the notices are a live region, so a screen reader hears what a swapped label would * not tell it. A copy that failed is a warning, not an error: the value is on screen and * selectable, so it is a convenience that failed, not the action. * * A feature file, not `ui/`: it speaks the app's notifications (as `copyItems` does). */ export function useCopyFeedback(): { copied: boolean; copy: (value: string) => Promise } { const { t } = useTranslation(); const [copied, setCopied] = useState(false); // One reset at a time: a copy again within the moment starts it over, not cut short by the last. const timer = useRef(undefined); useEffect(() => () => window.clearTimeout(timer.current), []); const copy = async (value: string) => { if (await copyText(value)) { setCopied(true); notify({ level: "success", message: t("common.copy.done") }); window.clearTimeout(timer.current); timer.current = window.setTimeout(() => setCopied(false), COPIED_MS); return; } notify({ level: "warning", message: t("common.copy.failed") }); }; return { copied, copy }; }