Skip to content

Commit bdb04e9

Browse files
committed
Only display reacted emojis, added sub-component, fixed re-render issue
1 parent 10c5e9e commit bdb04e9

File tree

9 files changed

+208
-93
lines changed

9 files changed

+208
-93
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
public function up()
10+
{
11+
Schema::create('comment_reactions', function (Blueprint $table) {
12+
$table->id();
13+
$table->foreignId('comment_id')->constrained(config('commentions.table_name'))->cascadeOnDelete();
14+
$table->morphs('reactor');
15+
$table->string('reaction', 50);
16+
$table->timestamps();
17+
18+
$table->unique(['comment_id', 'reactor_id', 'reactor_type', 'reaction'], 'comment_reactor_reaction_unique');
19+
});
20+
}
21+
22+
public function down(): void
23+
{
24+
Schema::dropIfExists('comment_reactions');
25+
}
26+
};

database/migrations/create_commentions_tables.php.stub

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,10 @@ return new class extends Migration
1515
$table->text('body');
1616
$table->timestamps();
1717
});
18-
19-
Schema::create('comment_reactions', function (Blueprint $table) {
20-
$table->id();
21-
$table->foreignId('comment_id')->constrained(config('commentions.table_name'))->cascadeOnDelete();
22-
$table->morphs('reactor');
23-
$table->string('reaction', 50);
24-
$table->timestamps();
25-
26-
$table->unique(['comment_id', 'reactor_id', 'reactor_type', 'reaction'], 'comment_reactor_reaction_unique');
27-
});
2818
}
2919

3020
public function down(): void
3121
{
32-
Schema::dropIfExists('comment_reactions');
3322
Schema::dropIfExists(config('commentions.table_name'));
3423
}
3524
};

resources/views/comment.blade.php

Lines changed: 5 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -82,48 +82,11 @@ class="text-xs text-gray-300 ml-1"
8282
<div class="mt-1 space-y-6 text-sm text-gray-800 dark:text-gray-200">{!! $comment->getParsedBody() !!}</div>
8383

8484
@if ($comment->isComment() && $comment instanceof \Kirschbaum\Commentions\Comment)
85-
<div class="mt-2 pt-2 border-t border-gray-200 dark:border-gray-700 flex items-center gap-x-1 flex-wrap">
86-
@php
87-
$allowedReactions = \Kirschbaum\Commentions\Config::getAllowedReactions();
88-
@endphp
89-
90-
{{-- Buttons for adding/removing reactions --}}
91-
@foreach ($allowedReactions as $reactionEmoji)
92-
@php
93-
$reactionData = $this->reactionSummary[$reactionEmoji] ?? ['count' => 0, 'reacted_by_current_user' => false];
94-
@endphp
95-
<button
96-
wire:click="toggleReaction('{{ $reactionEmoji }}')"
97-
type="button"
98-
@disabled(! auth()->check())
99-
class="inline-flex items-center justify-center gap-1 rounded-full border px-2 py-1 text-sm font-medium transition hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed
100-
{{ $reactionData['reacted_by_current_user']
101-
? 'bg-primary-100 dark:bg-primary-800 border-primary-300 dark:border-primary-600 text-primary-700 dark:text-primary-200'
102-
: 'bg-white dark:bg-gray-900 border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-200' }}"
103-
title="{{ $reactionEmoji }}"
104-
wire:key="reaction-button-{{ $reactionEmoji }}-{{ $comment->getId() }}"
105-
>
106-
<span>{{ $reactionEmoji }}</span>
107-
@if ($reactionData['count'] > 0)
108-
<span wire:key="reaction-count-{{ $reactionEmoji }}-{{ $comment->getId() }}">{{ $reactionData['count'] }}</span>
109-
@endif
110-
</button>
111-
@endforeach
112-
113-
{{-- Display summary of reactions not explicitly in the allowed list --}}
114-
@foreach ($this->reactionSummary as $reactionEmoji => $data)
115-
@if (!in_array($reactionEmoji, $allowedReactions) && $data['count'] > 0)
116-
<span
117-
wire:key="reaction-extra-{{ $reactionEmoji }}-{{ $comment->getId() }}"
118-
class="inline-flex items-center justify-center gap-1 rounded-full border border-gray-300 dark:border-gray-600 bg-gray-100 dark:bg-gray-800 px-2 py-1 text-sm font-medium text-gray-600 dark:text-gray-300"
119-
title="{{ $reactionEmoji }}"
120-
>
121-
<span>{{ $reactionEmoji }}</span>
122-
<span>{{ $data['count'] }}</span>
123-
</span>
124-
@endif
125-
@endforeach
126-
</div>
85+
<livewire:commentions::reaction-manager
86+
:comment="$comment"
87+
{{-- :reaction-summary="$this->reactionSummary" --}}
88+
:wire:key="'reaction-manager-' . $comment->getId()"
89+
/>
12790
@endif
12891
@endif
12992
</div>
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<div class="relative mt-2 pt-2 border-t border-gray-200 dark:border-gray-700 flex items-center gap-x-1 flex-wrap">
2+
{{-- Inline buttons for existing reactions --}}
3+
@foreach ($this->reactionSummary as $reactionData)
4+
<span wire:key="inline-reaction-button-{{ $reactionData['reaction'] }}-{{ $comment->getId() }}">
5+
<button
6+
x-cloak
7+
wire:click="handleReactionToggle('{{ $reactionData['reaction'] }}')"
8+
type="button"
9+
class="inline-flex items-center justify-center gap-1 rounded-full border px-2 py-1 text-sm font-medium transition hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed
10+
{{ $reactionData['reacted_by_current_user']
11+
? 'bg-primary-100 dark:bg-primary-800 border-primary-300 dark:border-primary-600 text-primary-700 dark:text-primary-200'
12+
: 'bg-white dark:bg-gray-900 border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-200' }}"
13+
title="{{ $reactionData['reaction'] }}"
14+
15+
>
16+
<span>{{ $reactionData['reaction'] }}</span>
17+
<span wire:key="inline-reaction-count-{{ $reactionData['reaction'] }}-{{ $comment->getId() }}">{{ $reactionData['count'] }}</span>
18+
</button>
19+
</span>
20+
@endforeach
21+
22+
{{-- Add Reaction Button --}}
23+
<div class="relative" x-data="{ open: false }" wire:ignore.self>
24+
<button
25+
x-on:click="open = !open"
26+
type="button"
27+
@disabled(! auth()->check())
28+
class="inline-flex items-center justify-center gap-1 rounded-full border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-900 px-2 py-1 text-sm font-medium text-gray-700 dark:text-gray-200 transition hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed"
29+
title="Add Reaction"
30+
wire:key="add-reaction-button-{{ $comment->getId() }}"
31+
>
32+
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
33+
<path stroke-linecap="round" stroke-linejoin="round" d="M14.828 14.828a4 4 0 01-5.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
34+
</svg>
35+
</button>
36+
37+
{{-- Reaction Popup --}}
38+
<div
39+
x-show="open"
40+
x-cloak
41+
x-on:click.away="open = false"
42+
class="absolute bottom-full mb-2 z-10 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg shadow-lg p-2 flex flex-wrap gap-1 w-max max-w-xs"
43+
>
44+
@foreach ($allowedReactions as $reactionEmoji)
45+
@php
46+
$reactionData = $this->reactionSummary[$reactionEmoji] ?? ['count' => 0, 'reacted_by_current_user' => false];
47+
@endphp
48+
49+
<button
50+
wire:click="handleReactionToggle('{{ $reactionEmoji }}')"
51+
x-on:click="open = false"
52+
type="button"
53+
@disabled(! auth()->check())
54+
class="inline-flex items-center justify-center gap-1 rounded-full border px-2 py-1 text-sm font-medium transition hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed
55+
{{ $reactionData['reacted_by_current_user']
56+
? 'bg-primary-100 dark:bg-primary-800 border-primary-300 dark:border-primary-600 text-primary-700 dark:text-primary-200'
57+
: 'bg-white dark:bg-gray-900 border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-200' }}"
58+
title="{{ $reactionEmoji }}"
59+
wire:key="popup-reaction-button-{{ $reactionEmoji }}-{{ $comment->getId() }}"
60+
>
61+
<span>{{ $reactionEmoji }}</span>
62+
</button>
63+
@endforeach
64+
</div>
65+
</div>
66+
67+
{{-- Display summary of reactions not explicitly in the allowed list --}}
68+
@foreach ($this->reactionSummary as $reactionEmoji => $data)
69+
@if (! in_array($reactionEmoji, $allowedReactions) && $data['count'] > 0)
70+
<span
71+
wire:key="reaction-extra-{{ $reactionEmoji }}-{{ $comment->getId() }}"
72+
class="inline-flex items-center justify-center gap-1 rounded-full border border-gray-300 dark:border-gray-600 bg-gray-100 dark:bg-gray-800 px-2 py-1 text-sm font-medium text-gray-600 dark:text-gray-300"
73+
title="{{ $reactionEmoji }}"
74+
>
75+
<span>{{ $reactionEmoji }}</span>
76+
<span>{{ $data['count'] }}</span>
77+
</span>
78+
@endif
79+
@endforeach
80+
</div>

src/CommentionsServiceProvider.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22

33
namespace Kirschbaum\Commentions;
44

5-
use Filament\Support\Assets\Css;
5+
use Livewire\Livewire;
66
use Filament\Support\Assets\Js;
7+
use Filament\Support\Assets\Css;
8+
use Spatie\LaravelPackageTools\Package;
79
use Filament\Support\Facades\FilamentAsset;
810
use Kirschbaum\Commentions\Livewire\Comment;
9-
use Kirschbaum\Commentions\Livewire\CommentList;
1011
use Kirschbaum\Commentions\Livewire\Comments;
11-
use Livewire\Livewire;
12-
use Spatie\LaravelPackageTools\Package;
12+
use Kirschbaum\Commentions\Livewire\CommentList;
13+
use Kirschbaum\Commentions\Livewire\ReactionManager;
1314
use Spatie\LaravelPackageTools\PackageServiceProvider;
1415

1516
class CommentionsServiceProvider extends PackageServiceProvider
@@ -27,15 +28,18 @@ public function configurePackage(Package $package): void
2728
->name(static::$name)
2829
->hasConfigFile()
2930
->hasViews()
30-
->hasMigration('create_commention_tables');
31+
->hasMigrations([
32+
'create_commentions_tables',
33+
'create_commentions_reactions_table',
34+
]);
3135
}
3236

3337
public function packageBooted(): void
3438
{
3539
Livewire::component('commentions::comment', Comment::class);
3640
Livewire::component('commentions::comment-list', CommentList::class);
3741
Livewire::component('commentions::comments', Comments::class);
38-
42+
Livewire::component('commentions::reaction-manager', ReactionManager::class);
3943
// Asset Registration
4044
FilamentAsset::register(
4145
[

src/Livewire/Comment.php

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
use Livewire\Attributes\Computed;
1212
use Livewire\Attributes\On;
1313
use Livewire\Attributes\Renderless;
14+
use Livewire\Attributes\Locked;
1415
use Livewire\Component;
16+
use Illuminate\Contracts\View\View;
1517

1618
class Comment extends Component
1719
{
@@ -29,6 +31,16 @@ class Comment extends Component
2931
'commentBody' => 'required|string',
3032
];
3133

34+
#[On('comment:reaction:toggled')]
35+
public function handleReactionToggledEvent(string $reaction, int $commentId): void
36+
{
37+
if ($this->comment->getId() !== $commentId) {
38+
return;
39+
}
40+
41+
$this->toggleReaction($reaction);
42+
}
43+
3244
#[Renderless]
3345
public function delete()
3446
{
@@ -47,7 +59,7 @@ public function delete()
4759
->send();
4860
}
4961

50-
public function render()
62+
public function render(): View
5163
{
5264
return view('commentions::comment');
5365
}
@@ -98,7 +110,7 @@ public function cancelEditing()
98110
$this->commentBody = '';
99111
}
100112

101-
#[Renderless]
113+
// #[Renderless]
102114
public function toggleReaction(string $reaction): void
103115
{
104116
$user = Config::resolveAuthenticatedUser();
@@ -111,6 +123,10 @@ public function toggleReaction(string $reaction): void
111123
return;
112124
}
113125

126+
if (! $this->comment instanceof CommentModel) {
127+
return;
128+
}
129+
114130
/** @var CommentReaction $existingReaction */
115131
$existingReaction = $this->comment
116132
->reactions()
@@ -129,32 +145,6 @@ public function toggleReaction(string $reaction): void
129145
]);
130146
}
131147

132-
$this->comment->refresh();
133-
$this->dispatch('comment:reactions-updated');
134-
}
135-
136-
#[Computed]
137-
public function reactionSummary()
138-
{
139-
if (! $this->comment instanceof CommentModel) {
140-
return [];
141-
}
142-
143-
if (! $this->comment->relationLoaded('reactions')) {
144-
$this->comment->load('reactions.reactor');
145-
}
146-
147-
return $this->comment->reactions
148-
->groupBy('reaction')
149-
->map(function ($group) {
150-
$user = Config::resolveAuthenticatedUser();
151-
152-
return [
153-
'count' => $group->count(),
154-
'reacted_by_current_user' => $user && $group->contains(fn ($reaction) => $reaction->reactor_id == $user->getKey() && $reaction->reactor_type == $user->getMorphClass()),
155-
];
156-
})
157-
->sortByDesc('count')
158-
->toArray();
148+
$this->dispatch('comment:reaction:saved');
159149
}
160150
}

src/Livewire/CommentList.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public function render()
2525
#[Computed]
2626
public function comments(): Collection
2727
{
28+
info('Getting comments');
2829
return $this->record->getComments();
2930
}
3031

@@ -34,6 +35,7 @@ public function comments(): Collection
3435
#[On('comment:reactions-updated')]
3536
public function reloadComments(): void
3637
{
38+
info('Reloading comments');
3739
unset($this->comments);
3840
}
3941
}

src/Livewire/ReactionManager.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
namespace Kirschbaum\Commentions\Livewire;
4+
5+
use Livewire\Component;
6+
use Livewire\Attributes\On;
7+
use Livewire\Attributes\Computed;
8+
use Kirschbaum\Commentions\Config;
9+
use Illuminate\Contracts\View\View;
10+
use Kirschbaum\Commentions\Comment as CommentModel;
11+
use Kirschbaum\Commentions\Contracts\RenderableComment;
12+
13+
class ReactionManager extends Component
14+
{
15+
public RenderableComment $comment;
16+
17+
public function handleReactionToggle(string $reaction): void
18+
{
19+
$this->dispatch(
20+
'comment:reaction:toggled',
21+
reaction: $reaction,
22+
commentId: $this->comment->getId()
23+
)->to(Comment::class);
24+
25+
unset($this->reactionSummary);
26+
}
27+
28+
public function render(): View
29+
{
30+
return view('commentions::reaction-manager', [
31+
'allowedReactions' => Config::getAllowedReactions(),
32+
]);
33+
}
34+
35+
#[On('comment:reaction:saved')]
36+
public function refreshReactionSummary()
37+
{
38+
unset($this->reactionSummary);
39+
}
40+
41+
#[Computed]
42+
public function reactionSummary()
43+
{
44+
if (! $this->comment instanceof CommentModel) {
45+
return [];
46+
}
47+
48+
if (! $this->comment->relationLoaded('reactions')) {
49+
$this->comment->load('reactions.reactor');
50+
}
51+
52+
return $this->comment->reactions
53+
->groupBy('reaction')
54+
->map(function ($group) {
55+
$user = Config::resolveAuthenticatedUser();
56+
57+
return [
58+
'count' => $group->count(),
59+
'reaction' => $group->first()->reaction,
60+
'reacted_by_current_user' => $user && $group->contains(fn ($reaction) => $reaction->reactor_id == $user->getKey() && $reaction->reactor_type == $user->getMorphClass()),
61+
];
62+
})
63+
->sortByDesc('count')
64+
->toArray();
65+
}
66+
}

0 commit comments

Comments
 (0)