diff --git a/components/post/ShareLinks.tsx b/components/post/ShareLinks.tsx
index 426c4b0..efee949 100644
--- a/components/post/ShareLinks.tsx
+++ b/components/post/ShareLinks.tsx
@@ -38,8 +38,9 @@ export function ShareLinks({ url, title, description }: ShareLinksProps) {
return (
{networks.map(({ name, icon, href }) => (
-
+
diff --git a/components/ui/CopyButton.tsx b/components/ui/CopyButton.tsx
index 4a65596..b22fa1b 100644
--- a/components/ui/CopyButton.tsx
+++ b/components/ui/CopyButton.tsx
@@ -1,41 +1,66 @@
'use client'
-import { ButtonHTMLAttributes, useRef } from 'react'
-import { Copy } from 'lucide-react'
+import { ButtonHTMLAttributes, useEffect, useState } from 'react'
+import { Check, Copy } from 'lucide-react'
import { cn } from '@/utils/cn'
type CopyButtonProps = ButtonHTMLAttributes & {
textToCopy: string
}
-export function CopyButton({ textToCopy, className, ...props }: CopyButtonProps) {
- const buttonRef = useRef(null)
+type CopyState = 'idle' | 'copied' | 'failed'
- const handleCopy = () => {
- navigator.clipboard.writeText(textToCopy)
- const button = buttonRef.current
- if (button) {
- const original = button.innerHTML
- button.innerHTML = 'Copied!'
- button.disabled = true
- setTimeout(() => {
- button.innerHTML = original
- button.disabled = false
- }, 2000)
+const labels: Record = {
+ idle: 'Copy code',
+ copied: 'Copied!',
+ failed: 'Copy failed',
+}
+
+export function CopyButton({ textToCopy, className, ...props }: CopyButtonProps) {
+ const [state, setState] = useState('idle')
+
+ useEffect(() => {
+ if (state === 'idle') return
+ const timeout = setTimeout(() => setState('idle'), 2000)
+ return () => clearTimeout(timeout)
+ }, [state])
+
+ const handleCopy = async () => {
+ try {
+ await navigator.clipboard.writeText(textToCopy)
+ setState('copied')
+ } catch {
+ setState('failed')
}
}
return (
-
+ <>
+
+ {/* Outside the button: a button's children are presentational, so they aren't announced */}
+
+ {state === 'idle' ? '' : labels[state]}
+
+ >
)
}