refactor: remove copy to clipboard everywhere as it's not possible on http

This commit is contained in:
Nicolas Meienberger 2026-01-09 20:40:01 +01:00
parent 9c5f7d6516
commit 6a696d090e
5 changed files with 8 additions and 109 deletions

View file

@ -1,6 +1,4 @@
import type React from "react";
import { toast } from "sonner";
import { copyToClipboard } from "~/utils/clipboard";
interface CodeBlockProps {
code: string;
@ -9,11 +7,6 @@ interface CodeBlockProps {
}
export const CodeBlock: React.FC<CodeBlockProps> = ({ code, filename }) => {
const handleCopy = async () => {
await copyToClipboard(code);
toast.success("Code copied to clipboard");
};
return (
<div className="overflow-hidden rounded-sm bg-card-header ring-1 ring-white/10">
<div className="flex items-center justify-between border-b border-white/10 px-4 py-2 text-xs">
@ -23,16 +16,9 @@ export const CodeBlock: React.FC<CodeBlockProps> = ({ code, filename }) => {
<span className="h-2.5 w-2.5 rounded-full bg-emerald-500" />
{filename && <span className="ml-3 font-medium">{filename}</span>}
</div>
<button
type="button"
onClick={() => handleCopy()}
className="cursor-pointer rounded-md bg-white/5 px-2 py-1 text-[11px] font-medium ring-1 ring-inset ring-white/10 transition hover:bg-white/10 active:translate-y-px"
>
Copy
</button>
</div>
<pre className="text-xs m-0 px-4 py-2 bg-card-header">
<code className="text-white/80">{code}</code>
<code className="text-white/80 select-all">{code}</code>
</pre>
</div>
);

View file

@ -1,7 +1,4 @@
import { toast } from "sonner";
import { Button } from "~/client/components/ui/button";
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "~/client/components/ui/dialog";
import { copyToClipboard } from "~/utils/clipboard";
const RESET_PASSWORD_COMMAND = "docker exec -it zerobyte bun run cli reset-password";
@ -11,11 +8,6 @@ type ResetPasswordDialogProps = {
};
export const ResetPasswordDialog = ({ open, onOpenChange }: ResetPasswordDialogProps) => {
const handleCopy = async () => {
await copyToClipboard(RESET_PASSWORD_COMMAND);
toast.success("Command copied to clipboard");
};
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-xl">
@ -26,13 +18,10 @@ export const ResetPasswordDialog = ({ open, onOpenChange }: ResetPasswordDialogP
</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<div className="rounded-md bg-muted p-4 font-mono text-sm break-all">{RESET_PASSWORD_COMMAND}</div>
<div className="rounded-md bg-muted p-4 font-mono text-sm break-all select-all">{RESET_PASSWORD_COMMAND}</div>
<p className="text-sm text-muted-foreground">
This command will start an interactive session where you can enter a new password for your account.
</p>
<Button onClick={handleCopy} variant="outline" className="w-full">
Copy Command
</Button>
</div>
</DialogContent>
</Dialog>

View file

@ -1,6 +1,6 @@
import { useState } from "react";
import { toast } from "sonner";
import { Copy, RefreshCw } from "lucide-react";
import { RefreshCw } from "lucide-react";
import { Button } from "~/client/components/ui/button";
import {
Dialog,
@ -13,7 +13,6 @@ import {
import { Input } from "~/client/components/ui/input";
import { Label } from "~/client/components/ui/label";
import { authClient } from "~/client/lib/auth-client";
import { copyToClipboard } from "~/utils/clipboard";
type BackupCodesDialogProps = {
open: boolean;
@ -64,11 +63,6 @@ export const BackupCodesDialog = ({ open, onOpenChange }: BackupCodesDialogProps
}, 200);
};
const copyAllBackupCodes = () => {
const text = backupCodes.join("\n");
void copyToClipboard(text);
};
return (
<Dialog open={open} onOpenChange={handleClose}>
<DialogContent>
@ -84,24 +78,11 @@ export const BackupCodesDialog = ({ open, onOpenChange }: BackupCodesDialogProps
<>
<div className="p-3 bg-muted rounded-md space-y-1 max-h-48 overflow-y-auto">
{backupCodes.map((code) => (
<div key={code} className="text-sm font-mono flex items-center justify-between">
<span>{code}</span>
<Button
type="button"
variant="ghost"
size="sm"
onClick={() => copyToClipboard(code)}
className="h-8 w-8 p-0"
>
<Copy className="h-4 w-4" />
</Button>
<div key={code} className="text-sm font-mono py-1">
<span className="select-all block w-full">{code}</span>
</div>
))}
</div>
<Button type="button" variant="outline" onClick={copyAllBackupCodes} className="w-full">
<Copy className="h-4 w-4 mr-2" />
Copy all
</Button>
</>
) : (
<form onSubmit={handleGenerate} className="space-y-4">

View file

@ -1,6 +1,5 @@
import { useState } from "react";
import { toast } from "sonner";
import { Copy } from "lucide-react";
import { QRCodeCanvas } from "qrcode.react";
import { Button } from "~/client/components/ui/button";
import {
@ -15,7 +14,6 @@ import { Input } from "~/client/components/ui/input";
import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from "~/client/components/ui/input-otp";
import { Label } from "~/client/components/ui/label";
import { authClient } from "~/client/lib/auth-client";
import { copyToClipboard } from "~/utils/clipboard";
type TwoFactorSetupDialogProps = {
open: boolean;
@ -107,11 +105,6 @@ export const TwoFactorSetupDialog = ({ open, onOpenChange, onSuccess }: TwoFacto
}, 200);
};
const copyAllBackupCodes = () => {
const text = backupCodes.join("\n");
void copyToClipboard(text);
};
return (
<Dialog open={open} onOpenChange={handleClose}>
<DialogContent className="max-w-md">
@ -165,16 +158,8 @@ export const TwoFactorSetupDialog = ({ open, onOpenChange, onSuccess }: TwoFacto
<Input
value={totpUri.split("secret=")[1]?.split("&")[0] || ""}
readOnly
className="text-xs font-mono"
className="text-xs font-mono select-all"
/>
<Button
type="button"
variant="outline"
size="sm"
onClick={() => copyToClipboard(totpUri.split("secret=")[1]?.split("&")[0] || "")}
>
<Copy className="h-4 w-4" />
</Button>
</div>
</div>
{backupCodes.length > 0 && (
@ -182,24 +167,11 @@ export const TwoFactorSetupDialog = ({ open, onOpenChange, onSuccess }: TwoFacto
<Label className="text-xs">Backup codes (save these securely)</Label>
<div className="p-3 bg-muted rounded-md space-y-1 max-h-32 overflow-y-auto">
{backupCodes.map((code) => (
<div key={code} className="text-xs font-mono flex items-center justify-between">
<span>{code}</span>
<Button
type="button"
variant="ghost"
size="sm"
onClick={() => copyToClipboard(code)}
className="h-6 w-6 p-0"
>
<Copy className="h-3 w-3" />
</Button>
<div key={code} className="text-xs font-mono py-1">
<span className="select-all block w-full">{code}</span>
</div>
))}
</div>
<Button type="button" variant="outline" size="sm" onClick={copyAllBackupCodes} className="w-full">
<Copy className="h-4 w-4 mr-2" />
Copy all
</Button>
</div>
)}
</div>

View file

@ -1,29 +0,0 @@
import { toast } from "sonner";
export async function copyToClipboard(textToCopy: string) {
// Navigator clipboard api needs a secure context (https)
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(textToCopy);
toast.success("Copied to clipboard");
} else {
// Use the 'out of viewport hidden text area' trick
const textArea = document.createElement("textarea");
textArea.value = textToCopy;
// Move textarea out of the viewport so it's not visible
textArea.style.position = "absolute";
textArea.style.left = "-999999px";
document.body.prepend(textArea);
textArea.select();
try {
document.execCommand("copy");
toast.success("Copied to clipboard");
} catch (error) {
console.error(error);
} finally {
textArea.remove();
}
}
}