import React, { lazy, Suspense, useCallback, useEffect, useRef, useState } from 'react'
import { createPortal } from 'react-dom'
import ShareToast from '../ui/ShareToast'
// Lazy-load the Feed share modal so artwork pages don't bundle the feed layer unless needed
const FeedShareArtworkModal = lazy(() => import('../Feed/ShareArtworkModal'))
/* ── Platform share URLs ─────────────────────────────────────────────────── */
function facebookUrl(url) {
return `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(url)}`
}
function twitterUrl(url, title) {
return `https://twitter.com/intent/tweet?url=${encodeURIComponent(url)}&text=${encodeURIComponent(title)}`
}
function pinterestUrl(url, imageUrl, title) {
return `https://pinterest.com/pin/create/button/?url=${encodeURIComponent(url)}&media=${encodeURIComponent(imageUrl)}&description=${encodeURIComponent(title)}`
}
function emailUrl(url, title) {
return `mailto:?subject=${encodeURIComponent(title)}&body=${encodeURIComponent(url)}`
}
/* ── Icons ────────────────────────────────────────────────────────────────── */
function CopyIcon() {
return (
)
}
function CheckIcon() {
return (
)
}
function FacebookIcon() {
return (
)
}
function XTwitterIcon() {
return (
)
}
function PinterestIcon() {
return (
)
}
function EmailIcon() {
return (
)
}
function EmbedIcon() {
return (
)
}
function CloseIcon() {
return (
)
}
/* ── Helpers ──────────────────────────────────────────────────────────────── */
function openShareWindow(url) {
window.open(url, '_blank', 'noopener,noreferrer,width=600,height=500')
}
function trackShare(artworkId, platform) {
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content')
fetch(`/api/artworks/${artworkId}/share`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': csrfToken || '' },
credentials: 'same-origin',
body: JSON.stringify({ platform }),
}).catch(() => {})
}
/* ── Main component ──────────────────────────────────────────────────────── */
/**
* ArtworkShareModal
*
* Props:
* open – boolean, whether modal is visible
* onClose – callback to close modal
* artwork – artwork object (id, title, description, thumbs, canonical_url, …)
* shareUrl – canonical share URL
*/
export default function ArtworkShareModal({ open, onClose, artwork, shareUrl, isLoggedIn = false }) {
const backdropRef = useRef(null)
const [linkCopied, setLinkCopied] = useState(false)
const [embedCopied, setEmbedCopied] = useState(false)
const [showEmbed, setShowEmbed] = useState(false)
const [toastVisible, setToastVisible] = useState(false)
const [toastMessage, setToastMessage] = useState('')
const [profileShareOpen, setProfileShareOpen] = useState(false)
const url = shareUrl || artwork?.canonical_url || (typeof window !== 'undefined' ? window.location.href : '#')
const title = artwork?.title || 'Artwork'
const imageUrl = artwork?.thumbs?.xl?.url || artwork?.thumbs?.lg?.url || artwork?.thumbs?.md?.url || ''
const thumbMdUrl = artwork?.thumbs?.md?.url || imageUrl
const embedCode = `\n \n`
// Lock body scroll when open
useEffect(() => {
if (open) {
document.body.style.overflow = 'hidden'
return () => { document.body.style.overflow = '' }
}
}, [open])
// Close on Escape
useEffect(() => {
if (!open) return
const handler = (e) => { if (e.key === 'Escape') onClose() }
window.addEventListener('keydown', handler)
return () => window.removeEventListener('keydown', handler)
}, [open, onClose])
// Reset state when re-opening
useEffect(() => {
if (open) {
setLinkCopied(false)
setEmbedCopied(false)
setShowEmbed(false)
}
}, [open])
const showToast = useCallback((msg) => {
setToastMessage(msg)
setToastVisible(true)
}, [])
const handleCopyLink = async () => {
try {
await navigator.clipboard.writeText(url)
setLinkCopied(true)
showToast('Link copied!')
trackShare(artwork?.id, 'copy')
setTimeout(() => setLinkCopied(false), 2500)
} catch { /* noop */ }
}
const handleCopyEmbed = async () => {
try {
await navigator.clipboard.writeText(embedCode)
setEmbedCopied(true)
showToast('Embed code copied!')
trackShare(artwork?.id, 'embed')
setTimeout(() => setEmbedCopied(false), 2500)
} catch { /* noop */ }
}
const handlePlatformShare = (platform, shareLink) => {
openShareWindow(shareLink)
trackShare(artwork?.id, platform)
onClose()
}
if (!open) return null
const SHARE_OPTIONS = [
{
label: linkCopied ? 'Copied!' : 'Copy Link',
icon: linkCopied ?
{title}
{artwork?.user?.username && (by {artwork.user.username}
)}