feat: increase gallery grid from 4 to 5 columns per row on desktopfeat: increase gallery grid from 4 to 5 columns per row on desktop
This commit is contained in:
@@ -7,6 +7,7 @@ namespace App\Http\Controllers\Web;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\ArtworkResource;
|
||||
use App\Models\Artwork;
|
||||
use App\Models\ArtworkComment;
|
||||
use App\Services\ThumbnailPresenter;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -102,6 +103,27 @@ final class ArtworkPageController extends Controller
|
||||
->values()
|
||||
->all();
|
||||
|
||||
$comments = ArtworkComment::with(['user.profile'])
|
||||
->where('artwork_id', $artwork->id)
|
||||
->where('is_approved', true)
|
||||
->orderBy('created_at')
|
||||
->limit(500)
|
||||
->get()
|
||||
->map(fn(ArtworkComment $c) => [
|
||||
'id' => $c->id,
|
||||
'content' => (string) $c->content,
|
||||
'created_at' => $c->created_at?->toIsoString(),
|
||||
'user' => [
|
||||
'id' => $c->user?->id,
|
||||
'name' => $c->user?->name,
|
||||
'username' => $c->user?->username,
|
||||
'profile_url' => $c->user?->username ? '/@' . $c->user->username : null,
|
||||
'avatar_url' => $c->user?->profile?->avatar_url,
|
||||
],
|
||||
])
|
||||
->values()
|
||||
->all();
|
||||
|
||||
return view('artworks.show', [
|
||||
'artwork' => $artwork,
|
||||
'artworkData' => $artworkData,
|
||||
@@ -111,6 +133,7 @@ final class ArtworkPageController extends Controller
|
||||
'presentSq' => $thumbSq,
|
||||
'meta' => $meta,
|
||||
'relatedItems' => $related,
|
||||
'comments' => $comments,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use App\Models\Category;
|
||||
use App\Models\ContentType;
|
||||
use App\Models\Artwork;
|
||||
use App\Services\ArtworkService;
|
||||
use App\Services\ArtworkSearchService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Pagination\AbstractPaginator;
|
||||
@@ -15,8 +16,17 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
|
||||
{
|
||||
private const CONTENT_TYPE_SLUGS = ['photography', 'wallpapers', 'skins', 'other'];
|
||||
|
||||
public function __construct(private ArtworkService $artworks)
|
||||
{
|
||||
private const SORT_MAP = [
|
||||
'latest' => 'created_at:desc',
|
||||
'popular' => 'views:desc',
|
||||
'liked' => 'likes:desc',
|
||||
'downloads' => 'downloads:desc',
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private ArtworkService $artworks,
|
||||
private ArtworkSearchService $search,
|
||||
) {
|
||||
}
|
||||
|
||||
public function browse(Request $request)
|
||||
@@ -24,7 +34,10 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
|
||||
$sort = (string) $request->query('sort', 'latest');
|
||||
$perPage = $this->resolvePerPage($request);
|
||||
|
||||
$artworks = $this->artworks->browsePublicArtworks($perPage, $sort);
|
||||
$artworks = Artwork::search('')->options([
|
||||
'filter' => 'is_public = true AND is_approved = true',
|
||||
'sort' => [self::SORT_MAP[$sort] ?? 'created_at:desc'],
|
||||
])->paginate($perPage);
|
||||
$seo = $this->buildPaginationSeo($request, url('/browse'), $artworks);
|
||||
|
||||
$mainCategories = $this->mainCategories();
|
||||
@@ -69,7 +82,10 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
|
||||
|
||||
$normalizedPath = trim((string) $path, '/');
|
||||
if ($normalizedPath === '') {
|
||||
$artworks = $this->artworks->getArtworksByContentType($contentSlug, $perPage, $sort);
|
||||
$artworks = Artwork::search('')->options([
|
||||
'filter' => 'is_public = true AND is_approved = true AND content_type = "' . $contentSlug . '"',
|
||||
'sort' => [self::SORT_MAP[$sort] ?? 'created_at:desc'],
|
||||
])->paginate($perPage);
|
||||
$seo = $this->buildPaginationSeo($request, url('/' . $contentSlug), $artworks);
|
||||
|
||||
return view('gallery.index', [
|
||||
@@ -98,7 +114,10 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$artworks = $this->artworks->getArtworksByCategoryPath(array_merge([$contentSlug], $segments), $perPage, $sort);
|
||||
$artworks = Artwork::search('')->options([
|
||||
'filter' => 'is_public = true AND is_approved = true AND category = "' . $category->slug . '"',
|
||||
'sort' => [self::SORT_MAP[$sort] ?? 'created_at:desc'],
|
||||
])->paginate($perPage);
|
||||
$seo = $this->buildPaginationSeo($request, url('/' . $contentSlug . '/' . strtolower($category->full_slug_path)), $artworks);
|
||||
|
||||
$subcategories = $category->children()->orderBy('sort_order')->orderBy('name')->get();
|
||||
|
||||
@@ -17,6 +17,11 @@ class CategoryController extends Controller
|
||||
$this->artworkService = $artworkService;
|
||||
}
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
return $this->browseCategories();
|
||||
}
|
||||
|
||||
public function show(Request $request, $id, $slug = null, $group = null)
|
||||
{
|
||||
$path = trim($request->path(), '/');
|
||||
|
||||
@@ -27,17 +27,20 @@ class HomeController extends Controller
|
||||
|
||||
$featuredResult = $this->artworks->getFeaturedArtworks(null, 39);
|
||||
if ($featuredResult instanceof \Illuminate\Pagination\LengthAwarePaginator) {
|
||||
$featured = $featuredResult->getCollection()->first();
|
||||
$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->first();
|
||||
$featured = $featuredResult->get(0);
|
||||
$memberFeatured = $featuredResult->get(1);
|
||||
} else {
|
||||
$featured = $featuredResult;
|
||||
$memberFeatured = null;
|
||||
}
|
||||
|
||||
$memberFeatured = $featured;
|
||||
|
||||
$latestUploads = $this->artworks->getLatestArtworks(20);
|
||||
|
||||
// Forum news (prefer migrated legacy news category id 2876, fallback to slug)
|
||||
|
||||
49
app/Http/Controllers/Web/SearchController.php
Normal file
49
app/Http/Controllers/Web/SearchController.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\ArtworkSearchService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class SearchController extends Controller
|
||||
{
|
||||
public function __construct(private readonly ArtworkSearchService $search) {}
|
||||
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$q = trim((string) $request->query('q', ''));
|
||||
$sort = $request->query('sort', 'latest');
|
||||
|
||||
$sortMap = [
|
||||
'popular' => 'views:desc',
|
||||
'likes' => 'likes:desc',
|
||||
'latest' => 'created_at:desc',
|
||||
'downloads' => 'downloads:desc',
|
||||
];
|
||||
|
||||
$artworks = null;
|
||||
$popular = collect();
|
||||
|
||||
if ($q !== '') {
|
||||
$artworks = $this->search->search($q, [
|
||||
'sort' => ($sortMap[$sort] ?? 'created_at:desc'),
|
||||
]);
|
||||
} else {
|
||||
$popular = $this->search->popular(16)->getCollection();
|
||||
}
|
||||
|
||||
return view('search.index', [
|
||||
'q' => $q,
|
||||
'sort' => $sort,
|
||||
'artworks' => $artworks ?? collect()->paginate(0),
|
||||
'popular' => $popular,
|
||||
'page_title' => $q !== '' ? 'Search: ' . $q . ' — Skinbase' : 'Search — Skinbase',
|
||||
'page_meta_description' => 'Search Skinbase for artworks, photography, wallpapers and skins.',
|
||||
'page_robots' => 'noindex,follow',
|
||||
]);
|
||||
}
|
||||
}
|
||||
47
app/Http/Controllers/Web/SectionsController.php
Normal file
47
app/Http/Controllers/Web/SectionsController.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ContentType;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SectionsController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
// Load all content types with full category tree (roots + children)
|
||||
$contentTypes = ContentType::with([
|
||||
'rootCategories' => function ($q) {
|
||||
$q->active()
|
||||
->withCount(['artworks as artwork_count'])
|
||||
->orderBy('sort_order')
|
||||
->orderBy('name');
|
||||
},
|
||||
'rootCategories.children' => function ($q) {
|
||||
$q->active()
|
||||
->withCount(['artworks as artwork_count'])
|
||||
->orderBy('sort_order')
|
||||
->orderBy('name');
|
||||
},
|
||||
])->orderBy('id')->get();
|
||||
|
||||
// Total artwork counts per content type via a single aggregation query
|
||||
$artworkCountsByType = DB::table('artworks')
|
||||
->join('artwork_category', 'artworks.id', '=', 'artwork_category.artwork_id')
|
||||
->join('categories', 'artwork_category.category_id', '=', 'categories.id')
|
||||
->where('artworks.is_approved', true)
|
||||
->where('artworks.is_public', true)
|
||||
->whereNull('artworks.deleted_at')
|
||||
->select('categories.content_type_id', DB::raw('COUNT(DISTINCT artworks.id) as total'))
|
||||
->groupBy('categories.content_type_id')
|
||||
->pluck('total', 'content_type_id');
|
||||
|
||||
return view('web.sections', [
|
||||
'contentTypes' => $contentTypes,
|
||||
'artworkCountsByType' => $artworkCountsByType,
|
||||
'page_title' => 'Browse Sections',
|
||||
'page_meta_description' => 'Browse all artwork sections on Skinbase — Photography, Wallpapers, Skins and more.',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -6,24 +6,57 @@ namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Tag;
|
||||
use App\Services\ArtworkSearchService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
final class TagController extends Controller
|
||||
{
|
||||
public function show(Tag $tag): View
|
||||
public function __construct(private readonly ArtworkSearchService $search) {}
|
||||
|
||||
public function show(Tag $tag, Request $request): View
|
||||
{
|
||||
$artworks = $tag->artworks()
|
||||
->public()
|
||||
->published()
|
||||
->leftJoin('artwork_stats', 'artwork_stats.artwork_id', '=', 'artworks.id')
|
||||
->orderByDesc('artwork_stats.views')
|
||||
->orderByDesc('artworks.published_at')
|
||||
->select('artworks.*')
|
||||
->paginate(24);
|
||||
$sort = $request->query('sort', 'popular'); // popular | latest | downloads
|
||||
$perPage = min((int) $request->query('per_page', 24), 100);
|
||||
|
||||
// Convert sort param to Meili sort expression
|
||||
$sortMap = [
|
||||
'popular' => 'views:desc',
|
||||
'likes' => 'likes:desc',
|
||||
'latest' => 'created_at:desc',
|
||||
'downloads' => 'downloads:desc',
|
||||
];
|
||||
$meiliSort = $sortMap[$sort] ?? 'views:desc';
|
||||
|
||||
$artworks = \App\Models\Artwork::search('')
|
||||
->options([
|
||||
'filter' => 'is_public = true AND is_approved = true AND tags = "' . addslashes($tag->slug) . '"',
|
||||
'sort' => [$meiliSort],
|
||||
])
|
||||
->paginate($perPage)
|
||||
->appends(['sort' => $sort]);
|
||||
|
||||
// Eager-load relations needed by the artwork-card component.
|
||||
// Scout returns bare Eloquent models; without this, each card triggers N+1 queries.
|
||||
$artworks->getCollection()->loadMissing(['user.profile']);
|
||||
|
||||
// OG image: first result's thumbnail
|
||||
$ogImage = null;
|
||||
if ($artworks->count() > 0) {
|
||||
$first = $artworks->getCollection()->first();
|
||||
$ogImage = $first?->thumbUrl('md');
|
||||
}
|
||||
|
||||
return view('tags.show', [
|
||||
'tag' => $tag,
|
||||
'tag' => $tag,
|
||||
'artworks' => $artworks,
|
||||
'sort' => $sort,
|
||||
'ogImage' => $ogImage,
|
||||
'page_title' => 'Artworks tagged "' . $tag->name . '" — Skinbase',
|
||||
'page_meta_description' => 'Browse all Skinbase artworks tagged "' . $tag->name . '". Discover photography, wallpapers and skins.',
|
||||
'page_canonical' => route('tags.show', $tag->slug),
|
||||
'page_robots' => 'index,follow',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user