diff --git a/app/Http/Controllers/ImageController.php b/app/Http/Controllers/ImageController.php index f77ada5..bf0737b 100644 --- a/app/Http/Controllers/ImageController.php +++ b/app/Http/Controllers/ImageController.php @@ -37,9 +37,33 @@ class ImageController extends Controller /** * Display the specified resource. */ - public function show(Image $image) : BinaryFileResponse + public function show(Image $image, string $size = 'original') : BinaryFileResponse { - return response()->file(Storage::disk('images')->path($image->album->id . '/thumbnail/' . $image->id . '.avif')); + return response()->file(Storage::disk('images')->path($image->album->id . '/' . $size . '/' . $image->id . '.avif')); + } + + /** + * Display the thumbnail of the specified resource. + */ + public function thumbnail(Image $image) : BinaryFileResponse + { + return $this->show($image, 'thumbnail'); + } + + /** + * Display the lightbox of the specified resource. + */ + public function lightbox(Image $image) : BinaryFileResponse + { + return $this->show($image, 'lightbox'); + } + + /** + * Display the lightbox of the specified resource. + */ + public function download(Image $image) + { + return Storage::disk('images')->download($image->album_id . '/original/' . $image->id . '.avif', name: $image->album->name . '_' . $image->id . '.avif'); } /** diff --git a/app/Importers/Image/Jobs/GenerateFullscreen.php b/app/Importers/Image/Jobs/GenerateFullscreen.php index bfbd9c0..988fc85 100644 --- a/app/Importers/Image/Jobs/GenerateFullscreen.php +++ b/app/Importers/Image/Jobs/GenerateFullscreen.php @@ -33,16 +33,11 @@ class GenerateFullscreen implements ShouldQueue return; } - $image = InterventionImage::read($this->source); - if($image->width() >= $image->height()) { - // landscape - $image->scaleDown(width: config('gallery.image.fullscreen.maxWidth', 2000)); - } else { - // portrait - $image->scaleDown(height: config('gallery.image.fullscreen.maxHeight', 2000)); - } + $lightbox = InterventionImage::read($this->source); + $lightbox = $lightbox->scaleDown(height: config('gallery.image.fullscreen.height', 2000)); - Storage::disk('images')->put($this->destination, $image->toAvif(config('gallery.image.quality', 80))); + Storage::disk('images')->put($this->destination, $lightbox->toAvif(config('gallery.image.quality', 80))); + $this->image->setLightboxSize($lightbox->width(), $lightbox->height()); } public function failed(?Throwable $exception): void diff --git a/app/Importers/Image/Jobs/GenerateThumbnail.php b/app/Importers/Image/Jobs/GenerateThumbnail.php index e43e26a..081e86e 100644 --- a/app/Importers/Image/Jobs/GenerateThumbnail.php +++ b/app/Importers/Image/Jobs/GenerateThumbnail.php @@ -33,16 +33,10 @@ class GenerateThumbnail implements ShouldQueue return; } - $image = InterventionImage::read($this->source); - if($image->width() >= $image->height()) { - // landscape - $image->scaleDown(width: config('gallery.image.thumbnail.maxWidth', 150)); - } else { - // portrait - $image->scaleDown(height: config('gallery.image.thumbnail.maxHeight', 150)); - } + $thumbnail = InterventionImage::read($this->source); + $thumbnail = $thumbnail->scaleDown(height: config('gallery.image.thumbnail.height', 640), width: config('gallery.image.thumbnail.width', 640)); - Storage::disk('images')->put($this->destination, $image->toAvif(config('gallery.image.quality', 80))); + Storage::disk('images')->put($this->destination, $thumbnail->toAvif(config('gallery.image.quality', 80))); } public function failed(?Throwable $exception): void diff --git a/app/Models/Image.php b/app/Models/Image.php index 4b021a9..ff0fc33 100644 --- a/app/Models/Image.php +++ b/app/Models/Image.php @@ -18,6 +18,8 @@ class Image extends Model implements HasThumbnail */ protected $attributes = [ 'isCover' => false, + 'lightboxWidth' => 0, + 'lightboxHeight' => 0, ]; /** @@ -25,7 +27,7 @@ class Image extends Model implements HasThumbnail * * @var array */ - protected $fillable = ['album_id']; + protected $fillable = ['album_id', 'lightboxWidth', 'lightboxHeight']; public function album(): BelongsTo { @@ -33,6 +35,24 @@ class Image extends Model implements HasThumbnail } public function getThumbnail() : string { - return route('image.show', $this); + return route('image.thumbnail', $this); + } + + public function getDownload() : string { + return route('image.download', $this); + } + + public function setLightboxSize(int $width, int $height) : void { + $this->lightboxWidth = $width; + $this->lightboxHeight = $height; + $this->save(); + } + + public function getLightboxAttribute() : array { + return [ + 'location' => route('image.lightbox', $this), + 'width' => $this->lightboxWidth, + 'height' => $this->lightboxHeight, + ]; } } diff --git a/database/migrations/2024_05_22_190526_create_images_table.php b/database/migrations/2024_05_22_190526_create_images_table.php index 749b041..67138cf 100644 --- a/database/migrations/2024_05_22_190526_create_images_table.php +++ b/database/migrations/2024_05_22_190526_create_images_table.php @@ -16,6 +16,8 @@ return new class extends Migration $table->id(); $table->timestamps(); $table->boolean('isCover'); + $table->bigInteger(column: 'lightboxWidth', unsigned: true)->default(0); + $table->bigInteger(column: 'lightboxHeight', unsigned: true)->default(0); $table->foreignIdFor(Album::class); }); } diff --git a/package-lock.json b/package-lock.json index ea017bb..fbc6e90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "filepond-plugin-file-validate-size": "^2.2.8", "filepond-plugin-file-validate-type": "^1.2.9", "filepond-plugin-image-exif-orientation": "^1.0.11", - "filepond-plugin-image-preview": "^4.6.12" + "filepond-plugin-image-preview": "^4.6.12", + "photoswipe": "^5.4.4" }, "devDependencies": { "@tailwindcss/forms": "^0.5.7", @@ -1662,6 +1663,14 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/photoswipe": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/photoswipe/-/photoswipe-5.4.4.tgz", + "integrity": "sha512-WNFHoKrkZNnvFFhbHL93WDkW3ifwVOXSW3w1UuZZelSmgXpIGiZSNlZJq37rR8YejqME2rHs9EhH9ZvlvFH2NA==", + "engines": { + "node": ">= 0.12.0" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", diff --git a/package.json b/package.json index 058daca..f860761 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "filepond-plugin-file-validate-size": "^2.2.8", "filepond-plugin-file-validate-type": "^1.2.9", "filepond-plugin-image-exif-orientation": "^1.0.11", - "filepond-plugin-image-preview": "^4.6.12" + "filepond-plugin-image-preview": "^4.6.12", + "photoswipe": "^5.4.4" } } diff --git a/resources/css/app.css b/resources/css/app.css index 99718fa..b634ead 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -1,5 +1,6 @@ @import 'filepond/dist/filepond.min.css'; @import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css'; +@import 'photoswipe/dist/photoswipe.css'; @tailwind base; @tailwind components; @@ -31,3 +32,14 @@ @apply text-gray-900 dark:text-white; } +.pswp__bg { + @apply backdrop-blur-md bg-white/30 dark:bg-gray-800/30; +} + +.pswp__top-bar { + @apply fixed bottom-2 top-auto w-auto left-1/2 -translate-x-1/2 text-gray-500 bg-white rounded-lg shadow dark:text-gray-400 dark:bg-gray-800; +} + +.pswp__button { + width: 60px; +} diff --git a/resources/js/app.js b/resources/js/app.js index 5cf4a15..4528914 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -1,4 +1,7 @@ import './bootstrap'; + +import PhotoSwipeLightbox from 'photoswipe/lightbox'; + import * as FilePond from 'filepond'; import FilePondPluginImageExifOrientation from 'filepond-plugin-image-exif-orientation'; import FilePondPluginImagePreview from 'filepond-plugin-image-preview'; @@ -19,4 +22,59 @@ document.addEventListener('alpine:init', () => { this.states[state] = value; }, }); -}); \ No newline at end of file +}); + +const lightbox = new PhotoSwipeLightbox({ + gallery: '#album', + children: 'a', + bgOpacity: 1, + arrowPrevSVG: '', + arrowNextSVG: '', + closeSVG: '
', + zoomSVG: '', + pswpModule: () => import('photoswipe') +}); + +lightbox.on('uiRegister', function() { + lightbox.pswp.ui.registerElement({ + name: 'download-button', + order: 9, + isButton: true, + tagName: 'button', + html: '', + + onInit: (el, pswp) => { + el.setAttribute('download', ''); + el.setAttribute('target', '_blank'); + el.setAttribute('rel', 'noopener'); + + pswp.on('change', () => { + el.href = pswp.currSlide.data.element.dataset.pswpDownload; + }); + } + }); + + lightbox.pswp.ui.registerElement({ + name: 'rotate-button-clk', + ariaLabel: 'Rotate clockwise', + order: 8, + isButton: true, + html: '', + onClick: (event, el) => { + // Rotate + } + }); + + lightbox.pswp.ui.registerElement({ + name: 'rotate-button-cclk', + ariaLabel: 'Rotate counter-clockwise', + order: 7, + isButton: true, + html: '', + onClick: (event, el) => { + // Rotate + } + }); +}); + +lightbox.init(); diff --git a/resources/views/album/show.blade.php b/resources/views/album/show.blade.php index ad95bb2..823c6cf 100644 --- a/resources/views/album/show.blade.php +++ b/resources/views/album/show.blade.php @@ -28,9 +28,16 @@ @if($album->mutations->count() > 0)