From 0ce904a7d8ef317f56a8f81994e1b548750ee2e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=B6ldi?= Date: Wed, 12 Jun 2024 19:51:41 +0200 Subject: [PATCH] WIP --- app/Http/Controllers/ImageController.php | 35 ++++++- app/Importers/Image.php | 15 ++- .../Image/Jobs/GenerateFullscreen.php | 2 +- .../Image/Jobs/GenerateThumbnail.php | 2 +- app/Importers/Image/Jobs/ImportImage.php | 2 +- app/ImportsMedia.php | 2 +- app/Livewire/Drawer/Album/AddImage.php | 15 +-- app/Models/Album.php | 8 ++ app/Models/Image.php | 9 +- app/Services/MediaImporter.php | 6 +- database/factories/ImageFactory.php | 17 ++-- .../2024_05_22_190526_create_images_table.php | 1 + resources/css/app.css | 6 +- resources/js/app.js | 96 ++++--------------- resources/views/album/show.blade.php | 22 +---- resources/views/category/show.blade.php | 4 +- resources/views/welcome.blade.php | 4 +- routes/web.php | 4 +- 18 files changed, 104 insertions(+), 146 deletions(-) diff --git a/app/Http/Controllers/ImageController.php b/app/Http/Controllers/ImageController.php index bf0737b..33daa79 100644 --- a/app/Http/Controllers/ImageController.php +++ b/app/Http/Controllers/ImageController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers; use App\Http\Requests\StoreImageRequest; use App\Http\Requests\UpdateImageRequest; use App\Models\Image; +use Illuminate\Http\Response; use Illuminate\Support\Facades\Storage; use Symfony\Component\HttpFoundation\BinaryFileResponse; @@ -37,15 +38,41 @@ class ImageController extends Controller /** * Display the specified resource. */ - public function show(Image $image, string $size = 'original') : BinaryFileResponse + public function show(Image $image, string $size = 'original') : BinaryFileResponse|Response { - return response()->file(Storage::disk('images')->path($image->album->id . '/' . $size . '/' . $image->id . '.avif')); + $server = request()->server; + $headerEtag = md5($image->updated_at->format('U') . $image->id . '_' . $size); + $headerExpires = $image->updated_at->addYear()->toRfc2822String(); + $headerLastModified = $image->updated_at->toRfc2822String(); + + $requestModifiedSince = + $server->has('HTTP_IF_MODIFIED_SINCE') && + $server->get('HTTP_IF_MODIFIED_SINCE') === $headerLastModified; + + $requestNoneMatch = + $server->has('HTTP_IF_NONE_MATCH') && + $server->get('HTTP_IF_NONE_MATCH') === $headerEtag; + + $headers = [ + 'Cache-Control' => 'max-age=0, must-revalidate, private', + 'Content-Disposition' => sprintf('inline; filename="%s"', $image->id . '_' . $size), + 'Etag' => $headerEtag, + 'Expires' => $headerExpires, + 'Last-Modified' => $headerLastModified, + 'Pragma' => 'no-cache', + ]; + + if ($requestModifiedSince || $requestNoneMatch) { + return response('', 304)->withHeaders($headers); + } + + return response()->file(Storage::disk('images')->path($image->album->id . '/' . $size . '/' . $image->id . '.avif'), $headers); } /** * Display the thumbnail of the specified resource. */ - public function thumbnail(Image $image) : BinaryFileResponse + public function thumbnail(Image $image) : BinaryFileResponse|Response { return $this->show($image, 'thumbnail'); } @@ -53,7 +80,7 @@ class ImageController extends Controller /** * Display the lightbox of the specified resource. */ - public function lightbox(Image $image) : BinaryFileResponse + public function lightbox(Image $image) : BinaryFileResponse|Response { return $this->show($image, 'lightbox'); } diff --git a/app/Importers/Image.php b/app/Importers/Image.php index 5cf95d6..d8aef5b 100644 --- a/app/Importers/Image.php +++ b/app/Importers/Image.php @@ -2,11 +2,13 @@ namespace App\Importers; +use App\Importers\Image\Jobs\FinishImageModification; use App\ImportsMedia; use Illuminate\Http\UploadedFile; use App\Importers\Image\Jobs\{ImportImage, GenerateFullscreen, GenerateThumbnail}; use App\Models\Image as ImageModel; use App\Models\Album; +use Illuminate\Support\Facades\Bus; class Image implements ImportsMedia { @@ -19,16 +21,19 @@ class Image implements ImportsMedia { { } - public function import(): array { + public function import(): void { $image = ImageModel::create([ 'album_id' => $this->location->id ]); $imageId = $image->id; - return [ + Bus::chain([ new ImportImage($this->file->getPathname(), $image), - new GenerateFullscreen($image), - new GenerateThumbnail($image), - ]; + Bus::batch([ + new GenerateFullscreen($image), + new GenerateThumbnail($image), + ]), + new FinishImageModification($image), + ])->dispatch(); } } \ No newline at end of file diff --git a/app/Importers/Image/Jobs/GenerateFullscreen.php b/app/Importers/Image/Jobs/GenerateFullscreen.php index 988fc85..db2502a 100644 --- a/app/Importers/Image/Jobs/GenerateFullscreen.php +++ b/app/Importers/Image/Jobs/GenerateFullscreen.php @@ -29,7 +29,7 @@ class GenerateFullscreen implements ShouldQueue public function handle(): void { - if ($this->batch()->cancelled()) { + if (method_exists($this, 'batch') && $this->batch()?->cancelled()) { return; } diff --git a/app/Importers/Image/Jobs/GenerateThumbnail.php b/app/Importers/Image/Jobs/GenerateThumbnail.php index 081e86e..f0f5ec9 100644 --- a/app/Importers/Image/Jobs/GenerateThumbnail.php +++ b/app/Importers/Image/Jobs/GenerateThumbnail.php @@ -29,7 +29,7 @@ class GenerateThumbnail implements ShouldQueue public function handle(): void { - if ($this->batch()->cancelled()) { + if (method_exists($this, 'batch') && $this->batch()?->cancelled()) { return; } diff --git a/app/Importers/Image/Jobs/ImportImage.php b/app/Importers/Image/Jobs/ImportImage.php index 2d1eda8..28936a5 100644 --- a/app/Importers/Image/Jobs/ImportImage.php +++ b/app/Importers/Image/Jobs/ImportImage.php @@ -24,7 +24,7 @@ class ImportImage implements ShouldQueue public function handle(): void { - if ($this->batch()->cancelled()) { + if (method_exists($this, 'batch') && $this->batch()?->cancelled()) { return; } diff --git a/app/ImportsMedia.php b/app/ImportsMedia.php index 07d469b..eab2d27 100644 --- a/app/ImportsMedia.php +++ b/app/ImportsMedia.php @@ -9,5 +9,5 @@ interface ImportsMedia { public static function supports(UploadedFile $file): bool; public function __construct(UploadedFile $file, Album $location); - public function import(): array; + public function import(): void; } diff --git a/app/Livewire/Drawer/Album/AddImage.php b/app/Livewire/Drawer/Album/AddImage.php index dace9c2..d5de18b 100644 --- a/app/Livewire/Drawer/Album/AddImage.php +++ b/app/Livewire/Drawer/Album/AddImage.php @@ -9,7 +9,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\View; use Illuminate\Support\Facades\Bus; use Livewire\Attributes\Locked; -use Livewire\Attributes\On; use Livewire\Attributes\Validate; use Livewire\Component; use Livewire\Features\SupportFileUploads\WithFileUploads; @@ -30,17 +29,9 @@ class AddImage extends Component public function save(MediaImporter $importer) : void { $this->validate(); - $jobs = array_map(fn($file) => $importer->import($file, $this->album), $this->media); - $batch = Bus::batch($jobs) - ->name('Media import in ' . $this->album->name) - ->allowFailures() - ->dispatch(); - - BatchMutation::create([ - 'album_id' => $this->album->id, - 'batch_id' => $batch->id, - ]); - + foreach ($this->media as $file) { + $importer->import($file, $this->album); + } $this->redirect(route('album.show', $this->album), navigate: true); } diff --git a/app/Models/Album.php b/app/Models/Album.php index 8560f36..409b32b 100644 --- a/app/Models/Album.php +++ b/app/Models/Album.php @@ -28,6 +28,14 @@ class Album extends Model return $this->images; } + public function getHasCoverAttribute() : bool { + return $this->images()->where('isCover', 1)->count() > 0; + } + + public function getHasProcessingMediaAttribute() : bool { + return $this->images()->where('isProcessing', 1)->count() > 0; + } + public function getThumbnailAttribute() : ?string { return $this->images()->where('isCover', 1)->first()?->getThumbnail(); } diff --git a/app/Models/Image.php b/app/Models/Image.php index ff0fc33..e66d42b 100644 --- a/app/Models/Image.php +++ b/app/Models/Image.php @@ -18,6 +18,7 @@ class Image extends Model implements HasThumbnail */ protected $attributes = [ 'isCover' => false, + 'isProcessing' => true, 'lightboxWidth' => 0, 'lightboxHeight' => 0, ]; @@ -27,7 +28,7 @@ class Image extends Model implements HasThumbnail * * @var array */ - protected $fillable = ['album_id', 'lightboxWidth', 'lightboxHeight']; + protected $fillable = ['album_id', 'lightboxWidth', 'lightboxHeight', 'isCover', 'isProcessing']; public function album(): BelongsTo { @@ -35,11 +36,11 @@ class Image extends Model implements HasThumbnail } public function getThumbnail() : string { - return route('image.thumbnail', $this); + return route('image.thumbnail', $this) . '?cacheBuster3000=' . $this->updated_at->timestamp; } public function getDownload() : string { - return route('image.download', $this); + return route('image.download', $this) . '?cacheBuster3000=' . $this->updated_at->timestamp; } public function setLightboxSize(int $width, int $height) : void { @@ -50,7 +51,7 @@ class Image extends Model implements HasThumbnail public function getLightboxAttribute() : array { return [ - 'location' => route('image.lightbox', $this), + 'location' => route('image.lightbox', $this) . '?cacheBuster3000=' . $this->updated_at->timestamp, 'width' => $this->lightboxWidth, 'height' => $this->lightboxHeight, ]; diff --git a/app/Services/MediaImporter.php b/app/Services/MediaImporter.php index da27b21..f821d43 100644 --- a/app/Services/MediaImporter.php +++ b/app/Services/MediaImporter.php @@ -4,7 +4,6 @@ namespace App\Services; use App\ImportsMedia; use App\Models\Album; -use Illuminate\Foundation\Bus\PendingChain; use Illuminate\Http\UploadedFile; use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException; @@ -17,10 +16,11 @@ class MediaImporter { } } - public function import(UploadedFile $file, Album $location): array { + public function import(UploadedFile $file, Album $location): void { foreach ($this->importers as $importer) { if($importer::supports($file)) { - return (new $importer($file, $location))->import(); + (new $importer($file, $location))->import(); + return; } } diff --git a/database/factories/ImageFactory.php b/database/factories/ImageFactory.php index 4787e72..f3b7ce5 100644 --- a/database/factories/ImageFactory.php +++ b/database/factories/ImageFactory.php @@ -2,6 +2,7 @@ namespace Database\Factories; +use App\Importers\Image\Jobs\FinishImageModification; use App\Models\Album; use App\Models\Image; use Illuminate\Database\Eloquent\Factories\Factory; @@ -23,11 +24,6 @@ class ImageFactory extends Factory public function configure(): static { return $this->afterCreating(function (Image $image) { - if($image->album->images->sortBy('id')->first()->id == $image->id) { - $image->isCover = true; - $image->save(); - } - $height = rand(2000, 4000); $width = rand(2000, 4000); $image_content = Http::get("https://picsum.photos/{$width}/{$height}")->body(); @@ -35,10 +31,13 @@ class ImageFactory extends Factory $image_path = $image->album_id . '/original/' . $image->id . '.avif'; Storage::disk('images')->put($image_path, $encoded); - Bus::batch([ - new GenerateThumbnail($image), - new GenerateFullscreen($image), - ])->name('seeder_import_batch_' . $image->id)->dispatch(); + Bus::chain([ + Bus::batch([ + new GenerateThumbnail($image), + new GenerateFullscreen($image), + ]), + new FinishImageModification($image), + ])->dispatch(); }); } 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 67138cf..7163739 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,7 @@ return new class extends Migration $table->id(); $table->timestamps(); $table->boolean('isCover'); + $table->boolean('isProcessing'); $table->bigInteger(column: 'lightboxWidth', unsigned: true)->default(0); $table->bigInteger(column: 'lightboxHeight', unsigned: true)->default(0); $table->foreignIdFor(Album::class); diff --git a/resources/css/app.css b/resources/css/app.css index b634ead..c293f4a 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -33,7 +33,7 @@ } .pswp__bg { - @apply backdrop-blur-md bg-white/30 dark:bg-gray-800/30; + @apply backdrop-blur-md bg-white/30 dark:bg-gray-800/50; } .pswp__top-bar { @@ -43,3 +43,7 @@ .pswp__button { width: 60px; } + +.pswp--zoomed-in .pswp__zoom-icn-bar-b { + display: block; +} diff --git a/resources/js/app.js b/resources/js/app.js index 4528914..cb49050 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -1,80 +1,22 @@ import './bootstrap'; +import './lightbox'; +import './filepond'; +import './notification'; -import PhotoSwipeLightbox from 'photoswipe/lightbox'; +ToastinTakin.init(); -import * as FilePond from 'filepond'; -import FilePondPluginImageExifOrientation from 'filepond-plugin-image-exif-orientation'; -import FilePondPluginImagePreview from 'filepond-plugin-image-preview'; -import FilePondPluginFileValidateSize from 'filepond-plugin-file-validate-size'; -import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type'; - -FilePond.registerPlugin(FilePondPluginImageExifOrientation); -FilePond.registerPlugin(FilePondPluginImagePreview); -FilePond.registerPlugin(FilePondPluginFileValidateSize); -FilePond.registerPlugin(FilePondPluginFileValidateType); - -window.FilePond = FilePond; - -document.addEventListener('alpine:init', () => { - Alpine.store('uploader', { - states: {}, - setState(state, value) { - this.states[state] = value; - }, - }); -}); - -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(); +window.setTimeout(() => { + ToastinTakin.error('Bilder speichern fehlgeschlagen.'); +}, 100); +window.setTimeout(() => { + ToastinTakin.info('Bilder werden geladen, bitte warten.'); +}, 2000); +window.setTimeout(() => { + ToastinTakin.success('Bilder erfolgreich gespeichert'); +}, 3000); +window.setTimeout(() => { + ToastinTakin.error('Bilder doch nicht erfolgreich gespeichert'); +}, 4000); +window.setTimeout(() => { + ToastinTakin.info('Diese Nachrichten sind komisch ;).'); +}, 5000); \ No newline at end of file diff --git a/resources/views/album/show.blade.php b/resources/views/album/show.blade.php index 823c6cf..f528390 100644 --- a/resources/views/album/show.blade.php +++ b/resources/views/album/show.blade.php @@ -28,27 +28,7 @@ @if($album->mutations->count() > 0) @else -
- @foreach ($album->images as $image) - - {{ $album->name }} Bild -
- -
-
- @endforeach -
+ Neue Bilder zu {{ $album->name }} hinzufügen diff --git a/resources/views/category/show.blade.php b/resources/views/category/show.blade.php index 18d31dc..3532f56 100644 --- a/resources/views/category/show.blade.php +++ b/resources/views/category/show.blade.php @@ -20,7 +20,7 @@ @endpush - +

{{ $category->name }} @@ -50,4 +50,4 @@ - + diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php index 2de2cdf..40d1f82 100644 --- a/resources/views/welcome.blade.php +++ b/resources/views/welcome.blade.php @@ -1,5 +1,5 @@ - + - + diff --git a/routes/web.php b/routes/web.php index 7a5335f..cd8be6d 100644 --- a/routes/web.php +++ b/routes/web.php @@ -3,11 +3,11 @@ use App\Http\Controllers\ImageController; use Illuminate\Support\Facades\Route; use App\Http\Controllers\CategoryController; -use App\Http\Controllers\AlbumController; +use App\Livewire\Album\Show as AlbumShow; Route::get('/', [CategoryController::class, 'index'])->name('index'); Route::get('/category/{category}', [CategoryController::class, 'show'])->name('category.show'); -Route::get('/album/{album}', [AlbumController::class, 'show'])->name('album.show'); +Route::get('/album/{album}', AlbumShow::class)->name('album.show'); Route::get('/image/{image}/thumbnail', [ImageController::class, 'thumbnail'])->name('image.thumbnail'); Route::get('/image/{image}/lightbox', [ImageController::class, 'lightbox'])->name('image.lightbox'); Route::get('/image/{image}/download', [ImageController::class, 'download'])->name('image.download');