Files
SkinbaseNova/resources/views/components/artwork-card.blade.php

188 lines
8.0 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@props([
'art',
'loading' => 'lazy',
'fetchpriority' => null,
])
@php
if (isset($art) && (is_array($art) || $art instanceof Illuminate\Support\Collection || $art instanceof Illuminate\Database\Eloquent\Collection)) {
$first = null;
if (is_array($art)) {
$first = reset($art);
} elseif ($art instanceof Illuminate\Support\Collection || $art instanceof Illuminate\Database\Eloquent\Collection) {
$first = $art->first();
}
if ($first) {
$art = $first;
}
}
$title = trim((string) ($art->name ?? $art->title ?? 'Untitled artwork'));
$author = trim((string) (
$art->uname
?? $art->author_name
?? $art->author
?? ($art->user->name ?? null)
?? ($art->user->username ?? null)
?? 'Skinbase'
));
$username = trim((string) (
$art->username
?? ($art->user->username ?? null)
?? ''
));
$category = trim((string) ($art->category_name ?? $art->category ?? ''));
$avatarUserId = $art->user->id ?? $art->user_id ?? null;
$avatarHash = $art->user->profile->avatar_hash ?? $art->avatar_hash ?? null;
$avatarUrl = \App\Support\AvatarUrl::forUser((int) ($avatarUserId ?? 0), $avatarHash, 40);
$license = trim((string) ($art->license ?? 'Standard'));
$resolution = trim((string) ($art->resolution ?? ((isset($art->width, $art->height) && $art->width && $art->height) ? ($art->width . '×' . $art->height) : '')));
$safeInt = function ($value, $fallback = 0) {
if (is_numeric($value)) {
return (int) $value;
}
if (is_array($value)) {
return count($value);
}
if (is_object($value)) {
if (method_exists($value, 'count')) {
return (int) $value->count();
}
if ($value instanceof Countable) {
return (int) count($value);
}
}
return (int) $fallback;
};
$likes = $safeInt($art->likes ?? $art->favourites ?? 0);
$comments = $safeInt($art->comments_count ?? $art->comment_count ?? $art->comments ?? 0);
$imgSrc = (string) ($art->thumb ?? $art->thumbnail_url ?? '/images/placeholder.jpg');
$imgSrcset = (string) ($art->thumb_srcset ?? $art->thumbnail_srcset ?? $imgSrc);
$imgAvifSrcset = (string) ($art->thumb_avif_srcset ?? $imgSrcset);
$imgWebpSrcset = (string) ($art->thumb_webp_srcset ?? $imgSrcset);
$resolveDimension = function ($value, string $field, $fallback) {
if (is_numeric($value)) {
return (int) $value;
}
if (is_array($value)) {
$current = reset($value);
return is_numeric($current) ? (int) $current : (int) $fallback;
}
if (is_object($value)) {
if (method_exists($value, 'first')) {
$first = $value->first();
if (is_object($first) && isset($first->{$field})) {
return (int) ($first->{$field} ?: $fallback);
}
}
if (isset($value->{$field})) {
return (int) $value->{$field};
}
}
return (int) $fallback;
};
$imgWidth = max(1, $resolveDimension($art->width ?? null, 'width', 800));
$imgHeight = max(1, $resolveDimension($art->height ?? null, 'height', 600));
$imgAspectRatio = $imgWidth . ' / ' . $imgHeight;
$contentUrl = $imgSrc;
$cardUrl = (string) ($art->url ?? '');
if ($cardUrl === '' || $cardUrl === '#') {
if (isset($art->id) && is_numeric($art->id)) {
$cardUrl = '/art/' . (int) $art->id . '/' . \Illuminate\Support\Str::slug($title);
} else {
$cardUrl = '#';
}
}
$authorUrl = $username !== '' ? '/@' . strtolower($username) : null;
$metaParts = [];
if ($resolution !== '') {
$metaParts[] = $resolution;
}
if ($category !== '') {
$metaParts[] = $category;
}
if ($license !== '') {
$metaParts[] = $license;
}
@endphp
<article class="nova-card gallery-item artwork" itemscope itemtype="https://schema.org/ImageObject"
data-art-id="{{ $art->id ?? '' }}"
data-art-url="{{ $cardUrl }}"
data-art-title="{{ e($title) }}"
data-art-img="{{ $imgSrc }}">
<meta itemprop="name" content="{{ $title }}">
<meta itemprop="contentUrl" content="{{ $contentUrl }}">
<meta itemprop="creator" content="{{ $author }}">
<meta itemprop="license" content="{{ $license }}">
<a href="{{ $cardUrl }}" itemprop="url" class="group relative block overflow-hidden rounded-2xl ring-1 ring-white/5 bg-black/20 shadow-lg shadow-black/40 transition-all duration-200 ease-out hover:-translate-y-0.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/70">
@if($category !== '')
<div class="absolute left-3 top-3 z-30 rounded-md bg-black/55 px-2 py-1 text-xs text-white backdrop-blur-sm">{{ $category }}</div>
@endif
<div class="nova-card-media relative overflow-hidden bg-neutral-900" style="aspect-ratio: {{ $imgAspectRatio }};">
<div class="absolute inset-0 bg-gradient-to-br from-white/10 via-white/5 to-transparent"></div>
<picture>
<source srcset="{{ $imgAvifSrcset }}" type="image/avif">
<source srcset="{{ $imgWebpSrcset }}" type="image/webp">
<img
src="{{ $imgSrc }}"
srcset="{{ $imgSrcset }}"
sizes="(max-width: 768px) 50vw, (max-width: 1280px) 33vw, 20vw"
loading="{{ $loading }}"
decoding="{{ $loading === 'eager' ? 'sync' : 'async' }}"
@if($fetchpriority) fetchpriority="{{ $fetchpriority }}" @endif
@if($loading !== 'eager') data-blur-preview @endif
alt="{{ e($title) }}"
width="{{ $imgWidth }}"
height="{{ $imgHeight }}"
class="h-full w-full object-cover transition-[transform,filter] duration-300 ease-out group-hover:scale-[1.04]"
itemprop="thumbnailUrl"
/>
</picture>
<div class="absolute right-3 top-3 z-30 flex items-center gap-2 opacity-0 transition-opacity duration-200 group-hover:opacity-100 group-focus-visible:opacity-100">
<span class="inline-flex items-center rounded-md bg-black/60 px-2 py-1 text-[11px] font-medium text-white ring-1 ring-white/10">View</span>
@if($authorUrl)
<span class="inline-flex items-center rounded-md bg-black/60 px-2 py-1 text-[11px] font-medium text-white ring-1 ring-white/10">Profile</span>
@endif
</div>
<div class="pointer-events-none absolute inset-x-0 bottom-0 z-20 bg-gradient-to-t from-black/80 via-black/40 to-transparent p-3 backdrop-blur-[2px] opacity-100 transition-opacity duration-200 md:opacity-0 md:group-hover:opacity-100 md:group-focus-visible:opacity-100">
<div class="truncate text-sm font-semibold text-white">{{ $title }}</div>
<div class="mt-1 flex items-center justify-between gap-3 text-xs text-white/80">
<span class="truncate flex items-center gap-2">
<img src="{{ $avatarUrl }}" alt="Avatar of {{ e($author) }}" class="w-6 h-6 rounded-full object-cover">
<span class="truncate">
<span>{{ $author }}</span>
@if($username !== '')
<span class="text-white/60">{{ '@' . $username }}</span>
@endif
</span>
</span>
<span class="shrink-0"> {{ $likes }} · 💬 {{ $comments }}</span>
</div>
@if(!empty($metaParts))
<div class="mt-1 text-[11px] text-white/70">{{ implode(' • ', $metaParts) }}</div>
@endif
</div>
</div>
<span class="sr-only">{{ $title }} by {{ $author }}</span>
</a>
</article>