mirror of
https://github.com/Viren070/AIOStreams.git
synced 2025-12-01 23:14:04 +01:00
refactor(frontend): implement copyToClipboard utility for consistent clipboard operations across components
This commit is contained in:
@@ -11,6 +11,7 @@ import { Toaster } from '@/components/ui/toaster';
|
||||
import { toast } from 'sonner';
|
||||
import { UserConfigAPI } from '@/services/api';
|
||||
import { BiCopy } from 'react-icons/bi';
|
||||
import { copyToClipboard } from '@/utils/clipboard';
|
||||
|
||||
function OAuthCallbackContent() {
|
||||
const [code, setCode] = useState<string | null>(null);
|
||||
@@ -45,17 +46,12 @@ function OAuthCallbackContent() {
|
||||
|
||||
const handleCopy = async () => {
|
||||
if (code) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(code);
|
||||
toast.success('Copied!', {
|
||||
description:
|
||||
'The authorization code has been copied to your clipboard.',
|
||||
});
|
||||
} catch (err) {
|
||||
toast.error('Failed to copy', {
|
||||
description: 'Please try copying the code manually.',
|
||||
});
|
||||
}
|
||||
await copyToClipboard(code, {
|
||||
successMessage: 'Copied!',
|
||||
errorMessage: 'Failed to copy',
|
||||
description:
|
||||
'The authorization code has been copied to your clipboard.',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ import { TbFilterCode } from 'react-icons/tb';
|
||||
import { PasswordInput } from '../ui/password-input';
|
||||
import MarkdownLite from '../shared/markdown-lite';
|
||||
import { useMode } from '@/context/mode';
|
||||
import { copyToClipboard } from '@/utils/clipboard';
|
||||
|
||||
type Resolution = (typeof RESOLUTIONS)[number];
|
||||
type Quality = (typeof QUALITIES)[number];
|
||||
@@ -2590,10 +2591,11 @@ function Content() {
|
||||
size="sm"
|
||||
intent="primary-subtle"
|
||||
icon={<FaRegCopy />}
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(url);
|
||||
toast.success('URL copied to clipboard');
|
||||
}}
|
||||
onClick={() =>
|
||||
copyToClipboard(url, {
|
||||
successMessage: 'URL copied to clipboard',
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -24,6 +24,7 @@ import { Tooltip } from '../ui/tooltip';
|
||||
import { FaFileImport, FaFileExport } from 'react-icons/fa';
|
||||
import { IconButton } from '../ui/button';
|
||||
import { ImportModal } from '../shared/import-modal';
|
||||
import { copyToClipboard } from '@/utils/clipboard';
|
||||
const formatterChoices = Object.values(constants.FORMATTER_DETAILS);
|
||||
|
||||
// Remove the throttle utility and replace with FormatQueue
|
||||
@@ -614,22 +615,10 @@ function SnippetsButton() {
|
||||
intent="primary-outline"
|
||||
className="sm:ml-4 flex-shrink-0"
|
||||
onClick={async () => {
|
||||
if (!navigator.clipboard) {
|
||||
toast.error(
|
||||
'The clipboard API is not available in this browser or context.'
|
||||
);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await navigator.clipboard.writeText(snippet.value);
|
||||
toast.success('Snippet copied to clipboard');
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'Failed to copy snippet to clipboard:',
|
||||
error
|
||||
);
|
||||
toast.error('Failed to copy snippet to clipboard');
|
||||
}
|
||||
await copyToClipboard(snippet.value, {
|
||||
successMessage: 'Snippet copied to clipboard',
|
||||
errorMessage: 'Failed to copy snippet to clipboard',
|
||||
});
|
||||
}}
|
||||
title="Copy snippet"
|
||||
>
|
||||
|
||||
@@ -11,6 +11,7 @@ import { toast } from 'sonner';
|
||||
import { CopyIcon, DownloadIcon, PlusIcon, UploadIcon } from 'lucide-react';
|
||||
import { useStatus } from '@/context/status';
|
||||
import { BiCopy } from 'react-icons/bi';
|
||||
import { copyToClipboard } from '@/utils/clipboard';
|
||||
import { PageControls } from '../shared/page-controls';
|
||||
import { useDisclosure } from '@/hooks/disclosure';
|
||||
import { Modal } from '../ui/modal';
|
||||
@@ -304,18 +305,10 @@ function Content() {
|
||||
const encodedManifest = encodeURIComponent(manifestUrl);
|
||||
|
||||
const copyManifestUrl = async () => {
|
||||
try {
|
||||
if (!navigator.clipboard) {
|
||||
toast.error(
|
||||
'The Clipboard API is not supported on this browser or context, please manually copy the URL'
|
||||
);
|
||||
return;
|
||||
}
|
||||
await navigator.clipboard.writeText(manifestUrl);
|
||||
toast.success('Manifest URL copied to clipboard');
|
||||
} catch (err) {
|
||||
toast.error('Failed to copy manifest URL');
|
||||
}
|
||||
await copyToClipboard(manifestUrl, {
|
||||
successMessage: 'Manifest URL copied to clipboard',
|
||||
errorMessage: 'Failed to copy manifest URL',
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
@@ -431,10 +424,11 @@ function Content() {
|
||||
</span>
|
||||
<BiCopy
|
||||
className="min-h-5 min-w-5 cursor-pointer"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(uuid);
|
||||
toast.success('UUID copied to clipboard');
|
||||
}}
|
||||
onClick={() =>
|
||||
copyToClipboard(uuid, {
|
||||
successMessage: 'UUID copied to clipboard',
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-sm text-[--muted]">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { copyToClipboard } from '@/utils/clipboard';
|
||||
|
||||
interface MarkdownLiteProps {
|
||||
children: string;
|
||||
@@ -120,14 +121,10 @@ function processInlineMarkdown(text: string, stopPropagation: boolean) {
|
||||
if (stopPropagation) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
// copy to clipboard
|
||||
try {
|
||||
await navigator.clipboard.writeText(match.slice(1, -1));
|
||||
toast.success('Copied to clipboard');
|
||||
} catch (error) {
|
||||
console.error('Failed to copy to clipboard:', error);
|
||||
toast.error('Failed to copy to clipboard');
|
||||
}
|
||||
await copyToClipboard(match.slice(1, -1), {
|
||||
successMessage: 'Copied to clipboard',
|
||||
errorMessage: 'Failed to copy to clipboard',
|
||||
});
|
||||
}}
|
||||
className="bg-muted px-1 py-0.5 rounded text-[--brand] font-mono text-xs break-all cursor-pointer"
|
||||
>
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { toast } from 'sonner';
|
||||
|
||||
type CopyOptions = {
|
||||
successMessage?: string;
|
||||
errorMessage?: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
export async function copyToClipboard(text: string, options: CopyOptions = {}) {
|
||||
const {
|
||||
successMessage = 'Copied to clipboard',
|
||||
errorMessage = 'Failed to copy to clipboard',
|
||||
description,
|
||||
} = options;
|
||||
|
||||
try {
|
||||
if (typeof navigator === 'undefined') {
|
||||
throw new Error('Navigator is not available');
|
||||
}
|
||||
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
await navigator.clipboard.writeText(text);
|
||||
toast.success(successMessage, description ? { description } : undefined);
|
||||
return { success: true as const };
|
||||
}
|
||||
|
||||
// Fallback for environments where Clipboard API is unavailable (e.g., http:, some iframes)
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.value = text;
|
||||
// Prevent scrolling to bottom on iOS
|
||||
textarea.style.position = 'fixed';
|
||||
textarea.style.opacity = '0';
|
||||
document.body.appendChild(textarea);
|
||||
textarea.focus();
|
||||
textarea.select();
|
||||
|
||||
const successful = document.execCommand('copy');
|
||||
document.body.removeChild(textarea);
|
||||
|
||||
if (!successful) {
|
||||
throw new Error('execCommand copy failed');
|
||||
}
|
||||
|
||||
toast.success(successMessage, description ? { description } : undefined);
|
||||
return { success: true as const };
|
||||
} catch (error) {
|
||||
toast.error(errorMessage);
|
||||
return { success: false as const, error: error as unknown };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user