diff --git a/app/Http/Controllers/CategoryPageController.php b/app/Http/Controllers/CategoryPageController.php index 38045619..f47e1b9a 100644 --- a/app/Http/Controllers/CategoryPageController.php +++ b/app/Http/Controllers/CategoryPageController.php @@ -33,7 +33,7 @@ class CategoryPageController extends Controller $perPage = 40; $artworks = $this->artworkService->getArtworksByContentType($contentType->slug, $perPage, $sort); - return view('legacy.content-type', compact( + return view('legacy::content-type', compact( 'contentType', 'rootCategories', 'artworks', @@ -92,7 +92,7 @@ class CategoryPageController extends Controller // resolved category and breadcrumbs are used by the view - return view('legacy.category-slug', compact( + return view('legacy::category-slug', compact( 'contentType', 'category', 'subcategories', diff --git a/app/Http/Controllers/Community/InterviewController.php b/app/Http/Controllers/Community/InterviewController.php index e715f26e..181c8be7 100644 --- a/app/Http/Controllers/Community/InterviewController.php +++ b/app/Http/Controllers/Community/InterviewController.php @@ -20,7 +20,7 @@ class InterviewController extends Controller $interviews = collect(); } - return view('legacy.interviews', [ + return view('legacy::interviews', [ 'interviews' => $interviews, 'page_title' => 'Interviews', ]); diff --git a/app/Http/Controllers/Community/LatestCommentsController.php b/app/Http/Controllers/Community/LatestCommentsController.php index 340927ca..d5a20a88 100644 --- a/app/Http/Controllers/Community/LatestCommentsController.php +++ b/app/Http/Controllers/Community/LatestCommentsController.php @@ -33,6 +33,7 @@ class LatestCommentsController extends Controller 'comment_id' => $c->getKey(), 'comment_description' => $c->content, 'commenter_id' => $c->user_id, + 'commenter_username' => $user?->username ?? null, 'country' => $user->country ?? null, 'icon' => $user ? DB::table('user_profiles')->where('user_id', $user->id)->value('avatar_hash') : null, 'uname' => $user->username ?? $user->name ?? 'User', diff --git a/app/Http/Controllers/GalleryController.php b/app/Http/Controllers/GalleryController.php index 5b6e3068..1df7a51a 100644 --- a/app/Http/Controllers/GalleryController.php +++ b/app/Http/Controllers/GalleryController.php @@ -30,7 +30,7 @@ class GalleryController extends Controller $artworks = $query->skip(($page - 1) * $hits)->take($hits)->get(); - return view('legacy.gallery', [ + return view('legacy::gallery', [ 'user' => $user, 'artworks' => $artworks, 'page' => $page, diff --git a/app/Http/Controllers/InterviewController.php b/app/Http/Controllers/InterviewController.php index 4f4558fa..dda8c915 100644 --- a/app/Http/Controllers/InterviewController.php +++ b/app/Http/Controllers/InterviewController.php @@ -92,7 +92,7 @@ class InterviewController extends Controller $page_title = 'Interview with ' . ($ar->username ?? ''); - return view('legacy.interview', [ + return view('legacy::interview', [ 'ar' => $ar, 'artworks' => $artworks, 'comments' => $comments, diff --git a/app/Http/Controllers/InterviewsController.php b/app/Http/Controllers/InterviewsController.php index 5b7dcf5e..ea4539dd 100644 --- a/app/Http/Controllers/InterviewsController.php +++ b/app/Http/Controllers/InterviewsController.php @@ -23,6 +23,6 @@ class InterviewsController extends Controller $page_title = 'Interviews'; - return view('legacy.interviews', compact('interviews', 'page_title')); + return view('legacy::interviews', compact('interviews', 'page_title')); } } diff --git a/app/Http/Controllers/Legacy/ArtController.php b/app/Http/Controllers/Legacy/ArtController.php index 10d666fe..8faf06d0 100644 --- a/app/Http/Controllers/Legacy/ArtController.php +++ b/app/Http/Controllers/Legacy/ArtController.php @@ -39,7 +39,7 @@ class ArtController extends Controller $data = $this->legacy->getArtwork((int) $id); if (! $data || empty($data['artwork'])) { - return view('legacy.placeholder', ['title' => 'Artwork Not Found']); + return view('legacy::placeholder', ['title' => 'Artwork Not Found']); } // load comments for artwork (legacy schema) @@ -57,6 +57,6 @@ class ArtController extends Controller $data['comments'] = $comments; - return view('legacy.art', $data); + return view('legacy::art', $data); } } diff --git a/app/Http/Controllers/Legacy/BrowseController.php b/app/Http/Controllers/Legacy/BrowseController.php index e44ecc76..3757052b 100644 --- a/app/Http/Controllers/Legacy/BrowseController.php +++ b/app/Http/Controllers/Legacy/BrowseController.php @@ -66,7 +66,7 @@ class BrowseController extends Controller $page_canonical = url('/browse'); - return view('legacy.browse', compact('page_title', 'page_meta_description', 'page_meta_keywords', 'page_canonical', 'artworks', 'rootCategories')); + return view('legacy::browse', compact('page_title', 'page_meta_description', 'page_meta_keywords', 'page_canonical', 'artworks', 'rootCategories')); } private function mapArtwork(Artwork $artwork): object diff --git a/app/Http/Controllers/Legacy/BuddiesController.php b/app/Http/Controllers/Legacy/BuddiesController.php index d1ec9ccc..504cdc52 100644 --- a/app/Http/Controllers/Legacy/BuddiesController.php +++ b/app/Http/Controllers/Legacy/BuddiesController.php @@ -32,6 +32,6 @@ class BuddiesController extends Controller $page_title = ($user->name ?? $user->username ?? 'User') . ': Followers'; - return view('legacy.buddies', compact('followers', 'page_title')); + return view('legacy::buddies', compact('followers', 'page_title')); } } diff --git a/app/Http/Controllers/Legacy/CategoryController.php b/app/Http/Controllers/Legacy/CategoryController.php index 66478f17..b56ea993 100644 --- a/app/Http/Controllers/Legacy/CategoryController.php +++ b/app/Http/Controllers/Legacy/CategoryController.php @@ -26,7 +26,7 @@ class CategoryController extends Controller // Expecting segments like ['category', '{contentType}', '{...categorySlugs}'] if (count($segments) < 2 || strtolower($segments[0]) !== 'category') { - return view('legacy.placeholder'); + return view('legacy::placeholder'); } $parts = array_slice($segments, 1); @@ -90,7 +90,7 @@ class CategoryController extends Controller $page_meta_description = $category->description ?? ($category->contentType->name . ' artworks on Skinbase'); $page_meta_keywords = strtolower($category->contentType->slug) . ', skinbase, artworks, wallpapers, skins, photography'; - return view('legacy.category', compact( + return view('legacy::category', compact( 'page_title', 'page_meta_description', 'page_meta_keywords', @@ -104,6 +104,6 @@ class CategoryController extends Controller public function browseCategories() { $data = $this->legacy->browseCategories(); - return view('legacy.categories', $data); + return view('legacy::categories', $data); } } diff --git a/app/Http/Controllers/Legacy/ChatController.php b/app/Http/Controllers/Legacy/ChatController.php index fc317103..ccd9ee3d 100644 --- a/app/Http/Controllers/Legacy/ChatController.php +++ b/app/Http/Controllers/Legacy/ChatController.php @@ -43,6 +43,6 @@ class ChatController extends Controller $smileys = collect(); } - return view('legacy.chat', compact('page_title', 'adHtml', 'chatHtml', 'smileys')); + return view('legacy::chat', compact('page_title', 'adHtml', 'chatHtml', 'smileys')); } } diff --git a/app/Http/Controllers/Legacy/DailyUploadsController.php b/app/Http/Controllers/Legacy/DailyUploadsController.php index 792f47d4..2eedcdd0 100644 --- a/app/Http/Controllers/Legacy/DailyUploadsController.php +++ b/app/Http/Controllers/Legacy/DailyUploadsController.php @@ -25,7 +25,7 @@ class DailyUploadsController extends Controller if ($isAjax && $datum) { // Return partial gallery for the given date $arts = $this->fetchByDate($datum); - return view('legacy.partials.daily-uploads-grid', ['arts' => $arts])->render(); + return view('legacy::partials.daily-uploads-grid', ['arts' => $arts])->render(); } // Build date tabs (today .. -14 days) @@ -41,7 +41,7 @@ class DailyUploadsController extends Controller // initial content: recent (last 7 days) $recent = $this->fetchRecent(); - return view('legacy.daily-uploads', [ + return view('legacy::daily-uploads', [ 'dates' => $dates, 'recent' => $recent, 'page_title' => 'Daily Uploads', diff --git a/app/Http/Controllers/Legacy/FavouritesController.php b/app/Http/Controllers/Legacy/FavouritesController.php index 121ce437..a7fcc242 100644 --- a/app/Http/Controllers/Legacy/FavouritesController.php +++ b/app/Http/Controllers/Legacy/FavouritesController.php @@ -120,7 +120,7 @@ class FavouritesController extends Controller $page_title = ($username ?: ($userNameCol ? DB::table('users')->where($userIdCol, $userId)->value($userNameCol) : '')) . ' Favourites'; - return view('legacy.favourites', [ + return view('legacy::favourites', [ 'results' => $results, 'page_title' => $page_title, 'user_id' => $userId, diff --git a/app/Http/Controllers/Legacy/FeaturedArtworksController.php b/app/Http/Controllers/Legacy/FeaturedArtworksController.php index ccdef506..264f3d54 100644 --- a/app/Http/Controllers/Legacy/FeaturedArtworksController.php +++ b/app/Http/Controllers/Legacy/FeaturedArtworksController.php @@ -53,7 +53,7 @@ class FeaturedArtworksController extends Controller $pageTitle = $artworkTypes[$type] ?? 'Featured Artworks'; - return view('legacy.featured-artworks', [ + return view('legacy::featured-artworks', [ 'artworks' => $artworks, 'type' => $type, 'artworkTypes' => $artworkTypes, diff --git a/app/Http/Controllers/Legacy/HomeController.php b/app/Http/Controllers/Legacy/HomeController.php index f75b0d40..ed57f9cd 100644 --- a/app/Http/Controllers/Legacy/HomeController.php +++ b/app/Http/Controllers/Legacy/HomeController.php @@ -43,7 +43,7 @@ class HomeController extends Controller $ourNews = []; $latestForumActivity = []; - return view('legacy.home', compact( + return view('legacy::home', compact( 'page_title', 'page_meta_description', 'page_meta_keywords', diff --git a/app/Http/Controllers/Legacy/InterviewController.php b/app/Http/Controllers/Legacy/InterviewController.php index 01c8cb2e..c4f2a144 100644 --- a/app/Http/Controllers/Legacy/InterviewController.php +++ b/app/Http/Controllers/Legacy/InterviewController.php @@ -95,7 +95,7 @@ class InterviewController extends Controller $page_title = 'Interview with ' . ($ar->username ?? ''); - return view('legacy.interview', [ + return view('legacy::interview', [ 'ar' => $ar, 'artworks' => $artworks, 'comments' => $comments, diff --git a/app/Http/Controllers/Legacy/InterviewsController.php b/app/Http/Controllers/Legacy/InterviewsController.php index 11e329ee..636414ca 100644 --- a/app/Http/Controllers/Legacy/InterviewsController.php +++ b/app/Http/Controllers/Legacy/InterviewsController.php @@ -23,6 +23,6 @@ class InterviewsController extends Controller $page_title = 'Interviews'; - return view('legacy.interviews', compact('interviews', 'page_title')); + return view('legacy::interviews', compact('interviews', 'page_title')); } } diff --git a/app/Http/Controllers/Legacy/LatestCommentsController.php b/app/Http/Controllers/Legacy/LatestCommentsController.php index 02db175f..1c883ad1 100644 --- a/app/Http/Controllers/Legacy/LatestCommentsController.php +++ b/app/Http/Controllers/Legacy/LatestCommentsController.php @@ -52,6 +52,6 @@ class LatestCommentsController extends Controller $page_title = 'Latest Comments'; - return view('legacy.latest-comments', compact('page_title', 'comments')); + return view('legacy::latest-comments', compact('page_title', 'comments')); } } diff --git a/app/Http/Controllers/Legacy/LatestController.php b/app/Http/Controllers/Legacy/LatestController.php index 156eb707..6ddc39ca 100644 --- a/app/Http/Controllers/Legacy/LatestController.php +++ b/app/Http/Controllers/Legacy/LatestController.php @@ -43,7 +43,7 @@ class LatestController extends Controller ]; }); - return view('legacy.latest-artworks', [ + return view('legacy::latest-artworks', [ 'artworks' => $artworks, 'page_title' => 'Latest Artworks', ]); diff --git a/app/Http/Controllers/Legacy/MembersController.php b/app/Http/Controllers/Legacy/MembersController.php index f721ca1e..ad26827b 100644 --- a/app/Http/Controllers/Legacy/MembersController.php +++ b/app/Http/Controllers/Legacy/MembersController.php @@ -46,6 +46,6 @@ class MembersController extends Controller }); } - return view('legacy.browse', compact('page_title', 'artworks')); + return view('legacy::browse', compact('page_title', 'artworks')); } } diff --git a/app/Http/Controllers/Legacy/MonthlyCommentatorsController.php b/app/Http/Controllers/Legacy/MonthlyCommentatorsController.php index 1e87d856..91415fcc 100644 --- a/app/Http/Controllers/Legacy/MonthlyCommentatorsController.php +++ b/app/Http/Controllers/Legacy/MonthlyCommentatorsController.php @@ -34,6 +34,6 @@ class MonthlyCommentatorsController extends Controller $page_title = 'Monthly Top Commentators'; - return view('legacy.monthly-commentators', compact('page_title', 'rows')); + return view('legacy::monthly-commentators', compact('page_title', 'rows')); } } diff --git a/app/Http/Controllers/Legacy/MyBuddiesController.php b/app/Http/Controllers/Legacy/MyBuddiesController.php index 9ab8db4b..3296eb8f 100644 --- a/app/Http/Controllers/Legacy/MyBuddiesController.php +++ b/app/Http/Controllers/Legacy/MyBuddiesController.php @@ -33,7 +33,7 @@ class MyBuddiesController extends Controller $page_title = ($user->name ?? $user->username ?? 'User') . ': Following List'; - return view('legacy.mybuddies', compact('buddies', 'page_title')); + return view('legacy::mybuddies', compact('buddies', 'page_title')); } public function destroy(Request $request, $id) diff --git a/app/Http/Controllers/Legacy/NewsController.php b/app/Http/Controllers/Legacy/NewsController.php index 25e6c2e9..79ef6bc5 100644 --- a/app/Http/Controllers/Legacy/NewsController.php +++ b/app/Http/Controllers/Legacy/NewsController.php @@ -39,6 +39,6 @@ class NewsController extends Controller $page_title = ($news->headline ?? 'News') . ' - SkinBase News'; - return view('legacy.news', compact('news', 'comments', 'page_title')); + return view('legacy::news', compact('news', 'comments', 'page_title')); } } diff --git a/app/Http/Controllers/Legacy/PhotographyController.php b/app/Http/Controllers/Legacy/PhotographyController.php index c735061e..97316c58 100644 --- a/app/Http/Controllers/Legacy/PhotographyController.php +++ b/app/Http/Controllers/Legacy/PhotographyController.php @@ -107,6 +107,6 @@ class PhotographyController extends Controller $page_meta_description = $tidy; - return view('legacy.content-type', compact('contentType','rootCategories','artworks','page_title','page_meta_description','subcategories','id')); + return view('legacy::content-type', compact('contentType','rootCategories','artworks','page_title','page_meta_description','subcategories','id')); } } diff --git a/app/Http/Controllers/Legacy/ProfileController.php b/app/Http/Controllers/Legacy/ProfileController.php index 5d3b1497..f23ef0cb 100644 --- a/app/Http/Controllers/Legacy/ProfileController.php +++ b/app/Http/Controllers/Legacy/ProfileController.php @@ -68,7 +68,7 @@ class ProfileController extends Controller 'about_me' => $user->bio ?? null, ]; - return view('legacy.profile', [ + return view('legacy::profile', [ 'user' => $legacyUser, 'artworks' => $artworks, ]); diff --git a/app/Http/Controllers/Legacy/ReceivedCommentsController.php b/app/Http/Controllers/Legacy/ReceivedCommentsController.php index 0bf8df83..2c670aba 100644 --- a/app/Http/Controllers/Legacy/ReceivedCommentsController.php +++ b/app/Http/Controllers/Legacy/ReceivedCommentsController.php @@ -28,7 +28,7 @@ class ReceivedCommentsController extends Controller $comments = $base->paginate($hits); - return view('legacy.received-comments', [ + return view('legacy::received-comments', [ 'comments' => $comments, 'page' => $page, 'hits' => $hits, diff --git a/app/Http/Controllers/Legacy/StatisticsController.php b/app/Http/Controllers/Legacy/StatisticsController.php index ba35a44e..15a10ddd 100644 --- a/app/Http/Controllers/Legacy/StatisticsController.php +++ b/app/Http/Controllers/Legacy/StatisticsController.php @@ -57,7 +57,7 @@ class StatisticsController extends Controller return $row; }); - return view('legacy.statistics', [ + return view('legacy::statistics', [ 'artworks' => $artworks, 'sort' => $sort, 'page_title' => 'Artwork Statistics', diff --git a/app/Http/Controllers/Legacy/TodayDownloadsController.php b/app/Http/Controllers/Legacy/TodayDownloadsController.php index 40f3e86d..d8bf8c2b 100644 --- a/app/Http/Controllers/Legacy/TodayDownloadsController.php +++ b/app/Http/Controllers/Legacy/TodayDownloadsController.php @@ -63,6 +63,6 @@ class TodayDownloadsController extends Controller $page_title = 'Today Downloaded Artworks'; - return view('legacy.browse', ['page_title' => $page_title, 'artworks' => $paginator]); + return view('legacy::browse', ['page_title' => $page_title, 'artworks' => $paginator]); } } diff --git a/app/Http/Controllers/Legacy/TodayInHistoryController.php b/app/Http/Controllers/Legacy/TodayInHistoryController.php index 2451a12c..1c0d9046 100644 --- a/app/Http/Controllers/Legacy/TodayInHistoryController.php +++ b/app/Http/Controllers/Legacy/TodayInHistoryController.php @@ -46,7 +46,7 @@ class TodayInHistoryController extends Controller }); } - return view('legacy.today-in-history', [ + return view('legacy::today-in-history', [ 'artworks' => $artworks, 'page_title' => 'Popular on this day in history', ]); diff --git a/app/Http/Controllers/Legacy/TopAuthorsController.php b/app/Http/Controllers/Legacy/TopAuthorsController.php index 02384dae..a2c3ebcc 100644 --- a/app/Http/Controllers/Legacy/TopAuthorsController.php +++ b/app/Http/Controllers/Legacy/TopAuthorsController.php @@ -55,6 +55,6 @@ class TopAuthorsController extends Controller $page_title = 'Top Authors'; - return view('legacy.top-authors', compact('page_title', 'authors', 'metric')); + return view('legacy::top-authors', compact('page_title', 'authors', 'metric')); } } diff --git a/app/Http/Controllers/Legacy/TopFavouritesController.php b/app/Http/Controllers/Legacy/TopFavouritesController.php index 8d2ae664..aeb15438 100644 --- a/app/Http/Controllers/Legacy/TopFavouritesController.php +++ b/app/Http/Controllers/Legacy/TopFavouritesController.php @@ -52,6 +52,6 @@ class TopFavouritesController extends Controller $page_title = 'Top Favourites'; - return view('legacy.top-favourites', ['page_title' => $page_title, 'artworks' => $paginator]); + return view('legacy::top-favourites', ['page_title' => $page_title, 'artworks' => $paginator]); } } diff --git a/app/Http/Controllers/Legacy/UserController.php b/app/Http/Controllers/Legacy/UserController.php index e7286a83..b2580c1d 100644 --- a/app/Http/Controllers/Legacy/UserController.php +++ b/app/Http/Controllers/Legacy/UserController.php @@ -173,7 +173,7 @@ class UserController extends Controller } } - return view('legacy.user', [ + return view('legacy::user', [ 'user' => $user, 'birthDay' => $birthDay, 'birthMonth' => $birthMonth, diff --git a/app/Http/Controllers/LegacyController.php b/app/Http/Controllers/LegacyController.php index f56f3e01..68d4b1bc 100644 --- a/app/Http/Controllers/LegacyController.php +++ b/app/Http/Controllers/LegacyController.php @@ -22,7 +22,7 @@ class LegacyController extends Controller $ourNews = $this->ourNews(); $latestForumActivity = $this->latestForumActivity(); - return view('legacy.home', compact( + return view('legacy::home', compact( 'page_title', 'page_meta_description', 'page_meta_keywords', @@ -75,7 +75,7 @@ class LegacyController extends Controller ); } - return view('legacy.browse', compact('page_title', 'page_meta_description', 'page_meta_keywords', 'artworks')); + return view('legacy::browse', compact('page_title', 'page_meta_description', 'page_meta_keywords', 'artworks')); } public function category(Request $request, string $group, ?string $slug = null, ?int $id = null) @@ -172,7 +172,7 @@ class LegacyController extends Controller } } - return view('legacy.category', compact( + return view('legacy::category', compact( 'group', 'category', 'artworks', @@ -246,7 +246,7 @@ class LegacyController extends Controller } } - return view('legacy.categories', compact( + return view('legacy::categories', compact( 'categories', 'subgroups', 'page_title', diff --git a/app/Http/Controllers/PhotographyController.php b/app/Http/Controllers/PhotographyController.php index 1b1483e6..63d48e14 100644 --- a/app/Http/Controllers/PhotographyController.php +++ b/app/Http/Controllers/PhotographyController.php @@ -96,6 +96,6 @@ class PhotographyController extends Controller $page_meta_description = $tidy; - return view('legacy.content-type', compact('contentType','rootCategories','artworks','page_title','page_meta_description','subcategories','id')); + return view('legacy::content-type', compact('contentType','rootCategories','artworks','page_title','page_meta_description','subcategories','id')); } } diff --git a/app/Http/Controllers/User/MonthlyCommentatorsController.php b/app/Http/Controllers/User/MonthlyCommentatorsController.php index 257b0013..11701fb9 100644 --- a/app/Http/Controllers/User/MonthlyCommentatorsController.php +++ b/app/Http/Controllers/User/MonthlyCommentatorsController.php @@ -19,6 +19,7 @@ class MonthlyCommentatorsController extends Controller ->whereRaw('t1.created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)') ->select( 't2.id as user_id', + 't2.username as user_username', DB::raw('COALESCE(t2.username, t2.name, "User") as uname'), DB::raw('COUNT(*) as num_comments') ) diff --git a/app/Http/Controllers/User/MyBuddiesController.php b/app/Http/Controllers/User/MyBuddiesController.php index 85b33f1c..2f9970d1 100644 --- a/app/Http/Controllers/User/MyBuddiesController.php +++ b/app/Http/Controllers/User/MyBuddiesController.php @@ -22,7 +22,7 @@ class MyBuddiesController extends Controller ->leftJoin('users as t2', 't1.friend_id', '=', 't2.id') ->leftJoin('user_profiles as p', 'p.user_id', '=', 't2.id') ->where('t1.user_id', $user->id) - ->select('t1.id', 't1.friend_id', 't1.user_id', 't2.name as uname', 'p.avatar_hash as icon', 't1.date_added') + ->select('t1.id', 't1.friend_id', 't1.user_id', 't2.name as uname', 't2.username as user_username', 'p.avatar_hash as icon', 't1.date_added') ->orderByDesc('t1.date_added'); $buddies = $query->paginate($perPage)->withQueryString(); diff --git a/app/Http/Controllers/User/ProfileController.php b/app/Http/Controllers/User/ProfileController.php index 31c38e86..d133906d 100644 --- a/app/Http/Controllers/User/ProfileController.php +++ b/app/Http/Controllers/User/ProfileController.php @@ -383,7 +383,7 @@ class ProfileController extends Controller // ── Favourites ─────────────────────────────────────────────────────── $favourites = collect(); if (Schema::hasTable('user_favorites')) { - $favourites = DB::table('user_favorites as uf') + $favIds = DB::table('user_favorites as uf') ->join('artworks as a', 'a.id', '=', 'uf.artwork_id') ->where('uf.user_id', $user->id) ->whereNull('a.deleted_at') @@ -391,20 +391,18 @@ class ProfileController extends Controller ->where('a.is_approved', true) ->orderByDesc('uf.created_at') ->limit(12) - ->select(['a.id', 'a.title as name', 'a.hash', 'a.thumb_ext', 'a.width', 'a.height', 'a.user_id']) - ->get() - ->map(function ($row) { - $thumbUrl = ($row->hash && $row->thumb_ext) - ? ThumbnailService::fromHash($row->hash, $row->thumb_ext, 'sm') - : '/images/placeholder.jpg'; - return (object) [ - 'id' => $row->id, - 'name' => $row->name, - 'thumb' => $thumbUrl, - 'width' => $row->width, - 'height'=> $row->height, - ]; - }); + ->pluck('a.id'); + + if ($favIds->isNotEmpty()) { + $indexed = Artwork::with('user:id,name,username') + ->whereIn('id', $favIds) + ->get() + ->keyBy('id'); + // Preserve the ordering from the favourites table + $favourites = $favIds + ->filter(fn ($id) => $indexed->has($id)) + ->map(fn ($id) => $indexed[$id]); + } } // ── Statistics ─────────────────────────────────────────────────────── @@ -499,6 +497,16 @@ class ProfileController extends Controller $countryName = $countryName ?? strtoupper((string) $profile->country_code); } + // ── Hero background artwork ───────────────────────────────────────── + $heroBgUrl = Artwork::public() + ->published() + ->where('user_id', $user->id) + ->whereNotNull('hash') + ->whereNotNull('thumb_ext') + ->inRandomOrder() + ->limit(1) + ->first()?->thumbUrl('lg'); + // ── Increment profile views (async-safe, ignore errors) ────────────── if (! $isOwner) { try { @@ -510,7 +518,7 @@ class ProfileController extends Controller } catch (\Throwable) {} } - return response()->view('legacy.profile', [ + return response()->view('legacy::profile', [ 'user' => $user, 'profile' => $profile, 'artworks' => $artworks, @@ -521,6 +529,7 @@ class ProfileController extends Controller 'followerCount' => $followerCount, 'recentFollowers' => $recentFollowers, 'viewerIsFollowing' => $viewerIsFollowing, + 'heroBgUrl' => $heroBgUrl, 'profileComments' => $profileComments, 'countryName' => $countryName, 'isOwner' => $isOwner, diff --git a/app/Http/Controllers/User/TodayInHistoryController.php b/app/Http/Controllers/User/TodayInHistoryController.php index e13e8624..ff3d6064 100644 --- a/app/Http/Controllers/User/TodayInHistoryController.php +++ b/app/Http/Controllers/User/TodayInHistoryController.php @@ -45,7 +45,7 @@ class TodayInHistoryController extends Controller }); } - return view('legacy.today-in-history', [ + return view('legacy::today-in-history', [ 'artworks' => $artworks, 'page_title' => 'Popular on this day in history', ]); diff --git a/app/Http/Controllers/User/TopFavouritesController.php b/app/Http/Controllers/User/TopFavouritesController.php index 42d551ec..e715538b 100644 --- a/app/Http/Controllers/User/TopFavouritesController.php +++ b/app/Http/Controllers/User/TopFavouritesController.php @@ -51,6 +51,6 @@ class TopFavouritesController extends Controller $page_title = 'Top Favourites'; - return view('legacy.top-favourites', ['page_title' => $page_title, 'artworks' => $paginator]); + return view('legacy::top-favourites', ['page_title' => $page_title, 'artworks' => $paginator]); } } diff --git a/app/Http/Controllers/User/UserController.php b/app/Http/Controllers/User/UserController.php index 97b2e25c..de7dc33f 100644 --- a/app/Http/Controllers/User/UserController.php +++ b/app/Http/Controllers/User/UserController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\User; use App\Http\Controllers\Controller; +use App\Models\Artwork; use Illuminate\Http\Request; class UserController extends Controller @@ -20,8 +21,28 @@ class UserController extends Controller $profile = null; } - return view('legacy.user', [ - 'profile' => $profile, + // Hero background: prefer featured wallpapers or photography + $heroBgUrl = Artwork::public() + ->published() + ->whereNotNull('hash') + ->whereNotNull('thumb_ext') + ->whereHas('features', function ($q) { + $q->where('is_active', true) + ->where(function ($q2) { + $q2->whereNull('expires_at')->orWhere('expires_at', '>', now()); + }); + }) + ->whereHas('categories', function ($q) { + // content_type_id 2 = Wallpapers, 3 = Photography + $q->whereIn('content_type_id', [2, 3]); + }) + ->inRandomOrder() + ->limit(1) + ->first()?->thumbUrl('lg'); + + return view('legacy::user', [ + 'profile' => $profile, + 'heroBgUrl' => $heroBgUrl, ]); } } diff --git a/app/Http/Controllers/Web/HomeController.php b/app/Http/Controllers/Web/HomeController.php index 30ca51fe..fef2ddbb 100644 --- a/app/Http/Controllers/Web/HomeController.php +++ b/app/Http/Controllers/Web/HomeController.php @@ -1,119 +1,34 @@ artworks = $artworks; - } + $sections = $this->homepage->all(); - public function index(Request $request) - { - $page_title = 'Skinbase - Photography, Skins & Wallpapers'; - $page_meta_description = 'Skinbase legacy home, rendered via Laravel.'; - $page_meta_keywords = 'wallpapers, skins, photography, community'; + $hero = $sections['hero']; - $featuredResult = $this->artworks->getFeaturedArtworks(null, 39); - if ($featuredResult instanceof \Illuminate\Pagination\LengthAwarePaginator) { - $featuredCollection = $featuredResult->getCollection(); - $featured = $featuredCollection->get(0); - $memberFeatured = $featuredCollection->get(1); - } elseif (is_array($featuredResult)) { - $featured = $featuredResult[0] ?? null; - $memberFeatured = $featuredResult[1] ?? null; - } elseif ($featuredResult instanceof Collection) { - $featured = $featuredResult->get(0); - $memberFeatured = $featuredResult->get(1); - } else { - $featured = $featuredResult; - $memberFeatured = null; - } + $meta = [ + 'title' => 'Skinbase – Digital Art & Wallpapers', + 'description' => 'Discover stunning digital art, wallpapers, and skins from a global community of creators. Browse trending works, fresh uploads, and beloved classics.', + 'keywords' => 'wallpapers, digital art, skins, photography, community, wallpaper downloads', + 'og_image' => $hero['thumb_lg'] ?? $hero['thumb'] ?? null, + 'canonical' => url('/'), + ]; - $latestUploads = $this->artworks->getLatestArtworks(20); - - // Forum news (prefer migrated legacy news category id 2876, fallback to slug) - try { - $forumNews = DB::table('forum_threads as t1') - ->leftJoin('users as u', 't1.user_id', '=', 'u.id') - ->leftJoin('forum_categories as c', 't1.category_id', '=', 'c.id') - ->selectRaw('t1.id as topic_id, t1.title as topic, COALESCE(u.name, ?) as uname, t1.created_at as post_date, t1.content as preview', ['Unknown']) - ->whereNull('t1.deleted_at') - ->where(function ($query) { - $query->where('t1.category_id', 2876) - ->orWhereIn('c.slug', ['news', 'forum-news']); - }) - ->orderByDesc('t1.created_at') - ->limit(8) - ->get(); - } catch (QueryException $e) { - Log::warning('Forum threads table missing or DB error when loading forum news', ['exception' => $e->getMessage()]); - $forumNews = collect(); - } - - // Our news (latest site news) - try { - $ourNews = DB::table('news as t1') - ->join('news_categories as c', 't1.category_id', '=', 'c.category_id') - ->join('users as u', 't1.user_id', '=', 'u.user_id') - ->selectRaw('t1.news_id, t1.headline, t1.user_id, t1.picture, t1.preview, u.uname, t1.create_date, t1.views, c.category_name, (SELECT COUNT(*) FROM news_comments WHERE news_id = t1.news_id) AS num_comments') - ->orderBy('t1.create_date', 'desc') - ->limit(5) - ->get(); - } catch (QueryException $e) { - Log::warning('News table missing or DB error when loading our news', ['exception' => $e->getMessage()]); - $ourNews = collect(); - } - - // Latest forum activity (exclude forum news category) - try { - $latestForumActivity = DB::table('forum_threads as t1') - ->leftJoin('forum_categories as c', 't1.category_id', '=', 'c.id') - ->leftJoin('forum_posts as p', function ($join) { - $join->on('p.thread_id', '=', 't1.id') - ->whereNull('p.deleted_at'); - }) - ->selectRaw('t1.id as topic_id, t1.title as topic, COUNT(p.id) as numPosts') - ->whereNull('t1.deleted_at') - ->where(function ($query) { - $query->where('t1.category_id', '<>', 2876) - ->orWhereNull('t1.category_id'); - }) - ->where(function ($query) { - $query->whereNull('c.slug') - ->orWhereNotIn('c.slug', ['news', 'forum-news']); - }) - ->groupBy('t1.id', 't1.title') - ->orderByDesc('t1.last_post_at') - ->orderByDesc('t1.created_at') - ->limit(10) - ->get(); - } catch (QueryException $e) { - Log::warning('Forum threads table missing or DB error when loading latest forum activity', ['exception' => $e->getMessage()]); - $latestForumActivity = collect(); - } - - return view('web.home', compact( - 'page_title', - 'page_meta_description', - 'page_meta_keywords', - 'featured', - 'memberFeatured', - 'latestUploads', - 'forumNews', - 'ourNews', - 'latestForumActivity' - )); + return view('web.home', [ + 'meta' => $meta, + 'props' => $sections, + ]); } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 3fe006be..8b14ea93 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -37,6 +37,11 @@ class AppServiceProvider extends ServiceProvider */ public function boot(): void { + // Map the 'legacy' view namespace to resources/views/_legacy so all + // view('legacy::foo') and @include('legacy::foo') calls resolve correctly + // after the folder was renamed from legacy/ to _legacy/. + View::addNamespace('legacy', resource_path('views/_legacy')); + $this->configureAuthRateLimiters(); $this->configureUploadRateLimiters(); $this->configureMailFailureLogging(); diff --git a/app/Services/HomepageService.php b/app/Services/HomepageService.php new file mode 100644 index 00000000..57c309aa --- /dev/null +++ b/app/Services/HomepageService.php @@ -0,0 +1,304 @@ + $this->getHeroArtwork(), + 'trending' => $this->getTrending(), + 'fresh' => $this->getFreshUploads(), + 'tags' => $this->getPopularTags(), + 'creators' => $this->getCreatorSpotlight(), + 'news' => $this->getNews(), + ]; + } + + // ───────────────────────────────────────────────────────────────────────── + // Sections + // ───────────────────────────────────────────────────────────────────────── + + /** + * Hero artwork: first item from the featured list. + */ + public function getHeroArtwork(): ?array + { + return Cache::remember('homepage.hero', self::CACHE_TTL, function (): ?array { + $result = $this->artworks->getFeaturedArtworks(null, 1); + + /** @var \Illuminate\Database\Eloquent\Model|\null $artwork */ + if ($result instanceof \Illuminate\Pagination\LengthAwarePaginator) { + $artwork = $result->getCollection()->first(); + } elseif ($result instanceof \Illuminate\Support\Collection) { + $artwork = $result->first(); + } elseif (is_array($result)) { + $artwork = $result[0] ?? null; + } else { + $artwork = null; + } + + return $artwork ? $this->serializeArtwork($artwork, 'lg') : null; + }); + } + + /** + * Trending: up to 12 artworks ordered by award score, views, downloads, recent activity. + * + * Award score = SUM(weight × medal_value) where gold=3, silver=2, bronze=1. + * Uses correlated subqueries to avoid GROUP BY issues with MySQL strict mode. + */ + public function getTrending(int $limit = 12): array + { + return Cache::remember("homepage.trending.{$limit}", self::CACHE_TTL, function () use ($limit): array { + $ids = DB::table('artworks') + ->select('id') + ->selectRaw( + '(SELECT COALESCE(SUM(weight * CASE medal' + . ' WHEN \'gold\' THEN 3' + . ' WHEN \'silver\' THEN 2' + . ' ELSE 1 END), 0)' + . ' FROM artwork_awards WHERE artwork_awards.artwork_id = artworks.id) AS award_score' + ) + ->selectRaw('COALESCE((SELECT views FROM artwork_stats WHERE artwork_stats.artwork_id = artworks.id), 0) AS stat_views') + ->selectRaw('COALESCE((SELECT downloads FROM artwork_stats WHERE artwork_stats.artwork_id = artworks.id), 0) AS stat_downloads') + ->where('is_public', true) + ->where('is_approved', true) + ->whereNull('deleted_at') + ->whereNotNull('published_at') + ->where('published_at', '>=', now()->subDays(30)) + ->orderByDesc('award_score') + ->orderByDesc('stat_views') + ->orderByDesc('stat_downloads') + ->orderByDesc('published_at') + ->limit($limit) + ->pluck('id'); + + if ($ids->isEmpty()) { + return []; + } + + $indexed = Artwork::with(['user:id,name,username', 'user.profile:user_id,avatar_hash']) + ->whereIn('id', $ids) + ->get() + ->keyBy('id'); + + return $ids + ->filter(fn ($id) => $indexed->has($id)) + ->map(fn ($id) => $this->serializeArtwork($indexed[$id])) + ->values() + ->all(); + }); + } + + /** + * Fresh uploads: latest 12 approved public artworks. + */ + public function getFreshUploads(int $limit = 12): array + { + return Cache::remember("homepage.fresh.{$limit}", self::CACHE_TTL, function () use ($limit): array { + $artworks = Artwork::public() + ->published() + ->with(['user:id,name,username', 'user.profile:user_id,avatar_hash']) + ->orderByDesc('published_at') + ->limit($limit) + ->get(); + + return $artworks->map(fn ($a) => $this->serializeArtwork($a))->values()->all(); + }); + } + + /** + * Top 12 popular tags by usage_count. + */ + public function getPopularTags(int $limit = 12): array + { + return Cache::remember("homepage.tags.{$limit}", self::CACHE_TTL, function () use ($limit): array { + return Tag::query() + ->where('is_active', true) + ->orderByDesc('usage_count') + ->limit($limit) + ->get(['id', 'name', 'slug', 'usage_count']) + ->map(fn ($t) => [ + 'id' => $t->id, + 'name' => $t->name, + 'slug' => $t->slug, + 'count' => (int) $t->usage_count, + ]) + ->values() + ->all(); + }); + } + + /** + * Creator spotlight: top 6 creators by weekly uploads, awards, and engagement. + * "Weekly uploads" drives ranking per spec; ties broken by total awards then views. + */ + public function getCreatorSpotlight(int $limit = 6): array + { + return Cache::remember("homepage.creators.{$limit}", self::CACHE_TTL, function () use ($limit): array { + try { + $since = now()->subWeek(); + + $rows = DB::table('artworks') + ->join('users as u', 'u.id', '=', 'artworks.user_id') + ->leftJoin('user_profiles as up', 'up.user_id', '=', 'u.id') + ->leftJoin('artwork_awards as aw', 'aw.artwork_id', '=', 'artworks.id') + ->leftJoin('artwork_stats as s', 's.artwork_id', '=', 'artworks.id') + ->select( + 'u.id', + 'u.name', + 'u.username', + 'up.avatar_hash', + DB::raw('COUNT(DISTINCT artworks.id) as upload_count'), + DB::raw('SUM(CASE WHEN artworks.published_at >= \'' . $since->toDateTimeString() . '\' THEN 1 ELSE 0 END) as weekly_uploads'), + DB::raw('COALESCE(SUM(s.views), 0) as total_views'), + DB::raw('COUNT(DISTINCT aw.id) as total_awards') + ) + ->where('artworks.is_public', true) + ->where('artworks.is_approved', true) + ->whereNull('artworks.deleted_at') + ->whereNotNull('artworks.published_at') + ->groupBy('u.id', 'u.name', 'u.username', 'up.avatar_hash') + ->orderByDesc('weekly_uploads') + ->orderByDesc('total_awards') + ->orderByDesc('total_views') + ->limit($limit) + ->get(); + + $userIds = $rows->pluck('id')->all(); + + // Pick one random artwork thumbnail per creator for the card background. + $thumbsByUser = Artwork::public() + ->published() + ->whereIn('user_id', $userIds) + ->whereNotNull('hash') + ->whereNotNull('thumb_ext') + ->inRandomOrder() + ->get(['id', 'user_id', 'hash', 'thumb_ext']) + ->groupBy('user_id'); + + return $rows->map(function ($u) use ($thumbsByUser) { + $artworkForBg = $thumbsByUser->get($u->id)?->first(); + $bgThumb = $artworkForBg ? $artworkForBg->thumbUrl('md') : null; + + return [ + 'id' => $u->id, + 'name' => $u->name, + 'uploads' => (int) $u->upload_count, + 'weekly_uploads' => (int) $u->weekly_uploads, + 'views' => (int) $u->total_views, + 'awards' => (int) $u->total_awards, + 'url' => $u->username ? '/@' . $u->username : '/profile/' . $u->id, + 'avatar' => AvatarUrl::forUser((int) $u->id, $u->avatar_hash ?: null, 128), + 'bg_thumb' => $bgThumb, + ]; + })->values()->all(); + } catch (QueryException $e) { + Log::warning('HomepageService::getCreatorSpotlight DB error', [ + 'exception' => $e->getMessage(), + ]); + + return []; + } + }); + } + + /** + * Latest 5 news posts from the forum news category. + */ + public function getNews(int $limit = 5): array + { + return Cache::remember("homepage.news.{$limit}", self::CACHE_TTL, function () use ($limit): array { + try { + $items = DB::table('forum_threads as t') + ->leftJoin('forum_categories as c', 'c.id', '=', 't.category_id') + ->select('t.id', 't.title', 't.created_at', 't.slug as thread_slug') + ->where(function ($q) { + $q->where('t.category_id', 2876) + ->orWhereIn('c.slug', ['news', 'forum-news']); + }) + ->whereNull('t.deleted_at') + ->orderByDesc('t.created_at') + ->limit($limit) + ->get(); + + return $items->map(fn ($row) => [ + 'id' => $row->id, + 'title' => $row->title, + 'date' => $row->created_at, + 'url' => '/forum/thread/' . $row->id . '-' . ($row->thread_slug ?? 'post'), + ])->values()->all(); + } catch (QueryException $e) { + Log::warning('HomepageService::getNews DB error', [ + 'exception' => $e->getMessage(), + ]); + + return []; + } + }); + } + + // ───────────────────────────────────────────────────────────────────────── + // Helpers + // ───────────────────────────────────────────────────────────────────────── + + private function serializeArtwork(Artwork $artwork, string $preferSize = 'md'): array + { + $thumbMd = $artwork->thumbUrl('md'); + $thumbLg = $artwork->thumbUrl('lg'); + $thumb = $preferSize === 'lg' ? ($thumbLg ?? $thumbMd) : ($thumbMd ?? $thumbLg); + + $authorId = $artwork->user_id; + $authorName = $artwork->user?->name ?? 'Artist'; + $authorUsername = $artwork->user?->username ?? ''; + $avatarHash = $artwork->user?->profile?->avatar_hash ?? null; + $authorAvatar = AvatarUrl::forUser((int) $authorId, $avatarHash, 40); + + return [ + 'id' => $artwork->id, + 'title' => $artwork->title ?? 'Untitled', + 'slug' => $artwork->slug, + 'author' => $authorName, + 'author_id' => $authorId, + 'author_username' => $authorUsername, + 'author_avatar' => $authorAvatar, + 'thumb' => $thumb, + 'thumb_md' => $thumbMd, + 'thumb_lg' => $thumbLg, + 'url' => '/art/' . $artwork->id . '/' . ($artwork->slug ?? ''), + 'width' => $artwork->width, + 'height' => $artwork->height, + 'published_at' => $artwork->published_at?->toIso8601String(), + ]; + } +} diff --git a/resources/js/Pages/Home/HomeCreators.jsx b/resources/js/Pages/Home/HomeCreators.jsx new file mode 100644 index 00000000..5365e33e --- /dev/null +++ b/resources/js/Pages/Home/HomeCreators.jsx @@ -0,0 +1,72 @@ +import React from 'react' + +const AVATAR_FALLBACK = 'https://files.skinbase.org/avatars/default.webp' + +function CreatorCard({ creator }) { + return ( +
+ {/* Background artwork thumbnail */} + {creator.bg_thumb && ( + <> + +
+ + )} + + {/* Content */} + + {creator.name} { e.currentTarget.src = AVATAR_FALLBACK }} + /> +

{creator.name}

+
+
+ 📁 {creator.uploads} + {creator.weekly_uploads > 0 && ( + ↑{creator.weekly_uploads} this week + )} + 👁 {creator.views.toLocaleString()} + {creator.awards > 0 && 🏆 {creator.awards}} +
+ + View Profile + +
+ ) +} + +export default function HomeCreators({ creators }) { + if (!Array.isArray(creators) || creators.length === 0) return null + + return ( +
+
+

👤 Creator Spotlight

+ + All creators → + +
+ +
+ {creators.map((c) => ( + + ))} +
+
+ ) +} diff --git a/resources/js/Pages/Home/HomeFresh.jsx b/resources/js/Pages/Home/HomeFresh.jsx new file mode 100644 index 00000000..8410a685 --- /dev/null +++ b/resources/js/Pages/Home/HomeFresh.jsx @@ -0,0 +1,73 @@ +import React from 'react' + +const FALLBACK = 'https://files.skinbase.org/default/missing_md.webp' +const AVATAR_FALLBACK = 'https://files.skinbase.org/avatars/default.webp' + +function FreshCard({ item }) { + const username = item.author_username ? `@${item.author_username}` : null + + return ( +
+ +
+ {/* Gloss sheen */} +
+ + {item.title} { e.currentTarget.src = FALLBACK }} + /> + + {/* Top-right View badge */} +
+ View +
+ + {/* Bottom info overlay — always visible on mobile, hover-only on md+ */} +
+
{item.title}
+
+ {item.author} { e.currentTarget.src = AVATAR_FALLBACK }} + /> + {item.author} + {username && {username}} +
+
+
+ {item.title} by {item.author} +
+
+ ) +} + +export default function HomeFresh({ items }) { + if (!Array.isArray(items) || items.length === 0) return null + + return ( +
+
+

🆕 Fresh Uploads

+ + See all → + +
+ +
+ {items.map((item) => ( + + ))} +
+
+ ) +} diff --git a/resources/js/Pages/Home/HomeHero.jsx b/resources/js/Pages/Home/HomeHero.jsx new file mode 100644 index 00000000..f838ffa9 --- /dev/null +++ b/resources/js/Pages/Home/HomeHero.jsx @@ -0,0 +1,77 @@ +import React from 'react' + +const FALLBACK = 'https://files.skinbase.org/default/missing_lg.webp' + +export default function HomeHero({ artwork }) { + if (!artwork) { + return ( +
+
+
+

+ Discover Digital Art +

+

+ Wallpapers, skins & digital creations from a global community. +

+
+ Explore + Upload +
+
+
+ ) + } + + const src = artwork.thumb_lg || artwork.thumb || FALLBACK + + return ( +
+ {/* Background image */} + {artwork.title} { e.currentTarget.src = FALLBACK }} + /> + + {/* Gradient overlay */} +
+ + {/* Content */} +
+

+ Featured Artwork +

+

+ {artwork.title} +

+

+ by {artwork.author} +

+
+ + Explore + + + Upload + + + View Artwork + +
+
+
+ ) +} diff --git a/resources/js/Pages/Home/HomeNews.jsx b/resources/js/Pages/Home/HomeNews.jsx new file mode 100644 index 00000000..db49249c --- /dev/null +++ b/resources/js/Pages/Home/HomeNews.jsx @@ -0,0 +1,42 @@ +import React from 'react' + +function formatDate(dateStr) { + if (!dateStr) return '' + try { + return new Date(dateStr).toLocaleDateString('en-US', { + year: 'numeric', month: 'short', day: 'numeric', + }) + } catch { + return '' + } +} + +export default function HomeNews({ items }) { + if (!Array.isArray(items) || items.length === 0) return null + + return ( +
+
+

📰 News & Updates

+ + All news → + +
+ +
+ {items.map((item) => ( + + {item.title} + {item.date && ( + {formatDate(item.date)} + )} + + ))} +
+
+ ) +} diff --git a/resources/js/Pages/Home/HomePage.jsx b/resources/js/Pages/Home/HomePage.jsx new file mode 100644 index 00000000..7c1d3c64 --- /dev/null +++ b/resources/js/Pages/Home/HomePage.jsx @@ -0,0 +1,63 @@ +import React, { lazy, Suspense } from 'react' +import { createRoot } from 'react-dom/client' + +// Sub-section components — lazy-loaded so only the hero blocks the initial bundle +import HomeHero from './HomeHero' + +const HomeTrending = lazy(() => import('./HomeTrending')) +const HomeFresh = lazy(() => import('./HomeFresh')) +const HomeTags = lazy(() => import('./HomeTags')) +const HomeCreators = lazy(() => import('./HomeCreators')) +const HomeNews = lazy(() => import('./HomeNews')) + +function SectionFallback() { + return ( +
+ ) +} + +function HomePage({ hero, trending, fresh, tags, creators, news }) { + return ( +
+ {/* Hero — above-fold, eager */} + + + {/* Below-fold sections — lazy */} + }> + + + + }> + + + + }> + + + + }> + + + + }> + + +
+ ) +} + +// Auto-mount when the Blade view provides #homepage-root +const mountEl = document.getElementById('homepage-root') +if (mountEl) { + let props = {} + try { + const propsEl = document.getElementById('homepage-props') + props = propsEl ? JSON.parse(propsEl.textContent || '{}') : {} + } catch { + props = {} + } + + createRoot(mountEl).render() +} + +export default HomePage diff --git a/resources/js/Pages/Home/HomeTags.jsx b/resources/js/Pages/Home/HomeTags.jsx new file mode 100644 index 00000000..232912be --- /dev/null +++ b/resources/js/Pages/Home/HomeTags.jsx @@ -0,0 +1,26 @@ +import React from 'react' + +export default function HomeTags({ tags }) { + if (!Array.isArray(tags) || tags.length === 0) return null + + return ( +
+

🏷️ Popular Tags

+ +
+ {tags.map((tag) => ( + + {tag.name} + {tag.count > 0 && ( + {tag.count.toLocaleString()} + )} + + ))} +
+
+ ) +} diff --git a/resources/js/Pages/Home/HomeTrending.jsx b/resources/js/Pages/Home/HomeTrending.jsx new file mode 100644 index 00000000..90ca2d89 --- /dev/null +++ b/resources/js/Pages/Home/HomeTrending.jsx @@ -0,0 +1,75 @@ +import React from 'react' + +const FALLBACK = 'https://files.skinbase.org/default/missing_md.webp' +const AVATAR_FALLBACK = 'https://files.skinbase.org/avatars/default.webp' + +function ArtCard({ item }) { + const username = item.author_username ? `@${item.author_username}` : null + + return ( + + ) +} + +export default function HomeTrending({ items }) { + if (!Array.isArray(items) || items.length === 0) return null + + return ( +
+
+

+ 🔥 Trending This Week +

+ + See all → + +
+ +
+ {items.map((item) => ( + + ))} +
+
+ ) +} diff --git a/resources/js/components/Topbar.jsx b/resources/js/components/Topbar.jsx index 92e257e6..e912a0a0 100644 --- a/resources/js/components/Topbar.jsx +++ b/resources/js/components/Topbar.jsx @@ -1,7 +1,11 @@ -import React from 'react' +import React, { useState } from 'react' import SearchBar from '../Search/SearchBar' -export default function Topbar() { +const DEFAULT_AVATAR = 'https://files.skinbase.org/avatars/default.webp' + +export default function Topbar({ user = null }) { + const [menuOpen, setMenuOpen] = useState(false) + return (
@@ -16,11 +20,53 @@ export default function Topbar() {
-
- Forum - +
+ Forum + + {user ? ( +
+ + + {menuOpen && ( +
diff --git a/resources/js/entry-topbar.jsx b/resources/js/entry-topbar.jsx index 8fa0fa69..49fedfce 100644 --- a/resources/js/entry-topbar.jsx +++ b/resources/js/entry-topbar.jsx @@ -6,8 +6,18 @@ function mount() { const container = document.getElementById('topbar-root') if (!container) return + const user = container.dataset.userId + ? { + id: container.dataset.userId, + displayName: container.dataset.displayName || 'Account', + username: container.dataset.username || '', + avatarUrl: container.dataset.avatarUrl || null, + uploadUrl: container.dataset.uploadUrl || '/upload', + } + : null + const root = createRoot(container) - root.render() + root.render() // hide legacy header if present const legacy = document.getElementById('legacy-topbar') diff --git a/resources/views/legacy/_artwork_card.blade.php b/resources/views/_legacy/_artwork_card.blade.php similarity index 100% rename from resources/views/legacy/_artwork_card.blade.php rename to resources/views/_legacy/_artwork_card.blade.php diff --git a/resources/views/legacy/toolbar.blade.php b/resources/views/_legacy/_toolbar.blade.php similarity index 100% rename from resources/views/legacy/toolbar.blade.php rename to resources/views/_legacy/_toolbar.blade.php diff --git a/resources/views/legacy/home.blade.php b/resources/views/_legacy/home.blade.php similarity index 64% rename from resources/views/legacy/home.blade.php rename to resources/views/_legacy/home.blade.php index 8c5897ce..d055dd24 100644 --- a/resources/views/legacy/home.blade.php +++ b/resources/views/_legacy/home.blade.php @@ -8,10 +8,10 @@ @section('content')
- @include('legacy.home.featured') + @include('legacy::home.featured') - @include('legacy.home.uploads') + @include('legacy::home.uploads') - @include('legacy.home.news') + @include('legacy::home.news')
@endsection diff --git a/resources/views/legacy/home/featured.blade.php b/resources/views/_legacy/home/featured.blade.php similarity index 100% rename from resources/views/legacy/home/featured.blade.php rename to resources/views/_legacy/home/featured.blade.php diff --git a/resources/views/legacy/home/news.blade.php b/resources/views/_legacy/home/news.blade.php similarity index 100% rename from resources/views/legacy/home/news.blade.php rename to resources/views/_legacy/home/news.blade.php diff --git a/resources/views/legacy/home/uploads.blade.php b/resources/views/_legacy/home/uploads.blade.php similarity index 100% rename from resources/views/legacy/home/uploads.blade.php rename to resources/views/_legacy/home/uploads.blade.php diff --git a/resources/views/legacy/interview.blade.php b/resources/views/_legacy/interview.blade.php similarity index 100% rename from resources/views/legacy/interview.blade.php rename to resources/views/_legacy/interview.blade.php diff --git a/resources/views/legacy/interviews.blade.php b/resources/views/_legacy/interviews.blade.php similarity index 100% rename from resources/views/legacy/interviews.blade.php rename to resources/views/_legacy/interviews.blade.php diff --git a/resources/views/legacy/latest-artworks.blade.php b/resources/views/_legacy/latest-artworks.blade.php similarity index 100% rename from resources/views/legacy/latest-artworks.blade.php rename to resources/views/_legacy/latest-artworks.blade.php diff --git a/resources/views/legacy/latest-comments.blade.php b/resources/views/_legacy/latest-comments.blade.php similarity index 100% rename from resources/views/legacy/latest-comments.blade.php rename to resources/views/_legacy/latest-comments.blade.php diff --git a/resources/views/legacy/monthly-commentators.blade.php b/resources/views/_legacy/monthly-commentators.blade.php similarity index 100% rename from resources/views/legacy/monthly-commentators.blade.php rename to resources/views/_legacy/monthly-commentators.blade.php diff --git a/resources/views/legacy/mybuddies.blade.php b/resources/views/_legacy/mybuddies.blade.php similarity index 90% rename from resources/views/legacy/mybuddies.blade.php rename to resources/views/_legacy/mybuddies.blade.php index 3830e70d..ea23c342 100644 --- a/resources/views/legacy/mybuddies.blade.php +++ b/resources/views/_legacy/mybuddies.blade.php @@ -19,15 +19,16 @@ $friendId = $b->friend_id ?? $b->friendId ?? null; @endphp +@php $buddyUrl = ($b->user_username ?? null) ? '/@' . $b->user_username : '/profile/' . $friendId; @endphp
diff --git a/resources/views/legacy/news.blade.php b/resources/views/_legacy/news.blade.php similarity index 100% rename from resources/views/legacy/news.blade.php rename to resources/views/_legacy/news.blade.php diff --git a/resources/views/legacy/profile.blade.php b/resources/views/_legacy/profile.blade.php similarity index 97% rename from resources/views/legacy/profile.blade.php rename to resources/views/_legacy/profile.blade.php index 0b6d9e04..3554838f 100644 --- a/resources/views/legacy/profile.blade.php +++ b/resources/views/_legacy/profile.blade.php @@ -65,13 +65,10 @@ +@endpush + +{{-- ── Hero background ──────────────────────────────────────────────── --}} +
+ @if(!empty($heroBgUrl)) +
+ @endif +
+ Your avatar +
+

Edit Profile

+

Manage your account settings

+
+
+
+ +
- -

- Edit Profile -

- @if ($errors->any())
Please fix the following errors:
@@ -38,7 +61,7 @@ -
+
@csrf @@ -237,7 +260,7 @@ -
+

Change Password diff --git a/resources/views/layouts/nova.blade.php b/resources/views/layouts/nova.blade.php index 59910890..44f8b114 100644 --- a/resources/views/layouts/nova.blade.php +++ b/resources/views/layouts/nova.blade.php @@ -59,9 +59,17 @@ -
+
@include('layouts.nova.toolbar') -
+
@yield('content')
diff --git a/resources/views/legacy/[#6223] Red Cloud XP.txt b/resources/views/legacy/[#6223] Red Cloud XP.txt deleted file mode 100644 index 7c1bcc42..00000000 --- a/resources/views/legacy/[#6223] Red Cloud XP.txt +++ /dev/null @@ -1,6 +0,0 @@ - [#6223] Red Cloud XP - → windows-logo, retro-computing, red-dominant-colour, dark-mood, pixelated-graphics, digital-art, grunge-texture, office-shortcuts-menu, 90s-aesthetic, chromatic-aberration, high-contrast, textured-background - [#6225] Helping Hand zoomers (part 1) - → desktop screenshot, computer icons, windows interface, digital-art, blue-grey tones, flat design, minimalist-style, iconography, organized layout, technical illustration, screen capture, system icons - [#6226] Helping Hand zoomers (part 2) -PS D:\Sites\Skinbase26> diff --git a/resources/views/web/comments/latest.blade.php b/resources/views/web/comments/latest.blade.php index a3b38960..f1c93de7 100644 --- a/resources/views/web/comments/latest.blade.php +++ b/resources/views/web/comments/latest.blade.php @@ -18,7 +18,7 @@ @foreach ($comments as $comment) @php $artUrl = '/art/' . (int)($comment->id ?? 0) . '/' . ($comment->artwork_slug ?? 'artwork'); - $userUrl = '/profile/' . (int)($comment->commenter_id ?? 0) . '/' . rawurlencode($comment->uname ?? 'user'); + $userUrl = ($comment->commenter_username ?? null) ? '/@' . $comment->commenter_username : '/profile/' . (int)($comment->commenter_id ?? 0); $avatarUrl = \App\Support\AvatarUrl::forUser((int)($comment->commenter_id ?? 0), $comment->icon ?? null, 40); $ago = \Carbon\Carbon::parse($comment->datetime ?? now())->diffForHumans(); $snippet = \Illuminate\Support\Str::limit(strip_tags($comment->comment_description ?? ''), 160); diff --git a/resources/views/web/comments/monthly.blade.php b/resources/views/web/comments/monthly.blade.php index 197da87c..00427b44 100644 --- a/resources/views/web/comments/monthly.blade.php +++ b/resources/views/web/comments/monthly.blade.php @@ -38,7 +38,7 @@ @foreach ($rows as $i => $row) @php $rank = $offset + $i + 1; - $profileUrl = '/profile/' . (int)($row->user_id ?? 0) . '/' . rawurlencode($row->uname ?? 'user'); + $profileUrl = ($row->user_username ?? null) ? '/@' . $row->user_username : '/profile/' . (int)($row->user_id ?? 0); $avatarUrl = \App\Support\AvatarUrl::forUser((int)($row->user_id ?? 0), null, 40); @endphp
+ + + + {{-- Open Graph --}} + + + + + + @if(!empty($meta['og_image'])) + + + @endif + + {{-- Twitter --}} + + + + @if(!empty($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 + + + {{-- Preload hero image for faster LCP --}} + @if(!empty($props['hero']['thumb_lg'])) + + @elseif(!empty($props['hero']['thumb'])) + + @endif +@endpush + +@section('main-class', '') @section('content') -
- @include('web.home.featured') + {{-- Inline props for the React component (avoids data-attribute length limits) --}} + - @include('web.home.uploads') - - @include('web.home.news') +
+ {{-- Loading skeleton (replaced by React on hydration) --}} +
+
+
+ + @vite(['resources/js/Pages/Home/HomePage.jsx']) @endsection + diff --git a/vite.config.js b/vite.config.js index 28862ddf..50b59c6c 100644 --- a/vite.config.js +++ b/vite.config.js @@ -13,7 +13,8 @@ export default defineConfig({ 'resources/js/entry-topbar.jsx', 'resources/js/entry-search.jsx', 'resources/js/upload.jsx', - 'resources/js/Pages/ArtworkPage.jsx' + 'resources/js/Pages/ArtworkPage.jsx', + 'resources/js/Pages/Home/HomePage.jsx' ], refresh: true, }),