fix: async clipboard support in Safari (#16619)
This commit is contained in:
1 parent
111e2f30c6
commit
fcb79888f3
2 files changed
+46
-11
No files matched your search
@@ -929,7 +929,7 @@ function SideBarContainer({ bannersHeight, isPlatformUser = false }: SideBarCont
|
||||
}
|
||||
|
||||
function SideBar({ bannersHeight, user }: SideBarProps) {
|
||||
const { copyToClipboard } = useCopy();
|
||||
const { fetchAndCopyToClipboard } = useCopy();
|
||||
const { t, isLocaleReady } = useLocale();
|
||||
const orgBranding = useOrgBranding();
|
||||
const pathname = usePathname();
|
||||
@@ -967,16 +967,19 @@ function SideBar({ bannersHeight, user }: SideBarProps) {
|
||||
? {
|
||||
name: "copy_referral_link",
|
||||
href: "",
|
||||
onClick: async (e: { preventDefault: () => void }) => {
|
||||
onClick: (e: { preventDefault: () => void }) => {
|
||||
e.preventDefault();
|
||||
const res = await fetch("/api/generate-referral-link", {
|
||||
method: "POST",
|
||||
});
|
||||
const { shortLink } = await res.json();
|
||||
copyToClipboard(shortLink, {
|
||||
onSuccess: () => showToast(t("link_copied"), "success"),
|
||||
onFailure: () => showToast("Copy to clipboard failed", "error"),
|
||||
});
|
||||
fetchAndCopyToClipboard(
|
||||
fetch("/api/generate-referral-link", {
|
||||
method: "POST",
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((res) => res.shortLink),
|
||||
{
|
||||
onSuccess: () => showToast(t("link_copied"), "success"),
|
||||
onFailure: () => showToast("Copy to clipboard failed", "error"),
|
||||
}
|
||||
);
|
||||
},
|
||||
icon: "gift",
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ export function useCopy() {
|
||||
"You need to use a secure context to use clipboard \n Please use the following link: ",
|
||||
text
|
||||
);
|
||||
onFailure();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -34,6 +35,37 @@ export function useCopy() {
|
||||
setIsCopied(false);
|
||||
};
|
||||
|
||||
/** @see https://wolfgangrittner.dev/how-to-use-clipboard-api-in-safari/ */
|
||||
const fetchAndCopyToClipboard = (
|
||||
promise: Promise<string>,
|
||||
options: { onSuccess?: () => void; onFailure?: () => void } = {}
|
||||
) => {
|
||||
const { onSuccess = noop, onFailure = noop } = options;
|
||||
if (typeof ClipboardItem && navigator.clipboard.write) {
|
||||
// NOTE: Safari locks down the clipboard API to only work when triggered
|
||||
// by a direct user interaction. You can't use it async in a promise.
|
||||
// But! You can wrap the promise in a ClipboardItem, and give that to
|
||||
// the clipboard API.
|
||||
// Found this on https://developer.apple.com/forums/thread/691873
|
||||
const text = new ClipboardItem({
|
||||
"text/plain": promise
|
||||
.then((text) => new Blob([text], { type: "text/plain" }))
|
||||
.catch(() => {
|
||||
onFailure();
|
||||
return "";
|
||||
}),
|
||||
});
|
||||
navigator.clipboard.write([text]);
|
||||
onSuccess();
|
||||
} else {
|
||||
// NOTE: Firefox has support for ClipboardItem and navigator.clipboard.write,
|
||||
// but those are behind `dom.events.asyncClipboard.clipboardItem` preference.
|
||||
// Good news is that other than Safari, Firefox does not care about
|
||||
// Clipboard API being used async in a Promise.
|
||||
promise.then((text) => copyToClipboard(text, options)).catch(() => onFailure());
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isCopied) {
|
||||
const timer = setTimeout(resetCopyStatus, 3000); // Reset copy status after 3 seconds
|
||||
@@ -41,5 +73,5 @@ export function useCopy() {
|
||||
}
|
||||
}, [isCopied]);
|
||||
|
||||
return { isCopied, copyToClipboard, resetCopyStatus };
|
||||
return { isCopied, copyToClipboard, resetCopyStatus, fetchAndCopyToClipboard };
|
||||
}
|
||||
Reference in new issue
Block a user