fix: safe copy to clipboard

This commit is contained in:
Carlos Valente
2025-11-30 20:03:07 +01:00
committed by Carlos Valente
parent 9ba5ebd4bd
commit d116670318
3 changed files with 32 additions and 14 deletions
@@ -1,7 +1,7 @@
import { PropsWithChildren, useRef, useState } from 'react';
import { IoCheckmark, IoCopy } from 'react-icons/io5';
import copyToClipboard from '../../utils/copyToClipboard';
import { copyToClipboard } from '../../utils/copyToClipboard';
import { cx } from '../../utils/styleUtils';
import Button from '../buttons/Button';
import IconButton from '../buttons/IconButton';
@@ -25,15 +25,19 @@ export default function CopyTag({
const [copied, setCopied] = useState(false);
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const handleClick = () => {
copyToClipboard(copyValue);
setCopied(true);
const handleClick = async () => {
try {
await copyToClipboard(copyValue);
setCopied(true);
// reset copied state
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
// reset copied state
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => setCopied(false), 2000);
} catch {
// ignore errors
}
timeoutRef.current = setTimeout(() => setCopied(false), 2000);
};
return (
@@ -1,4 +1,18 @@
// we need to this as a promise because safari
export default async function copyToClipboard(text: string) {
setTimeout(async () => await navigator.clipboard?.writeText(text));
/**
* copy text to clipboard
* @throws if not supported or permission denied
*/
export async function copyToClipboard(text: string) {
await navigator.clipboard?.writeText(text);
}
/**
* Copy to clipboard but safely ignore errors
*/
export async function safeCopyToClipboard(text: string): Promise<void> {
try {
await copyToClipboard(text);
} catch {
// Silently ignore errors
}
}
@@ -13,7 +13,7 @@ import Input from '../../common/components/input/input/Input';
import Select from '../../common/components/select/Select';
import Switch from '../../common/components/switch/Switch';
import { useUpdateUrlPreset } from '../../common/hooks-query/useUrlPresets';
import copyToClipboard from '../../common/utils/copyToClipboard';
import { safeCopyToClipboard } from '../../common/utils/copyToClipboard';
import { preventEscape } from '../../common/utils/keyEvent';
import { isUrlSafe } from '../../common/utils/regex';
import { isOntimeCloud, serverURL } from '../../externals';
@@ -125,7 +125,7 @@ export default function GenerateLinkForm({ hostOptions, pathOptions, presets, is
lockNav: options.lockNav,
preset: urlPreset.alias,
});
await copyToClipboard(url);
await safeCopyToClipboard(url);
setUrl(url);
} else {
const presetPath = options.path.startsWith('preset-') ? options.path.replace('preset-', '') : undefined;
@@ -143,7 +143,7 @@ export default function GenerateLinkForm({ hostOptions, pathOptions, presets, is
preset: presetPath,
});
await copyToClipboard(url);
await safeCopyToClipboard(url);
setUrl(url);
}
reset(options, {