Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/Http/Controllers/AssetController.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public function __invoke(Request $request, string $slug)
{
return Storage::disk('s3')
->response(Asset::where('slug', $slug)->firstOrFail()->path, null, [
'Cache-Control' => 'public, max-age=31536000'
'Cache-Control' => 'public, max-age=31536000',
]);
}
}
28 changes: 25 additions & 3 deletions app/Http/Controllers/Prezet/ImageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,46 @@

use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Storage;
use Prezet\Prezet\Models\Document;
use Prezet\Prezet\Prezet;

class ImageController
{
public function __invoke(Request $request, string $path): Response
{
$file = Prezet::getImage($path);
$size = strlen($file);
/* resolve img path from sub dir, trim off 'images/' & pass to prezet */
$file = Prezet::getImage(ltrim($this->resolveImagePath($path), 'images/'));

return response($file, 200, [
'Content-Type' => match (pathinfo($path, PATHINFO_EXTENSION)) {
'jpg', 'jpeg' => 'image/jpeg',
'png' => 'image/png',
default => 'image/webp'
},
'Content-Length' => $size,
'Content-Length' => strlen($file),
'Accept-Ranges' => 'bytes',
'Cache-Control' => 'public, max-age=31536000',
]);
}

private function resolveImagePath(string $filename): ?string
{
/* remove any appended size version when looking up path */
$originalFilename = preg_replace('/-\d+w\./', '.', $filename);

$slugs = Document::pluck('slug')->toArray();

/* loop through the articles' slugs to get dir & file name from prezet disk */
foreach ($slugs as $slug) {
$imagePath = "images/{$slug}/{$originalFilename}";

if (Storage::disk('prezet')->exists($imagePath)) {
return $imagePath;
}
}

/* images should always be in their article's sub dir, so we should really never reach here. */
return null;
}
}
36 changes: 35 additions & 1 deletion app/Livewire/Blog.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ class Blog extends Component
#[Validate('required|file|mimes:md')]
public $file;

#[Validate(['images' => 'nullable|array', 'images.*' => 'image|max:4096'])]
public $images = [];

public function render()
{
return view('livewire.blog');
Expand All @@ -39,9 +42,26 @@ public function store()
options: ['disk' => 'prezet']
);

/* index in prezet disk */
Artisan::call('prezet:index');

$document = Document::query()->latest()->first();

if ($document && $this->images) {
foreach ($this->images as $image) {
$image->storeAs(
path: "images/{$document->slug}",
name: $image->getClientOriginalName(),
options: ['disk' => 'prezet']
);
}

/* index in prezet disk again (for the images) */
Artisan::call('prezet:index');
}

$this->file = null;
$this->images = [];

Flux::toast(variant: 'success', text: 'Blog Post Uploaded!', duration: 3000);
}
Expand All @@ -53,11 +73,18 @@ public function destroy($id)
if ($document) {
try {
/* delete the file */
Storage::disk('prezet')->delete('content/'.$document->slug.'.md');
Storage::disk('prezet')->delete("content/{$document->slug}.md");

/* delete the tags */
DB::connection('prezet')->table('document_tags')->where('document_id', $id)->delete();

/* delete images if there are any */
$imageDirectory = "images/{$document->slug}";

if (Storage::disk('prezet')->exists($imageDirectory)) {
Storage::disk('prezet')->deleteDirectory($imageDirectory);
}

/* delete the reference */
Document::where('id', $id)->delete();

Expand Down Expand Up @@ -90,4 +117,11 @@ public function posts()

return $documents;
}

public function clearUploads()
{
$this->file = null;
$this->images = [];
$this->resetValidation();
}
}
4 changes: 2 additions & 2 deletions app/Livewire/Forms/PhotoForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,9 @@ private function getStorageDisk(): string

private function processImage(): void
{
$manager = new ImageManager(new Driver());
$manager = new ImageManager(new Driver);
$image = $manager->read($this->photo->getRealPath());

if ($image->width() > 2000 || $image->height() > 1500) {
$image->scaleDown(width: 2000, height: 1500);
$image->save($this->photo->getRealPath());
Expand Down
2 changes: 1 addition & 1 deletion config/prezet.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@

'sizes' => '92vw, (max-width: 1024px) 92vw, 768px',

'zoomable' => true,
'zoomable' => false,
],

/*
Expand Down
126 changes: 0 additions & 126 deletions resources/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -156,132 +156,6 @@
white-space: nowrap;
width: 1px;
}

/*
|--------------------------------------------------------------------------
| Alpinejs Zoomable
|--------------------------------------------------------------------------
| https://github.com/benbjurstrom/alpinejs-zoomable
|
*/

img[x-zoomable]:hover {
cursor: zoom-in;
}

.zoomable-fullscreen-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
background: rgba(255, 255, 255, 0.9);
display: none;
z-index: 998;
cursor: zoom-out;
overflow: hidden;
touch-action: none;
}

.zoomable-fullscreen-image {
position: absolute;
max-height: none;
max-width: none;
cursor: grab;
transition: transform 0.2s ease-out;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
touch-action: none;
-webkit-user-select: none;
user-select: none;
}

.zoomable-fullscreen-image.dragging {
cursor: grabbing;
transition: none;
}

.zoomable-controls-panel {
position: fixed;
top: 20px;
right: 20px;
display: flex;
gap: 10px;
z-index: 999;
}

.zoomable-control-button {
background: #a1a1aa;
border: none;
color: white;
width: 40px;
height: 40px;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.2s;
}

.zoomable-control-button > svg {
width: 20px;
height: 20px;
}

.zoomable-control-button:hover,
.zoomable-control-button:focus {
background: #71717a;
outline: none;
}

@media (max-width: 768px) {
.zoomable-controls-panel {
top: 10px;
right: 10px;
}

.zoomable-control-button {
width: 44px;
height: 44px;
-webkit-tap-highlight-color: transparent; /* Prevents gray flash on iOS */
touch-action: manipulation; /* Optimize for touch */
}
}

.zoomable-loading-indicator {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1000;
display: flex;
flex-direction: column;
align-items: center;
color: #737373;
font-size: 1rem;
}

.zoomable-spinner {
width: 40px;
height: 40px;
border: 4px solid rgba(255, 255, 255, 0.3);
border-top: 4px solid #737373;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 10px;
}

@keyframes spin {
to {
transform: rotate(360deg);
}
}

.zoomable-loading-indicator[hidden] {
display: none;
}
}

@theme {
Expand Down
68 changes: 55 additions & 13 deletions resources/views/livewire/blog.blade.php
Original file line number Diff line number Diff line change
@@ -1,20 +1,62 @@
<div>
<x-slot name="header">Blog Posts</x-slot>

<flux:card>
<div class="flex justify-between">
<div>
<flux:input type="file" wire:model="file" label="Upload Blog Post" />
</div>
</div>

<div class="my-4">
<flux:button variant="primary" size="xs" wire:click="store" icon="arrow-up-tray">Upload</flux:button>
</div>
<flux:card class="space-y-4">
<flux:card>
<div class="space-y-4">
<div class="flex justify-between">
<div class="flex-1 space-y-4">
<flux:input
type="file"
wire:model="file"
label="Upload Blog Post"
description="Make sure that the post slug matches the name of the markdown file itself."
accept=".md"
size="sm"
/>

<flux:input
type="file"
wire:model="images"
label="Upload Images (Optional)"
multiple
accept="image/*"
size="sm"
/>
</div>
</div>

@if ($images)
<div class="mt-2">
<flux:badge variant="lime" size="sm">
{{ count($images) }} image(s) selected
</flux:badge>
</div>
@endif

<div class="flex gap-2 my-4">
<flux:button variant="primary" size="xs" wire:click="store" icon="arrow-up-tray">
Upload Blog Post
</flux:button>

@if ($file || $images)
<flux:button variant="ghost" size="xs" wire:click="clearUploads" icon="x-mark">
Clear
</flux:button>
@endif
</div>

<flux:separator />
@error('file')
<flux:error>{{ $message }}</flux:error>
@enderror

@error('images.*')
<flux:error>{{ $message }}</flux:error>
@enderror
</div>
</flux:card>

<div class="py-2">
<flux:card>
<div class="overflow-hidden shadow-2xs sm:rounded-lg">
<flux:table :paginate="$this->posts">
@forelse ($this->posts as $post)
Expand Down Expand Up @@ -82,6 +124,6 @@
@endforelse
</flux:table>
</div>
</div>
</flux:card>
</flux:card>
</div>