Skip to content

Commit 9a8b41f

Browse files
authored
Merge pull request #15 from TappNetwork/add_forum_post_reaction
Add forum post reaction
2 parents d877141 + 5299f9f commit 9a8b41f

17 files changed

Lines changed: 547 additions & 68 deletions

config/filament-forum.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,15 @@
4646
'slug' => 'forum-posts',
4747
],
4848

49+
'reactions' => [
50+
'available' => [
51+
'👍' => 'Like',
52+
'❤️' => 'Love',
53+
'😂' => 'Laugh',
54+
'😮' => 'Wow',
55+
'😢' => 'Sad',
56+
'😡' => 'Angry',
57+
],
58+
],
59+
4960
];
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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(): void
10+
{
11+
Schema::create('forum_post_reactions', function (Blueprint $table) {
12+
$table->id();
13+
14+
// Add tenant relationship if tenancy is enabled
15+
if (config('filament-forum.tenancy.enabled')) {
16+
$tenantModel = config('filament-forum.tenancy.model');
17+
$table->foreignIdFor($tenantModel)
18+
->constrained()
19+
->cascadeOnDelete();
20+
}
21+
22+
$table->foreignId('forum_post_id')->constrained()->cascadeOnDelete();
23+
$table->morphs('reactor');
24+
25+
if (config('database.default') === 'mysql') {
26+
$table->string('type', 50)->collation('utf8mb4_bin');
27+
} else {
28+
$table->string('type', 50);
29+
}
30+
31+
$table->timestamps();
32+
33+
$table->unique(['forum_post_id', 'reactor_type', 'reactor_id', 'type'], 'unique_post_reaction');
34+
$table->index(['forum_post_id', 'type']);
35+
});
36+
}
37+
38+
public function down(): void
39+
{
40+
Schema::dropIfExists('forum_post_reactions');
41+
}
42+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
@php
2+
$record = $getRecord();
3+
@endphp
4+
5+
<div class="fi-in-entry-wrp">
6+
@livewire('tapp.filament-forum.forum-post-reactions', [
7+
'post' => $record,
8+
], key('post-reactions-' . $record->id))
9+
</div>

resources/views/filament/tables/components/forum-post-card-column.blade.php

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -68,33 +68,40 @@ class="w-6 h-6 text-primary-500"
6868
</div>
6969
</a>
7070

71-
{{-- Footer with stats and comment authors --}}
71+
{{-- Footer with stats, reactions and comment authors --}}
7272
<div class="flex items-center justify-between mt-4 pt-4 border-t border-gray-100">
7373
<div class="text-sm text-gray-500">
7474
@php
7575
$viewsCount = $record->views_count ?? 0;
7676
$replies = $record->comments()->count();
7777
@endphp
78-
78+
7979
{{ trans_choice('filament-forum::filament-forum.forum-post.views', $viewsCount, ['value' => $viewsCount]) }} &bull; {{ trans_choice('filament-forum::filament-forum.forum-post.replies', $replies, ['value' => $replies]) }}
8080
</div>
8181

82-
{{-- Comment Authors --}}
83-
<div class="flex items-center -space-x-2">
84-
@foreach($record->getCommentAuthors() as $author)
85-
<img
86-
src="{{ $author->profile_photo ?? 'https://ui-avatars.com/api/?name=' . urlencode($author->name) }}"
87-
alt="{{ $author->name }}"
88-
class="w-8 h-8 rounded-full border-2 border-white"
89-
>
90-
@endforeach
91-
@if($record->getCountDistinctUsersWhoCommented() > 3)
92-
<div class="flex items-center justify-center w-8 h-8 rounded-full bg-gray-100 border-2 border-white">
93-
<span class="text-xs text-gray-500 font-medium">
94-
+{{ $record->getCountDistinctUsersWhoCommented() - 3 }}
95-
</span>
96-
</div>
97-
@endif
82+
<div class="flex items-center gap-3">
83+
{{-- Post Reactions --}}
84+
<div>
85+
@livewire('tapp.filament-forum.forum-post-reactions', ['post' => $record], key('post-reactions-' . $record->id))
86+
</div>
87+
88+
{{-- Comment Authors --}}
89+
<div class="flex items-center -space-x-2">
90+
@foreach($record->getCommentAuthors() as $author)
91+
<img
92+
src="{{ $author->profile_photo ?? 'https://ui-avatars.com/api/?name=' . urlencode($author->name) }}"
93+
alt="{{ $author->name }}"
94+
class="w-8 h-8 rounded-full border-2 border-white"
95+
>
96+
@endforeach
97+
@if($record->getCountDistinctUsersWhoCommented() > 3)
98+
<div class="flex items-center justify-center w-8 h-8 rounded-full bg-gray-100 border-2 border-white">
99+
<span class="text-xs text-gray-500 font-medium">
100+
+{{ $record->getCountDistinctUsersWhoCommented() - 3 }}
101+
</span>
102+
</div>
103+
@endif
104+
</div>
98105
</div>
99106
</div>
100107
</div>

resources/views/livewire/forum-comment-reactions.blade.php

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,55 @@
1-
<div class="flex items-center justify-between">
2-
{{-- Existing Reactions Display --}}
3-
<div class="flex items-center space-x-1">
4-
@foreach($reactionCounts as $type => $count)
5-
@if($count > 0)
6-
<button
7-
wire:click="toggleReaction('{{ $type }}')"
8-
@class([
9-
'flex items-center space-x-1 px-2 py-1 rounded-full text-sm transition-colors border',
10-
'bg-blue-100 text-blue-700 border-blue-200 dark:bg-blue-900 dark:text-blue-300 dark:border-blue-700' => in_array($type, $userReactions),
11-
'hover:bg-gray-100 text-gray-600 border-gray-200 dark:hover:bg-gray-700 dark:text-gray-400 dark:border-gray-600' => !in_array($type, $userReactions),
12-
])
13-
@auth
14-
title="{{ $availableReactions[$type] ?? $type }}"
15-
@else
16-
disabled
17-
title="{{ __('filament-forum::filament-forum.comments.login-to-react') }}"
18-
@endauth
19-
>
20-
<span>{{ $type }}</span>
21-
<span class="text-xs font-medium">{{ $count }}</span>
22-
</button>
23-
@endif
24-
@endforeach
25-
</div>
1+
<div class="flex items-center gap-1">
2+
{{-- Existing Reactions Display (clickable to toggle) --}}
3+
@foreach($reactionCounts as $type => $count)
4+
@if($count > 0)
5+
<button
6+
wire:click="toggleReaction('{{ $type }}')"
7+
@class([
8+
'flex items-center gap-0.5 px-1.5 py-0.5 rounded-full text-sm transition-colors border',
9+
'bg-blue-100 text-blue-700 border-blue-200 dark:bg-blue-900 dark:text-blue-300 dark:border-blue-700' => in_array($type, $userReactions),
10+
'hover:bg-gray-100 text-gray-600 border-gray-200 dark:hover:bg-gray-700 dark:text-gray-400 dark:border-gray-600' => !in_array($type, $userReactions),
11+
])
12+
@auth
13+
x-tooltip.raw="{{ $availableReactions[$type] ?? $type }}"
14+
@else
15+
disabled
16+
x-tooltip.raw="{{ __('filament-forum::filament-forum.comments.login-to-react') }}"
17+
@endauth
18+
>
19+
<span>{{ $type }}</span>
20+
<span class="text-xs font-medium">{{ $count }}</span>
21+
</button>
22+
@endif
23+
@endforeach
2624

27-
{{-- Add Reaction Button --}}
25+
{{-- Smiley icon to open reaction picker --}}
2826
<div class="relative" x-data="{ open: @entangle('showReactionPicker') }" x-on:click.outside="open = false">
2927
@auth
3028
<button
3129
wire:click="toggleReactionPicker"
32-
class="flex items-center space-x-1 px-2 py-1 rounded-full text-sm transition-colors hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-400"
33-
title="{{ __('filament-forum::filament-forum.comments.add-reaction') }}"
30+
class="flex items-center justify-center w-7 h-7 rounded-full transition-colors hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300"
31+
x-tooltip.raw="{{ __('filament-forum::filament-forum.comments.add-reaction') }}"
3432
>
35-
<span class="text-lg">😊</span>
36-
<span class="text-xs">{{ __('filament-forum::filament-forum.comments.add-reaction') }}</span>
33+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" class="w-5 h-5">
34+
<path stroke-linecap="round" stroke-linejoin="round" d="M15.182 15.182a4.5 4.5 0 0 1-6.364 0M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0ZM9.75 9.75c0 .414-.168.75-.375.75S9 10.164 9 9.75 9.168 9 9.375 9s.375.336.375.75Zm-.375 0h.008v.015h-.008V9.75Zm5.625 0c0 .414-.168.75-.375.75s-.375-.336-.375-.75.168-.75.375-.75.375.336.375.75Zm-.375 0h.008v.015h-.008V9.75Z" />
35+
</svg>
3736
</button>
3837

3938
{{-- Reaction Picker --}}
40-
<div x-show="open"
39+
<div x-show="open"
4140
x-transition:enter="transition ease-out duration-100"
4241
x-transition:enter-start="transform opacity-0 scale-95"
4342
x-transition:enter-end="transform opacity-100 scale-100"
4443
x-transition:leave="transition ease-in duration-75"
4544
x-transition:leave-start="transform opacity-100 scale-100"
4645
x-transition:leave-end="transform opacity-0 scale-95"
47-
class="absolute bottom-full left-0 mb-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-lg p-2 z-10">
46+
class="absolute bottom-full right-0 mb-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-lg p-2 z-10">
4847
<div class="flex space-x-1">
4948
@foreach($availableReactions as $emoji => $label)
5049
<button
5150
wire:click="toggleReaction('{{ $emoji }}')"
5251
class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
53-
title="{{ $label }}"
52+
x-tooltip.raw="{{ $label }}"
5453
>
5554
<span class="text-lg">{{ $emoji }}</span>
5655
</button>
@@ -60,11 +59,12 @@ class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
6059
@else
6160
<button
6261
disabled
63-
class="flex items-center space-x-1 px-2 py-1 rounded-full text-sm text-gray-400 cursor-not-allowed"
64-
title="{{ __('filament-forum::filament-forum.comments.login-to-react') }}"
62+
class="flex items-center justify-center w-7 h-7 rounded-full text-gray-300 cursor-not-allowed dark:text-gray-600"
63+
x-tooltip.raw="{{ __('filament-forum::filament-forum.comments.login-to-react') }}"
6564
>
66-
<span class="text-lg">😊</span>
67-
<span class="text-xs">{{ __('filament-forum::filament-forum.comments.add-reaction') }}</span>
65+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" class="w-5 h-5">
66+
<path stroke-linecap="round" stroke-linejoin="round" d="M15.182 15.182a4.5 4.5 0 0 1-6.364 0M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0ZM9.75 9.75c0 .414-.168.75-.375.75S9 10.164 9 9.75 9.168 9 9.375 9s.375.336.375.75Zm-.375 0h.008v.015h-.008V9.75Zm5.625 0c0 .414-.168.75-.375.75s-.375-.336-.375-.75.168-.75.375-.75.375.336.375.75Zm-.375 0h.008v.015h-.008V9.75Z" />
67+
</svg>
6868
</button>
6969
@endauth
7070
</div>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<div class="flex items-center gap-1">
2+
{{-- Existing Reactions Display (clickable to toggle) --}}
3+
@foreach($reactionCounts as $type => $count)
4+
@if($count > 0)
5+
<button
6+
wire:click="toggleReaction('{{ $type }}')"
7+
@class([
8+
'flex items-center gap-0.5 px-1.5 py-0.5 rounded-full text-sm transition-colors border',
9+
'bg-blue-100 text-blue-700 border-blue-200 dark:bg-blue-900 dark:text-blue-300 dark:border-blue-700' => in_array($type, $userReactions),
10+
'hover:bg-gray-100 text-gray-600 border-gray-200 dark:hover:bg-gray-700 dark:text-gray-400 dark:border-gray-600' => !in_array($type, $userReactions),
11+
])
12+
@auth
13+
x-tooltip.raw="{{ $availableReactions[$type] ?? $type }}"
14+
@else
15+
disabled
16+
x-tooltip.raw="{{ __('filament-forum::filament-forum.comments.login-to-react') }}"
17+
@endauth
18+
>
19+
<span>{{ $type }}</span>
20+
<span class="text-xs font-medium">{{ $count }}</span>
21+
</button>
22+
@endif
23+
@endforeach
24+
25+
{{-- Smiley icon to open reaction picker --}}
26+
<div class="relative" x-data="{ open: @entangle('showReactionPicker') }" x-on:click.outside="open = false">
27+
@auth
28+
<button
29+
wire:click="toggleReactionPicker"
30+
class="flex items-center justify-center w-7 h-7 rounded-full transition-colors hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300"
31+
x-tooltip.raw="{{ __('filament-forum::filament-forum.comments.add-reaction') }}"
32+
>
33+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" class="w-5 h-5">
34+
<path stroke-linecap="round" stroke-linejoin="round" d="M15.182 15.182a4.5 4.5 0 0 1-6.364 0M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0ZM9.75 9.75c0 .414-.168.75-.375.75S9 10.164 9 9.75 9.168 9 9.375 9s.375.336.375.75Zm-.375 0h.008v.015h-.008V9.75Zm5.625 0c0 .414-.168.75-.375.75s-.375-.336-.375-.75.168-.75.375-.75.375.336.375.75Zm-.375 0h.008v.015h-.008V9.75Z" />
35+
</svg>
36+
</button>
37+
38+
{{-- Reaction Picker --}}
39+
<div x-show="open"
40+
x-transition:enter="transition ease-out duration-100"
41+
x-transition:enter-start="transform opacity-0 scale-95"
42+
x-transition:enter-end="transform opacity-100 scale-100"
43+
x-transition:leave="transition ease-in duration-75"
44+
x-transition:leave-start="transform opacity-100 scale-100"
45+
x-transition:leave-end="transform opacity-0 scale-95"
46+
class="absolute bottom-full right-0 mb-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-lg p-2 z-10">
47+
<div class="flex space-x-1">
48+
@foreach($availableReactions as $emoji => $label)
49+
<button
50+
wire:click="toggleReaction('{{ $emoji }}')"
51+
class="p-2 rounded hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
52+
x-tooltip.raw="{{ $label }}"
53+
>
54+
<span class="text-lg">{{ $emoji }}</span>
55+
</button>
56+
@endforeach
57+
</div>
58+
</div>
59+
@else
60+
<button
61+
disabled
62+
class="flex items-center justify-center w-7 h-7 rounded-full text-gray-300 cursor-not-allowed dark:text-gray-600"
63+
x-tooltip.raw="{{ __('filament-forum::filament-forum.comments.login-to-react') }}"
64+
>
65+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" class="w-5 h-5">
66+
<path stroke-linecap="round" stroke-linejoin="round" d="M15.182 15.182a4.5 4.5 0 0 1-6.364 0M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0ZM9.75 9.75c0 .414-.168.75-.375.75S9 10.164 9 9.75 9.168 9 9.375 9s.375.336.375.75Zm-.375 0h.008v.015h-.008V9.75Zm5.625 0c0 .414-.168.75-.375.75s-.375-.336-.375-.75.168-.75.375-.75.375.336.375.75Zm-.375 0h.008v.015h-.008V9.75Z" />
67+
</svg>
68+
</button>
69+
@endauth
70+
</div>
71+
</div>

src/Events/PostWasReacted.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace Tapp\FilamentForum\Events;
4+
5+
use Illuminate\Broadcasting\InteractsWithSockets;
6+
use Illuminate\Contracts\Auth\Authenticatable;
7+
use Illuminate\Database\Eloquent\Model;
8+
use Illuminate\Foundation\Events\Dispatchable;
9+
use Illuminate\Queue\SerializesModels;
10+
use Tapp\FilamentForum\Models\ForumPost;
11+
use Tapp\FilamentForum\Models\ForumPostReaction;
12+
13+
class PostWasReacted
14+
{
15+
use Dispatchable;
16+
use InteractsWithSockets;
17+
use SerializesModels;
18+
19+
/**
20+
* Create a new event instance.
21+
*/
22+
public function __construct(
23+
public Model|Authenticatable $reactor,
24+
public ForumPost $post,
25+
public ForumPostReaction $reaction,
26+
) {}
27+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Tapp\FilamentForum\Filament\Infolists\Components;
4+
5+
use Filament\Infolists\Components\Entry;
6+
7+
class ForumPostReactionsEntry extends Entry
8+
{
9+
protected string $view = 'filament-forum::filament.infolists.components.forum-post-reactions-entry';
10+
}

src/Filament/Resources/ForumPosts/Schemas/ForumPostInfolist.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Filament\Schemas\Components\Section;
1010
use Filament\Schemas\Schema;
1111
use Tapp\FilamentForum\Filament\Infolists\Components\ForumCommentsEntry;
12+
use Tapp\FilamentForum\Filament\Infolists\Components\ForumPostReactionsEntry;
1213
use Tapp\FilamentForum\Models\ForumPost;
1314

1415
class ForumPostInfolist
@@ -65,6 +66,9 @@ public static function configure(Schema $schema): Schema
6566
->columnSpanFull(),
6667
]),
6768

69+
ForumPostReactionsEntry::make('reactions')
70+
->hiddenLabel(),
71+
6872
Group::make()
6973
->schema([
7074
ForumCommentsEntry::make('comments')

src/Filament/Resources/ForumPosts/Tables/ForumPostsTable.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public static function configure(Table $table): Table
2020
return $table
2121
->recordUrl(null)
2222
->modifyQueryUsing(function (Builder $query) {
23-
return $query->with(['user', 'forum']);
23+
return $query->with(['user', 'forum', 'reactions']);
2424
})
2525
->columns([
2626
Stack::make([
@@ -35,9 +35,6 @@ public static function configure(Table $table): Table
3535
'default' => 1,
3636
'sm' => 1,
3737
])
38-
->filters([
39-
//
40-
])
4138
->recordActions([
4239
ActionGroup::make([
4340
EditAction::make()

0 commit comments

Comments
 (0)