minor fixes

This commit is contained in:
2026-04-09 08:50:36 +02:00
parent 23d363a50c
commit a2457f4e49
75 changed files with 3848 additions and 387 deletions

View File

@@ -119,6 +119,24 @@ function getCsrfToken() {
return document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || ''
}
function swapImageToFallbackOnce(event, fallbackSrc, { clearResponsive = false } = {}) {
const image = event.currentTarget
if (!image || image.dataset.fallbackApplied === '1') {
return
}
image.dataset.fallbackApplied = '1'
image.onerror = null
if (clearResponsive) {
image.removeAttribute('srcset')
image.removeAttribute('sizes')
}
image.src = fallbackSrc
}
function sendDiscoveryEvent(endpoint, payload) {
if (!endpoint) return
@@ -437,18 +455,27 @@ export default function ArtworkCard({
const item = artwork || {}
const rawAuthor = item.author || item.creator
const publisher = item.publisher && typeof item.publisher === 'object' ? item.publisher : null
const isGroupPublisher = (publisher?.type === 'group') || item.published_as_type === 'group'
const title = decodeHtml(item.title || item.name || 'Untitled artwork')
const author = decodeHtml(
(typeof rawAuthor === 'string' ? rawAuthor : rawAuthor?.name)
(isGroupPublisher ? publisher?.name : null)
|| (typeof rawAuthor === 'string' ? rawAuthor : rawAuthor?.name)
|| item.author_name
|| item.uname
|| 'Skinbase Artist'
)
const username = rawAuthor?.username || item.author_username || item.username || null
const authorLevel = Number(rawAuthor?.level ?? item.author_level ?? item.creator?.level ?? 0)
const authorRank = rawAuthor?.rank || item.author_rank || item.creator?.rank || ''
const username = isGroupPublisher ? null : (rawAuthor?.username || item.author_username || item.username || null)
const authorLevel = isGroupPublisher ? 0 : Number(rawAuthor?.level ?? item.author_level ?? item.creator?.level ?? 0)
const authorRank = isGroupPublisher ? '' : (rawAuthor?.rank || item.author_rank || item.creator?.rank || '')
const image = item.image || item.thumb || item.thumb_url || item.thumbnail_url || IMAGE_FALLBACK
const avatar = rawAuthor?.avatar_url || rawAuthor?.avatar || item.avatar || item.author_avatar || item.avatar_url || AVATAR_FALLBACK
const avatar = (isGroupPublisher ? publisher?.avatar_url : null)
|| rawAuthor?.avatar_url
|| rawAuthor?.avatar
|| item.avatar
|| item.author_avatar
|| item.avatar_url
|| AVATAR_FALLBACK
const likes = item.likes ?? item.favourites ?? 0
const views = item.views ?? item.views_count ?? item.view_count ?? 0
const downloads = item.downloads ?? item.downloads_count ?? item.download_count ?? 0
@@ -470,7 +497,7 @@ export default function ArtworkCard({
const aspectClass = compact ? 'aspect-square' : 'aspect-[4/5]'
const titleClass = compact ? 'text-[0.96rem]' : 'text-[1rem] sm:text-[1.08rem]'
const metadataLine = [contentType, category, resolution].filter(Boolean).join(' • ')
const authorHref = username ? `/@${username}` : null
const authorHref = publisher?.profile_url || rawAuthor?.profile_url || item.profile_url || item.author_url || (username ? `/@${username}` : null)
const resolvedMetricBadge = metricBadge || item.metric_badge || null
const relativePublishedAt = useMemo(
() => formatRelativeTime(item.published_at || item.publishedAt || null),
@@ -750,7 +777,7 @@ export default function ArtworkCard({
decoding={decoding}
className="h-full w-full object-cover transition-transform duration-300 group-hover:scale-105"
onError={(event) => {
event.currentTarget.src = IMAGE_FALLBACK
swapImageToFallbackOnce(event, IMAGE_FALLBACK)
}}
/>
</div>
@@ -761,7 +788,7 @@ export default function ArtworkCard({
<div className="mt-0.5 flex items-center gap-2 text-xs text-slate-400">
{authorHref ? (
<span>
by {author} <span className="text-slate-500">@{username}</span>
by {author} {username ? <span className="text-slate-500">@{username}</span> : null}
</span>
) : (
<span>by {author}</span>
@@ -810,7 +837,7 @@ export default function ArtworkCard({
fetchPriority={fetchPriority || undefined}
className={cx('h-full w-full object-cover transition duration-500 group-hover:scale-110 group-focus-within:scale-110', imageClassName)}
onError={(event) => {
event.currentTarget.src = IMAGE_FALLBACK
swapImageToFallbackOnce(event, IMAGE_FALLBACK, { clearResponsive: true })
}}
/>
@@ -880,14 +907,14 @@ export default function ArtworkCard({
decoding="async"
className="h-9 w-9 shrink-0 rounded-full object-cover"
onError={(event) => {
event.currentTarget.src = AVATAR_FALLBACK
swapImageToFallbackOnce(event, AVATAR_FALLBACK)
}}
/>
<span className="min-w-0 flex-1">
<span className="flex items-center gap-2">
<span className="block min-w-0 truncate text-sm font-medium text-white/90">
{author}
{username && <span className="text-[11px] text-white/60"> @{username}</span>}
{username ? <span className="text-[11px] text-white/60"> @{username}</span> : null}
</span>
{authorLevel > 0 ? <LevelBadge level={authorLevel} rank={authorRank} compact className="shrink-0" /> : null}
</span>