2026-01-15 11:31:09 +01:00

134 lines
3.7 KiB
TypeScript

import { Button } from './ui'
import { t } from '@/lib/i18n'
import { useToast } from './ui/ToastContainer'
interface ShareButtonsProps {
articleId: string
articleTitle: string
authorPubkey?: string
}
export function ShareButtons({ articleId, articleTitle, authorPubkey }: ShareButtonsProps): React.ReactElement {
const { showToast } = useToast()
const articleUrl = typeof window !== 'undefined' ? `${window.location.origin}/article/${articleId}` : ''
return (
<div className="flex flex-wrap gap-2">
<CopyLinkButton url={articleUrl} showToast={showToast} />
{authorPubkey !== undefined && (
<ShareToNostrButton articleId={articleId} articleTitle={articleTitle} authorPubkey={authorPubkey} showToast={showToast} />
)}
</div>
)
}
function CopyLinkButton({
url,
showToast,
}: {
url: string
showToast: (message: string, variant?: 'success' | 'info' | 'warning' | 'error', duration?: number) => void
}): React.ReactElement {
const handleCopy = async (): Promise<void> => {
try {
await navigator.clipboard.writeText(url)
showToast(t('share.copySuccess'), 'success', 2000)
} catch (error) {
console.error('Failed to copy link:', error)
showToast(t('share.copyFailed'), 'error')
}
}
return (
<Button
type="button"
variant="secondary"
size="small"
onClick={() => {
void handleCopy()
}}
aria-label={t('share.copyLink')}
>
{t('share.copyLink')}
</Button>
)
}
function handleNostrShare(params: {
articleId: string
authorPubkey: string
articleTitle: string
showToast: (message: string, variant?: 'success' | 'info' | 'warning' | 'error', duration?: number) => void
}): Promise<void> {
const articleUrl = typeof window !== 'undefined' ? `${window.location.origin}/article/${params.articleId}` : ''
const nostrNote = `nostr:${params.authorPubkey}:${params.articleId}`
const shareData: ShareData = {
title: params.articleTitle,
text: params.articleTitle,
url: articleUrl,
}
if (navigator.share !== undefined) {
return handleNativeShare(shareData, params.showToast)
}
return handleClipboardShare(nostrNote, params.showToast)
}
async function handleNativeShare(
shareData: ShareData,
showToast: (message: string, variant?: 'success' | 'info' | 'warning' | 'error', duration?: number) => void
): Promise<void> {
try {
await navigator.share(shareData)
showToast(t('share.shareSuccess'), 'success', 2000)
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
return
}
throw error
}
}
async function handleClipboardShare(
nostrNote: string,
showToast: (message: string, variant?: 'success' | 'info' | 'warning' | 'error', duration?: number) => void
): Promise<void> {
await navigator.clipboard.writeText(nostrNote)
showToast(t('share.nostrLinkCopied'), 'success', 2000)
}
function ShareToNostrButton({
articleId,
articleTitle,
authorPubkey,
showToast,
}: {
articleId: string
articleTitle: string
authorPubkey: string
showToast: (message: string, variant?: 'success' | 'info' | 'warning' | 'error', duration?: number) => void
}): React.ReactElement {
const handleShare = async (): Promise<void> => {
try {
await handleNostrShare({ articleId, authorPubkey, articleTitle, showToast })
} catch (error) {
console.error('Failed to share:', error)
showToast(t('share.shareFailed'), 'error')
}
}
return (
<Button
type="button"
variant="secondary"
size="small"
onClick={() => {
void handleShare()
}}
aria-label={t('share.shareToNostr')}
>
{t('share.shareToNostr')}
</Button>
)
}