This commit is contained in:
2026-03-20 21:17:26 +01:00
parent 1a62fcb81d
commit 29c3ff8572
229 changed files with 13147 additions and 2577 deletions

View File

@@ -192,15 +192,25 @@ function ReportModal({ open, onClose, onSubmit, submitting }) {
export default function ArtworkActionBar({ artwork, stats, canonicalUrl, onStatsChange }) {
const [favorited, setFavorited] = useState(Boolean(artwork?.viewer?.is_favorited))
const [bookmarked, setBookmarked] = useState(Boolean(artwork?.viewer?.is_bookmarked))
const [bookmarkCount, setBookmarkCount] = useState(Number(stats?.bookmarks ?? artwork?.stats?.bookmarks ?? 0))
const [downloading, setDownloading] = useState(false)
const [reporting, setReporting] = useState(false)
const [reported, setReported] = useState(false)
const [reportOpen, setReportOpen] = useState(false)
const isLoggedIn = artwork?.viewer != null
const isLoggedIn = Boolean(artwork?.viewer?.is_authenticated)
useEffect(() => {
setFavorited(Boolean(artwork?.viewer?.is_favorited))
}, [artwork?.id, artwork?.viewer?.is_favorited])
useEffect(() => {
setBookmarked(Boolean(artwork?.viewer?.is_bookmarked))
}, [artwork?.id, artwork?.viewer?.is_bookmarked])
useEffect(() => {
setBookmarkCount(Number(stats?.bookmarks ?? artwork?.stats?.bookmarks ?? 0))
}, [artwork?.id, artwork?.stats?.bookmarks, stats?.bookmarks])
const shareUrl = canonicalUrl || artwork?.canonical_url || (typeof window !== 'undefined' ? window.location.href : '#')
const csrfToken = typeof document !== 'undefined'
? document.querySelector('meta[name="csrf-token"]')?.getAttribute('content')
@@ -249,6 +259,11 @@ export default function ArtworkActionBar({ artwork, stats, canonicalUrl, onStats
}
const onToggleFavorite = async () => {
if (!isLoggedIn) {
window.location.href = '/login'
return
}
const nextState = !favorited
setFavorited(nextState)
try {
@@ -257,6 +272,26 @@ export default function ArtworkActionBar({ artwork, stats, canonicalUrl, onStats
} catch { setFavorited(!nextState) }
}
const onToggleBookmark = async () => {
if (!isLoggedIn) {
window.location.href = '/login'
return
}
const nextState = !bookmarked
setBookmarked(nextState)
setBookmarkCount((current) => Math.max(0, current + (nextState ? 1 : -1)))
try {
const payload = await postInteraction(`/api/artworks/${artwork.id}/bookmark`, { state: nextState })
setBookmarked(Boolean(payload?.is_bookmarked))
setBookmarkCount(Number(payload?.stats?.bookmarks ?? 0))
} catch {
setBookmarked(!nextState)
setBookmarkCount((current) => Math.max(0, current + (nextState ? -1 : 1)))
}
}
const openReport = () => {
if (reported) return
setReportOpen(true)
@@ -274,6 +309,7 @@ export default function ArtworkActionBar({ artwork, stats, canonicalUrl, onStats
}
const favCount = formatCount(stats?.favorites ?? artwork?.stats?.favorites ?? 0)
const savedCount = formatCount(bookmarkCount)
const viewCount = formatCount(stats?.views ?? artwork?.stats?.views ?? 0)
return (
@@ -296,6 +332,21 @@ export default function ArtworkActionBar({ artwork, stats, canonicalUrl, onStats
<span className="tabular-nums">{favCount}</span>
</button>
<button
type="button"
aria-label={bookmarked ? 'Remove bookmark' : 'Save artwork'}
onClick={onToggleBookmark}
className={[
'inline-flex items-center gap-2 rounded-full border px-5 py-2.5 text-sm font-medium transition-all duration-200',
bookmarked
? 'border-amber-400/35 bg-amber-400/14 text-amber-200 shadow-lg shadow-amber-500/10 hover:bg-amber-400/18'
: 'border-white/[0.08] bg-white/[0.04] text-white/70 hover:border-white/[0.15] hover:bg-white/[0.07] hover:text-white',
].join(' ')}
>
<BookmarkIcon filled={bookmarked} />
<span className="tabular-nums">{savedCount}</span>
</button>
{/* Views stat pill */}
<div className="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">
<CloudDownIcon />
@@ -353,6 +404,21 @@ export default function ArtworkActionBar({ artwork, stats, canonicalUrl, onStats
<span className="tabular-nums">{favCount}</span>
</button>
<button
type="button"
aria-label={bookmarked ? 'Remove bookmark' : 'Save artwork'}
onClick={onToggleBookmark}
className={[
'inline-flex items-center gap-1.5 rounded-full border px-3.5 py-2 text-xs font-medium transition-all',
bookmarked
? 'border-amber-400/35 bg-amber-400/14 text-amber-200'
: 'border-white/[0.08] bg-white/[0.04] text-white/70',
].join(' ')}
>
<BookmarkIcon filled={bookmarked} />
<span className="tabular-nums">{savedCount}</span>
</button>
{/* Share */}
<ArtworkShareButton artwork={artwork} shareUrl={shareUrl} size="small" isLoggedIn={isLoggedIn} />