diff --git a/app/Http/Controllers/Api/BrowseController.php b/app/Http/Controllers/Api/BrowseController.php index e68e4136..4c45cd4b 100644 --- a/app/Http/Controllers/Api/BrowseController.php +++ b/app/Http/Controllers/Api/BrowseController.php @@ -23,10 +23,14 @@ class BrowseController extends Controller */ public function index(Request $request) { - $perPage = min(max((int) $request->get('per_page', 24), 1), 100); + $perPage = $this->resolvePerPage($request); $sort = (string) $request->get('sort', 'latest'); $paginator = $this->service->browsePublicArtworks($perPage, $sort); + $paginator->appends([ + 'limit' => $perPage, + 'sort' => $sort, + ]); return ArtworkListResource::collection($paginator); } @@ -37,7 +41,7 @@ class BrowseController extends Controller */ public function byContentType(Request $request, string $contentTypeSlug) { - $perPage = min(max((int) $request->get('per_page', 24), 1), 100); + $perPage = $this->resolvePerPage($request); $sort = (string) $request->get('sort', 'latest'); try { @@ -46,6 +50,11 @@ class BrowseController extends Controller abort(404); } + $paginator->appends([ + 'limit' => $perPage, + 'sort' => $sort, + ]); + if ($paginator->count() === 0) { return response()->json(['message' => 'Gone'], 410); } @@ -59,7 +68,7 @@ class BrowseController extends Controller */ public function byCategoryPath(Request $request, string $contentTypeSlug, string $categoryPath) { - $perPage = min(max((int) $request->get('per_page', 24), 1), 100); + $perPage = $this->resolvePerPage($request); $sort = (string) $request->get('sort', 'latest'); $slugs = array_merge([ @@ -72,10 +81,25 @@ class BrowseController extends Controller abort(404); } + $paginator->appends([ + 'limit' => $perPage, + 'sort' => $sort, + ]); + if ($paginator->count() === 0) { return response()->json(['message' => 'Gone'], 410); } return ArtworkListResource::collection($paginator); } + + private function resolvePerPage(Request $request): int + { + $limit = (int) $request->query('limit', 0); + $perPage = (int) $request->query('per_page', 0); + + $value = $limit > 0 ? $limit : ($perPage > 0 ? $perPage : 24); + + return min(max($value, 1), 100); + } } diff --git a/app/Http/Controllers/Web/BrowseGalleryController.php b/app/Http/Controllers/Web/BrowseGalleryController.php index 942576fc..02525b9e 100644 --- a/app/Http/Controllers/Web/BrowseGalleryController.php +++ b/app/Http/Controllers/Web/BrowseGalleryController.php @@ -8,6 +8,8 @@ use App\Models\Artwork; use App\Services\ArtworkService; use Illuminate\Http\Request; use Illuminate\Support\Collection; +use Illuminate\Pagination\AbstractPaginator; +use Illuminate\Pagination\AbstractCursorPaginator; class BrowseGalleryController extends \App\Http\Controllers\Controller { @@ -23,6 +25,7 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller $perPage = $this->resolvePerPage($request); $artworks = $this->artworks->browsePublicArtworks($perPage, $sort); + $seo = $this->buildPaginationSeo($request, url('/browse'), $artworks); $mainCategories = $this->mainCategories(); @@ -39,7 +42,10 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller 'page_title' => 'Browse Uploaded Artworks - Photography, Wallpapers and Skins at SkinBase', 'page_meta_description' => "Browse Uploaded Photography, Wallpapers and Skins to one of the world's oldest online social community for artists and art enthusiasts.", 'page_meta_keywords' => 'photography, wallpapers, skins, stock, browse, social, community, artist, picture, photo', - 'page_canonical' => url('/browse'), + 'page_canonical' => $seo['canonical'], + 'page_rel_prev' => $seo['prev'], + 'page_rel_next' => $seo['next'], + 'page_robots' => 'index,follow', ]); } @@ -64,6 +70,7 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller $normalizedPath = trim((string) $path, '/'); if ($normalizedPath === '') { $artworks = $this->artworks->getArtworksByContentType($contentSlug, $perPage, $sort); + $seo = $this->buildPaginationSeo($request, url('/' . $contentSlug), $artworks); return view('gallery.index', [ 'gallery_type' => 'content-type', @@ -78,7 +85,10 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller 'page_title' => $contentType->name, 'page_meta_description' => $contentType->description ?? ($contentType->name . ' artworks on Skinbase'), 'page_meta_keywords' => strtolower($contentType->slug) . ', skinbase, artworks, wallpapers, skins, photography', - 'page_canonical' => url('/' . $contentSlug), + 'page_canonical' => $seo['canonical'], + 'page_rel_prev' => $seo['prev'], + 'page_rel_next' => $seo['next'], + 'page_robots' => 'index,follow', ]); } @@ -89,6 +99,7 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller } $artworks = $this->artworks->getArtworksByCategoryPath(array_merge([$contentSlug], $segments), $perPage, $sort); + $seo = $this->buildPaginationSeo($request, url('/' . $contentSlug . '/' . strtolower($category->full_slug_path)), $artworks); $subcategories = $category->children()->orderBy('sort_order')->orderBy('name')->get(); if ($subcategories->isEmpty()) { @@ -116,7 +127,10 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller 'page_title' => $category->name, 'page_meta_description' => $category->description ?? ($contentType->name . ' artworks on Skinbase'), 'page_meta_keywords' => strtolower($contentType->slug) . ', skinbase, artworks, wallpapers, skins, photography', - 'page_canonical' => url('/' . $contentSlug . '/' . strtolower($category->full_slug_path)), + 'page_canonical' => $seo['canonical'], + 'page_rel_prev' => $seo['prev'], + 'page_rel_next' => $seo['next'], + 'page_robots' => 'index,follow', ]); } @@ -180,7 +194,10 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller private function resolvePerPage(Request $request): int { - $value = (int) $request->query('per_page', 40); + $limit = (int) $request->query('limit', 0); + $perPage = (int) $request->query('per_page', 0); + + $value = $limit > 0 ? $limit : ($perPage > 0 ? $perPage : 40); return max(12, min($value, 80)); } @@ -198,4 +215,79 @@ class BrowseGalleryController extends \App\Http\Controllers\Controller ]; }); } + + private function buildPaginationSeo(Request $request, string $canonicalBaseUrl, mixed $paginator): array + { + $canonicalQuery = $request->query(); + unset($canonicalQuery['grid']); + if (($canonicalQuery['page'] ?? null) !== null && (int) $canonicalQuery['page'] <= 1) { + unset($canonicalQuery['page']); + } + + $canonical = $canonicalBaseUrl; + if ($canonicalQuery !== []) { + $canonical .= '?' . http_build_query($canonicalQuery); + } + + $prev = null; + $next = null; + + if ($paginator instanceof AbstractPaginator || $paginator instanceof AbstractCursorPaginator) { + $prev = $this->stripQueryParamFromUrl($paginator->previousPageUrl(), 'grid'); + $next = $this->stripQueryParamFromUrl($paginator->nextPageUrl(), 'grid'); + } + + return [ + 'canonical' => $canonical, + 'prev' => $prev, + 'next' => $next, + ]; + } + + private function stripQueryParamFromUrl(?string $url, string $queryParam): ?string + { + if ($url === null || $url === '') { + return null; + } + + $parts = parse_url($url); + if (!is_array($parts)) { + return $url; + } + + $query = []; + if (!empty($parts['query'])) { + parse_str($parts['query'], $query); + unset($query[$queryParam]); + } + + $rebuilt = ''; + if (isset($parts['scheme'])) { + $rebuilt .= $parts['scheme'] . '://'; + } + if (isset($parts['user'])) { + $rebuilt .= $parts['user']; + if (isset($parts['pass'])) { + $rebuilt .= ':' . $parts['pass']; + } + $rebuilt .= '@'; + } + if (isset($parts['host'])) { + $rebuilt .= $parts['host']; + } + if (isset($parts['port'])) { + $rebuilt .= ':' . $parts['port']; + } + $rebuilt .= $parts['path'] ?? ''; + + if ($query !== []) { + $rebuilt .= '?' . http_build_query($query); + } + + if (isset($parts['fragment'])) { + $rebuilt .= '#' . $parts['fragment']; + } + + return $rebuilt; + } } diff --git a/public/js/custom.js b/public/js/custom.js index d3d5f569..14354012 100644 --- a/public/js/custom.js +++ b/public/js/custom.js @@ -44,6 +44,13 @@ $(document).on("change", ".quickThumbShow", function(evt) { }); var numCols = 4; +var GRID_V2_ENABLED = (function () { + try { + return new URLSearchParams(window.location.search).get('grid') === 'v2'; + } catch (e) { + return false; + } +})(); $(document).ready(function() { @@ -73,22 +80,28 @@ $(document).ready(function() { $(".photo_frame").css("width", "250px"); } - $container1.isotope({ - masonry: { columnWidth: wc } - }); + if (!GRID_V2_ENABLED) { + $container1.isotope({ + masonry: { columnWidth: wc } + }); + } } - $container1.imagesLoaded( function() { - $container1.isotope({ - itemSelector : '.photo_frame', - layoutMode : 'masonry' + if (!GRID_V2_ENABLED) { + $container1.imagesLoaded( function() { + $container1.isotope({ + itemSelector : '.photo_frame', + layoutMode : 'masonry' + }); + size(); }); - size(); - }); + } - $(window).smartresize(size); + if (!GRID_V2_ENABLED) { + $(window).smartresize(size); + } $(".summernote").summernote(); $(".summernote_lite").summernote({ @@ -101,21 +114,25 @@ $(document).ready(function() { }); var $container = $('.container_gallery'); - $container.imagesLoaded( function(){ - $container.isotope({ - itemSelector : '.photo_frame', - layoutMode : 'masonry' + if (!GRID_V2_ENABLED) { + $container.imagesLoaded( function(){ + $container.isotope({ + itemSelector : '.photo_frame', + layoutMode : 'masonry' + }); + size(); }); - size(); - }); + } var $container = $('.container_news'); - $container.imagesLoaded( function(){ - $container.isotope({ - itemSelector : '.news_frame', - layoutMode : 'masonry' + if (!GRID_V2_ENABLED) { + $container.imagesLoaded( function(){ + $container.isotope({ + itemSelector : '.news_frame', + layoutMode : 'masonry' + }); }); - }); + } if ($("a[rel^='prettyPhoto']").length > 0) { $("a[rel^='prettyPhoto']").prettyPhoto({theme:'dark_rounded'}); diff --git a/public/js/legacy-gallery-init.js b/public/js/legacy-gallery-init.js index d3b5166f..a7bd7b9b 100644 --- a/public/js/legacy-gallery-init.js +++ b/public/js/legacy-gallery-init.js @@ -6,6 +6,14 @@ (function () { 'use strict'; + var GRID_V2_ENABLED = (function () { + try { + return new URLSearchParams(window.location.search).get('grid') === 'v2'; + } catch (e) { + return false; + } + })(); + var MAX_DOM_CARDS_FOR_VIRTUAL_HINT = 220; var LOAD_TRIGGER_MARGIN = '900px'; @@ -28,11 +36,16 @@ box.classList.remove('is-loading'); return; } + var templateHost = root.querySelector('[data-gallery-skeleton-template]'); + var templateNode = templateHost ? templateHost.firstElementChild : null; box.classList.add('is-loading'); var total = Math.max(4, count || 8); for (var i = 0; i < total; i += 1) { - var sk = document.createElement('div'); - sk.className = 'nova-skeleton-card'; + var sk = templateNode ? templateNode.cloneNode(true) : document.createElement('div'); + if (!templateNode) { + sk.className = 'nova-skeleton-card'; + sk.innerHTML = '




You have no favourites yet.
@else -| Thumb | -Name | -Author | -Published | -Actions | -
|---|---|---|---|---|
|
-
- |
- - {{ $art->title }} - | -{{ $art->author }} | -{{ optional($art->published_at)->format('Y-m-d') }} | -- - | -