From c1a507e7277fabd360d4ab0aae07a2cdebb75915 Mon Sep 17 00:00:00 2001 From: Sam Maosa Date: Thu, 5 Jun 2025 06:18:45 +0300 Subject: [PATCH 1/3] Feature: Remove Closures from Config and Add them as arguments to Livewire Components - WIP --- .../views/livewire/comment-card.blade.php | 6 ++--- resources/views/livewire/comments.blade.php | 7 ++++-- src/Livewire/AddComment.php | 7 ++++-- src/Livewire/CommentCard.php | 23 +++++++++++++++++-- src/Livewire/Comments.php | 8 ++++++- src/Models/Comment.php | 4 ---- 6 files changed, 41 insertions(+), 14 deletions(-) diff --git a/resources/views/livewire/comment-card.blade.php b/resources/views/livewire/comment-card.blade.php index e72f699..f06cdd4 100644 --- a/resources/views/livewire/comment-card.blade.php +++ b/resources/views/livewire/comment-card.blade.php @@ -4,14 +4,14 @@

- {{ $this->comment->commentator }} + {{ $this->getCommentator() }}

diff --git a/resources/views/livewire/comments.blade.php b/resources/views/livewire/comments.blade.php index 8444736..50cad17 100644 --- a/resources/views/livewire/comments.blade.php +++ b/resources/views/livewire/comments.blade.php @@ -17,7 +17,10 @@ @foreach($this->comments as $comment) + :comment="$comment" + :get-user-name-using="$this->getUserNameUsing" + :get-user-avatar-using="$this->getUserAvatarUsing" + /> @endforeach - + diff --git a/src/Livewire/AddComment.php b/src/Livewire/AddComment.php index 1a4fce2..7787f69 100644 --- a/src/Livewire/AddComment.php +++ b/src/Livewire/AddComment.php @@ -30,13 +30,16 @@ class AddComment extends Component implements HasForms public ?Comment $replyTo = null; - public function mount(?Model $commentable = null, ?Comment $replyTo = null): void + public ?\Closure $getMentionsUsing; + + public function mount(?Model $commentable, ?Comment $replyTo, ?\Closure $getMentionsUsing): void { if (! $commentable) { throw new \Error('The $commentable property is required.'); } $this->commentable = $commentable; $this->replyTo = $replyTo; + $this->getMentionsUsing = $getMentionsUsing; $this->form->fill(); } @@ -51,7 +54,7 @@ public function getCommentable(): Model public function form(Form $form): Form { - $mentionsClosure = config('nested-comments.closures.getMentionsUsing', fn (string $query, Model $commentable) => app(NestedComments::class)->getUserMentions($query)); + $mentionsClosure = $this->getMentionsUsing ?? fn (string $query, Model $commentable) => app(NestedComments::class)->getUserMentions($query); return $form ->schema([ diff --git a/src/Livewire/CommentCard.php b/src/Livewire/CommentCard.php index d566b6e..4543832 100644 --- a/src/Livewire/CommentCard.php +++ b/src/Livewire/CommentCard.php @@ -12,17 +12,24 @@ class CommentCard extends Component public bool $showReplies = false; + public ?\Closure $getUserNameUsing = null; + + public ?\Closure $getUserAvatarUsing = null; + protected $listeners = [ 'refresh' => 'refreshReplies', ]; - public function mount(?Comment $comment = null): void + public function mount(?Comment $comment = null, ?\Closure $getUserNameUsing = null, ?\Closure $getUserAvatarUsing = null): void { if (! $comment) { throw new \Error('The $comment property is required.'); } $this->comment = $comment; + + $this->getUserNameUsing = $getUserNameUsing ?? fn ($user) => $user->getAttribute('name'); + $this->getUserAvatarUsing = $getUserAvatarUsing ?? fn ($user) => app(\Coolsam\NestedComments\NestedComments::class)->getDefaultUserAvatar($user); } public function render() @@ -48,6 +55,18 @@ public function getAvatar() return ''; } - return call_user_func(config('nested-comments.closures.getUserAvatarUsing'), $this->comment->commentator); + return call_user_func($this->getUserAvatarUsing, $this->comment->commentator); + } + + public function getCommentator(): string + { + if (! $this->comment) { + return ''; + } + if (! $this->comment->user) { + return $this->comment->getAttribute('guest_name') ?? 'Guest'; + } + + return call_user_func($this->getUserNameUsing, $this->comment->user); } } diff --git a/src/Livewire/Comments.php b/src/Livewire/Comments.php index dfbbe05..d36c90b 100644 --- a/src/Livewire/Comments.php +++ b/src/Livewire/Comments.php @@ -25,10 +25,16 @@ class Comments extends Component * @var Collection */ public Collection $comments; + public ?\Closure $getMentionsUsing = null; + public ?\Closure $getUserAvatarUsing = null; + public ?\Closure $getUserNameUsing = null; - public function mount(): void + public function mount(?\Closure $getMentionsUsing = null, ?\Closure $getUserAvatarUsing=null, ?\Closure $getUserNameUsing = null): void { $this->comments = collect(); + $this->getMentionsUsing = $getMentionsUsing; + $this->getUserAvatarUsing = $getUserAvatarUsing ?? fn ($user) => app(NestedComments::class)->getDefaultUserAvatar($user); + $this->getUserNameUsing = $getUserNameUsing ?? config('nested-comments.closures.getUserNameUsing', fn ($user) => $user->getAttribute('name')); if (! $this->record) { throw new \Error('Record model (Commentable) is required'); } diff --git a/src/Models/Comment.php b/src/Models/Comment.php index 339e20e..b163d44 100644 --- a/src/Models/Comment.php +++ b/src/Models/Comment.php @@ -27,10 +27,6 @@ public function user(): BelongsTo public function getCommentatorAttribute() { - if ($this->user) { - return call_user_func(config('nested-comments.closures.getUserNameUsing'), $this->user); - } - return $this->getAttribute('guest_name'); } From 5a2195c8e1aff4244f60f8f9ad142f4c09058211 Mon Sep 17 00:00:00 2001 From: Sam Maosa Date: Fri, 6 Jun 2025 13:28:06 +0300 Subject: [PATCH 2/3] WIP Feature: Use Entries for Comment Cards and Replies - Evaluate closures at the card level - Added a Panel that uses Repeatable Entry - TODO: Fix the comments avatars and user names --- config/nested-comments.php | 12 ---- .../views/components/comment-card.blade.php | 13 ++++ .../infolists/comment-card-entry.blade.php | 8 +++ .../infolists/comments-entry.blade.php | 7 ++- .../infolists/comments-panel.blade.php | 53 +++++++++++++++++ resources/views/livewire/comments.blade.php | 6 +- src/Filament/Infolists/CommentCardEntry.php | 59 +++++++++++++++++++ src/Filament/Infolists/CommentsEntry.php | 40 +++++++++++++ src/Filament/Infolists/CommentsPanel.php | 20 +++++++ src/Livewire/AddComment.php | 19 +++--- src/Livewire/CommentCard.php | 20 ++++--- src/Livewire/Comments.php | 28 ++++++++- src/NestedComments.php | 20 ++++--- 13 files changed, 259 insertions(+), 46 deletions(-) create mode 100644 resources/views/components/comment-card.blade.php create mode 100644 resources/views/filament/infolists/comment-card-entry.blade.php create mode 100644 resources/views/filament/infolists/comments-panel.blade.php create mode 100644 src/Filament/Infolists/CommentCardEntry.php create mode 100644 src/Filament/Infolists/CommentsPanel.php diff --git a/config/nested-comments.php b/config/nested-comments.php index dc1e777..bff56fa 100644 --- a/config/nested-comments.php +++ b/config/nested-comments.php @@ -1,6 +1,5 @@ env('ALLOW_MULTIPLE_REACTIONS', false), // Allow multiple reactions from the same user 'allow-guest-reactions' => env('ALLOW_GUEST_REACTIONS', false), // Allow guest users to react 'allow-guest-comments' => env('ALLOW_GUEST_COMMENTS', false), // Allow guest users to comment - 'closures' => [ - 'getUserNameUsing' => fn (Authenticatable | Model $user) => $user->getAttribute('name'), - 'getUserAvatarUsing' => fn ( - Authenticatable | Model | string $user - ) => app(\Coolsam\NestedComments\NestedComments::class)->geDefaultUserAvatar($user), - // 'getMentionsUsing' => fn (string $query) => app(\Coolsam\NestedComments\NestedComments::class)->getUserMentions($query), // Get mentions of all users in the DB - 'getMentionsUsing' => fn ( - string $query, - Model $commentable - ) => app(\Coolsam\NestedComments\NestedComments::class)->getCurrentThreadUsers($query, $commentable), - ], 'mentions' => [ 'items-placeholder' => 'Search users by name or email address', 'empty-items-message' => 'No users found', diff --git a/resources/views/components/comment-card.blade.php b/resources/views/components/comment-card.blade.php new file mode 100644 index 0000000..141be1d --- /dev/null +++ b/resources/views/components/comment-card.blade.php @@ -0,0 +1,13 @@ +@props([ + 'comment' => null, +]) +@if ($comment?->getKey()) + + @else +

+ {{ __('No comment provided.') }} +

+@endif \ No newline at end of file diff --git a/resources/views/filament/infolists/comment-card-entry.blade.php b/resources/views/filament/infolists/comment-card-entry.blade.php new file mode 100644 index 0000000..4106ea2 --- /dev/null +++ b/resources/views/filament/infolists/comment-card-entry.blade.php @@ -0,0 +1,8 @@ + + + diff --git a/resources/views/filament/infolists/comments-entry.blade.php b/resources/views/filament/infolists/comments-entry.blade.php index 1ea5917..2a90fd1 100644 --- a/resources/views/filament/infolists/comments-entry.blade.php +++ b/resources/views/filament/infolists/comments-entry.blade.php @@ -1,3 +1,8 @@ - + diff --git a/resources/views/filament/infolists/comments-panel.blade.php b/resources/views/filament/infolists/comments-panel.blade.php new file mode 100644 index 0000000..188d4dd --- /dev/null +++ b/resources/views/filament/infolists/comments-panel.blade.php @@ -0,0 +1,53 @@ +@php + $isContained = $isContained(); +@endphp + + +
merge([ + 'id' => $getId(), + ], escape: false) + ->merge($getExtraAttributes(), escape: false) + ->class([ + 'fi-in-repeatable my-0', + 'fi-contained' => $isContained, + ]) + }} + > + @if (count($childComponentContainers = $getChildComponentContainers())) +
    + + @foreach ($childComponentContainers as $container) +
  • $isContained, + ]) + > + {{ $container }} +
  • + @endforeach +
    +
+ @elseif (($placeholder = $getPlaceholder()) !== null) + + {{ $placeholder }} + + @endif + + +
+
diff --git a/resources/views/livewire/comments.blade.php b/resources/views/livewire/comments.blade.php index 50cad17..375af8a 100644 --- a/resources/views/livewire/comments.blade.php +++ b/resources/views/livewire/comments.blade.php @@ -18,9 +18,9 @@ @endforeach - + diff --git a/src/Filament/Infolists/CommentCardEntry.php b/src/Filament/Infolists/CommentCardEntry.php new file mode 100644 index 0000000..1d8d6bf --- /dev/null +++ b/src/Filament/Infolists/CommentCardEntry.php @@ -0,0 +1,59 @@ + 'full']; + + public static function make(?string $name = null): static + { + $name = $name ?? 'body'; + + return parent::make($name); // TODO: Change the autogenerated stub + } + + public function getUserNameUsing(Closure | string | null $callback = null): static + { + $this->userNameUsingClosure = $callback; + + return $this; + } + + public function getUserAvatarUsing(Closure | string | null $callback = null): static + { + $this->userAvatarUsingClosure = $callback; + + return $this; + } + + public function evaluateUserAvatar(): ?string + { + return $this->evaluate($this->userAvatarUsingClosure + ?? fn (Comment | Model $record) => app(NestedComments::class) + ->getDefaultUserAvatar($record->getAttribute('user') + ?? $record->getAttribute('guest_name') ?? 'Guest')); + } + + public function evaluateUserName(): ?string + { + return $this->evaluate($this->userNameUsingClosure + ?? fn (Comment | Model | null $record) => $record?->getAttribute('user')?->getAttribute('name') + ?? $record->getAttribute('guest_name')); + } +} diff --git a/src/Filament/Infolists/CommentsEntry.php b/src/Filament/Infolists/CommentsEntry.php index 1761200..d1cab02 100644 --- a/src/Filament/Infolists/CommentsEntry.php +++ b/src/Filament/Infolists/CommentsEntry.php @@ -13,4 +13,44 @@ class CommentsEntry extends Entry protected string $view = 'nested-comments::filament.infolists.comments-entry'; protected array $columnSpan = ['default' => 'full']; + + public Closure | string | null $userNameUsingClosure = null; + public Closure | string | null $userAvatarUsingClosure = null; + public Closure | string | null $mentionsUsingClosure = null; + + public function getUserNameUsing(Closure | string | null $callback = null): static + { + $this->userNameUsingClosure = $callback; + + return $this; + } + + public function getUserAvatarUsing(Closure | string | null $callback = null): static + { + $this->userAvatarUsingClosure = $callback; + + return $this; + } + + public function getMentionsUsing(Closure | string | null $callback = null): static + { + $this->mentionsUsingClosure = $callback; + + return $this; + } + + public function getMentionsUsingClosure(): ?Closure + { + return $this->mentionsUsingClosure; + } + + public function getUserNameUsingClosure(): ?Closure + { + return $this->userNameUsingClosure; + } + + public function getUserAvatarUsingClosure(): ?Closure + { + return $this->userAvatarUsingClosure; + } } diff --git a/src/Filament/Infolists/CommentsPanel.php b/src/Filament/Infolists/CommentsPanel.php new file mode 100644 index 0000000..3e1d7cb --- /dev/null +++ b/src/Filament/Infolists/CommentsPanel.php @@ -0,0 +1,20 @@ + 'full']; + + public static function make(?string $name = null): static + { + $name = $name ?? 'comments'; + return parent::make($name); + } +} diff --git a/src/Livewire/AddComment.php b/src/Livewire/AddComment.php index 7787f69..7c1852e 100644 --- a/src/Livewire/AddComment.php +++ b/src/Livewire/AddComment.php @@ -2,9 +2,12 @@ namespace Coolsam\NestedComments\Livewire; +use Closure; use Coolsam\NestedComments\Models\Comment; use Coolsam\NestedComments\NestedComments; use Coolsam\NestedComments\NestedCommentsServiceProvider; +use Error; +use Exception; use Filament\Forms\Concerns\InteractsWithForms; use Filament\Forms\Contracts\HasForms; use Filament\Forms\Form; @@ -30,23 +33,22 @@ class AddComment extends Component implements HasForms public ?Comment $replyTo = null; - public ?\Closure $getMentionsUsing; + public ?Closure $getMentionsUsing = null; - public function mount(?Model $commentable, ?Comment $replyTo, ?\Closure $getMentionsUsing): void + public function mount(?Model $commentable, ?Comment $replyTo): void { if (! $commentable) { - throw new \Error('The $commentable property is required.'); + throw new Error('The $commentable property is required.'); } $this->commentable = $commentable; $this->replyTo = $replyTo; - $this->getMentionsUsing = $getMentionsUsing; $this->form->fill(); } public function getCommentable(): Model { if (! $this->commentable) { - throw new \Error('The $commentable property is required.'); + throw new Error('The $commentable property is required.'); } return $this->commentable; @@ -54,8 +56,6 @@ public function getCommentable(): Model public function form(Form $form): Form { - $mentionsClosure = $this->getMentionsUsing ?? fn (string $query, Model $commentable) => app(NestedComments::class)->getUserMentions($query); - return $form ->schema([ TiptapEditor::make('body') @@ -64,7 +64,8 @@ public function form(Form $form): Form ->extraInputAttributes(['style' => 'min-height: 12rem;']) ->mentionItemsPlaceholder(config('nested-comments.mentions.items-placeholder', __('Search users by name or email address'))) ->emptyMentionItemsMessage(config('nested-comments.mentions.empty-items-message', __('No users found'))) - ->getMentionItemsUsing(fn (string $query) => $mentionsClosure($query, $this->getCommentable())) + ->getMentionItemsUsing(fn (string $query) => app(NestedComments::class)->getUserMentions($query)) + ->maxContentWidth('full') ->required() ->autofocus(), ]) @@ -73,7 +74,7 @@ public function form(Form $form): Form } /** - * @throws \Exception + * @throws Exception */ public function create(): void { diff --git a/src/Livewire/CommentCard.php b/src/Livewire/CommentCard.php index 4543832..68c0c6b 100644 --- a/src/Livewire/CommentCard.php +++ b/src/Livewire/CommentCard.php @@ -4,32 +4,33 @@ use Coolsam\NestedComments\Models\Comment; use Coolsam\NestedComments\NestedCommentsServiceProvider; +use Error; +use Filament\Support\Concerns\EvaluatesClosures; use Livewire\Component; class CommentCard extends Component { + use EvaluatesClosures; + public ?Comment $comment = null; public bool $showReplies = false; - public ?\Closure $getUserNameUsing = null; + public ?string $userAvatar = null; - public ?\Closure $getUserAvatarUsing = null; + public ?string $userName = null; protected $listeners = [ 'refresh' => 'refreshReplies', ]; - public function mount(?Comment $comment = null, ?\Closure $getUserNameUsing = null, ?\Closure $getUserAvatarUsing = null): void + public function mount(?Comment $comment = null): void { if (! $comment) { - throw new \Error('The $comment property is required.'); + throw new Error('The $comment property is required.'); } $this->comment = $comment; - - $this->getUserNameUsing = $getUserNameUsing ?? fn ($user) => $user->getAttribute('name'); - $this->getUserAvatarUsing = $getUserAvatarUsing ?? fn ($user) => app(\Coolsam\NestedComments\NestedComments::class)->getDefaultUserAvatar($user); } public function render() @@ -55,7 +56,7 @@ public function getAvatar() return ''; } - return call_user_func($this->getUserAvatarUsing, $this->comment->commentator); + return $this->userAvatar ?? ''; } public function getCommentator(): string @@ -63,10 +64,11 @@ public function getCommentator(): string if (! $this->comment) { return ''; } + if (! $this->comment->user) { return $this->comment->getAttribute('guest_name') ?? 'Guest'; } - return call_user_func($this->getUserNameUsing, $this->comment->user); + return $this->userName ?? 'Guest'; } } diff --git a/src/Livewire/Comments.php b/src/Livewire/Comments.php index d36c90b..27c6eca 100644 --- a/src/Livewire/Comments.php +++ b/src/Livewire/Comments.php @@ -25,16 +25,23 @@ class Comments extends Component * @var Collection */ public Collection $comments; + public ?\Closure $getMentionsUsing = null; + public ?\Closure $getUserAvatarUsing = null; + public ?\Closure $getUserNameUsing = null; - public function mount(?\Closure $getMentionsUsing = null, ?\Closure $getUserAvatarUsing=null, ?\Closure $getUserNameUsing = null): void + public function mount( + \Closure|null $getMentionsUsing = null, + \Closure|null $getUserAvatarUsing = null, + \Closure|null $getUserNameUsing = null): void { $this->comments = collect(); $this->getMentionsUsing = $getMentionsUsing; - $this->getUserAvatarUsing = $getUserAvatarUsing ?? fn ($user) => app(NestedComments::class)->getDefaultUserAvatar($user); - $this->getUserNameUsing = $getUserNameUsing ?? config('nested-comments.closures.getUserNameUsing', fn ($user) => $user->getAttribute('name')); + $this->getUserAvatarUsing = $getUserAvatarUsing; + $this->getUserNameUsing = $getUserNameUsing; + if (! $this->record) { throw new \Error('Record model (Commentable) is required'); } @@ -60,4 +67,19 @@ public function render() return view($namespace . '::livewire.comments'); } + + public function getMentionsUsingClosure() + { + return $this->getMentionsUsing; + } + + public function getUserAvatarUsingClosure() + { + return $this->getUserAvatarUsing; + } + + public function getUserNameUsingClosure() + { + return $this->getUserNameUsing; + } } diff --git a/src/NestedComments.php b/src/NestedComments.php index 14df329..8542b88 100644 --- a/src/NestedComments.php +++ b/src/NestedComments.php @@ -25,13 +25,15 @@ public function getGuestId(): ?string return session(self::GUEST_ID_FIELD); } + public function getUserName(Authenticatable | Model | null $user): string + { + return $user?->getAttribute('name') ?? 'Guest'; + } + public function getGuestName(): string { if (Auth::check()) { - return call_user_func(config( - 'nested-comments.closures.getUserNameUsing', - fn (Authenticatable | Model $user) => $user->getAttribute('name') - ), Auth::user()); + return $this->getUserName(Auth::user()); } return session(self::GUEST_NAME_FIELD, 'Guest'); @@ -66,7 +68,7 @@ public function classHasTrait(object | string $classInstance, string $trait): bo return in_array($trait, class_uses_recursive($classInstance)); } - public function geDefaultUserAvatar(Authenticatable | Model | string $user) + public function getDefaultUserAvatar(Authenticatable | Model | string $user) { if (is_string($user)) { $name = str($user) @@ -95,8 +97,8 @@ public function getUserMentions(string $query) ->map(function ($user) { return new MentionItem( id: $user->getKey(), - label: call_user_func(config('nested-comments.closures.getUserNameUsing'), $user), - image: call_user_func(config('nested-comments.closures.getUserAvatarUsing'), $user), + label: $this->getGuestName($user), + image: $this->getDefaultUserAvatar($user), roundedImage: true, ); })->toArray(); @@ -122,8 +124,8 @@ public function getCurrentThreadUsers(string $searchQuery, $commentable): mixed ->map(function ($user) { return new MentionItem( id: $user->getKey(), - label: call_user_func(config('nested-comments.closures.getUserNameUsing'), $user), - image: call_user_func(config('nested-comments.closures.getUserAvatarUsing'), $user), + label: $this->getUserName($user), + image: $this->getDefaultUserAvatar($user), roundedImage: true, ); })->toArray(); From 0191c22371478761712f5eb98ae83e8bf88af5a7 Mon Sep 17 00:00:00 2001 From: Sam Maosa Date: Sat, 7 Jun 2025 01:10:54 +0300 Subject: [PATCH 3/3] Improvements and Bug Fixes: Definition of Closures - Moved closures for getting the user name, avatar and mention items to the commentable model - Override getUserAvatar and getUserName in the commentable model to customize these. - Override getMentionsQuery to modify the query used to get mentions in the commentable model - Removed closures from the config to allow optimization through cache. - Bug Fix: Mentions item name was using the currently logged in user - New Component: Comment Card view component - New Component: Comment Card Entry --- .../infolists/comments-entry.blade.php | 7 +-- .../infolists/comments-panel.blade.php | 53 ------------------ resources/views/livewire/comments.blade.php | 4 +- src/Concerns/HasComments.php | 55 ++++++++++++++++++- src/Filament/Infolists/CommentsEntry.php | 40 -------------- src/Filament/Infolists/CommentsPanel.php | 20 ------- src/Livewire/AddComment.php | 22 ++++++-- src/Livewire/CommentCard.php | 14 +++-- src/Livewire/Comments.php | 29 +--------- src/NestedComments.php | 21 ++++--- 10 files changed, 94 insertions(+), 171 deletions(-) delete mode 100644 resources/views/filament/infolists/comments-panel.blade.php delete mode 100644 src/Filament/Infolists/CommentsPanel.php diff --git a/resources/views/filament/infolists/comments-entry.blade.php b/resources/views/filament/infolists/comments-entry.blade.php index 2a90fd1..1ea5917 100644 --- a/resources/views/filament/infolists/comments-entry.blade.php +++ b/resources/views/filament/infolists/comments-entry.blade.php @@ -1,8 +1,3 @@ - + diff --git a/resources/views/filament/infolists/comments-panel.blade.php b/resources/views/filament/infolists/comments-panel.blade.php deleted file mode 100644 index 188d4dd..0000000 --- a/resources/views/filament/infolists/comments-panel.blade.php +++ /dev/null @@ -1,53 +0,0 @@ -@php - $isContained = $isContained(); -@endphp - - -
merge([ - 'id' => $getId(), - ], escape: false) - ->merge($getExtraAttributes(), escape: false) - ->class([ - 'fi-in-repeatable my-0', - 'fi-contained' => $isContained, - ]) - }} - > - @if (count($childComponentContainers = $getChildComponentContainers())) -
    - - @foreach ($childComponentContainers as $container) -
  • $isContained, - ]) - > - {{ $container }} -
  • - @endforeach -
    -
- @elseif (($placeholder = $getPlaceholder()) !== null) - - {{ $placeholder }} - - @endif - - -
-
diff --git a/resources/views/livewire/comments.blade.php b/resources/views/livewire/comments.blade.php index 375af8a..09abd1f 100644 --- a/resources/views/livewire/comments.blade.php +++ b/resources/views/livewire/comments.blade.php @@ -18,9 +18,7 @@ @endforeach - + diff --git a/src/Concerns/HasComments.php b/src/Concerns/HasComments.php index 707dc3b..2233269 100644 --- a/src/Concerns/HasComments.php +++ b/src/Concerns/HasComments.php @@ -5,10 +5,15 @@ use Coolsam\NestedComments\Models\Comment; use Coolsam\NestedComments\NestedComments; use Exception; +use FilamentTiptapEditor\Data\MentionItem; +use Illuminate\Contracts\Auth\Authenticatable; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Support\Collection; +use function auth; + /** * @mixin Model */ @@ -97,7 +102,7 @@ public function editComment(Comment $comment, string $body, ?string $name = null app(NestedComments::class)->setGuestName($name); } - if (\auth()->check() && $comment->getAttribute('user_id') !== auth()->id()) { + if (auth()->check() && $comment->getAttribute('user_id') !== auth()->id()) { throw new Exception('You are not authorized to edit this comment.'); } @@ -123,7 +128,7 @@ public function deleteComment(Comment $comment): ?bool throw new Exception('You must be logged in to edit your comment.'); } - if (\auth()->check() && $comment->getAttribute('user_id') !== auth()->id()) { + if (auth()->check() && $comment->getAttribute('user_id') !== auth()->id()) { throw new Exception('You are not authorized to edit this comment.'); } @@ -136,4 +141,50 @@ public function deleteComment(Comment $comment): ?bool return $comment->delete(); } + + final public function getUserNameUsing(Comment $comment): string + { + return $this->getUserName($comment->getAttribute('user')); + } + + final public function getUserAvatarUsing(Comment $comment): ?string + { + $user = $comment->user ?? $comment->guest_name ?? 'Guest'; + + return $this->getUserAvatar($user); + } + + public function getUserName(Model | Authenticatable | null $user): string + { + return app(NestedComments::class)->getUserName($user); + } + + public function getUserAvatar(Model | Authenticatable | string | null $user): ?string + { + return app(NestedComments::class)->getDefaultUserAvatar($user); + } + + /** + * @return array + */ + public function getMentionsUsing(string $query): array + { + return $this->getMentionsQuery($query) +// ->where('username', 'like', "%{$query}%") + ->take(50) + ->get() + ->map(function ($user) { + return new MentionItem( + id: $user->getKey(), + label: $this->getUserName($user), + image: $this->getUserAvatar($user), + roundedImage: true, + ); + })->toArray(); + } + + public function getMentionsQuery(string $query): Builder + { + return app(NestedComments::class)->getUserMentionsQuery($query); + } } diff --git a/src/Filament/Infolists/CommentsEntry.php b/src/Filament/Infolists/CommentsEntry.php index d1cab02..1761200 100644 --- a/src/Filament/Infolists/CommentsEntry.php +++ b/src/Filament/Infolists/CommentsEntry.php @@ -13,44 +13,4 @@ class CommentsEntry extends Entry protected string $view = 'nested-comments::filament.infolists.comments-entry'; protected array $columnSpan = ['default' => 'full']; - - public Closure | string | null $userNameUsingClosure = null; - public Closure | string | null $userAvatarUsingClosure = null; - public Closure | string | null $mentionsUsingClosure = null; - - public function getUserNameUsing(Closure | string | null $callback = null): static - { - $this->userNameUsingClosure = $callback; - - return $this; - } - - public function getUserAvatarUsing(Closure | string | null $callback = null): static - { - $this->userAvatarUsingClosure = $callback; - - return $this; - } - - public function getMentionsUsing(Closure | string | null $callback = null): static - { - $this->mentionsUsingClosure = $callback; - - return $this; - } - - public function getMentionsUsingClosure(): ?Closure - { - return $this->mentionsUsingClosure; - } - - public function getUserNameUsingClosure(): ?Closure - { - return $this->userNameUsingClosure; - } - - public function getUserAvatarUsingClosure(): ?Closure - { - return $this->userAvatarUsingClosure; - } } diff --git a/src/Filament/Infolists/CommentsPanel.php b/src/Filament/Infolists/CommentsPanel.php deleted file mode 100644 index 3e1d7cb..0000000 --- a/src/Filament/Infolists/CommentsPanel.php +++ /dev/null @@ -1,20 +0,0 @@ - 'full']; - - public static function make(?string $name = null): static - { - $name = $name ?? 'comments'; - return parent::make($name); - } -} diff --git a/src/Livewire/AddComment.php b/src/Livewire/AddComment.php index 7c1852e..6a4ebce 100644 --- a/src/Livewire/AddComment.php +++ b/src/Livewire/AddComment.php @@ -2,9 +2,8 @@ namespace Coolsam\NestedComments\Livewire; -use Closure; +use Coolsam\NestedComments\Concerns\HasComments; use Coolsam\NestedComments\Models\Comment; -use Coolsam\NestedComments\NestedComments; use Coolsam\NestedComments\NestedCommentsServiceProvider; use Error; use Exception; @@ -33,8 +32,6 @@ class AddComment extends Component implements HasForms public ?Comment $replyTo = null; - public ?Closure $getMentionsUsing = null; - public function mount(?Model $commentable, ?Comment $replyTo): void { if (! $commentable) { @@ -56,6 +53,13 @@ public function getCommentable(): Model public function form(Form $form): Form { + /** + * @var Model|HasComments $commentable + * + * @phpstan-ignore-next-line + */ + $commentable = $this->getCommentable(); + return $form ->schema([ TiptapEditor::make('body') @@ -64,7 +68,10 @@ public function form(Form $form): Form ->extraInputAttributes(['style' => 'min-height: 12rem;']) ->mentionItemsPlaceholder(config('nested-comments.mentions.items-placeholder', __('Search users by name or email address'))) ->emptyMentionItemsMessage(config('nested-comments.mentions.empty-items-message', __('No users found'))) - ->getMentionItemsUsing(fn (string $query) => app(NestedComments::class)->getUserMentions($query)) + /** + * @phpstan-ignore-next-line + */ + ->getMentionItemsUsing(fn (string $query) => $commentable->getMentionsUsing($query)) ->maxContentWidth('full') ->required() ->autofocus(), @@ -80,6 +87,11 @@ public function create(): void { $data = $this->form->getState(); + /** + * @var Model|HasComments $commentable + * + * @phpstan-ignore-next-line + */ $commentable = $this->getCommentable(); /** diff --git a/src/Livewire/CommentCard.php b/src/Livewire/CommentCard.php index 68c0c6b..873a6f5 100644 --- a/src/Livewire/CommentCard.php +++ b/src/Livewire/CommentCard.php @@ -56,7 +56,10 @@ public function getAvatar() return ''; } - return $this->userAvatar ?? ''; + /** + * @phpstan-ignore-next-line + */ + return $this->comment->commentable?->getUserAvatarUsing($this->comment); } public function getCommentator(): string @@ -65,10 +68,9 @@ public function getCommentator(): string return ''; } - if (! $this->comment->user) { - return $this->comment->getAttribute('guest_name') ?? 'Guest'; - } - - return $this->userName ?? 'Guest'; + /** + * @phpstan-ignore-next-line + */ + return $this->comment->commentable?->getUserNameUsing($this->comment); } } diff --git a/src/Livewire/Comments.php b/src/Livewire/Comments.php index 27c6eca..e67dd33 100644 --- a/src/Livewire/Comments.php +++ b/src/Livewire/Comments.php @@ -26,21 +26,9 @@ class Comments extends Component */ public Collection $comments; - public ?\Closure $getMentionsUsing = null; - - public ?\Closure $getUserAvatarUsing = null; - - public ?\Closure $getUserNameUsing = null; - - public function mount( - \Closure|null $getMentionsUsing = null, - \Closure|null $getUserAvatarUsing = null, - \Closure|null $getUserNameUsing = null): void + public function mount(): void { $this->comments = collect(); - $this->getMentionsUsing = $getMentionsUsing; - $this->getUserAvatarUsing = $getUserAvatarUsing; - $this->getUserNameUsing = $getUserNameUsing; if (! $this->record) { throw new \Error('Record model (Commentable) is required'); @@ -67,19 +55,4 @@ public function render() return view($namespace . '::livewire.comments'); } - - public function getMentionsUsingClosure() - { - return $this->getMentionsUsing; - } - - public function getUserAvatarUsingClosure() - { - return $this->getUserAvatarUsing; - } - - public function getUserNameUsingClosure() - { - return $this->getUserNameUsing; - } } diff --git a/src/NestedComments.php b/src/NestedComments.php index 8542b88..cac52eb 100644 --- a/src/NestedComments.php +++ b/src/NestedComments.php @@ -27,7 +27,7 @@ public function getGuestId(): ?string public function getUserName(Authenticatable | Model | null $user): string { - return $user?->getAttribute('name') ?? 'Guest'; + return $user?->getAttribute('name') ?? $user?->getAttribute('guest_name') ?? 'Guest'; } public function getGuestName(): string @@ -85,25 +85,30 @@ public function getDefaultUserAvatar(Authenticatable | Model | string $user) } } - public function getUserMentions(string $query) + public function getUserMentions(string $query): array { - $userModel = config('nested-comments.models.user', config('auth.providers.users.model', 'App\\Models\\User')); - - return $userModel::query() - ->where('name', 'like', "%{$query}%") - ->orWhere('email', 'like', "%{$query}%") + return $this->getUserMentionsQuery($query) ->take(20) ->get() ->map(function ($user) { return new MentionItem( id: $user->getKey(), - label: $this->getGuestName($user), + label: $this->getUserName($user), image: $this->getDefaultUserAvatar($user), roundedImage: true, ); })->toArray(); } + public function getUserMentionsQuery(string $query): \Illuminate\Database\Eloquent\Builder + { + $userModel = config('nested-comments.models.user', config('auth.providers.users.model', 'App\\Models\\User')); + + return $userModel::query() + ->where('name', 'like', "%{$query}%") + ->orWhere('email', 'like', "%{$query}%"); + } + public function getCurrentThreadUsers(string $searchQuery, $commentable): mixed { $userModel = config('nested-comments.models.user', config('auth.providers.users.model', 'App\\Models\\User'));