feat: ship creator journey v2 and profile updates

This commit is contained in:
2026-04-12 21:42:07 +02:00
parent a2457f4e49
commit d5cff21ea2
335 changed files with 20147 additions and 1545 deletions

View File

@@ -7,7 +7,10 @@ use App\Models\ContentType;
use App\Models\Artwork;
use App\Services\ArtworkSearchService;
use App\Services\ArtworkService;
use App\Services\ContentTypes\ContentTypeSlugResolver;
use App\Services\Maturity\ArtworkMaturityService;
use App\Services\ThumbnailPresenter;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
@@ -17,8 +20,6 @@ use Illuminate\Pagination\AbstractCursorPaginator;
class BrowseGalleryController extends \App\Http\Controllers\Controller
{
private const CONTENT_TYPE_SLUGS = ['photography', 'wallpapers', 'skins', 'other', 'digital-art'];
/**
* Meilisearch sort-field arrays per sort alias.
* First element is primary sort; subsequent elements are tie-breakers.
@@ -74,6 +75,8 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
public function __construct(
private ArtworkService $artworks,
private ArtworkSearchService $search,
private ContentTypeSlugResolver $contentTypeResolver,
private ArtworkMaturityService $maturity,
) {
}
@@ -121,14 +124,18 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
public function content(Request $request, string $contentTypeSlug, ?string $path = null)
{
$contentSlug = strtolower($contentTypeSlug);
if (! in_array($contentSlug, self::CONTENT_TYPE_SLUGS, true)) {
$requestedSlug = strtolower($contentTypeSlug);
$resolution = $this->contentTypeResolver->resolve($requestedSlug);
if (! $resolution->found() || $resolution->contentType === null) {
abort(404);
}
$contentType = ContentType::where('slug', $contentSlug)->first();
if (! $contentType) {
abort(404);
$contentType = $resolution->contentType;
$contentSlug = strtolower((string) $contentType->slug);
if ($resolution->requiresRedirect()) {
return $this->redirectToContentTypePath($request, $contentSlug, $path, 301);
}
// Default sort: trending (not chronological)
@@ -265,12 +272,25 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
$contentTypeSlug = strtolower((string) $contentTypeSlug);
$categoryPath = $categoryPath !== null ? trim((string) $categoryPath, '/') : (isset($pathSegments[1]) ? implode('/', array_slice($pathSegments, 1, max(0, count($pathSegments) - 2))) : '');
$resolution = $this->contentTypeResolver->resolve($contentTypeSlug);
if (! $resolution->found() || $resolution->contentType === null) {
abort(404);
}
$resolvedContentTypeSlug = strtolower((string) $resolution->contentType->slug);
// Normalize artwork param if route-model binding returned an Artwork model
$artworkSlug = $artwork instanceof Artwork ? (string) $artwork->slug : (string) $artwork;
if ($resolution->requiresRedirect()) {
$path = trim($categoryPath . '/' . $artworkSlug, '/');
return $this->redirectToContentTypePath($req, $resolvedContentTypeSlug, $path, 301);
}
return app(\App\Http\Controllers\ArtworkController::class)->show(
$req,
$contentTypeSlug,
$resolvedContentTypeSlug,
$categoryPath,
$artworkSlug
);
@@ -293,7 +313,7 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
$username = $isGroupPublisher ? '' : ($artwork->user?->username ?? '');
$profileUrl = $isGroupPublisher ? $group->publicUrl() : ($username !== '' ? '/@' . $username : null);
return (object) [
return (object) $this->maturity->decoratePayload([
'id' => $artwork->id,
'name' => $artwork->title,
'content_type_name' => $primaryCategory?->contentType?->name ?? '',
@@ -317,7 +337,7 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
'published_at' => $artwork->published_at,
'width' => $artwork->width ?? null,
'height' => $artwork->height ?? null,
];
], $artwork, request()->user());
}
/**
@@ -372,9 +392,8 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
private function mainCategories(): Collection
{
return ContentType::ordered()
->whereIn('slug', self::CONTENT_TYPE_SLUGS)
->get(['name', 'slug'])
return $this->contentTypeResolver
->publicContentTypes()
->map(function (ContentType $type) {
return (object) [
'id' => $type->id,
@@ -385,6 +404,18 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
});
}
private function redirectToContentTypePath(Request $request, string $contentTypeSlug, ?string $path = null, int $status = 301): RedirectResponse
{
$target = url('/' . trim($contentTypeSlug . '/' . trim((string) $path, '/'), '/'));
$queryString = $request->getQueryString();
if ($queryString) {
$target .= '?' . $queryString;
}
return redirect()->to($target, $status);
}
private function buildPaginationSeo(Request $request, string $canonicalBaseUrl, mixed $paginator): array
{
$canonicalQuery = $request->query();