Defer profile featured artworks

This commit is contained in:
2026-08-29 16:33:36 +02:00
parent ff80f5bf97
commit 872f420190
7 changed files with 336 additions and 39 deletions
+66 -37
View File
@@ -189,6 +189,24 @@ class ProfileController extends Controller
return $this->renderProfilePage($request, $user, 'Profile/ProfileShow', false, $normalizedTab);
}
/**
* Read-only featured artwork payload for the client-side Artworks tab.
* This deliberately does not render the profile document or track a view.
*/
public function featuredArtworks(Request $request, string $username): JsonResponse
{
$normalized = UsernamePolicy::normalize($username);
$user = User::query()->whereRaw('LOWER(username) = ?', [$normalized])->first();
if (! $user) {
return response()->json(['error' => 'User not found'], 404);
}
return response()->json([
'data' => $this->loadFeaturedArtworks($user, $request->user())->values(),
]);
}
public function legacyById(Request $request, int $id, ?string $username = null)
{
$user = User::query()->findOrFail($id);
@@ -989,42 +1007,9 @@ class ProfileController extends Controller
});
// ── Featured artworks for this user ─────────────────────────────────
$featuredArtworks = collect();
if (Schema::hasTable('artwork_features')) {
$featuredQuery = Artwork::query()
->with([
'user:id,name,username,level,rank',
'user.profile:user_id,avatar_hash',
'group:id,name,slug,avatar_path',
'stats:artwork_id,views,downloads,favorites',
'categories' => function ($query) {
$query->select('categories.id', 'categories.content_type_id', 'categories.parent_id', 'categories.name', 'categories.slug', 'categories.sort_order')
->with(['parent:id,parent_id,content_type_id,name,slug', 'contentType:id,slug,name']);
},
])
->join('artwork_features as af', 'af.artwork_id', '=', 'artworks.id')
->where('artworks.user_id', $user->id)
->where('af.is_active', true)
->whereNull('af.deleted_at')
->whereNull('artworks.deleted_at')
->where('artworks.is_public', true)
->where('artworks.is_approved', true)
->whereNotNull('artworks.published_at')
->select(['artworks.*', 'af.label as featured_label', 'af.featured_at as featured_slot_at'])
->orderByDesc('af.featured_at')
->limit(3);
app(ArtworkMaturityService::class)->applyViewerFilter($featuredQuery, $viewer);
$featuredArtworks = $featuredQuery
->get()
->map(function (Artwork $artwork) {
return (object) array_merge($this->mapArtworkCardPayload($artwork), [
'label' => $artwork->featured_label,
'featured_at' => $this->formatIsoDate($artwork->featured_slot_at),
]);
});
}
$featuredArtworks = $initialTab === 'artworks'
? $this->loadFeaturedArtworks($user, $viewer)
: null;
// ── Favourites ───────────────────────────────────────────────────────
$favouriteLimit = 12;
@@ -1397,7 +1382,7 @@ class ProfileController extends Controller
'cover_image' => $profile->cover_image ?? null,
] : null,
'artworks' => $artworkPayload,
'featuredArtworks' => $featuredArtworks->values(),
'featuredArtworks' => $featuredArtworks?->values(),
'favourites' => $favourites,
'stats' => $statsPayload,
'socialLinks' => $socialLinks,
@@ -1440,6 +1425,50 @@ class ProfileController extends Controller
]);
}
/**
* Build the canonical featured artwork representation shared by SSR and
* the lazy Artworks-tab endpoint.
*/
private function loadFeaturedArtworks(User $user, ?User $viewer): ?\Illuminate\Support\Collection
{
if (! Schema::hasTable('artwork_features')) {
return collect();
}
$featuredQuery = Artwork::query()
->with([
'user:id,name,username,level,rank',
'user.profile:user_id,avatar_hash',
'group:id,name,slug,avatar_path',
'stats:artwork_id,views,downloads,favorites',
'categories' => function ($query) {
$query->select('categories.id', 'categories.content_type_id', 'categories.parent_id', 'categories.name', 'categories.slug', 'categories.sort_order')
->with(['parent:id,parent_id,content_type_id,name,slug', 'contentType:id,slug,name']);
},
])
->join('artwork_features as af', 'af.artwork_id', '=', 'artworks.id')
->where('artworks.user_id', $user->id)
->where('af.is_active', true)
->whereNull('af.deleted_at')
->whereNull('artworks.deleted_at')
->where('artworks.is_public', true)
->where('artworks.is_approved', true)
->whereNotNull('artworks.published_at')
->select(['artworks.*', 'af.label as featured_label', 'af.featured_at as featured_slot_at'])
->orderByDesc('af.featured_at')
->limit(3);
app(ArtworkMaturityService::class)->applyViewerFilter($featuredQuery, $viewer);
return $featuredQuery->get()
->map(function (Artwork $artwork) {
return (object) array_merge($this->mapArtworkCardPayload($artwork), [
'label' => $artwork->featured_label,
'featured_at' => $this->formatIsoDate($artwork->featured_slot_at),
]);
});
}
private function normalizeProfileTab(mixed $tab): ?string
{
if (! is_string($tab)) {