88 lines
3.3 KiB
JavaScript
88 lines
3.3 KiB
JavaScript
import React, { lazy, Suspense, useCallback, useState } from 'react'
|
||
import useWebShare from '../../hooks/useWebShare'
|
||
|
||
const ArtworkShareModal = lazy(() => import('./ArtworkShareModal'))
|
||
|
||
/* ── Share icon (lucide-style) ───────────────────────────────────────────── */
|
||
function ShareIcon() {
|
||
return (
|
||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="h-5 w-5">
|
||
<path strokeLinecap="round" strokeLinejoin="round" d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 1 1 0-2.684m0 2.684 6.632 3.316m-6.632-6 6.632-3.316m0 0a3 3 0 1 0 5.367-2.684 3 3 0 0 0-5.367 2.684Zm0 9.316a3 3 0 1 0 5.368 2.684 3 3 0 0 0-5.368-2.684Z" />
|
||
</svg>
|
||
)
|
||
}
|
||
|
||
/**
|
||
* ArtworkShareButton – renders the Share pill and manages modal / native share.
|
||
*
|
||
* Props:
|
||
* artwork – artwork object
|
||
* shareUrl – canonical URL to share
|
||
* size – 'default' | 'small' (for mobile bar)
|
||
*/
|
||
export default function ArtworkShareButton({ artwork, shareUrl, size = 'default', isLoggedIn = false }) {
|
||
const [modalOpen, setModalOpen] = useState(false)
|
||
|
||
const openModal = useCallback(
|
||
() => setModalOpen(true),
|
||
[],
|
||
)
|
||
const closeModal = useCallback(
|
||
() => setModalOpen(false),
|
||
[],
|
||
)
|
||
|
||
const { share } = useWebShare({ onFallback: openModal })
|
||
|
||
const handleClick = async () => {
|
||
const result = await share({
|
||
title: artwork?.title || 'Artwork',
|
||
text: artwork?.description?.substring(0, 120) || '',
|
||
url: shareUrl || artwork?.canonical_url || window.location.href,
|
||
})
|
||
if (result?.shared && result?.native && artwork?.id) {
|
||
const csrfToken = document.head.querySelector('meta[name="csrf-token"]')?.content
|
||
fetch(`/api/artworks/${artwork.id}/share`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken || '' },
|
||
credentials: 'same-origin',
|
||
body: JSON.stringify({ platform: 'native' }),
|
||
}).catch(() => {})
|
||
}
|
||
}
|
||
|
||
const isSmall = size === 'small'
|
||
|
||
return (
|
||
<>
|
||
<button
|
||
type="button"
|
||
aria-label="Share artwork"
|
||
onClick={handleClick}
|
||
className={
|
||
isSmall
|
||
? 'inline-flex items-center gap-1.5 rounded-full border border-white/[0.08] bg-white/[0.04] px-3.5 py-2 text-xs font-medium text-white/70 transition-all hover:border-white/[0.15] hover:bg-white/[0.07] hover:text-white'
|
||
: 'group inline-flex items-center gap-2 rounded-full border border-white/[0.08] bg-white/[0.04] px-5 py-2.5 text-sm font-medium text-white/70 transition-all duration-200 hover:border-white/[0.15] hover:bg-white/[0.07] hover:text-white hover:shadow-lg hover:shadow-white/[0.03]'
|
||
}
|
||
title="Share"
|
||
>
|
||
<ShareIcon />
|
||
{!isSmall && <span>Share</span>}
|
||
</button>
|
||
|
||
{/* Lazy-loaded modal – only rendered when opened */}
|
||
{modalOpen && (
|
||
<Suspense fallback={null}>
|
||
<ArtworkShareModal
|
||
open={modalOpen}
|
||
onClose={closeModal}
|
||
artwork={artwork}
|
||
shareUrl={shareUrl}
|
||
isLoggedIn={isLoggedIn}
|
||
/>
|
||
</Suspense>
|
||
)}
|
||
</>
|
||
)
|
||
}
|