134 lines
3.7 KiB
TypeScript
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>
|
|
)
|
|
}
|