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
88 changes: 88 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,94 @@ protected function getHeaderActions(): array
}
```

4. Or directly in form schemas for Edit pages (Filament 4):

```php
use Filament\Forms\Components\ViewField;

public static function configure(Schema $schema): Schema
{
return $schema
->components([
// Your other form fields...

ViewField::make('comments_section')
->view('commentions::filament.forms.comments-section')
->viewData(fn ($livewire) => [
'record' => $livewire->record ?? null
])
->columnSpanFull()
->hiddenLabel(),
]);
}
```

To make the form comments readonly, pass the `readonly` flag in the viewData:

```php
ViewField::make('comments_section')
->view('commentions::filament.forms.comments-section')
->viewData(fn ($livewire) => [
'record' => $livewire->record ?? null,
'readonly' => true, // Enable readonly mode
])
->columnSpanFull()
->hiddenLabel(),
```

**Note:** For View pages, continue using the infolist approach (option 1) as it works perfectly in that context.

### Readonly Mode

You can make comments readonly by chaining the `readonly()` method on the action. In readonly mode:
- Users cannot add new comments
- Users cannot edit existing comments
- Users cannot delete comments
- Users cannot react to comments (reactions are displayed but not interactive)

```php
// Make comments readonly for all actions
CommentsAction::make()
->readonly()
->mentionables(User::all())

CommentsTableAction::make()
->readonly()
->mentionables(User::all())

// You can also conditionally enable readonly mode
CommentsAction::make()
->readonly(auth()->user()->cannot('create', Comment::class))
->mentionables(User::all())
```

This is useful for scenarios like:
- Archived or closed records where no further comments should be allowed
- View-only access for certain user roles
- Historical comment viewing
- Audit trails where comments should be preserved but not modified

#### Testing Readonly Functionality

The package includes comprehensive tests for readonly functionality designed for Filament 4. To run the readonly-specific tests:

```bash
# Run all readonly tests
./vendor/bin/pest tests/Livewire/Readonly* tests/Filament/ReadonlyActionsTest.php tests/Concerns/ReadonlyTraitsTest.php tests/Integration/ReadonlyIntegrationTest.php

# Run specific test categories
./vendor/bin/pest tests/Livewire/ReadonlyCommentsTest.php # Comments component tests
./vendor/bin/pest tests/Livewire/ReadonlyCommentTest.php # Individual comment tests
./vendor/bin/pest tests/Livewire/ReadonlyReactionsTest.php # Reactions tests
./vendor/bin/pest tests/Filament/ReadonlyActionsTest.php # Filament 4 action tests
./vendor/bin/pest tests/Integration/ReadonlyIntegrationTest.php # Integration tests

# Run trait tests
./vendor/bin/pest tests/Concerns/ReadonlyTraitsTest.php # IsReadonly trait tests
```

**Note**: These tests are specifically designed for Filament 4 and may not be compatible with Filament 3.

### Subscription Management

Commentions includes a subscription system that allows users to subscribe to receive notifications when new comments are added to a commentable resource.
Expand Down
6 changes: 0 additions & 6 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,6 @@ parameters:
count: 1
path: src/RenderableComment.php

-
message: '#^Call to an undefined method PHPUnit\\Framework\\TestCase::[a-zA-Z0-9_]+\(\)\.$#'
identifier: method.notFound
count: 3
path: tests/**/*.php

-
message: '#^Undefined variable\: \$this$#'
identifier: variable.undefined
Expand Down
1 change: 1 addition & 0 deletions resources/lang/en/comments.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
return [
'label' => 'Comments',
'no_comments_yet' => 'No comments yet.',
'save_record_first' => 'Save the record first to enable comments.',

'user_avatar_alt' => 'User Avatar',

Expand Down
1 change: 1 addition & 0 deletions resources/views/comment-list.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class="comm:w-8 comm:h-8 comm:text-gray-400 comm:dark:text-gray-500"
:key="$comment->getContentHash()"
:comment="$comment"
:mentionables="$mentionables"
:readonly="$this->isReadonly()"
/>
@endforeach

Expand Down
3 changes: 2 additions & 1 deletion resources/views/comment.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class="comm:text-xs comm:text-gray-300 comm:ml-1"
@endif
</div>

@if ($comment->isComment() && Config::resolveAuthenticatedUser()?->canAny(['update', 'delete'], $comment))
@if (!$this->isReadonly() && $comment->isComment() && Config::resolveAuthenticatedUser()?->canAny(['update', 'delete'], $comment))
<div class="comm:flex comm:gap-x-1">
@if (Config::resolveAuthenticatedUser()?->can('update', $comment))
<x-filament::icon-button
Expand Down Expand Up @@ -120,6 +120,7 @@ class="comm:text-xs comm:text-gray-300 comm:ml-1"
@if ($comment->isComment())
<livewire:commentions::reactions
:comment="$comment"
:readonly="$this->isReadonly()"
:wire:key="'reaction-manager-' . $comment->getId()"
/>
@endif
Expand Down
1 change: 1 addition & 0 deletions resources/views/comments-modal.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
:per-page-increment="$perPageIncrement ?? null"
:sidebar-enabled="$sidebarEnabled ?? true"
:show-subscribers="$showSubscribers ?? true"
:readonly="$readonly ?? false"
/>
</div>
3 changes: 2 additions & 1 deletion resources/views/comments.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<div class="comm:flex comm:gap-4 comm:h-full" x-data="{ wasFocused: false }">
{{-- Main Comments Area --}}
<div class="comm:flex-1 comm:space-y-2">
@if (Config::resolveAuthenticatedUser()?->can('create', Config::getCommentModel()))
@if (!$this->isReadonly() && Config::resolveAuthenticatedUser()?->can('create', Config::getCommentModel()))
<form wire:submit.prevent="save" x-cloak>
{{-- tiptap editor --}}
<div class="comm:relative tip-tap-container comm:mb-2" x-on:click="wasFocused = true" wire:ignore>
Expand Down Expand Up @@ -40,6 +40,7 @@
:per-page="$perPage ?? 5"
:load-more-label="$loadMoreLabel ?? 'Show more'"
:per-page-increment="$perPageIncrement ?? null"
:readonly="$this->isReadonly()"
/>
</div>

Expand Down
13 changes: 13 additions & 0 deletions resources/views/filament/forms/comments-section.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<div class="p-6 bg-white rounded-lg border border-gray-200 dark:bg-gray-800 dark:border-gray-700">
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-4">{{ __('commentions::comments.label') }}</h3>

@if($record ?? null)
@livewire('commentions::comments', [
'record' => $record,
'mentionables' => config('commentions.mentionables.default', \App\Models\User::all()),
'readonly' => $readonly ?? false
])
@else
<p class="text-sm text-gray-500 dark:text-gray-400">{{ __('commentions::comments.save_record_first', ['default' => 'Save the record first to enable comments.']) }}</p>
@endif
</div>
45 changes: 30 additions & 15 deletions resources/views/reactions.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,39 @@
{{-- Inline buttons for existing reactions --}}
@foreach ($this->reactionSummary as $reactionData)
<span wire:key="inline-reaction-button-{{ $reactionData['reaction'] }}-{{ $comment->getId() }}">
<button
x-cloak
wire:click="handleReactionToggle('{{ $reactionData['reaction'] }}')"
type="button"
class="comm:inline-flex comm:items-center comm:justify-center comm:gap-1 comm:rounded-full comm:border comm:px-2 comm:h-8 comm:text-xs comm:font-medium comm:transition comm:focus:outline-none comm:focus:ring-2 comm:focus:ring-offset-2 comm:disabled:opacity-50 comm:disabled:cursor-not-allowed
{{ $reactionData['reacted_by_current_user']
? 'comm:bg-gray-50 comm:dark:bg-gray-800 comm:border-gray-300 comm:dark:border-gray-600 comm:text-gray-700 comm:dark:text-gray-200 comm:hover:bg-gray-200 comm:dark:hover:bg-gray-600'
: 'comm:bg-white comm:dark:bg-gray-900 comm:border-gray-300 comm:dark:border-gray-600 comm:text-gray-700 comm:dark:text-gray-200 comm:hover:bg-gray-100 comm:dark:hover:bg-gray-600' }}"
title="{{ $reactionData['reaction'] }}"
@if ($this->isReadonly())
<span
class="comm:inline-flex comm:items-center comm:justify-center comm:gap-1 comm:rounded-full comm:border comm:px-2 comm:h-8 comm:text-xs comm:font-medium
{{ $reactionData['reacted_by_current_user']
? 'comm:bg-gray-50 comm:dark:bg-gray-800 comm:border-gray-300 comm:dark:border-gray-600 comm:text-gray-700 comm:dark:text-gray-200'
: 'comm:bg-white comm:dark:bg-gray-900 comm:border-gray-300 comm:dark:border-gray-600 comm:text-gray-700 comm:dark:text-gray-200' }}"
title="{{ $reactionData['reaction'] }}"
>
<span>{{ $reactionData['reaction'] }}</span>
<span wire:key="inline-reaction-count-{{ $reactionData['reaction'] }}-{{ $comment->getId() }}">{{ $reactionData['count'] }}</span>
</span>
@else
<button
x-cloak
wire:click="handleReactionToggle('{{ $reactionData['reaction'] }}')"
type="button"
class="comm:inline-flex comm:items-center comm:justify-center comm:gap-1 comm:rounded-full comm:border comm:px-2 comm:h-8 comm:text-xs comm:font-medium comm:transition comm:focus:outline-none comm:focus:ring-2 comm:focus:ring-offset-2 comm:disabled:opacity-50 comm:disabled:cursor-not-allowed
{{ $reactionData['reacted_by_current_user']
? 'comm:bg-gray-50 comm:dark:bg-gray-800 comm:border-gray-300 comm:dark:border-gray-600 comm:text-gray-700 comm:dark:text-gray-200 comm:hover:bg-gray-200 comm:dark:hover:bg-gray-600'
: 'comm:bg-white comm:dark:bg-gray-900 comm:border-gray-300 comm:dark:border-gray-600 comm:text-gray-700 comm:dark:text-gray-200 comm:hover:bg-gray-100 comm:dark:hover:bg-gray-600' }}"
title="{{ $reactionData['reaction'] }}"

>
<span>{{ $reactionData['reaction'] }}</span>
<span wire:key="inline-reaction-count-{{ $reactionData['reaction'] }}-{{ $comment->getId() }}">{{ $reactionData['count'] }}</span>
</button>
>
<span>{{ $reactionData['reaction'] }}</span>
<span wire:key="inline-reaction-count-{{ $reactionData['reaction'] }}-{{ $comment->getId() }}">{{ $reactionData['count'] }}</span>
</button>
@endif
</span>
@endforeach

{{-- Add Reaction Button --}}
<div class="comm:relative" x-data="{ open: false }" wire:ignore.self>
@if (!$this->isReadonly())
<div class="comm:relative" x-data="{ open: false }" wire:ignore.self>
<button
x-on:click="open = !open"
type="button"
Expand Down Expand Up @@ -63,7 +77,8 @@ class="comm:inline-flex comm:items-center comm:justify-center comm:gap-1 comm:ro
</button>
@endforeach
</div>
</div>
</div>
@endif

{{-- Display summary of reactions not explicitly in the allowed list --}}
@foreach ($this->reactionSummary as $reactionEmoji => $data)
Expand Down
3 changes: 3 additions & 0 deletions src/Filament/Actions/CommentsAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
use Kirschbaum\Commentions\Filament\Concerns\HasPagination;
use Kirschbaum\Commentions\Filament\Concerns\HasPolling;
use Kirschbaum\Commentions\Filament\Concerns\HasSidebar;
use Kirschbaum\Commentions\Filament\Concerns\IsReadonly;

class CommentsAction extends Action
{
use HasMentionables;
use HasPagination;
use HasPolling;
use HasSidebar;
use IsReadonly;

protected function setUp(): void
{
Expand All @@ -32,6 +34,7 @@ protected function setUp(): void
'perPageIncrement' => $this->getPerPageIncrement() ?: $this->getPerPage(),
'sidebarEnabled' => $this->isSidebarEnabled(),
'showSubscribers' => $this->showSubscribers(),
'readonly' => $this->readonly,
]))
->modalWidth($this->isSidebarEnabled() ? '4xl' : 'xl')
->label(__('commentions::comments.label'))
Expand Down
3 changes: 3 additions & 0 deletions src/Filament/Actions/CommentsTableAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
use Kirschbaum\Commentions\Filament\Concerns\HasMentionables;
use Kirschbaum\Commentions\Filament\Concerns\HasPolling;
use Kirschbaum\Commentions\Filament\Concerns\HasSidebar;
use Kirschbaum\Commentions\Filament\Concerns\IsReadonly;

class CommentsTableAction extends Action
{
use HasMentionables;
use HasPolling;
use HasSidebar;
use IsReadonly;

protected function setUp(): void
{
Expand All @@ -26,6 +28,7 @@ protected function setUp(): void
'pollingInterval' => $this->getPollingInterval(),
'sidebarEnabled' => $this->isSidebarEnabled(),
'showSubscribers' => $this->showSubscribers(),
'readonly' => $this->readonly,
]))
->modalWidth($this->isSidebarEnabled() ? '4xl' : 'xl')
->label(__('commentions::comments.label'))
Expand Down
20 changes: 20 additions & 0 deletions src/Filament/Concerns/IsReadonly.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Kirschbaum\Commentions\Filament\Concerns;

trait IsReadonly
{
protected bool $readonly = false;

public function readonly(bool $readonly = true): static
{
$this->readonly = $readonly;

return $this;
}

public function isReadonly(): bool
{
return $this->readonly;
}
}
8 changes: 5 additions & 3 deletions src/Livewire/Comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
use Kirschbaum\Commentions\Config;
use Kirschbaum\Commentions\Contracts\RenderableComment;
use Kirschbaum\Commentions\Livewire\Concerns\HasMentions;
use Kirschbaum\Commentions\Livewire\Concerns\IsReadonly;
use Livewire\Attributes\On;
use Livewire\Attributes\Renderless;
use Livewire\Component;

class Comment extends Component
{
use HasMentions;
use IsReadonly;

public CommentModel|RenderableComment $comment;

Expand All @@ -39,7 +41,7 @@ public function handleReactionToggledEvent(string $reaction, int $commentId): vo
#[Renderless]
public function delete()
{
if (! auth()->user()?->can('delete', $this->comment)) {
if ($this->isReadonly() || ! auth()->user()?->can('delete', $this->comment)) {
return;
}

Expand Down Expand Up @@ -75,7 +77,7 @@ public function clear(): void

public function edit(): void
{
if (! Config::resolveAuthenticatedUser()?->can('update', $this->comment)) {
if ($this->isReadonly() || ! Config::resolveAuthenticatedUser()?->can('update', $this->comment)) {
return;
}

Expand All @@ -87,7 +89,7 @@ public function edit(): void

public function updateComment()
{
if (! Config::resolveAuthenticatedUser()?->can('update', $this->comment)) {
if ($this->isReadonly() || ! Config::resolveAuthenticatedUser()?->can('update', $this->comment)) {
return;
}

Expand Down
2 changes: 2 additions & 0 deletions src/Livewire/CommentList.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Kirschbaum\Commentions\Livewire\Concerns\HasMentions;
use Kirschbaum\Commentions\Livewire\Concerns\HasPagination;
use Kirschbaum\Commentions\Livewire\Concerns\HasPolling;
use Kirschbaum\Commentions\Livewire\Concerns\IsReadonly;
use Livewire\Attributes\Computed;
use Livewire\Attributes\On;
use Livewire\Component;
Expand All @@ -16,6 +17,7 @@ class CommentList extends Component
use HasMentions;
use HasPagination;
use HasPolling;
use IsReadonly;

public Model $record;

Expand Down
6 changes: 6 additions & 0 deletions src/Livewire/Comments.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Kirschbaum\Commentions\Livewire\Concerns\HasPagination;
use Kirschbaum\Commentions\Livewire\Concerns\HasPolling;
use Kirschbaum\Commentions\Livewire\Concerns\HasSidebar;
use Kirschbaum\Commentions\Livewire\Concerns\IsReadonly;
use Livewire\Attributes\On;
use Livewire\Attributes\Renderless;
use Livewire\Component;
Expand All @@ -19,6 +20,7 @@ class Comments extends Component
use HasPagination;
use HasPolling;
use HasSidebar;
use IsReadonly;

public Model $record;

Expand All @@ -31,6 +33,10 @@ class Comments extends Component
#[Renderless]
public function save()
{
if ($this->isReadonly()) {
return;
}

$this->validate();

SaveComment::run(
Expand Down
Loading