From 5f575024e2f720f5abf1b504a66a126081faba88 Mon Sep 17 00:00:00 2001 From: w33b Date: Sat, 10 Jan 2026 15:02:14 +0100 Subject: [PATCH] Add Livewire comment system --- app/Livewire/Comment.php | 90 +++++++++++++++++ app/Livewire/Comments.php | 56 +++++++++++ app/Models/Comment.php | 61 ++++++++++++ app/Models/Episode.php | 5 + app/Models/Presenters/CommentPresenter.php | 28 ++++++ app/Policies/CommentPolicy.php | 22 +++++ ...026_01_10_120521_create_comments_table.php | 38 +++++++ .../views/home/partials/comments.blade.php | 4 +- resources/views/livewire/comment.blade.php | 99 +++++++++++++++++++ resources/views/livewire/comments.blade.php | 54 ++++++++++ resources/views/partials/comment.blade.php | 22 ----- resources/views/stream/index.blade.php | 2 +- .../views/stream/partials/comments.blade.php | 8 -- 13 files changed, 456 insertions(+), 33 deletions(-) create mode 100644 app/Livewire/Comment.php create mode 100644 app/Livewire/Comments.php create mode 100644 app/Models/Comment.php create mode 100644 app/Models/Presenters/CommentPresenter.php create mode 100644 app/Policies/CommentPolicy.php create mode 100644 database/migrations/2026_01_10_120521_create_comments_table.php create mode 100644 resources/views/livewire/comment.blade.php create mode 100644 resources/views/livewire/comments.blade.php delete mode 100644 resources/views/partials/comment.blade.php delete mode 100644 resources/views/stream/partials/comments.blade.php diff --git a/app/Livewire/Comment.php b/app/Livewire/Comment.php new file mode 100644 index 0000000..65d823f --- /dev/null +++ b/app/Livewire/Comment.php @@ -0,0 +1,90 @@ + '' + ]; + + public $isEditing = false; + + public $editState = [ + 'body' => '' + ]; + + protected $listeners = [ + 'refresh' => '$refresh' + ]; + + protected $validationAttributes = [ + 'replyState.body' => 'reply' + ]; + + public function updatedIsEditing($isEditing) + { + if (! $isEditing) { + return; + } + + $this->editState = [ + 'body' => $this->comment->body + ]; + } + + public function editComment() + { + $this->authorize('update', $this->comment); + + $this->comment->update($this->editState); + + $this->isEditing = false; + } + + public function deleteComment() + { + $this->authorize('destroy', $this->comment); + + $this->comment->delete(); + } + + public function postReply() + { + if (! $this->comment->depth() < 2) { + return; + } + + $this->validate([ + 'replyState.body' => 'required' + ]); + + $reply = $this->comment->children()->make($this->replyState); + $reply->user()->associate(auth()->user()); + $reply->commentable()->associate($this->comment->commentable); + + $reply->save(); + + $this->replyState = [ + 'body' => '' + ]; + + $this->isReplying = false; + + $this->dispatch('refresh')->self(); + } + + public function render() + { + return view('livewire.comment'); + } +} \ No newline at end of file diff --git a/app/Livewire/Comments.php b/app/Livewire/Comments.php new file mode 100644 index 0000000..824a4ec --- /dev/null +++ b/app/Livewire/Comments.php @@ -0,0 +1,56 @@ + '' + ]; + + protected $validationAttributes = [ + 'newCommentState.body' => 'comment' + ]; + + protected $listeners = [ + 'refresh' => '$refresh' + ]; + + public function postComment() + { + $this->validate([ + 'newCommentState.body' => 'required' + ]); + + $comment = $this->model->comments()->make($this->newCommentState); + $comment->user()->associate(auth()->user()); + $comment->save(); + + $this->newCommentState = [ + 'body' => '' + ]; + + $this->resetPage(); + } + + public function render() + { + $comments = $this->model + ->comments() + ->with('user', 'children.user', 'children.children') + ->parent() + ->latest() + ->paginate(3); + + return view('livewire.comments', [ + 'comments' => $comments + ]); + } +} \ No newline at end of file diff --git a/app/Models/Comment.php b/app/Models/Comment.php new file mode 100644 index 0000000..45b4071 --- /dev/null +++ b/app/Models/Comment.php @@ -0,0 +1,61 @@ +belongsTo(User::class); + } + + public function scopeParent(Builder $builder) + { + $builder->whereNull('parent_id'); + } + + public function children() + { + return $this->hasMany(Comment::class, 'parent_id')->oldest(); + } + + public function commentable() + { + return $this->morphTo(); + } + + public function parent() + { + return $this->hasOne(Comment::class, 'id', 'parent_id'); + } + + // Recursevly calculates how deep the nesting is + public function depth(): int + { + return $this->parent + ? $this->parent->depth() + 1 + : 0; + } +} diff --git a/app/Models/Episode.php b/app/Models/Episode.php index 7029eb3..31ef83d 100644 --- a/app/Models/Episode.php +++ b/app/Models/Episode.php @@ -160,6 +160,11 @@ class Episode extends Model implements Sitemapable return cache()->remember('episodeComments' . $this->id, 300, fn() => $this->comments->count()); } + public function comments() + { + return $this->morphMany(Comment::class, 'commentable'); + } + public function getProblematicTags(): string { $problematicTags = ['Gore', 'Scat', 'Horror']; diff --git a/app/Models/Presenters/CommentPresenter.php b/app/Models/Presenters/CommentPresenter.php new file mode 100644 index 0000000..645f85f --- /dev/null +++ b/app/Models/Presenters/CommentPresenter.php @@ -0,0 +1,28 @@ +comment = $comment; + } + + public function markdownBody() + { + return Str::of($this->comment->body)->markdown([ + 'html_input' => 'strip', + ]); + } + + public function relativeCreatedAt() + { + return $this->comment->created_at->diffForHumans(); + } +} \ No newline at end of file diff --git a/app/Policies/CommentPolicy.php b/app/Policies/CommentPolicy.php new file mode 100644 index 0000000..4f52686 --- /dev/null +++ b/app/Policies/CommentPolicy.php @@ -0,0 +1,22 @@ +id === $comment->user_id; + } + + public function destroy(User $user, Comment $comment): bool + { + return $user->id === $comment->user_id; + } +} \ No newline at end of file diff --git a/database/migrations/2026_01_10_120521_create_comments_table.php b/database/migrations/2026_01_10_120521_create_comments_table.php new file mode 100644 index 0000000..98806de --- /dev/null +++ b/database/migrations/2026_01_10_120521_create_comments_table.php @@ -0,0 +1,38 @@ +id(); + $table->foreignId('user_id')->constrained()->cascadeOnDelete(); + $table->foreignId('parent_id')->nullable()->constrained('comments')->cascadeOnDelete(); + $table->morphs('commentable'); // What is being commented on + $table->text('body'); + $table->softDeletes(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('comments'); + + // Revert to old table from laravelista/comments + Schema::rename('comments_old', 'comments'); + } +}; diff --git a/resources/views/home/partials/comments.blade.php b/resources/views/home/partials/comments.blade.php index 5ed6af4..2548d70 100644 --- a/resources/views/home/partials/comments.blade.php +++ b/resources/views/home/partials/comments.blade.php @@ -31,7 +31,7 @@
- @include('partials.comment', ['comment' => $comment]) + {{--@include('partials.comment', ['comment' => $comment])--}}
@elseif($comment->commentable_type == 'App\Models\Hentai') @@ -53,7 +53,7 @@
- @include('partials.comment', ['comment' => $comment]) + {{--@include('partials.comment', ['comment' => $comment])--}}
@endif diff --git a/resources/views/livewire/comment.blade.php b/resources/views/livewire/comment.blade.php new file mode 100644 index 0000000..fccc75d --- /dev/null +++ b/resources/views/livewire/comment.blade.php @@ -0,0 +1,99 @@ +
+
+
+ {{ $comment->user->name }} +
+
+
+ {{ $comment->user->name }} +
+
+ @if ($isEditing) +
+
+ + + @error('editState.body') +

{{ $message }}

+ @enderror +
+
+ +
+
+ @else +

{!! $comment->presenter()->markdownBody() !!}

+ @endif +
+
+ + {{ $comment->presenter()->relativeCreatedAt() }} + + @auth + {{--@if ($comment->hasParent())--}} + @if ($comment->depth() < 2) + + @endif + {{--@endif--}} + + @can ('update', $comment) + + @endcan + + @can ('destroy', $comment) + + @endcan + @endauth +
+
+
+ +
+ @if ($isReplying) +
+
+ + + @error('replyState.body') +

{{ $message }}

+ @enderror +
+
+ +
+
+ @endif + + @foreach ($comment->children as $child) + + @endforeach +
+
\ No newline at end of file diff --git a/resources/views/livewire/comments.blade.php b/resources/views/livewire/comments.blade.php new file mode 100644 index 0000000..2aa0d26 --- /dev/null +++ b/resources/views/livewire/comments.blade.php @@ -0,0 +1,54 @@ +
+
+
+
+

Comments

+
+
+
+ @if ($comments->isNotEmpty()) + @foreach($comments as $comment) + + @endforeach + {{ $comments->links() }} + @else +

No comments yet.

+ @endif +
+
+
+
+ @auth +
+
+ {{ auth()->user()->name }} +
+
+
+
+ + + @error('newCommentState.body') +

{{ $message }}

+ @enderror +
+
+ +
+
+
+
+ @endauth + + @guest +

Log in to comment.

+ @endguest +
+
+
\ No newline at end of file diff --git a/resources/views/partials/comment.blade.php b/resources/views/partials/comment.blade.php deleted file mode 100644 index c9eb525..0000000 --- a/resources/views/partials/comment.blade.php +++ /dev/null @@ -1,22 +0,0 @@ -@inject('markdown', 'Parsedown') -@php - // TODO: There should be a better place for this. - $markdown->setSafeMode(true); -@endphp -
- @php $user = cache()->rememberForever('commentUser'.$comment->commenter_id, fn () => \App\Models\User::where('id', $comment->commenter_id)->first()); @endphp -
- {{ $user->name }} Avatar -
-
-
- @if($user->is_patreon) -
{{ $user->name }} - {{ \Carbon\Carbon::parse($comment->created_at)->diffForHumans() }}
- @else -
{{ $user->name }} - {{ \Carbon\Carbon::parse($comment->created_at)->diffForHumans() }}
- @endif -
-
{!! $markdown->line($comment->comment) !!}
-
-
-
\ No newline at end of file diff --git a/resources/views/stream/index.blade.php b/resources/views/stream/index.blade.php index 89fd342..826828d 100644 --- a/resources/views/stream/index.blade.php +++ b/resources/views/stream/index.blade.php @@ -26,7 +26,7 @@ @include('stream.partials.info') - @include('stream.partials.comments') +
@if(! $isMobile) diff --git a/resources/views/stream/partials/comments.blade.php b/resources/views/stream/partials/comments.blade.php deleted file mode 100644 index 6427f85..0000000 --- a/resources/views/stream/partials/comments.blade.php +++ /dev/null @@ -1,8 +0,0 @@ -
-
-

- {{ __('home.latest-comments') }} -

- {{--@comments(['model' => $episode])--}} -
-