mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-05-01 16:17:55 +02:00
refactor: reuse copy-to-clipboard button
This commit is contained in:
@@ -2,8 +2,6 @@
|
||||
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { LuCopy } from "react-icons/lu";
|
||||
import { toast } from "sonner";
|
||||
import { LoadingButton } from "@/components/loading-button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import {
|
||||
@@ -31,6 +29,7 @@ import { useProfileEvents } from "@/hooks/use-profile-events";
|
||||
import { useProxyEvents } from "@/hooks/use-proxy-events";
|
||||
import { getBrowserDisplayName, getBrowserIcon } from "@/lib/browser-utils";
|
||||
import type { BrowserProfile } from "@/types";
|
||||
import { CopyToClipboard } from "./ui/copy-to-clipboard";
|
||||
import { RippleButton } from "./ui/ripple";
|
||||
|
||||
interface ProfileSelectorDialogProps {
|
||||
@@ -122,18 +121,6 @@ export function ProfileSelectorDialog({
|
||||
onClose();
|
||||
}, [onClose]);
|
||||
|
||||
const handleCopyUrl = useCallback(async () => {
|
||||
if (!url) return;
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(url);
|
||||
toast.success("URL copied to clipboard!");
|
||||
} catch (error) {
|
||||
console.error("Failed to copy URL:", error);
|
||||
toast.error("Failed to copy URL to clipboard");
|
||||
}
|
||||
}, [url]);
|
||||
|
||||
const selectedProfileData = profiles.find((p) => p.name === selectedProfile);
|
||||
|
||||
// Check if the selected profile can be used for opening links
|
||||
@@ -186,15 +173,10 @@ export function ProfileSelectorDialog({
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between items-center">
|
||||
<Label className="text-sm font-medium">Opening URL:</Label>
|
||||
<RippleButton
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => void handleCopyUrl()}
|
||||
className="flex gap-2 items-center"
|
||||
>
|
||||
<LuCopy className="w-3 h-3" />
|
||||
Copy
|
||||
</RippleButton>
|
||||
<CopyToClipboard
|
||||
text={url}
|
||||
successMessage="URL copied to clipboard!"
|
||||
/>
|
||||
</div>
|
||||
<div className="p-2 text-sm break-all rounded bg-muted">
|
||||
{url}
|
||||
|
||||
@@ -46,6 +46,7 @@ import {
|
||||
THEMES,
|
||||
} from "@/lib/themes";
|
||||
import { showErrorToast, showSuccessToast } from "@/lib/toast-utils";
|
||||
import { CopyToClipboard } from "./ui/copy-to-clipboard";
|
||||
import { RippleButton } from "./ui/ripple";
|
||||
|
||||
interface AppSettings {
|
||||
@@ -839,16 +840,10 @@ export function SettingsDialog({ isOpen, onClose }: SettingsDialogProps) {
|
||||
readOnly
|
||||
className="flex-1 px-3 py-2 font-mono text-sm rounded-md border bg-muted"
|
||||
/>
|
||||
<RippleButton
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(settings.api_token || "");
|
||||
showSuccessToast("API token copied to clipboard");
|
||||
}}
|
||||
>
|
||||
Copy
|
||||
</RippleButton>
|
||||
<CopyToClipboard
|
||||
text={settings.api_token || ""}
|
||||
successMessage="API token copied to clipboard"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Include this token in the Authorization header as "Bearer{" "}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useState } from "react";
|
||||
import { LuCheck, LuCopy } from "react-icons/lu";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { showSuccessToast } from "@/lib/toast-utils";
|
||||
|
||||
interface CopyToClipboardProps {
|
||||
text: string;
|
||||
variant?:
|
||||
| "default"
|
||||
| "destructive"
|
||||
| "outline"
|
||||
| "secondary"
|
||||
| "ghost"
|
||||
| "link";
|
||||
size?: "default" | "sm" | "lg" | "icon";
|
||||
className?: string;
|
||||
successMessage?: string;
|
||||
}
|
||||
|
||||
export function CopyToClipboard({
|
||||
text,
|
||||
variant = "outline",
|
||||
size = "icon",
|
||||
className,
|
||||
successMessage = "Copied to clipboard",
|
||||
}: CopyToClipboardProps) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const copyToClipboard = useCallback(async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
setCopied(true);
|
||||
showSuccessToast(successMessage);
|
||||
setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 2000);
|
||||
} catch (error) {
|
||||
console.error("Failed to copy to clipboard:", error);
|
||||
}
|
||||
}, [text, successMessage]);
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant={variant}
|
||||
size={size}
|
||||
className={`relative ${className || ""}`}
|
||||
onClick={copyToClipboard}
|
||||
aria-label={copied ? "Copied" : "Copy to clipboard"}
|
||||
>
|
||||
<span className="sr-only">{copied ? "Copied" : "Copy"}</span>
|
||||
<LuCopy
|
||||
className={`h-4 w-4 transition-all duration-300 ${
|
||||
copied ? "scale-0" : "scale-100"
|
||||
}`}
|
||||
/>
|
||||
<LuCheck
|
||||
className={`absolute inset-0 m-auto h-4 w-4 transition-all duration-300 ${
|
||||
copied ? "scale-100" : "scale-0"
|
||||
}`}
|
||||
/>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user