chore: commit remaining workspace changes

This commit is contained in:
2026-05-08 21:51:29 +02:00
parent 8d108b8a76
commit ff96ef796e
97 changed files with 18020 additions and 2196 deletions

View File

@@ -5,5 +5,6 @@
@endpush
@section('content')
@include('partials.seo.forum-microdata', ['forumMicrodata' => $forum_microdata ?? null])
@inertia
@endsection

View File

@@ -59,9 +59,76 @@
'per_page' => $posts->perPage(),
'total' => $posts->total(),
];
$makeForumAuthor = function ($user) {
if (! $user) {
return null;
}
$name = trim((string) ($user->name ?? $user->username ?? ''));
if ($name === '') {
return null;
}
$username = trim((string) ($user->username ?? ''));
return array_filter([
'name' => $name,
'url' => $username !== '' ? url('/@' . ltrim($username, '@')) : null,
]);
};
// Ensure we always provide a top-level author object for structured data.
$topAuthor = $makeForumAuthor($author ?? $opPost?->user ?? null);
if (! $topAuthor) {
$topAuthor = ['name' => (string) ($opPost?->user?->name ?? $thread->user?->name ?? 'Skinbase')];
}
$forumMicrodata = [
'kind' => 'topic',
'canonical' => route('forum.thread.show', ['thread' => $thread->id, 'slug' => $thread->slug]),
'title' => (string) $thread->title,
'text' => $threadDescription,
'date_published' => $thread->created_at?->toIso8601String(),
'date_modified' => ($thread->last_post_at ?? $thread->updated_at)?->toIso8601String(),
'comment_count' => (int) ($reply_count ?? 0),
'author' => $topAuthor,
'comments' => collect([$opPost])
->filter()
->merge($posts->getCollection())
->map(function ($post) use ($makeForumAuthor, $thread) {
$rendered = (string) ForumPostContent::render((string) ($post->content ?? ''));
$text = trim((string) preg_replace('/\s+/u', ' ', strip_tags(html_entity_decode($rendered, ENT_QUOTES | ENT_HTML5, 'UTF-8'))));
// If a post has no textual content, provide a short fallback so
// the Comment structured data contains one of the required
// representations (text/image/video). Prefer a simple label for
// attachments or a generic reply placeholder.
if ($text === '') {
$hasAttachments = ! empty($post->attachments ?? null) && count($post->attachments ?? []) > 0;
if ($hasAttachments) {
$text = 'Attachment';
} else {
$text = 'Reply';
}
}
return array_filter([
'url' => route('forum.thread.show', ['thread' => $post->thread_id, 'slug' => $thread->slug]) . '#post-' . $post->id,
'text' => Str::limit($text, 300),
'date_published' => $post->created_at?->toIso8601String(),
'date_modified' => ($post->edited_at ?? $post->created_at)?->toIso8601String(),
'author' => $makeForumAuthor($post->user ?? null) ?: ['name' => (string) ($post->user?->name ?? 'Skinbase')],
], fn ($value) => $value !== null && $value !== '');
})
->values()
->all(),
];
@endphp
@section('content')
@include('partials.seo.forum-microdata', ['forumMicrodata' => $forumMicrodata])
<div id="forum-thread-root"></div>
@php
$forumThreadProps = json_encode([
@@ -76,7 +143,7 @@
'created_at' => $thread->created_at?->toIso8601String(),
],
'category' => ['id' => $category->id ?? null, 'name' => $category->name ?? '', 'slug' => $category->slug ?? ''],
'author' => ['name' => $author->name ?? 'Unknown'],
'author' => ['name' => $author->name ?? 'Skinbase'],
'opPost' => $serializedOp,
'posts' => $serializedPosts,
'pagination' => $paginationData,

View File

@@ -10,7 +10,7 @@
<title>{{ $page_title ?? 'Similar Artworks — Skinbase' }}</title>
<meta name="description" content="{{ $page_meta_description ?? '' }}">
<link rel="canonical" href="{{ $page_canonical ?? url()->current() }}">
<meta name="robots" content="{{ $page_robots ?? 'noindex,follow' }}">
<meta name="robots" content="{{ $page_robots ?? 'index,follow' }}">
{{-- OpenGraph --}}
<meta property="og:type" content="website" />

View File

@@ -1,6 +1,8 @@
@php
$isPreview = (bool) ($previewMode ?? false);
$articleUrl = $isPreview ? ($previewCanonical ?? url()->current()) : route('news.show', $article->slug);
$articleImageLicenseUrl = route('terms-of-service');
$articleImageCreditText = (string) config('seo.site_name', 'Skinbase');
$articleSchemaImage = $article->effective_og_image
? url($article->effective_og_image)
: url((string) config('seo.fallback_image_path', '/gfx/skinbase_back_001.webp'));
@@ -24,6 +26,30 @@
: null,
])->filter()->values(),
])
->addJsonLd($articleSchemaImage
? array_filter([
'@context' => 'https://schema.org',
'@type' => 'ImageObject',
'url' => $articleSchemaImage,
'contentUrl' => $articleSchemaImage,
'thumbnailUrl' => $article->cover_mobile_url,
'caption' => $article->title,
'creditText' => $articleImageCreditText,
'license' => $articleImageLicenseUrl,
'acquireLicensePage' => $articleImageLicenseUrl,
'creator' => [
'@type' => 'Organization',
'name' => $articleImageCreditText,
'url' => url('/'),
],
'publisher' => [
'@type' => 'Organization',
'name' => $articleImageCreditText,
'url' => url('/'),
],
'representativeOfPage' => true,
], fn (mixed $value): bool => $value !== null && $value !== '')
: null)
->addJsonLd(array_filter([
'@context' => 'https://schema.org',
'@type' => 'NewsArticle',
@@ -36,6 +62,9 @@
'contentUrl' => $articleSchemaImage,
'thumbnailUrl' => $article->cover_mobile_url,
'caption' => $article->title,
'creditText' => $articleImageCreditText,
'license' => $articleImageLicenseUrl,
'acquireLicensePage' => $articleImageLicenseUrl,
], fn (mixed $value): bool => $value !== null && $value !== '')
: null,
'datePublished' => $article->published_at?->toIso8601String(),
@@ -133,7 +162,14 @@
<article class="min-w-0">
@if($article->cover_url)
<div class="overflow-hidden rounded-[32px] border border-white/[0.06] bg-black/20 shadow-[0_24px_60px_rgba(0,0,0,0.24)]">
<a href="{{ $articleCoverPreloadHref }}" class="group block focus:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/70 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-950" aria-label="Open full cover image">
<a
href="{{ $articleCoverPreloadHref }}"
class="group block focus:outline-none focus-visible:ring-2 focus-visible:ring-sky-300/70 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-950"
aria-label="Open full cover image"
data-news-image-preview
data-news-image-src="{{ $articleCoverPreloadHref }}"
data-news-image-alt="{{ $article->title }}"
>
<div class="relative">
<img src="{{ $article->cover_url }}" @if($article->cover_srcset) srcset="{{ $article->cover_srcset }}" sizes="{{ $articleCoverSizes }}" @endif alt="{{ $article->title }}" fetchpriority="high" loading="eager" decoding="async" class="h-auto max-h-[520px] w-full object-cover transition duration-300 group-hover:scale-[1.01]">
<div class="pointer-events-none absolute inset-x-4 bottom-4 flex items-center justify-between gap-3 rounded-full border border-white/10 bg-slate-950/72 px-4 py-2 text-xs font-semibold uppercase tracking-[0.18em] text-white/82 backdrop-blur-sm">
@@ -263,6 +299,8 @@
@endsection
@push('scripts')
@vite(['resources/js/Pages/News/NewsImagePreview.jsx'])
@if($needsFacebookEmbeds)
<div id="fb-root"></div>
<script async defer crossorigin="anonymous" src="https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v19.0"></script>

View File

@@ -0,0 +1,100 @@
@php
$forumMicrodata = is_array($forumMicrodata ?? null) ? $forumMicrodata : null;
@endphp
@if($forumMicrodata)
<div class="hidden" aria-hidden="true">
@if(($forumMicrodata['kind'] ?? null) === 'topic')
<div itemscope itemtype="https://schema.org/DiscussionForumPosting">
@if(!empty($forumMicrodata['canonical']))<meta itemprop="mainEntityOfPage" content="{{ $forumMicrodata['canonical'] }}" />@endif
@if(!empty($forumMicrodata['canonical']))<meta itemprop="url" content="{{ $forumMicrodata['canonical'] }}" />@endif
@if(!empty($forumMicrodata['title']))<meta itemprop="headline" content="{{ $forumMicrodata['title'] }}" />@endif
@if(!empty($forumMicrodata['text']))<meta itemprop="text" content="{{ $forumMicrodata['text'] }}" />@endif
@if(!empty($forumMicrodata['date_published']))<meta itemprop="datePublished" content="{{ $forumMicrodata['date_published'] }}" />@endif
@if(!empty($forumMicrodata['date_modified']))<meta itemprop="dateModified" content="{{ $forumMicrodata['date_modified'] }}" />@endif
@if(isset($forumMicrodata['comment_count']))<meta itemprop="commentCount" content="{{ (int) $forumMicrodata['comment_count'] }}" />@endif
@if(!empty($forumMicrodata['author']))
<div itemprop="author" itemscope itemtype="https://schema.org/Person">
@if(!empty($forumMicrodata['author']['url']))<meta itemprop="url" content="{{ $forumMicrodata['author']['url'] }}" />@endif
@if(!empty($forumMicrodata['author']['name']))<meta itemprop="name" content="{{ $forumMicrodata['author']['name'] }}" />@endif
</div>
@endif
@if(!empty($forumMicrodata['board']))
<div itemprop="isPartOf" itemscope itemtype="https://schema.org/WebPage">
@if(!empty($forumMicrodata['board']['url']))<meta itemprop="url" content="{{ $forumMicrodata['board']['url'] }}" />@endif
@if(!empty($forumMicrodata['board']['name']))<meta itemprop="name" content="{{ $forumMicrodata['board']['name'] }}" />@endif
@if(!empty($forumMicrodata['board']['category']))
<div itemprop="isPartOf" itemscope itemtype="https://schema.org/WebPage">
@if(!empty($forumMicrodata['board']['category']['url']))<meta itemprop="url" content="{{ $forumMicrodata['board']['category']['url'] }}" />@endif
@if(!empty($forumMicrodata['board']['category']['name']))<meta itemprop="name" content="{{ $forumMicrodata['board']['category']['name'] }}" />@endif
</div>
@endif
</div>
@endif
@foreach(($forumMicrodata['interactions'] ?? []) as $interaction)
<div itemprop="interactionStatistic" itemscope itemtype="https://schema.org/InteractionCounter">
@if(!empty($interaction['type']))<meta itemprop="interactionType" content="{{ $interaction['type'] }}" />@endif
@if(isset($interaction['count']))<meta itemprop="userInteractionCount" content="{{ (int) $interaction['count'] }}" />@endif
</div>
@endforeach
@foreach(($forumMicrodata['comments'] ?? []) as $comment)
<div itemprop="comment" itemscope itemtype="https://schema.org/Comment">
@if(!empty($comment['url']))<meta itemprop="url" content="{{ $comment['url'] }}" />@endif
@if(!empty($comment['text']))<meta itemprop="text" content="{{ $comment['text'] }}" />@endif
@if(!empty($comment['date_published']))<meta itemprop="datePublished" content="{{ $comment['date_published'] }}" />@endif
@if(!empty($comment['date_modified']))<meta itemprop="dateModified" content="{{ $comment['date_modified'] }}" />@endif
@if(!empty($comment['author']))
<div itemprop="author" itemscope itemtype="https://schema.org/Person">
@if(!empty($comment['author']['url']))<meta itemprop="url" content="{{ $comment['author']['url'] }}" />@endif
@if(!empty($comment['author']['name']))<meta itemprop="name" content="{{ $comment['author']['name'] }}" />@endif
</div>
@endif
@foreach(($comment['interactions'] ?? []) as $interaction)
<div itemprop="interactionStatistic" itemscope itemtype="https://schema.org/InteractionCounter">
@if(!empty($interaction['type']))<meta itemprop="interactionType" content="{{ $interaction['type'] }}" />@endif
@if(isset($interaction['count']))<meta itemprop="userInteractionCount" content="{{ (int) $interaction['count'] }}" />@endif
</div>
@endforeach
</div>
@endforeach
</div>
@elseif(($forumMicrodata['kind'] ?? null) === 'collection')
<div itemscope itemtype="https://schema.org/CollectionPage">
@if(!empty($forumMicrodata['canonical']))<meta itemprop="url" content="{{ $forumMicrodata['canonical'] }}" />@endif
@if(!empty($forumMicrodata['name']))<meta itemprop="name" content="{{ $forumMicrodata['name'] }}" />@endif
@if(!empty($forumMicrodata['description']))<meta itemprop="description" content="{{ $forumMicrodata['description'] }}" />@endif
<div itemprop="mainEntity" itemscope itemtype="https://schema.org/ItemList">
@if(!empty($forumMicrodata['list_name']))<meta itemprop="name" content="{{ $forumMicrodata['list_name'] }}" />@endif
@foreach(($forumMicrodata['items'] ?? []) as $index => $item)
<div itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<meta itemprop="position" content="{{ $index + 1 }}" />
<div itemprop="item" itemscope itemtype="https://schema.org/{{ $item['type'] ?? 'WebPage' }}">
@if(!empty($item['url']))<meta itemprop="url" content="{{ $item['url'] }}" />@endif
@if(!empty($item['title']))<meta itemprop="{{ ($item['type'] ?? null) === 'DiscussionForumPosting' ? 'headline' : 'name' }}" content="{{ $item['title'] }}" />@endif
@if(!empty($item['description']))<meta itemprop="description" content="{{ $item['description'] }}" />@endif
@if(!empty($item['text']))<meta itemprop="text" content="{{ $item['text'] }}" />@endif
@if(!empty($item['date_published']))<meta itemprop="datePublished" content="{{ $item['date_published'] }}" />@endif
@if(isset($item['comment_count']))<meta itemprop="commentCount" content="{{ (int) $item['comment_count'] }}" />@endif
@if(!empty($item['date_modified']))<meta itemprop="dateModified" content="{{ $item['date_modified'] }}" />@endif
@if(!empty($item['author']))
<div itemprop="author" itemscope itemtype="https://schema.org/Person">
@if(!empty($item['author']['url']))<meta itemprop="url" content="{{ $item['author']['url'] }}" />@endif
@if(!empty($item['author']['name']))<meta itemprop="name" content="{{ $item['author']['name'] }}" />@endif
</div>
@endif
</div>
</div>
@endforeach
</div>
</div>
@endif
</div>
@endif