import React, { useCallback, useEffect, useRef, useState } from 'react' import { createPortal } from 'react-dom' import ShareToast from '../ui/ShareToast' /* ── 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 }) { 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 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 ${title.replace(/\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 ? : , onClick: handleCopyLink, className: linkCopied ? 'border-emerald-500/40 bg-emerald-500/15 text-emerald-400' : 'border-white/[0.08] bg-white/[0.04] text-white/70 hover:border-white/[0.15] hover:bg-white/[0.07] hover:text-white', }, { label: 'Facebook', icon: , onClick: () => handlePlatformShare('facebook', facebookUrl(url)), className: 'border-white/[0.08] bg-white/[0.04] text-white/70 hover:border-[#1877F2]/40 hover:bg-[#1877F2]/15 hover:text-[#1877F2]', }, { label: 'X (Twitter)', icon: , onClick: () => handlePlatformShare('twitter', twitterUrl(url, title)), className: 'border-white/[0.08] bg-white/[0.04] text-white/70 hover:border-white/30 hover:bg-white/[0.10] hover:text-white', }, { label: 'Pinterest', icon: , onClick: () => handlePlatformShare('pinterest', pinterestUrl(url, imageUrl, title)), className: 'border-white/[0.08] bg-white/[0.04] text-white/70 hover:border-[#E60023]/40 hover:bg-[#E60023]/15 hover:text-[#E60023]', }, { label: 'Email', icon: , onClick: () => { window.location.href = emailUrl(url, title); trackShare(artwork?.id, 'email') }, className: 'border-white/[0.08] bg-white/[0.04] text-white/70 hover:border-white/[0.15] hover:bg-white/[0.07] hover:text-white', }, ] return createPortal( <> {/* Backdrop */}
{ if (e.target === backdropRef.current) onClose() }} className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/60 backdrop-blur-sm p-4" role="dialog" aria-modal="true" aria-label="Share this artwork" > {/* Modal container — glassmorphism */}
{/* Header */}

Share this artwork

{/* Artwork preview */} {thumbMdUrl && (
{title}

{title}

{artwork?.user?.username && (

by {artwork.user.username}

)}
)} {/* Share buttons grid */}
{SHARE_OPTIONS.map((opt) => ( ))}
{/* Embed section */}
{showEmbed && (