import React, { useState, useCallback } from 'react' import { createRoot } from 'react-dom/client' import ArtworkHero from '../components/artwork/ArtworkHero' import ArtworkMeta from '../components/artwork/ArtworkMeta' import ArtworkActions from '../components/artwork/ArtworkActions' import ArtworkAwards from '../components/artwork/ArtworkAwards' import ArtworkStats from '../components/artwork/ArtworkStats' import ArtworkTags from '../components/artwork/ArtworkTags' import ArtworkAuthor from '../components/artwork/ArtworkAuthor' import ArtworkRelated from '../components/artwork/ArtworkRelated' import ArtworkDescription from '../components/artwork/ArtworkDescription' import ArtworkComments from '../components/artwork/ArtworkComments' import ArtworkReactions from '../components/artwork/ArtworkReactions' import ArtworkNavigator from '../components/viewer/ArtworkNavigator' import ArtworkViewer from '../components/viewer/ArtworkViewer' function ArtworkPage({ artwork: initialArtwork, related: initialRelated, presentMd: initialMd, presentLg: initialLg, presentXl: initialXl, presentSq: initialSq, canonicalUrl: initialCanonical, isAuthenticated = false, comments: initialComments = [] }) { const [viewerOpen, setViewerOpen] = useState(false) const openViewer = useCallback(() => setViewerOpen(true), []) const closeViewer = useCallback(() => setViewerOpen(false), []) // Navigable state — updated on client-side navigation const [artwork, setArtwork] = useState(initialArtwork) const [liveStats, setLiveStats] = useState(initialArtwork?.stats || {}) const handleStatsChange = useCallback((delta) => { setLiveStats(prev => { const next = { ...prev } Object.entries(delta).forEach(([key, val]) => { next[key] = Math.max(0, (Number(next[key]) || 0) + val) }) return next }) }, []) const [presentMd, setPresentMd] = useState(initialMd) const [presentLg, setPresentLg] = useState(initialLg) const [presentXl, setPresentXl] = useState(initialXl) const [presentSq, setPresentSq] = useState(initialSq) const [related, setRelated] = useState(initialRelated) const [comments, setComments] = useState(initialComments) const [canonicalUrl, setCanonicalUrl] = useState(initialCanonical) // Nav arrow state — populated by ArtworkNavigator once neighbors resolve const [navState, setNavState] = useState({ hasPrev: false, hasNext: false, navigatePrev: null, navigateNext: null }) /** * Called by ArtworkNavigator after a successful no-reload navigation. * data = ArtworkResource JSON from /api/artworks/{id}/page */ const handleNavigate = useCallback((data) => { setArtwork(data) setLiveStats(data.stats || {}) setPresentMd(data.thumbs?.md ?? null) setPresentLg(data.thumbs?.lg ?? null) setPresentXl(data.thumbs?.xl ?? null) setPresentSq(data.thumbs?.sq ?? null) setRelated([]) // cleared on navigation; user can scroll down for related setComments([]) // cleared; per-page server data setCanonicalUrl(data.canonical_url ?? window.location.href) setViewerOpen(false) // close viewer when navigating away }, []) if (!artwork) return null const initialAwards = artwork?.awards ?? null return ( <>
{/* Artwork navigator — prev/next arrows, keyboard, swipe, no page reload */} {/* Fullscreen viewer modal */} ) } // Auto-mount if the Blade view provided data attributes const el = document.getElementById('artwork-page') if (el) { const parse = (key, fallback = null) => { try { return JSON.parse(el.dataset[key] || 'null') ?? fallback } catch { return fallback } } const root = createRoot(el) root.render( , ) } export default ArtworkPage