feat: add safari clipboard copy support

This commit is contained in:
David Nguyen
2023-10-02 17:55:21 +11:00
parent fa61bb660e
commit 6b801fff34
8 changed files with 147 additions and 131 deletions

View File

@ -0,0 +1,54 @@
import { trpc } from '@documenso/trpc/react';
import { TCreateOrGetShareLinkMutationSchema } from '@documenso/trpc/server/share-link-router/schema';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { useCopyToClipboard } from './use-copy-to-clipboard';
export function useCopyShareLink() {
const { toast } = useToast();
const [, copyToClipboard] = useCopyToClipboard();
const { mutateAsync: createOrGetShareLink, isLoading: isCreatingShareLink } =
trpc.shareLink.createOrGetShareLink.useMutation();
/**
* Copy a share link to the user's clipboard.
*
* Will create or get a share link if one is not provided.
*
* @param payload Either the share link itself or the input to create a new share link.
*/
const copyShareLink = async (payload: TCreateOrGetShareLinkMutationSchema | string) => {
const valueToCopy =
typeof payload === 'string'
? payload
: createOrGetShareLink(payload).then(
(result) => `${window.location.origin}/share/${result.slug}`,
);
try {
const isCopySuccess = await copyToClipboard(valueToCopy);
if (!isCopySuccess) {
throw new Error('Copy to clipboard failed');
}
toast({
title: 'Copied to clipboard',
description: 'The sharing link has been copied to your clipboard.',
});
} catch {
toast({
variant: 'destructive',
title: 'Something went wrong',
description: 'The sharing link could not be created at this time. Please try again.',
duration: 5000,
});
}
};
return {
isCopyingShareLink: isCreatingShareLink,
copyShareLink,
};
}

View File

@ -0,0 +1,55 @@
import { useState } from 'react';
export type CopiedValue = string | null;
export type CopyFn = (_text: CopyValue, _blobType?: string) => Promise<boolean>;
type CopyValue = Promise<string> | string;
export function useCopyToClipboard(): [CopiedValue, CopyFn] {
const [copiedText, setCopiedText] = useState<CopiedValue>(null);
const copy: CopyFn = async (text, blobType = 'text/plain') => {
if (!navigator?.clipboard) {
console.warn('Clipboard not supported');
return false;
}
const isClipboardApiSupported = Boolean(typeof ClipboardItem && navigator.clipboard.write);
// Try to save to clipboard then save it in the state if worked
try {
isClipboardApiSupported
? await handleClipboardApiCopy(text, blobType)
: await handleWriteTextCopy(text);
setCopiedText(await text);
return true;
} catch (error) {
console.warn('Copy failed', error);
setCopiedText(null);
return false;
}
};
/**
* Handle copying values to the clipboard using the ClipboardItem API.
*
* Allows us to copy async values for Safari. Does not work in FireFox.
*
* https://caniuse.com/mdn-api_clipboarditem
*/
const handleClipboardApiCopy = async (value: CopyValue, blobType = 'text/plain') => {
await navigator.clipboard.write([new ClipboardItem({ [blobType]: value })]);
};
/**
* Handle copying values to the clipboard using `writeText`.
*
* Will not work in Safari for async values.
*/
const handleWriteTextCopy = async (value: CopyValue) => {
await navigator.clipboard.writeText(await value);
};
return [copiedText, copy];
}