Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,36 @@ By default, Commentions ships with the following reactions: `['👍', '❤️',
],
```

#### Comment Ratings

Commentions can attach an optional star rating to a comment, review-style. Ratings are **disabled by default**.

Enable them globally in your `config/commentions.php` file (or via the matching environment variables):

```php
'ratings' => [
'enabled' => env('COMMENTIONS_RATINGS_ENABLED', false),

'max' => (int) env('COMMENTIONS_RATINGS_MAX', 5),
],
```

You can also enable ratings per component, which overrides the global config. This works on `CommentsEntry`, `CommentsAction`, and `CommentsTableAction`:

```php
CommentsEntry::make('comments')
->enableRatings()
->maxRating(10)
```

Available methods:

- `enableRatings(bool|Closure $condition = true)` — enable the rating input for this component.
- `disableRatings()` — disable the rating input, even if enabled globally.
- `maxRating(int|Closure $max)` — set the highest selectable rating (defaults to `ratings.max`).

When ratings are enabled, commenters can pick a rating while writing or editing a comment, and each rated comment renders its score as filled stars. The rating is stored in a nullable `rating` column added by the package's `add_rating_to_commentions_comments_table` migration.

### Configuring the Commenter name

By default, the `name` property will be used to render the mention names. You can customize it either by implementing the Filament `HasName` interface OR by implementing the optional `getCommenterName` method.
Expand Down
16 changes: 16 additions & 0 deletions config/commentions.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,22 @@
'allowed' => ['👍', '❤️', '😂', '😮', '😢', '🤔'],
],

/*
|--------------------------------------------------------------------------
| Ratings
|--------------------------------------------------------------------------
|
| Allow a star rating to be attached to a comment. Disabled by default;
| enable globally here or per component with
| CommentsEntry::make()->enableRatings().
|
*/
'ratings' => [
'enabled' => env('COMMENTIONS_RATINGS_ENABLED', false),

'max' => (int) env('COMMENTIONS_RATINGS_MAX', 5),
],

/*
|--------------------------------------------------------------------------
| Subscriptions
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
public function up(): void
{
Schema::table(config('commentions.tables.comments', 'comments'), function (Blueprint $table) {
$table->unsignedTinyInteger('rating')->nullable()->after('body');
});
}

public function down(): void
{
Schema::table(config('commentions.tables.comments', 'comments'), function (Blueprint $table) {
$table->dropColumn('rating');
});
}
};
1 change: 0 additions & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ parameters:
- tests/

excludePaths:
- src/Filament/Actions/CommentsTableAction.php
- src/Filament/Actions/SubscriptionTableAction.php

# Level 10 is the highest level
Expand Down
9 changes: 9 additions & 0 deletions resources/css/commentions.css
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,12 @@
.mention-item:hover {
@apply comm:bg-gray-100;
}

.commentions-rating-star {
@apply comm:flex comm:cursor-pointer comm:items-center comm:justify-center comm:text-gray-300 comm:dark:text-gray-600;
}

.commentions-rating-star:hover,
.commentions-rating-star-active {
@apply comm:text-amber-400;
}
2 changes: 1 addition & 1 deletion resources/dist/commentions.css

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions resources/lang/en/comments.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
'add_reaction' => 'Add Reaction',
'show_more' => 'Show More',

'rating_input_label' => 'Rating',
'rate_stars' => 'Rate :count star|Rate :count stars',
'rating_display_label' => 'Rated :rating out of :max',

'notifications' => 'Notifications',
'unsubscribe' => 'Unsubscribe',
'subscribe' => 'Subscribe',
Expand Down
2 changes: 2 additions & 0 deletions resources/views/comment-list.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class="comm:w-8 comm:h-8 comm:text-gray-400 comm:dark:text-gray-500"
:comment="$comment"
:mentionables="$mentionables"
:tip-tap-css-classes="$tipTapCssClasses"
:ratings-enabled="$ratingsEnabled"
:max-rating="$maxRating"
/>
@endforeach

Expand Down
24 changes: 24 additions & 0 deletions resources/views/comment.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ class="comm:text-xs comm:text-gray-300 comm:ml-1"

@if ($editing)
<div class="comm:mt-2">
@if ($this->ratingsAreEnabled())
@include('commentions::partials.rating-input', ['maxRating' => $this->getMaxRating()])
@endif

<div class="tip-tap-container comm:mb-2" wire:ignore>
<div x-data="editor(@js($commentBody), @js($mentionables), 'comment', null, @js($this->getTipTapCssClasses()), @js($commentionsComponentPrefix . 'comment'))">
<div x-ref="element"></div>
Expand All @@ -115,6 +119,26 @@ class="comm:text-xs comm:text-gray-300 comm:ml-1"
</div>
</div>
@else
@if ($comment->isComment() && $comment->rating)
<div
class="comm:mt-1 comm:flex comm:items-center comm:gap-0.5"
role="img"
title="{{ $comment->rating }}/{{ $this->getMaxRating() }}"
aria-label="{{ __('commentions::comments.rating_display_label', ['rating' => $comment->rating, 'max' => $this->getMaxRating()]) }}"
>
@for ($ratingStar = 1; $ratingStar <= $this->getMaxRating(); $ratingStar++)
<x-filament::icon
icon="heroicon-s-star"
@class([
'comm:h-4 comm:w-4',
'comm:text-amber-400' => $ratingStar <= $comment->rating,
'comm:text-gray-300 comm:dark:text-gray-600' => $ratingStar > $comment->rating,
])
/>
@endfor
</div>
@endif

<div class="comm:mt-1 comm:space-y-6 comm:text-sm comm:text-gray-800 comm:dark:text-gray-200">{!! $comment->getParsedBody() !!}</div>

@if ($comment->isComment())
Expand Down
2 changes: 2 additions & 0 deletions resources/views/comments-modal.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@
:sidebar-enabled="$sidebarEnabled ?? true"
:show-subscribers="$showSubscribers ?? true"
:tip-tap-css-classes="$tipTapCssClasses ?? null"
:ratings-enabled="$ratingsEnabled ?? null"
:max-rating="$maxRating ?? null"
/>
</div>
6 changes: 6 additions & 0 deletions resources/views/comments.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
<div class="comm:flex-1 comm:space-y-2">
@if (Config::resolveAuthenticatedUser()?->can('create', Config::getCommentModel()))
<form wire:submit.prevent="save" x-cloak>
@if ($this->ratingsAreEnabled())
@include('commentions::partials.rating-input', ['maxRating' => $this->getMaxRating()])
@endif

{{-- tiptap editor --}}
<div class="comm:relative tip-tap-container comm:mb-2" x-on:click="wasFocused = true" wire:ignore>
<div
Expand Down Expand Up @@ -42,6 +46,8 @@
:load-more-label="$loadMoreLabel ?? __('commentions::comments.show_more')"
:per-page-increment="$perPageIncrement ?? null"
:tip-tap-css-classes="$tipTapCssClasses"
:ratings-enabled="$this->ratingsAreEnabled()"
:max-rating="$this->getMaxRating()"
/>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@
:per-page-increment="$getPerPageIncrement()"
:sidebar-enabled="$isSidebarEnabled()"
:tip-tap-css-classes="$getTipTapCssClasses()"
:ratings-enabled="$ratingsAreEnabled()"
:max-rating="$getMaxRating()"
/>
</x-dynamic-component>
26 changes: 26 additions & 0 deletions resources/views/partials/rating-input.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<div
class="comm:mb-2 comm:flex comm:items-center comm:gap-0.5"
x-data="{ hover: 0 }"
role="group"
aria-label="{{ __('commentions::comments.rating_input_label') }}"
>
@for ($ratingStar = 1; $ratingStar <= $maxRating; $ratingStar++)
<button
type="button"
wire:key="comment-rating-star-{{ $ratingStar }}"
x-on:click="$wire.rating = ($wire.rating === {{ $ratingStar }} ? null : {{ $ratingStar }})"
x-on:mouseenter="hover = {{ $ratingStar }}"
x-on:mouseleave="hover = 0"
class="commentions-rating-star"
:class="{ 'commentions-rating-star-active': (hover || $wire.rating) >= {{ $ratingStar }} }"
:aria-pressed="($wire.rating === {{ $ratingStar }}).toString()"
aria-label="{{ trans_choice('commentions::comments.rate_stars', $ratingStar, ['count' => $ratingStar]) }}"
>
<x-filament::icon icon="heroicon-s-star" class="comm:h-5 comm:w-5" />
</button>
@endfor
</div>

@error('rating')
<p class="comm:mb-2 comm:text-xs comm:text-red-600">{{ $message }}</p>
@enderror
12 changes: 9 additions & 3 deletions src/Actions/SaveComment.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,23 @@ class SaveComment
/**
* @throws AuthorizationException
*/
public function __invoke(Model $commentable, Commenter $author, string $body): Comment
public function __invoke(Model $commentable, Commenter $author, string $body, ?int $rating = null): Comment
{
if ($author->cannot('create', Config::getCommentModel())) {
throw new AuthorizationException('Cannot create comment');
}

$comment = $commentable->comments()->create([
$attributes = [
'body' => $body,
'author_id' => $author->getKey(),
'author_type' => $author->getMorphClass(),
]);
];

if ($rating !== null) {
$attributes['rating'] = $rating;
}

$comment = $commentable->comments()->create($attributes);

$this->dispatchEvents($comment);

Expand Down
7 changes: 7 additions & 0 deletions src/Comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
/**
* @property int $id
* @property string $body
* @property int|null $rating
* @property string $body_markdown
* @property string $body_parsed
* @property int $author_id
Expand All @@ -38,10 +39,15 @@ class Comment extends Model implements RenderableComment

protected $fillable = [
'body',
'rating',
'author_type',
'author_id',
];

protected $casts = [
'rating' => 'integer',
];

public function getTable()
{
return Config::getCommentTable();
Expand Down Expand Up @@ -179,6 +185,7 @@ public function getContentHash(): string
{
return md5(json_encode([
'body' => $this->body,
'rating' => $this->rating,
'reactions' => $this->reactions->pluck('id'),
]));
}
Expand Down
27 changes: 27 additions & 0 deletions src/CommentionsServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Illuminate\Support\Facades\View;
use Kirschbaum\Commentions\Comment as CommentModel;
use Kirschbaum\Commentions\Events\UserWasMentionedEvent;
use Kirschbaum\Commentions\Filament\Actions\TableAction;
use Kirschbaum\Commentions\Listeners\SendUserMentionedNotification;
use Kirschbaum\Commentions\Livewire\Comment;
use Kirschbaum\Commentions\Livewire\CommentList;
Expand Down Expand Up @@ -40,9 +41,15 @@ public function configurePackage(Package $package): void
'create_commentions_tables',
'create_commentions_reactions_table',
'create_commentions_subscriptions_table',
'add_rating_to_commentions_comments_table',
]);
}

public function packageRegistered(): void
{
$this->aliasTableAction();
}

public function packageBooted(): void
{
$prefix = Config::getComponentPrefix();
Expand Down Expand Up @@ -82,4 +89,24 @@ public function packageBooted(): void
Event::listen(UserWasMentionedEvent::class, $listenerClass);
}
}

/**
* Bridge the package's {@see TableAction} to the correct Filament base class.
*
* Filament 4 unified actions under `Filament\Actions\Action`, removing the
* `Filament\Tables\Actions\Action` base that table actions extend on
* Filament 3. The bundled TableAction class already extends the unified base
* (correct for Filament 4/5); on Filament 3 it must instead resolve to the
* legacy table-action base, so alias it before CommentsTableAction loads.
*/
protected function aliasTableAction(): void
{
if (class_exists(TableAction::class, false)) {
return;
}

if (class_exists('Filament\Tables\Actions\Action')) {
class_alias('Filament\Tables\Actions\Action', TableAction::class);
}
}
}
10 changes: 10 additions & 0 deletions src/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ public static function getCommentSubscriptionTable(): string
return config('commentions.tables.comment_subscriptions', 'comment_subscriptions');
}

public static function ratingsAreEnabled(): bool
{
return (bool) config('commentions.ratings.enabled', false);
}

public static function getMaxRating(): int
{
return (int) config('commentions.ratings.max', 5);
}

public static function resolveCommentUrlUsing(Closure $callback): void
{
static::$resolveCommentUrl = $callback;
Expand Down
4 changes: 4 additions & 0 deletions src/Filament/Actions/CommentsAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Kirschbaum\Commentions\Filament\Concerns\HasMentionables;
use Kirschbaum\Commentions\Filament\Concerns\HasPagination;
use Kirschbaum\Commentions\Filament\Concerns\HasPolling;
use Kirschbaum\Commentions\Filament\Concerns\HasRatings;
use Kirschbaum\Commentions\Filament\Concerns\HasSidebar;
use Kirschbaum\Commentions\Filament\Concerns\HasTipTapCssClasses;

Expand All @@ -15,6 +16,7 @@ class CommentsAction extends Action
use HasMentionables;
use HasPagination;
use HasPolling;
use HasRatings;
use HasSidebar;
use HasTipTapCssClasses;

Expand All @@ -35,6 +37,8 @@ protected function setUp(): void
'sidebarEnabled' => $this->isSidebarEnabled(),
'showSubscribers' => $this->showSubscribers(),
'tipTapCssClasses' => $this->getTipTapCssClasses(),
'ratingsEnabled' => $this->ratingsAreEnabled(),
'maxRating' => $this->getMaxRating(),
]))
->modalWidth($this->isSidebarEnabled() ? '4xl' : 'xl')
->label(__('commentions::comments.label'))
Expand Down
Loading
Loading