refactor: remove copy to clipboard everywhere as it's not possible on http
This commit is contained in:
parent
9c5f7d6516
commit
6a696d090e
5 changed files with 8 additions and 109 deletions
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue