Save workspace changes
This commit is contained in:
@@ -47,7 +47,11 @@ final class ArtworkPageController extends Controller
|
||||
return response(view('errors.410'), 410);
|
||||
}
|
||||
|
||||
if (! $raw->is_public || ! $raw->is_approved) {
|
||||
if (! $raw->is_public
|
||||
|| ! $raw->is_approved
|
||||
|| (string) ($raw->visibility ?? '') === Artwork::VISIBILITY_PRIVATE
|
||||
|| $raw->published_at === null
|
||||
|| $raw->published_at->isFuture()) {
|
||||
// Artwork exists but is private/unapproved → 403 Forbidden.
|
||||
// Show other public artworks by the same creator as recovery suggestions.
|
||||
$suggestions = app(ErrorSuggestionService::class);
|
||||
@@ -63,8 +67,7 @@ final class ArtworkPageController extends Controller
|
||||
->with('user')
|
||||
->where('user_id', $raw->user_id)
|
||||
->where('id', '!=', $raw->id)
|
||||
->public()
|
||||
->published()
|
||||
->catalogVisible()
|
||||
->limit(6)
|
||||
->get()
|
||||
->map(function (Artwork $a) {
|
||||
@@ -185,6 +188,9 @@ final class ArtworkPageController extends Controller
|
||||
'id' => (int) $item->id,
|
||||
'title' => html_entity_decode((string) $item->title, ENT_QUOTES | ENT_HTML5, 'UTF-8'),
|
||||
'author' => html_entity_decode((string) ($item->group?->name ?: $item->user?->name ?: $item->user?->username ?: 'Artist'), ENT_QUOTES | ENT_HTML5, 'UTF-8'),
|
||||
'author_id' => (int) ($item->user?->id ?? 0),
|
||||
'publisher_type' => $item->group ? 'group' : 'user',
|
||||
'publisher_id' => $item->group ? (int) $item->group->id : (int) ($item->user?->id ?? 0),
|
||||
'url' => route('art.show', ['id' => $item->id, 'slug' => $itemSlug]),
|
||||
'thumb' => $md['url'] ?? null,
|
||||
'thumb_srcset' => ($md['url'] ?? '') . ' 640w, ' . ($lg['url'] ?? '') . ' 1280w',
|
||||
|
||||
@@ -88,12 +88,12 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
|
||||
$ttl = self::SORT_TTL_MAP[$sort] ?? 300;
|
||||
|
||||
$artworks = Cache::remember(
|
||||
"browse.all.{$sort}.{$page}",
|
||||
"browse.all.catalog-visible.v2.{$sort}.{$page}",
|
||||
$ttl,
|
||||
fn () => Artwork::search('')->options([
|
||||
fn () => $this->search->searchWithThumbnailPreference([
|
||||
'filter' => 'is_public = true AND is_approved = true',
|
||||
'sort' => self::SORT_MAP[$sort] ?? ['created_at:desc'],
|
||||
])->paginate($perPage)
|
||||
], $perPage, false, $page)
|
||||
);
|
||||
$artworks->getCollection()->transform(fn ($a) => $this->presentArtwork($a));
|
||||
$seo = $this->buildPaginationSeo($request, url('/browse'), $artworks);
|
||||
@@ -150,12 +150,12 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
|
||||
$normalizedPath = trim((string) $path, '/');
|
||||
if ($normalizedPath === '') {
|
||||
$artworks = Cache::remember(
|
||||
"gallery.ct.{$contentSlug}.{$sort}.{$page}",
|
||||
"gallery.ct.catalog-visible.v2.{$contentSlug}.{$sort}.{$page}",
|
||||
$ttl,
|
||||
fn () => Artwork::search('')->options([
|
||||
fn () => $this->search->searchWithThumbnailPreference([
|
||||
'filter' => 'is_public = true AND is_approved = true AND content_type = "' . $contentSlug . '"',
|
||||
'sort' => self::SORT_MAP[$sort] ?? ['created_at:desc'],
|
||||
])->paginate($perPage)
|
||||
], $perPage, false, $page)
|
||||
);
|
||||
$artworks->getCollection()->transform(fn ($a) => $this->presentArtwork($a));
|
||||
$seo = $this->buildPaginationSeo($request, url('/' . $contentSlug), $artworks);
|
||||
@@ -197,12 +197,12 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
|
||||
->implode(' OR ');
|
||||
|
||||
$artworks = Cache::remember(
|
||||
'gallery.cat.' . md5($contentSlug . '|' . implode('|', $categorySlugs)) . ".{$sort}.{$page}",
|
||||
'gallery.cat.catalog-visible.v2.' . md5($contentSlug . '|' . implode('|', $categorySlugs)) . ".{$sort}.{$page}",
|
||||
$ttl,
|
||||
fn () => Artwork::search('')->options([
|
||||
fn () => $this->search->searchWithThumbnailPreference([
|
||||
'filter' => 'is_public = true AND is_approved = true AND (' . $categoryFilter . ')',
|
||||
'sort' => self::SORT_MAP[$sort] ?? ['created_at:desc'],
|
||||
])->paginate($perPage)
|
||||
], $perPage, false, $page)
|
||||
);
|
||||
$artworks->getCollection()->transform(fn ($a) => $this->presentArtwork($a));
|
||||
$seo = $this->buildPaginationSeo($request, url('/' . $contentSlug . '/' . strtolower($category->full_slug_path)), $artworks);
|
||||
|
||||
@@ -171,8 +171,7 @@ final class DiscoverController extends Controller
|
||||
$today = now();
|
||||
|
||||
$artworks = Artwork::query()
|
||||
->public()
|
||||
->published()
|
||||
->catalogVisible()
|
||||
->with([
|
||||
'user:id,name',
|
||||
'user.profile:user_id,avatar_hash',
|
||||
@@ -209,16 +208,14 @@ final class DiscoverController extends Controller
|
||||
|
||||
if ($hasStats) {
|
||||
$sub = Artwork::query()
|
||||
->public()
|
||||
->published()
|
||||
->catalogVisible()
|
||||
->join('artwork_stats', 'artwork_stats.artwork_id', '=', 'artworks.id')
|
||||
->where('artworks.published_at', '>=', now()->subDays(90))
|
||||
->selectRaw('artworks.user_id, SUM(artwork_stats.views) as recent_views, MAX(artworks.published_at) as latest_published')
|
||||
->groupBy('artworks.user_id');
|
||||
} else {
|
||||
$sub = Artwork::query()
|
||||
->public()
|
||||
->published()
|
||||
->catalogVisible()
|
||||
->where('published_at', '>=', now()->subDays(90))
|
||||
->selectRaw('user_id, COUNT(*) as recent_views, MAX(published_at) as latest_published')
|
||||
->groupBy('user_id');
|
||||
@@ -346,8 +343,7 @@ final class DiscoverController extends Controller
|
||||
|
||||
$artworks = Cache::remember($cacheKey, 60, function () use ($user, $followingIds, $perPage): \Illuminate\Pagination\LengthAwarePaginator {
|
||||
return Artwork::query()
|
||||
->public()
|
||||
->published()
|
||||
->catalogVisible()
|
||||
->with(['user:id,name,username', 'categories:id,name,slug,content_type_id,parent_id,sort_order'])
|
||||
->whereIn('user_id', $followingIds)
|
||||
->orderMissingThumbnailsLast()
|
||||
@@ -414,8 +410,7 @@ final class DiscoverController extends Controller
|
||||
private function fallbackFreshFromDatabase(int $perPage)
|
||||
{
|
||||
return Artwork::query()
|
||||
->public()
|
||||
->published()
|
||||
->catalogVisible()
|
||||
->with([
|
||||
'user:id,name,username',
|
||||
'user.profile:user_id,avatar_hash',
|
||||
@@ -434,8 +429,7 @@ final class DiscoverController extends Controller
|
||||
$cutoff = now()->subDays($windowDays)->startOfDay();
|
||||
|
||||
return Artwork::query()
|
||||
->public()
|
||||
->published()
|
||||
->catalogVisible()
|
||||
->with([
|
||||
'user:id,name,username',
|
||||
'user.profile:user_id,avatar_hash',
|
||||
@@ -460,8 +454,7 @@ final class DiscoverController extends Controller
|
||||
$cutoff = now()->subDays($windowDays)->startOfDay();
|
||||
|
||||
return Artwork::query()
|
||||
->public()
|
||||
->published()
|
||||
->catalogVisible()
|
||||
->with([
|
||||
'user:id,name,username',
|
||||
'user.profile:user_id,avatar_hash',
|
||||
@@ -488,8 +481,7 @@ final class DiscoverController extends Controller
|
||||
$recentActivity = $this->risingRecentActivitySubquery();
|
||||
|
||||
return Artwork::query()
|
||||
->public()
|
||||
->published()
|
||||
->catalogVisible()
|
||||
->with([
|
||||
'user:id,name,username',
|
||||
'user.profile:user_id,avatar_hash',
|
||||
@@ -552,6 +544,7 @@ final class DiscoverController extends Controller
|
||||
}
|
||||
|
||||
$byId = Artwork::query()
|
||||
->catalogVisible()
|
||||
->whereIn('id', $ids)
|
||||
->with([
|
||||
'user:id,name,username',
|
||||
@@ -571,6 +564,10 @@ final class DiscoverController extends Controller
|
||||
return $this->presentArtwork($full);
|
||||
}
|
||||
|
||||
if ($id > 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (object) [
|
||||
'id' => $item->id ?? 0,
|
||||
'name' => $item->title ?? $item->name ?? 'Untitled',
|
||||
@@ -588,7 +585,7 @@ final class DiscoverController extends Controller
|
||||
'width' => isset($item->width) && $item->width ? (int) $item->width : null,
|
||||
'height' => isset($item->height) && $item->height ? (int) $item->height : null,
|
||||
];
|
||||
})
|
||||
})->filter()->values()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -680,8 +677,7 @@ final class DiscoverController extends Controller
|
||||
}
|
||||
|
||||
return Artwork::query()
|
||||
->public()
|
||||
->published()
|
||||
->catalogVisible()
|
||||
->with(['user:id,name,username', 'user.profile:user_id,avatar_hash', 'stats:artwork_id,views,favorites,comments_count,heat_score'])
|
||||
->whereIn('user_id', $followingIds)
|
||||
->where('published_at', '>=', now()->subDays(30))
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\Group;
|
||||
use App\Models\User;
|
||||
use App\Services\ArtworkSearchService;
|
||||
use App\Services\ContentTypes\ContentTypeSlugResolver;
|
||||
use App\Services\EarlyGrowth\EarlyGrowth;
|
||||
@@ -77,10 +79,12 @@ final class ExploreController extends Controller
|
||||
$page = max(1, (int) $request->query('page', 1));
|
||||
$ttl = self::SORT_TTL[$sort] ?? 300;
|
||||
$cacheVersion = $this->cacheVersion();
|
||||
$filter = $this->buildExploreFilterExpression($request);
|
||||
$cacheSuffix = $this->requestCacheSuffix($request);
|
||||
|
||||
$artworks = Cache::remember("explore.all.v{$cacheVersion}.{$sort}.{$page}", $ttl, fn () =>
|
||||
$artworks = Cache::remember("explore.all.v{$cacheVersion}.{$cacheSuffix}.{$page}", $ttl, fn () =>
|
||||
$this->search->searchWithThumbnailPreference([
|
||||
'filter' => 'is_public = true AND is_approved = true',
|
||||
'filter' => $filter,
|
||||
'sort' => self::SORT_MAP[$sort] ?? ['created_at:desc'],
|
||||
], $perPage, false, $page)
|
||||
);
|
||||
@@ -148,13 +152,10 @@ final class ExploreController extends Controller
|
||||
$page = max(1, (int) $request->query('page', 1));
|
||||
$ttl = self::SORT_TTL[$sort] ?? 300;
|
||||
$cacheVersion = $this->cacheVersion();
|
||||
$filter = $this->buildExploreFilterExpression($request, $isAll ? null : $resolvedTypeSlug);
|
||||
$cacheSuffix = $this->requestCacheSuffix($request);
|
||||
|
||||
$filter = 'is_public = true AND is_approved = true';
|
||||
if (!$isAll) {
|
||||
$filter .= ' AND content_type = "' . $type . '"';
|
||||
}
|
||||
|
||||
$artworks = Cache::remember("explore.{$resolvedTypeSlug}.v{$cacheVersion}.{$sort}.{$page}", $ttl, fn () =>
|
||||
$artworks = Cache::remember("explore.{$resolvedTypeSlug}.v{$cacheVersion}.{$cacheSuffix}.{$page}", $ttl, fn () =>
|
||||
$this->search->searchWithThumbnailPreference([
|
||||
'filter' => $filter,
|
||||
'sort' => self::SORT_MAP[$sort] ?? ['created_at:desc'],
|
||||
@@ -288,11 +289,122 @@ final class ExploreController extends Controller
|
||||
return max(12, min($v, 80));
|
||||
}
|
||||
|
||||
private function requestCacheSuffix(Request $request): string
|
||||
{
|
||||
$query = $request->query();
|
||||
unset($query['grid'], $query['page']);
|
||||
ksort($query);
|
||||
|
||||
return md5(json_encode($query, JSON_THROW_ON_ERROR));
|
||||
}
|
||||
|
||||
private function cacheVersion(): int
|
||||
{
|
||||
return max(1, (int) Cache::get('explore.cache.version', 1));
|
||||
}
|
||||
|
||||
private function buildExploreFilterExpression(Request $request, ?string $contentType = null): string
|
||||
{
|
||||
$filterParts = [
|
||||
'is_public = true',
|
||||
'is_approved = true',
|
||||
];
|
||||
|
||||
if ($contentType !== null && $contentType !== '') {
|
||||
$filterParts[] = 'content_type = "' . addslashes($contentType) . '"';
|
||||
}
|
||||
|
||||
$orientation = strtolower(trim((string) $request->query('orientation', '')));
|
||||
if (in_array($orientation, ['landscape', 'portrait', 'square'], true)) {
|
||||
$filterParts[] = 'orientation = "' . addslashes($orientation) . '"';
|
||||
}
|
||||
|
||||
$resolution = $this->resolutionFilterValue((string) $request->query('resolution', ''));
|
||||
if ($resolution !== null) {
|
||||
$filterParts[] = 'resolution = "' . addslashes($resolution) . '"';
|
||||
}
|
||||
|
||||
$dateFrom = $this->normalizeDateQuery((string) $request->query('date_from', ''));
|
||||
if ($dateFrom !== null) {
|
||||
$filterParts[] = 'created_at >= "' . $dateFrom . '"';
|
||||
}
|
||||
|
||||
$dateTo = $this->normalizeDateQuery((string) $request->query('date_to', ''));
|
||||
if ($dateTo !== null) {
|
||||
$filterParts[] = 'created_at <= "' . $dateTo . '"';
|
||||
}
|
||||
|
||||
$authorFilter = $this->authorFilterExpression((string) $request->query('author', ''));
|
||||
if ($authorFilter !== null) {
|
||||
$filterParts[] = $authorFilter;
|
||||
}
|
||||
|
||||
return implode(' AND ', $filterParts);
|
||||
}
|
||||
|
||||
private function resolutionFilterValue(string $resolution): ?string
|
||||
{
|
||||
return match (strtolower(trim($resolution))) {
|
||||
'hd' => '1280x720',
|
||||
'fhd' => '1920x1080',
|
||||
'2k' => '2560x1440',
|
||||
'4k' => '3840x2160',
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
private function normalizeDateQuery(string $value): ?string
|
||||
{
|
||||
$value = trim($value);
|
||||
|
||||
if (! preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private function authorFilterExpression(string $author): ?string
|
||||
{
|
||||
$author = trim($author);
|
||||
|
||||
if ($author === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$userIds = User::query()
|
||||
->where(function ($query) use ($author): void {
|
||||
$query->where('username', 'like', '%' . $author . '%')
|
||||
->orWhere('name', 'like', '%' . $author . '%');
|
||||
})
|
||||
->limit(20)
|
||||
->pluck('id');
|
||||
|
||||
$groupIds = Group::query()
|
||||
->where(function ($query) use ($author): void {
|
||||
$query->where('name', 'like', '%' . $author . '%')
|
||||
->orWhere('slug', 'like', '%' . $author . '%');
|
||||
})
|
||||
->limit(20)
|
||||
->pluck('id');
|
||||
|
||||
$clauses = [];
|
||||
|
||||
foreach ($userIds as $userId) {
|
||||
$clauses[] = '(author_id = ' . (int) $userId . ' AND published_as_type = "user")';
|
||||
}
|
||||
|
||||
foreach ($groupIds as $groupId) {
|
||||
$clauses[] = '(author_id = ' . (int) $groupId . ' AND published_as_type = "group")';
|
||||
}
|
||||
|
||||
if ($clauses === []) {
|
||||
return 'id = 0';
|
||||
}
|
||||
|
||||
return '(' . implode(' OR ', $clauses) . ')';
|
||||
}
|
||||
|
||||
private function filterBrowsableArtworks(AbstractPaginator $paginator): AbstractPaginator
|
||||
{
|
||||
$paginator->setCollection(
|
||||
|
||||
@@ -32,6 +32,7 @@ final class HelpCenterPageController extends Controller
|
||||
'links' => [
|
||||
'studio_help' => route('help.studio'),
|
||||
'upload_help' => route('help.upload'),
|
||||
'help_worlds' => route('help.worlds'),
|
||||
'groups_documentation' => route('help.groups'),
|
||||
'groups_quickstart' => route('help.groups.quickstart'),
|
||||
'groups_faq' => route('help.groups.faq'),
|
||||
@@ -42,10 +43,14 @@ final class HelpCenterPageController extends Controller
|
||||
'studio_home' => route('studio.index'),
|
||||
'studio_content' => route('studio.content'),
|
||||
'studio_artworks' => route('studio.artworks'),
|
||||
'studio_worlds' => route('studio.worlds.index'),
|
||||
'studio_worlds_create' => route('studio.worlds.create'),
|
||||
'studio_cards' => route('studio.cards.index'),
|
||||
'studio_drafts' => route('studio.drafts'),
|
||||
'cards_create' => route('studio.cards.create'),
|
||||
'upload' => route('upload'),
|
||||
'worlds_index' => route('worlds.index'),
|
||||
'create_world' => route('worlds.create.redirect'),
|
||||
'cards_index' => route('cards.index'),
|
||||
'help_cards' => route('help.cards'),
|
||||
'help_profile' => route('help.profile'),
|
||||
|
||||
@@ -21,6 +21,7 @@ class LeaderboardPageController extends Controller
|
||||
'artworks', Leaderboard::TYPE_ARTWORK => Leaderboard::TYPE_ARTWORK,
|
||||
'groups', Leaderboard::TYPE_GROUP => Leaderboard::TYPE_GROUP,
|
||||
'stories', Leaderboard::TYPE_STORY => Leaderboard::TYPE_STORY,
|
||||
'worlds', Leaderboard::TYPE_WORLD => Leaderboard::TYPE_WORLD,
|
||||
default => Leaderboard::TYPE_CREATOR,
|
||||
};
|
||||
|
||||
@@ -28,6 +29,7 @@ class LeaderboardPageController extends Controller
|
||||
Leaderboard::TYPE_GROUP => 'Top Groups Leaderboard — Skinbase',
|
||||
Leaderboard::TYPE_STORY => 'Top Stories Leaderboard — Skinbase',
|
||||
Leaderboard::TYPE_ARTWORK => 'Top Artworks Leaderboard — Skinbase',
|
||||
Leaderboard::TYPE_WORLD => 'Top Worlds Leaderboard — Skinbase',
|
||||
default => 'Top Creators & Artworks Leaderboard — Skinbase',
|
||||
};
|
||||
|
||||
@@ -35,7 +37,8 @@ class LeaderboardPageController extends Controller
|
||||
Leaderboard::TYPE_GROUP => 'Track the leading groups across Skinbase by daily, weekly, monthly, and all-time performance.',
|
||||
Leaderboard::TYPE_STORY => 'Track the leading stories across Skinbase by daily, weekly, monthly, and all-time performance.',
|
||||
Leaderboard::TYPE_ARTWORK => 'Track the leading artworks across Skinbase by daily, weekly, monthly, and all-time performance.',
|
||||
default => 'Track the leading creators, groups, artworks, and stories across Skinbase by daily, weekly, monthly, and all-time performance.',
|
||||
Leaderboard::TYPE_WORLD => 'Track the leading Worlds across Skinbase by daily, weekly, monthly, and all-time performance.',
|
||||
default => 'Track the leading creators, groups, artworks, stories, and Worlds across Skinbase by daily, weekly, monthly, and all-time performance.',
|
||||
};
|
||||
|
||||
return Inertia::render('Leaderboard/LeaderboardPage', [
|
||||
|
||||
@@ -5,8 +5,10 @@ declare(strict_types=1);
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\ArtworkListResource;
|
||||
use App\Services\ArtworkSearchService;
|
||||
use App\Services\GroupDiscoveryService;
|
||||
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
use cPad\Plugins\News\Models\NewsArticle;
|
||||
@@ -62,9 +64,18 @@ final class SearchController extends Controller
|
||||
$groupResultCount = $groupResults->count();
|
||||
$newsResultCount = $newsResults->count();
|
||||
$hasAnyResults = $resultCount > 0 || $groupResultCount > 0 || $newsResultCount > 0;
|
||||
$galleryArtworks = collect(method_exists($artworks, 'items') ? $artworks->items() : $artworks)
|
||||
->map(fn ($art) => $this->mapArtworkCard($art))
|
||||
->values();
|
||||
|
||||
$galleryItems = method_exists($artworks, 'getCollection')
|
||||
? $artworks->getCollection()
|
||||
: new EloquentCollection(collect($artworks)->all());
|
||||
|
||||
$galleryItems->loadMissing(['user.profile', 'group', 'categories.contentType']);
|
||||
|
||||
$galleryArtworks = $galleryItems
|
||||
->map(fn ($artwork) => (new ArtworkListResource($artwork))->resolve($request))
|
||||
->values()
|
||||
->all();
|
||||
|
||||
$galleryNextPageUrl = method_exists($artworks, 'nextPageUrl') ? $artworks->nextPageUrl() : null;
|
||||
|
||||
return view('search.index', [
|
||||
@@ -87,28 +98,4 @@ final class SearchController extends Controller
|
||||
'page_robots' => 'noindex,follow',
|
||||
]);
|
||||
}
|
||||
|
||||
private function mapArtworkCard(mixed $artwork): array
|
||||
{
|
||||
return [
|
||||
'id' => $artwork->id ?? null,
|
||||
'name' => $artwork->name ?? null,
|
||||
'thumb' => $artwork->thumb_url ?? $artwork->thumb ?? null,
|
||||
'thumb_srcset' => $artwork->thumb_srcset ?? null,
|
||||
'uname' => $artwork->uname ?? '',
|
||||
'username' => $artwork->username ?? '',
|
||||
'avatar_url' => $artwork->avatar_url ?? null,
|
||||
'profile_url' => $artwork->profile_url ?? null,
|
||||
'published_as_type' => $artwork->published_as_type ?? null,
|
||||
'publisher' => $artwork->publisher ?? null,
|
||||
'category_name' => $artwork->category_name ?? '',
|
||||
'category_slug' => $artwork->category_slug ?? '',
|
||||
'slug' => $artwork->slug ?? '',
|
||||
'width' => $artwork->width ?? null,
|
||||
'height' => $artwork->height ?? null,
|
||||
'views' => $artwork->views ?? null,
|
||||
'likes' => $artwork->likes ?? null,
|
||||
'downloads' => $artwork->downloads ?? null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
51
app/Http/Controllers/Web/WorldController.php
Normal file
51
app/Http/Controllers/Web/WorldController.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\World;
|
||||
use App\Services\Worlds\WorldService;
|
||||
use App\Support\Seo\SeoFactory;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
final class WorldController extends Controller
|
||||
{
|
||||
public function __construct(private readonly WorldService $worlds)
|
||||
{
|
||||
}
|
||||
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$payload = $this->worlds->publicIndexPayload($request->user());
|
||||
$seo = app(SeoFactory::class)->collectionListing(
|
||||
'Worlds — Skinbase Nova',
|
||||
$payload['description'],
|
||||
route('worlds.index'),
|
||||
)->toArray();
|
||||
|
||||
return Inertia::render('World/WorldIndex', array_merge($payload, [
|
||||
'seo' => $seo,
|
||||
]))->rootView('collections');
|
||||
}
|
||||
|
||||
public function show(Request $request, World $world): Response
|
||||
{
|
||||
abort_unless($world->isPubliclyVisible(), 404);
|
||||
|
||||
$payload = $this->worlds->publicShowPayload($world, $request->user());
|
||||
$seo = app(SeoFactory::class)->collectionPage(
|
||||
$world->seo_title ?: ($world->title . ' — Skinbase Nova'),
|
||||
$world->seo_description ?: ($world->summary ?: $world->description ?: 'Seasonal and editorial discovery world on Skinbase Nova.'),
|
||||
route('worlds.show', ['world' => $world->slug]),
|
||||
$world->ogImageUrl(),
|
||||
)->toArray();
|
||||
|
||||
return Inertia::render('World/WorldShow', array_merge($payload, [
|
||||
'seo' => $seo,
|
||||
]))->rootView('collections');
|
||||
}
|
||||
}
|
||||
51
app/Http/Controllers/Web/WorldsHelpPageController.php
Normal file
51
app/Http/Controllers/Web/WorldsHelpPageController.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Support\Seo\SeoFactory;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
final class WorldsHelpPageController extends Controller
|
||||
{
|
||||
public function __invoke(Request $request): Response
|
||||
{
|
||||
$canonical = route('help.worlds');
|
||||
$seo = app(SeoFactory::class)
|
||||
->collectionPage(
|
||||
'Worlds Help — Skinbase',
|
||||
'Learn how Worlds work on Skinbase Nova, including editorial purpose, attached content, section control, preview, publishing, recurrence, and homepage promotion.',
|
||||
$canonical,
|
||||
)
|
||||
->toArray();
|
||||
|
||||
$seo['og_type'] = 'article';
|
||||
|
||||
return Inertia::render('Help/WorldsHelpPage', [
|
||||
'title' => 'Worlds Help',
|
||||
'description' => 'A complete guide to creating, attaching content to, previewing, and publishing Worlds on Skinbase Nova.',
|
||||
'seo' => $seo,
|
||||
'links' => [
|
||||
'help_home' => route('help'),
|
||||
'studio_help' => route('help.studio'),
|
||||
'upload_help' => route('help.upload'),
|
||||
'help_cards' => route('help.cards'),
|
||||
'groups_help' => route('help.groups'),
|
||||
'worlds_index' => route('worlds.index'),
|
||||
'create_world' => route('worlds.create.redirect'),
|
||||
'studio_worlds' => route('studio.worlds.index'),
|
||||
'studio_worlds_create' => route('studio.worlds.create'),
|
||||
'open_studio' => route('studio.index'),
|
||||
'contact_support' => route('contact.show'),
|
||||
'report_issue' => route('bug-report'),
|
||||
],
|
||||
'auth' => [
|
||||
'signed_in' => $request->user() !== null,
|
||||
],
|
||||
])->rootView('collections');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user