Implement creator studio and upload updates

This commit is contained in:
2026-04-04 10:12:02 +02:00
parent 1da7d3bf88
commit 0b216b7ecd
15107 changed files with 31206 additions and 626514 deletions

View File

@@ -5,31 +5,37 @@
@php
$hero_title = $post->title;
$seo = \App\Support\Seo\SeoDataBuilder::fromArray(
app(\App\Support\Seo\SeoFactory::class)->fromViewData(get_defined_vars())
)
->og(
type: 'article',
title: $post->meta_title ?: $post->title,
description: $post->meta_description ?: $post->excerpt ?: '',
url: $post->url,
image: $post->featured_image,
)
->addJsonLd([
'@context' => 'https://schema.org',
'@type' => 'Article',
'headline' => $post->title,
'datePublished' => $post->published_at?->toIso8601String(),
'dateModified' => $post->updated_at?->toIso8601String(),
'author' => [
'@type' => 'Organization',
'name' => 'Skinbase',
],
'publisher' => [
'@type' => 'Organization',
'name' => 'Skinbase',
],
'description' => $post->meta_description ?: $post->excerpt ?: '',
'mainEntityOfPage' => $post->url,
'image' => $post->featured_image,
])
->build();
@endphp
@push('head')
{{-- Article structured data --}}
<script type="application/ld+json">
{!! json_encode([
'@context' => 'https://schema.org',
'@type' => 'Article',
'headline' => $post->title,
'datePublished' => $post->published_at?->toIso8601String(),
'dateModified' => $post->updated_at?->toIso8601String(),
'author' => [
'@type' => 'Organization',
'name' => 'Skinbase',
],
'publisher' => [
'@type' => 'Organization',
'name' => 'Skinbase',
],
'description' => $post->meta_description ?: $post->excerpt ?: '',
'mainEntityOfPage' => $post->url,
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) !!}
</script>
@endpush
@section('page-content')
<article class="max-w-3xl">

View File

@@ -1,16 +1,6 @@
@extends('layouts.nova')
@push('head')
<link rel="canonical" href="{{ $page_canonical ?? url('/categories') }}">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Skinbase">
<meta property="og:title" content="{{ $page_title ?? 'Categories' }}">
<meta property="og:description" content="{{ $page_meta_description ?? '' }}">
<meta property="og:url" content="{{ $page_canonical ?? url('/categories') }}">
@if(!empty($structured_data ?? null))
<script type="application/ld+json">{!! json_encode($structured_data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_HEX_TAG) !!}</script>
@endif
@endpush
@php($useUnifiedSeo = true)
@section('main-class', '')

View File

@@ -3,6 +3,7 @@
@php
use Illuminate\Support\Str;
use App\Banner;
$useUnifiedSeo = true;
@endphp
@section('content')

View File

@@ -1,9 +1,21 @@
@extends('layouts.nova')
@php
@php
$page_title = $page_title ?? 'Community Activity';
$page_meta_description = 'Track comments, replies, reactions, and mentions from across the Skinbase community in one live feed.';
$page_canonical = route('community.activity', array_filter([
'filter' => ($initialFilter ?? null) && ($initialFilter ?? 'all') !== 'all' ? $initialFilter : null,
'user_id' => $initialUserId ?? null,
], fn (mixed $value): bool => $value !== null && $value !== ''));
$useUnifiedSeo = true;
$headerBreadcrumbs = collect([
(object) ['name' => $page_title ?? 'Community Activity', 'url' => route('community.activity')],
(object) ['name' => $page_title, 'url' => $page_canonical],
]);
$breadcrumbs = $headerBreadcrumbs;
$seo = \App\Support\Seo\SeoDataBuilder::fromArray(
app(\App\Support\Seo\SeoFactory::class)->fromViewData(get_defined_vars())
)->build();
$initialFilterLabel = match (($initialFilter ?? 'all')) {
'comments' => 'Comments',

View File

@@ -1,9 +1,17 @@
@extends('layouts.nova')
@php
$page_title = $page_title ?? 'Monthly Top Commentators';
$page_meta_description = 'Members who posted the most comments in the last 30 days.';
$page_canonical = route('comments.monthly', request()->query());
$useUnifiedSeo = true;
$headerBreadcrumbs = collect([
(object) ['name' => $page_title ?? 'Monthly Top Commentators', 'url' => route('comments.monthly')],
(object) ['name' => $page_title, 'url' => $page_canonical],
]);
$breadcrumbs = $headerBreadcrumbs;
$seo = \App\Support\Seo\SeoDataBuilder::fromArray(
app(\App\Support\Seo\SeoFactory::class)->fromViewData(get_defined_vars())
)->build();
@endphp
@section('content')

View File

@@ -1,9 +1,17 @@
@extends('layouts.nova')
@php
$page_title = $page_title ?? 'Daily Uploads';
$page_meta_description = 'Browse public artworks grouped by upload date across the last two weeks on Skinbase.';
$page_canonical = route('uploads.daily', request()->query());
$useUnifiedSeo = true;
$headerBreadcrumbs = collect([
(object) ['name' => $page_title ?? 'Daily Uploads', 'url' => route('uploads.daily')],
(object) ['name' => $page_title, 'url' => $page_canonical],
]);
$breadcrumbs = $headerBreadcrumbs;
$seo = \App\Support\Seo\SeoDataBuilder::fromArray(
app(\App\Support\Seo\SeoFactory::class)->fromViewData(get_defined_vars())
)->build();
@endphp
@section('content')

View File

@@ -1,9 +1,15 @@
@extends('layouts.nova')
@php
$page_title = $page_title ?? 'Most Downloaded Today';
$page_meta_description = 'Browse the artworks downloaded the most today on Skinbase.';
$page_canonical = route('downloads.today', request()->query());
$gallery_type = 'today-downloads';
$useUnifiedSeo = true;
$headerBreadcrumbs = collect([
(object) ['name' => $page_title ?? 'Most Downloaded Today', 'url' => route('downloads.today')],
(object) ['name' => $page_title, 'url' => $page_canonical],
]);
$breadcrumbs = $headerBreadcrumbs;
$galleryArtworks = collect($artworks->items())->map(fn ($art) => [
'id' => $art->id,
'name' => $art->name ?? null,
@@ -26,6 +32,9 @@
] : null,
])->values();
$galleryNextPageUrl = $artworks->nextPageUrl();
$seo = \App\Support\Seo\SeoDataBuilder::fromArray(
app(\App\Support\Seo\SeoFactory::class)->fromViewData(get_defined_vars())
)->build();
@endphp
@section('content')

View File

@@ -1,10 +1,16 @@
@extends('layouts.nova')
@php
$page_title = $page_title ?? 'Featured Artworks';
$page_meta_description = 'Browse staff-picked and community-highlighted artwork in the shared gallery feed.';
$page_canonical = request()->fullUrl();
$gallery_type = 'featured';
$useUnifiedSeo = true;
$featuredBreadcrumbs = collect([
(object) ['name' => 'Featured', 'url' => request()->path()],
(object) ['name' => $page_title ?? 'Featured Artworks', 'url' => request()->fullUrl()],
(object) ['name' => $page_title, 'url' => $page_canonical],
]);
$breadcrumbs = $featuredBreadcrumbs;
$galleryArtworks = collect($artworks->items())->map(fn ($art) => [
'id' => $art->id,
@@ -24,6 +30,9 @@
])->values();
$galleryNextPageUrl = $artworks->nextPageUrl();
$seo = \App\Support\Seo\SeoDataBuilder::fromArray(
app(\App\Support\Seo\SeoFactory::class)->fromViewData(get_defined_vars())
)->build();
@endphp
@section('content')

View File

@@ -1,48 +1,8 @@
@extends('layouts.nova')
@section('meta-description', $meta['description'])
@section('meta-keywords', $meta['keywords'])
@php($useUnifiedSeo = true)
@push('head')
<title>{{ $meta['title'] }}</title>
<link rel="canonical" href="{{ $meta['canonical'] }}">
{{-- Open Graph --}}
<meta property="og:type" content="website">
<meta property="og:site_name" content="Skinbase">
<meta property="og:title" content="{{ $meta['title'] }}">
<meta property="og:description" content="{{ $meta['description'] }}">
<meta property="og:url" content="{{ $meta['canonical'] }}">
@if(!empty($meta['og_image']))
<meta property="og:image" content="{{ $meta['og_image'] }}">
<meta property="og:image:type" content="image/webp">
@endif
{{-- Twitter --}}
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{ $meta['title'] }}">
<meta name="twitter:description" content="{{ $meta['description'] }}">
@if(!empty($meta['og_image']))
<meta name="twitter:image" content="{{ $meta['og_image'] }}">
@endif
{{-- JSON-LD WebSite schema --}}
@php
$websiteSchema = [
'@context' => 'https://schema.org',
'@type' => 'WebSite',
'name' => 'Skinbase',
'url' => url('/'),
'description' => $meta['description'],
'potentialAction' => [
'@type' => 'SearchAction',
'target' => url('/search') . '?q={search_term_string}',
'query-input' => 'required name=search_term_string',
],
];
@endphp
<script type="application/ld+json">{!! json_encode($websiteSchema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_HEX_TAG) !!}</script>
{{-- Preload hero image for faster LCP --}}
@if(!empty($props['hero']['thumb_lg']))
<link rel="preload" as="image" href="{{ $props['hero']['thumb_lg'] }}">

View File

@@ -1,10 +1,16 @@
@extends('layouts.nova')
@php
$page_title = $page_title ?? 'Member Photos';
$page_meta_description = 'Artwork submitted by the Skinbase community, presented in the shared Nova gallery feed.';
$page_canonical = route('members.photos', request()->query());
$gallery_type = 'member-photos';
$useUnifiedSeo = true;
$memberPhotoBreadcrumbs = collect([
(object) ['name' => 'Members', 'url' => route('creators.top')],
(object) ['name' => $page_title ?? 'Member Photos', 'url' => route('members.photos')],
(object) ['name' => $page_title, 'url' => $page_canonical],
]);
$breadcrumbs = $memberPhotoBreadcrumbs;
$memberPhotoItems = collect(method_exists($artworks ?? null, 'items') ? $artworks->items() : ($artworks ?? []));
$memberPhotoGallery = $memberPhotoItems->map(fn ($art) => [
'id' => $art->id ?? null,
@@ -28,6 +34,9 @@
: null,
]);
$memberPhotosNextPageUrl = method_exists($artworks ?? null, 'nextPageUrl') ? $artworks->nextPageUrl() : null;
$seo = \App\Support\Seo\SeoDataBuilder::fromArray(
app(\App\Support\Seo\SeoFactory::class)->fromViewData(get_defined_vars())
)->build();
@endphp
@section('content')

View File

@@ -1,3 +1,17 @@
@php
$useUnifiedSeo = true;
$seo = \App\Support\Seo\SeoDataBuilder::fromArray(
app(\App\Support\Seo\SeoFactory::class)->fromViewData(get_defined_vars())
)
->addJsonLd([
'@context' => 'https://schema.org',
'@type' => 'CollectionPage',
'name' => $page_title,
'description' => $page_meta_description,
'url' => $page_canonical,
])
->build();
@endphp
@extends('layouts.nova')
@section('content')

View File

@@ -2,36 +2,27 @@
@php
$storySummary = $story->excerpt ?: \Illuminate\Support\Str::limit(trim(strip_tags($safeContent)), 160);
@endphp
@push('head')
@php
$storyUrl = $story->canonical_url ?: route('stories.show', ['slug' => $story->slug]);
$creatorName = $story->creator?->display_name ?: $story->creator?->username ?: 'Unknown creator';
$metaDescription = $story->meta_description ?: $storySummary;
$metaTitle = $story->meta_title ?: $story->title;
$ogImage = $story->og_image ?: $story->cover_url;
$creatorFollowProps = $story->creator ? [
'username' => $story->creator->username,
'following' => (bool) ($storySocialProps['state']['is_following_creator'] ?? false),
'followers_count' => (int) ($storySocialProps['creator']['followers_count'] ?? 0),
] : null;
@endphp
<meta property="og:type" content="article" />
<meta property="og:title" content="{{ $metaTitle }}" />
<meta property="og:description" content="{{ $metaDescription }}" />
<meta property="og:url" content="{{ $storyUrl }}" />
@if($ogImage)
<meta property="og:image" content="{{ $ogImage }}" />
@endif
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="{{ $metaTitle }}" />
<meta name="twitter:description" content="{{ $metaDescription }}" />
@if($ogImage)
<meta name="twitter:image" content="{{ $ogImage }}" />
@endif
<script type="application/ld+json">
{!! json_encode([
$storyUrl = $story->canonical_url ?: route('stories.show', ['slug' => $story->slug]);
$creatorName = $story->creator?->display_name ?: $story->creator?->username ?: 'Unknown creator';
$metaDescription = $story->meta_description ?: $storySummary;
$metaTitle = $story->meta_title ?: $story->title;
$ogImage = $story->og_image ?: $story->cover_url;
$creatorFollowProps = $story->creator ? [
'username' => $story->creator->username,
'following' => (bool) ($storySocialProps['state']['is_following_creator'] ?? false),
'followers_count' => (int) ($storySocialProps['creator']['followers_count'] ?? 0),
] : null;
$seo = \App\Support\Seo\SeoDataBuilder::fromArray(
app(\App\Support\Seo\SeoFactory::class)->fromViewData(get_defined_vars())
)
->og(
type: 'article',
title: $metaTitle,
description: $metaDescription,
url: $storyUrl,
image: $ogImage,
)
->addJsonLd(array_filter([
'@context' => 'https://schema.org',
'@type' => 'Article',
'headline' => $story->title,
@@ -44,9 +35,9 @@
'datePublished' => optional($story->published_at)->toIso8601String(),
'dateModified' => optional($story->updated_at)->toIso8601String(),
'mainEntityOfPage' => $storyUrl,
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) !!}
</script>
@endpush
], fn (mixed $value): bool => $value !== null && $value !== ''))
->build();
@endphp
@section('page-hero')
<div class="hidden" aria-hidden="true"></div>

View File

@@ -1,6 +1,7 @@
@extends('layouts.nova')
@php
$useUnifiedSeo = true;
$hero_title = 'Tags';
$hero_description = 'Browse all artwork tags on Skinbase.';
$breadcrumbs = $breadcrumbs ?? collect([
@@ -9,18 +10,6 @@
]);
@endphp
@push('head')
@isset($page_canonical)
<link rel="canonical" href="{{ $page_canonical }}" />
@endisset
<meta name="robots" content="{{ $page_robots ?? 'index,follow' }}">
<meta property="og:type" content="website" />
<meta property="og:url" content="{{ $page_canonical ?? url()->current() }}" />
<meta property="og:title" content="{{ $page_title ?? 'Skinbase' }}" />
<meta property="og:description" content="{{ $page_meta_description ?? $hero_description }}" />
<meta property="og:site_name" content="Skinbase" />
@endpush
@section('content')
@php
$query = trim((string) ($query ?? ''));

View File

@@ -1,10 +1,16 @@
@extends('layouts.nova')
@php
$page_title = $page_title ?? 'Latest Artworks';
$page_meta_description = 'Fresh public uploads across skins, photography, wallpapers, and the rest of the Skinbase catalog.';
$page_canonical = route('uploads.latest', request()->query());
$gallery_type = 'latest-uploads';
$useUnifiedSeo = true;
$latestBreadcrumbs = collect([
(object) ['name' => 'Uploads', 'url' => route('uploads.latest')],
(object) ['name' => $page_title ?? 'Latest Artworks', 'url' => route('uploads.latest')],
(object) ['name' => $page_title, 'url' => $page_canonical],
]);
$breadcrumbs = $latestBreadcrumbs;
$cursorStateLabel = request()->filled('cursor') ? 'Browsing archive' : 'Latest slice';
$cursorStateCopy = request()->filled('cursor')
? 'You are viewing an older slice of the cursor-based feed.'
@@ -29,6 +35,9 @@
'url' => isset($art->id) ? '/art/' . $art->id . '/' . ($art->slug ?: \Illuminate\Support\Str::slug($art->name ?? 'artwork')) : '#',
])->values();
$galleryNextPageUrl = method_exists($artworks, 'nextPageUrl') ? $artworks->nextPageUrl() : null;
$seo = \App\Support\Seo\SeoDataBuilder::fromArray(
app(\App\Support\Seo\SeoFactory::class)->fromViewData(get_defined_vars())
)->build();
@endphp
@section('content')