mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-25 00:36:31 +02:00
Add clipboard utility with fallback and show selectable URLs
This commit is contained in:
parent
ab3d99d9e0
commit
1cf7205a81
3 changed files with 57 additions and 51 deletions
|
|
@ -38,6 +38,13 @@ export function PublicChatSnapshotRow({
|
|||
{snapshot.message_count}
|
||||
</span>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
readOnly
|
||||
value={snapshot.public_url}
|
||||
className="mt-2 w-full text-xs text-muted-foreground bg-muted/50 border rounded px-2 py-1 select-all focus:outline-none focus:ring-1 focus:ring-ring"
|
||||
onClick={(e) => (e.target as HTMLInputElement).select()}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useCallback, useEffect, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { getBearerToken } from "@/lib/auth-utils";
|
||||
import { copyToClipboard as copyToClipboardUtil } from "@/lib/utils";
|
||||
|
||||
interface UseApiKeyReturn {
|
||||
apiKey: string | null;
|
||||
|
|
@ -33,60 +34,17 @@ export function useApiKey(): UseApiKeyReturn {
|
|||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
const fallbackCopyTextToClipboard = (text: string) => {
|
||||
const textArea = document.createElement("textarea");
|
||||
textArea.value = text;
|
||||
|
||||
// Avoid scrolling to bottom
|
||||
textArea.style.top = "0";
|
||||
textArea.style.left = "0";
|
||||
textArea.style.position = "fixed";
|
||||
textArea.style.opacity = "0";
|
||||
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
|
||||
try {
|
||||
const successful = document.execCommand("copy");
|
||||
document.body.removeChild(textArea);
|
||||
|
||||
if (successful) {
|
||||
setCopied(true);
|
||||
toast.success("API key copied to clipboard");
|
||||
|
||||
setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 2000);
|
||||
} else {
|
||||
toast.error("Failed to copy API key");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Fallback: Oops, unable to copy", err);
|
||||
document.body.removeChild(textArea);
|
||||
toast.error("Failed to copy API key");
|
||||
}
|
||||
};
|
||||
|
||||
const copyToClipboard = useCallback(async () => {
|
||||
if (!apiKey) return;
|
||||
|
||||
try {
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
// Use Clipboard API if available and in secure context
|
||||
await navigator.clipboard.writeText(apiKey);
|
||||
setCopied(true);
|
||||
toast.success("API key copied to clipboard");
|
||||
|
||||
setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 2000);
|
||||
} else {
|
||||
// Fallback for non-secure contexts or browsers without clipboard API
|
||||
fallbackCopyTextToClipboard(apiKey);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to copy:", err);
|
||||
const success = await copyToClipboardUtil(apiKey);
|
||||
if (success) {
|
||||
setCopied(true);
|
||||
toast.success("API key copied to clipboard");
|
||||
setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 2000);
|
||||
} else {
|
||||
toast.error("Failed to copy API key");
|
||||
}
|
||||
}, [apiKey]);
|
||||
|
|
|
|||
|
|
@ -12,3 +12,44 @@ export const formatDate = (date: Date): string => {
|
|||
day: "numeric",
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Copy text to clipboard with fallback for older browsers and non-secure contexts.
|
||||
* Returns true if successful, false otherwise.
|
||||
*/
|
||||
export async function copyToClipboard(text: string): Promise<boolean> {
|
||||
// Use modern Clipboard API if available and in secure context
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error("Clipboard API failed:", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback for non-secure contexts or browsers without Clipboard API
|
||||
const textArea = document.createElement("textarea");
|
||||
textArea.value = text;
|
||||
|
||||
// Avoid scrolling to bottom
|
||||
textArea.style.top = "0";
|
||||
textArea.style.left = "0";
|
||||
textArea.style.position = "fixed";
|
||||
textArea.style.opacity = "0";
|
||||
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
|
||||
try {
|
||||
const successful = document.execCommand("copy");
|
||||
document.body.removeChild(textArea);
|
||||
return successful;
|
||||
} catch (err) {
|
||||
console.error("Fallback copy failed:", err);
|
||||
document.body.removeChild(textArea);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue