Defer profile featured artworks
This commit is contained in:
@@ -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)) {
|
||||
|
||||
Reference in New Issue
Block a user