This commit is contained in:
yuriko 🦊 2025-05-24 17:01:54 -04:00
parent afc1fb10bc
commit 8004c8d69e
Signed by: jaiden
SSH key fingerprint: SHA256:f8tvveBoXBrKZIQDWLLcpQrKbATUCGg98x2N4YzkDM8
12 changed files with 320 additions and 5 deletions

View file

@ -0,0 +1,22 @@
<?php
namespace App\Http\Controllers;
use App\Models\Comment;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class DeletionController extends Controller
{
public function deleteComment(Comment $comment)
{
if (Auth::id() != $comment->user->id)
{
abort(401);
}
$postid = $comment->post->id;
$comment->delete();
return redirect("/posts/$postid");
}
}

View file

@ -0,0 +1,16 @@
<?php
namespace App\Livewire\Posts;
use App\Models\Comment as PostComment;
use Livewire\Component;
class Comment extends Component
{
public PostComment $comment;
public function render()
{
return view('livewire.posts.comment');
}
}

View file

@ -3,6 +3,8 @@
namespace App\Livewire\Posts; namespace App\Livewire\Posts;
use App\Models\Post; use App\Models\Post;
use App\Models\Comment as PostComment;
use Illuminate\Support\Facades\Auth;
use Livewire\Attributes\Validate; use Livewire\Attributes\Validate;
use Livewire\Component; use Livewire\Component;
@ -11,12 +13,24 @@ class View extends Component
public Post $post; public Post $post;
#[Validate('string|max:240')] #[Validate('string|max:240')]
public $comment = ''; public string $message = '';
public function render() public function render()
{ {
return view('livewire.posts.view', [ return view('livewire.posts.view', [
// 'comments' => $this->post->comments 'comments' => $this->post->comments->sortByDesc('created_at'),
])->title("Post {$this->post->id}"); ])->title("Post {$this->post->id}");
} }
public function postComment()
{
$this->validate();
$user = Auth::user();
if ($comment = PostComment::create(['message' => $this->message]))
{
$this->post->comments()->save($comment);
$user->comments()->save($comment);
}
return $this->redirect("/posts/{$this->post->id}");
}
} }

24
app/Models/Comment.php Normal file
View file

@ -0,0 +1,24 @@
<?php
namespace App\Models;
use MongoDB\Laravel\Eloquent\Model;
use MongoDB\Laravel\Eloquent\SoftDeletes;
use MongoDB\Laravel\Relations\BelongsTo;
class Comment extends Model
{
use SoftDeletes;
protected $fillable = [ 'message' ];
public function post(): BelongsTo
{
return $this->belongsTo(Post::class);
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}

View file

@ -5,12 +5,16 @@ namespace App\Models;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use MongoDB\Laravel\Eloquent\Model; use MongoDB\Laravel\Eloquent\Model;
use MongoDB\Laravel\Eloquent\SoftDeletes;
use MongoDB\Laravel\Relations\BelongsTo; use MongoDB\Laravel\Relations\BelongsTo;
use MongoDB\Laravel\Relations\BelongsToMany; use MongoDB\Laravel\Relations\BelongsToMany;
use MongoDB\Laravel\Relations\HasMany;
use Symfony\Component\HttpFoundation\StreamedResponse; use Symfony\Component\HttpFoundation\StreamedResponse;
class Post extends Model class Post extends Model
{ {
use SoftDeletes;
protected $fillable = [ 'rating', 'extension', 'featured' ]; protected $fillable = [ 'rating', 'extension', 'featured' ];
public function user(): BelongsTo public function user(): BelongsTo
@ -23,6 +27,11 @@ class Post extends Model
return $this->belongsToMany(Tag::class); return $this->belongsToMany(Tag::class);
} }
public function comments(): HasMany
{
return $this->hasMany(Comment::class);
}
protected function toBase64(string $path): string protected function toBase64(string $path): string
{ {
$ext = $this->extension; $ext = $this->extension;

View file

@ -19,7 +19,6 @@ class User extends Authenticatable
protected $fillable = [ protected $fillable = [
'name', 'name',
'email', 'email',
'password',
]; ];
protected $hidden = [ protected $hidden = [
@ -39,4 +38,9 @@ class User extends Authenticatable
{ {
return $this->hasMany(Post::class); return $this->hasMany(Post::class);
} }
public function comments(): HasMany
{
return $this->hasMany(Comment::class);
}
} }

160
config/livewire.php Normal file
View file

@ -0,0 +1,160 @@
<?php
return [
/*
|---------------------------------------------------------------------------
| Class Namespace
|---------------------------------------------------------------------------
|
| This value sets the root class namespace for Livewire component classes in
| your application. This value will change where component auto-discovery
| finds components. It's also referenced by the file creation commands.
|
*/
'class_namespace' => 'App\\Livewire',
/*
|---------------------------------------------------------------------------
| View Path
|---------------------------------------------------------------------------
|
| This value is used to specify where Livewire component Blade templates are
| stored when running file creation commands like `artisan make:livewire`.
| It is also used if you choose to omit a component's render() method.
|
*/
'view_path' => resource_path('views/livewire'),
/*
|---------------------------------------------------------------------------
| Layout
|---------------------------------------------------------------------------
| The view that will be used as the layout when rendering a single component
| as an entire page via `Route::get('/post/create', CreatePost::class);`.
| In this case, the view returned by CreatePost will render into $slot.
|
*/
'layout' => 'components.layouts.app',
/*
|---------------------------------------------------------------------------
| Lazy Loading Placeholder
|---------------------------------------------------------------------------
| Livewire allows you to lazy load components that would otherwise slow down
| the initial page load. Every component can have a custom placeholder or
| you can define the default placeholder view for all components below.
|
*/
'lazy_placeholder' => null,
/*
|---------------------------------------------------------------------------
| Temporary File Uploads
|---------------------------------------------------------------------------
|
| Livewire handles file uploads by storing uploads in a temporary directory
| before the file is stored permanently. All file uploads are directed to
| a global endpoint for temporary storage. You may configure this below:
|
*/
'temporary_file_upload' => [
'disk' => null, // Example: 'local', 's3' | Default: 'default'
'rules' => ['required', 'file', 'max:20480'], // Example: ['file', 'mimes:png,jpg'] | Default: ['required', 'file', 'max:12288'] (12MB)
'directory' => null, // Example: 'tmp' | Default: 'livewire-tmp'
'middleware' => null, // Example: 'throttle:5,1' | Default: 'throttle:60,1'
'preview_mimes' => [ // Supported file types for temporary pre-signed file URLs...
'png', 'gif', 'bmp', 'svg', 'mp4',
'mov', 'avi', 'wmv', 'mp3',
'jpg', 'jpeg', 'webp',
],
'max_upload_time' => 5, // Max duration (in minutes) before an upload is invalidated...
'cleanup' => true, // Should cleanup temporary uploads older than 24 hrs...
],
/*
|---------------------------------------------------------------------------
| Render On Redirect
|---------------------------------------------------------------------------
|
| This value determines if Livewire will run a component's `render()` method
| after a redirect has been triggered using something like `redirect(...)`
| Setting this to true will render the view once more before redirecting
|
*/
'render_on_redirect' => false,
/*
|---------------------------------------------------------------------------
| Eloquent Model Binding
|---------------------------------------------------------------------------
|
| Previous versions of Livewire supported binding directly to eloquent model
| properties using wire:model by default. However, this behavior has been
| deemed too "magical" and has therefore been put under a feature flag.
|
*/
'legacy_model_binding' => false,
/*
|---------------------------------------------------------------------------
| Auto-inject Frontend Assets
|---------------------------------------------------------------------------
|
| By default, Livewire automatically injects its JavaScript and CSS into the
| <head> and <body> of pages containing Livewire components. By disabling
| this behavior, you need to use @livewireStyles and @livewireScripts.
|
*/
'inject_assets' => true,
/*
|---------------------------------------------------------------------------
| Navigate (SPA mode)
|---------------------------------------------------------------------------
|
| By adding `wire:navigate` to links in your Livewire application, Livewire
| will prevent the default link handling and instead request those pages
| via AJAX, creating an SPA-like effect. Configure this behavior here.
|
*/
'navigate' => [
'show_progress_bar' => true,
'progress_bar_color' => '#2299dd',
],
/*
|---------------------------------------------------------------------------
| HTML Morph Markers
|---------------------------------------------------------------------------
|
| Livewire intelligently "morphs" existing HTML into the newly rendered HTML
| after each update. To make this process more reliable, Livewire injects
| "markers" into the rendered Blade surrounding @if, @class & @foreach.
|
*/
'inject_morph_markers' => true,
/*
|---------------------------------------------------------------------------
| Pagination Theme
|---------------------------------------------------------------------------
|
| When enabling Livewire's pagination feature by using the `WithPagination`
| trait, Livewire will use Tailwind templates to render pagination views
| on the page. If you want Bootstrap CSS, you can specify: "bootstrap"
|
*/
'pagination_theme' => 'tailwind',
];

View file

@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('comments', function (Blueprint $table) {
$table->id();
$table->string('message');
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('comments');
}
};

View file

@ -2,7 +2,7 @@
<h1>Upload</h1> <h1>Upload</h1>
<form wire:submit="createPost" class="wa-stack wa-gap-xl"> <form wire:submit="createPost" class="wa-stack wa-gap-xl">
<wa-card> <wa-card class="wa-stack">
<input wire:model="file" type="file" label="file" placeholder="Select a file to upload." /> <input wire:model="file" type="file" label="file" placeholder="Select a file to upload." />
@error('file') @error('file')
<span class="wa-caption-m">{{ $message }}</span> <span class="wa-caption-m">{{ $message }}</span>

View file

@ -0,0 +1,16 @@
<div class="wa-flank wa-align-items-start">
<wa-avatar initials="" label="{{ $comment->user->name }}"></wa-avatar>
<div class="wa-split">
<div class="wa-stack wa-gap-3xs">
<div class="wa-cluster wa-align-items-center">
<strong>{{ $comment->user->name }}</strong>
<span class="wa-caption-m" wire:poll.visible>{{ $comment->created_at->diffForHumans() }}</span>
</div>
<span>{{ $comment->message }}</span>
</div>
@if (Auth::id() == $comment->user->id)
<wa-icon-button href="{{ url("/delete/comment/$comment->id") }}" name="times" label="Delete"></wa-icon-button>
@endif
</div>
</div>

View file

@ -65,8 +65,23 @@
</div> </div>
{{-- Main content --}} {{-- Main content --}}
<div class="wa-stack wa-gap-2xl" wire:poll> <div class="wa-stack wa-gap-2xl" wire:poll.visible>
<livewire:posts.image :$post lazy /> <livewire:posts.image :$post lazy />
<span class="wa-heading-m">
<wa-format-number value="{{ $post->comments->count() }}"></wa-format-number>
{{ Str::plural("Comment", $post->comments->count()) }}
</span>
<form wire:submit="postComment">
<wa-input wire:model.live="message" type="text" placeholder="Leave a comment, press enter to post." clearable multiline>
<wa-icon name="comment" slot="prefix"></wa-icon>
</wa-input>
</form>
@foreach ($comments as $comment)
<livewire:posts.comment :$comment :key="$comment->id" />
@endforeach
</div> </div>
</div> </div>

View file

@ -1,5 +1,6 @@
<?php <?php
use App\Http\Controllers\DeletionController;
use App\Livewire\App\Home as AppHome; use App\Livewire\App\Home as AppHome;
use App\Livewire\Pages\Upload as UploadPage; use App\Livewire\Pages\Upload as UploadPage;
use App\Livewire\Posts\Index as PostsPage; use App\Livewire\Posts\Index as PostsPage;
@ -25,3 +26,8 @@ Route::middleware('auth')->prefix('posts')->group(function () {
return Storage::download("posts/$post->id/full", config('app.name') . "_$post->id.$post->extension"); return Storage::download("posts/$post->id/full", config('app.name') . "_$post->id.$post->extension");
}); });
}); });
// Object deletion routes
Route::middleware('auth')->prefix('delete')->controller(DeletionController::class)->group(function () {
Route::get('comment/{comment}', 'deleteComment');
});