Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ const router = createBrowserRouter([
function App() {
return (
<>
<Toaster />
<Toaster
toastOptions={{
ariaProps: {
role: 'status',
'aria-live': 'polite',
},
}}
/>
<RouterProvider router={router} />
</>
);
Expand Down
46 changes: 37 additions & 9 deletions src/components/common/TransactionFailureDrawer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import {
Dialog,
DialogContent,
Expand All @@ -7,7 +7,7 @@ import {
DialogFooter,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { AlertCircle, Copy } from 'lucide-react';
import { AlertCircle, Copy, Check } from 'lucide-react';
import toast from 'react-hot-toast';
import { formatAbsoluteDateTime, formatRelativeTime } from '@/utils/time.utils';

Expand All @@ -34,9 +34,13 @@ const TransactionFailureDrawer: React.FC<TransactionFailureDrawerProps> = ({
onRetry,
onDismiss,
}) => {
const copyToClipboard = (text: string) => {
const [copiedField, setCopiedField] = useState<'errorCode' | 'txHash' | null>(null);

const copyToClipboard = (text: string, field: 'errorCode' | 'txHash') => {
navigator.clipboard.writeText(text);
toast.success('Copied to clipboard');
setCopiedField(field);
setTimeout(() => setCopiedField(null), 2000);
};

const handleClose = () => {
Expand Down Expand Up @@ -100,14 +104,26 @@ const TransactionFailureDrawer: React.FC<TransactionFailureDrawerProps> = ({
</code>
<button
onClick={() =>
copyToClipboard(failureDetails.errorCode!)
copyToClipboard(failureDetails.errorCode!, 'errorCode')
}
className="shrink-0 p-2 hover:bg-white/10 rounded transition-colors"
aria-label="Copy error code"
aria-label={copiedField === 'errorCode' ? 'Error code copied' : 'Copy error code'}
>
<Copy className="size-4 text-white/60" />
{copiedField === 'errorCode' ? (
<Check className="size-4 text-emerald-400" aria-hidden="true" />
) : (
<Copy className="size-4 text-white/60" aria-hidden="true" />
)}
</button>
</div>
<span
role="status"
aria-live="polite"
aria-atomic="true"
className="sr-only"
>
{copiedField === 'errorCode' ? 'Error code copied to clipboard' : ''}
</span>
</div>
)}

Expand All @@ -122,14 +138,26 @@ const TransactionFailureDrawer: React.FC<TransactionFailureDrawerProps> = ({
</code>
<button
onClick={() =>
copyToClipboard(failureDetails.txHash!)
copyToClipboard(failureDetails.txHash!, 'txHash')
}
className="shrink-0 p-2 hover:bg-white/10 rounded transition-colors"
aria-label="Copy transaction hash"
aria-label={copiedField === 'txHash' ? 'Transaction hash copied' : 'Copy transaction hash'}
>
<Copy className="size-4 text-white/60" />
{copiedField === 'txHash' ? (
<Check className="size-4 text-emerald-400" aria-hidden="true" />
) : (
<Copy className="size-4 text-white/60" aria-hidden="true" />
)}
</button>
</div>
<span
role="status"
aria-live="polite"
aria-atomic="true"
className="sr-only"
>
{copiedField === 'txHash' ? 'Transaction hash copied to clipboard' : ''}
</span>
</div>
)}

Expand Down
14 changes: 11 additions & 3 deletions src/components/common/TransactionHashRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,22 @@ const TransactionHashRow: React.FC<TransactionHashRowProps> = ({
<button
onClick={handleCopy}
className="inline-flex size-6 items-center justify-center rounded-md bg-white/5 text-white/40 transition-colors hover:bg-white/10 hover:text-white"
aria-label="Copy transaction hash"
aria-label={copied ? 'Transaction hash copied' : 'Copy transaction hash'}
>
{copied ? (
<Check className="size-3 text-emerald-400" />
<Check className="size-3 text-emerald-400" aria-hidden="true" />
) : (
<Copy className="size-3" />
<Copy className="size-3" aria-hidden="true" />
)}
</button>
<span
role="status"
aria-live="polite"
aria-atomic="true"
className="sr-only"
>
{copied ? 'Transaction hash copied to clipboard' : ''}
</span>
{explorerUrl && (
<a
href={explorerUrl}
Expand Down
32 changes: 21 additions & 11 deletions src/components/common/TruncatedAddress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,27 @@ const TruncatedAddress: React.FC<TruncatedAddressProps> = ({
>
<span>{truncate(address, prefixChars, suffixChars)}</span>
{copyable && (
<button
onClick={handleCopy}
aria-label="Copy address"
className="text-white/40 transition-colors hover:text-amber-500"
>
{copied ? (
<Check className="size-3" />
) : (
<Copy className="size-3" />
)}
</button>
<>
<button
onClick={handleCopy}
aria-label={copied ? 'Address copied' : 'Copy address'}
className="text-white/40 transition-colors hover:text-amber-500"
>
{copied ? (
<Check className="size-3" aria-hidden="true" />
) : (
<Copy className="size-3" aria-hidden="true" />
)}
</button>
<span
role="status"
aria-live="polite"
aria-atomic="true"
className="sr-only"
>
{copied ? 'Address copied to clipboard' : ''}
</span>
</>
)}
</span>
);
Expand Down
Loading