Skip to content

Commit 7667f36

Browse files
committed
Allow restoring comments
1 parent e372757 commit 7667f36

18 files changed

Lines changed: 194 additions & 134 deletions

app/Enums/ModerationTypeEnum.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ enum ModerationTypeEnum: string
1111
case Edit = 'edit';
1212
case Remove = 'remove';
1313
case Replace = 'replace';
14+
case Restore = 'restore';
1415
case Wrap = 'wrap';
1516

1617
public function label(): string
@@ -21,6 +22,7 @@ public function label(): string
2122
self::Edit => 'Edit',
2223
self::Remove => 'Remove',
2324
self::Replace => 'Replace',
25+
self::Restore => 'Restore',
2426
self::Wrap => 'Wrap',
2527
};
2628
}

app/Livewire/Comments/CommentBlur.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
namespace App\Livewire\Comments;
66

7-
use App\Enums\ModerationTypeEnum;
87
use App\Traits\CommentComponentTrait;
98
use App\Traits\CommentComponentStateTrait;
109
use Illuminate\Contracts\View\View;
@@ -20,12 +19,10 @@ final class CommentBlur extends Component
2019

2120
public function render(): View
2221
{
23-
$moderatorComment = $this->moderatorCommentsByType?->get(ModerationTypeEnum::Blur->value);
24-
2522
return view('livewire.comments.comment-blur', [
2623
'comment' => $this->comment,
2724
'childComments' => $this->childComments,
28-
'blurMessage' => $moderatorComment?->body ?? '',
25+
'blurMessage' => $this->blurComment?->body ?? '',
2926
]);
3027
}
3128
}

app/Livewire/Comments/CommentComponent.php

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
use App\Enums\CommentStateEnum;
88
use App\Enums\LivewireEventEnum;
99
use App\Enums\ModerationTypeEnum;
10+
use App\Enums\RoleNameEnum;
1011
use App\Models\Comment;
1112
use App\Traits\CommentComponentTrait;
1213
use Illuminate\Contracts\View\View;
1314
use Illuminate\Support\Collection;
14-
use Livewire\Attributes\Computed;
1515
use Livewire\Attributes\On;
1616
use Livewire\Component;
1717

@@ -22,12 +22,6 @@ final class CommentComponent extends Component
2222
// State
2323
public CommentStateEnum $state = CommentStateEnum::Viewing;
2424

25-
#[Computed]
26-
public function isInitiallyBlurred(): bool
27-
{
28-
return $this->moderatorCommentsByType?->get(ModerationTypeEnum::Blur->value) !== null;
29-
}
30-
3125
public function mount(int $commentId, ?Comment $comment, ?Collection $childComments): void
3226
{
3327
// On mount we expect the comment list to provide the comment model
@@ -42,31 +36,17 @@ public function mount(int $commentId, ?Comment $comment, ?Collection $childComme
4236

4337
public function render(): View
4438
{
45-
// If the comment has been replaced, just render the moderation message.
46-
// TODO: if it is possible to have a top-level comment be marked with a
47-
// moderation type, we may want to render it via this view. But it may
48-
// also just be about changing the border for a regular rendering.
49-
$moderatorReplaceComment = $this->moderatorCommentsByType?->get(ModerationTypeEnum::Replace->value);
50-
$moderatorWrapComment = $this->moderatorCommentsByType?->get(ModerationTypeEnum::Wrap->value);
51-
$moderatorBlurComment = $this->moderatorCommentsByType?->get(ModerationTypeEnum::Blur->value);
52-
$moderationType = null;
53-
54-
if ($moderatorReplaceComment !== null &&
55-
($moderatorWrapComment === null || $moderatorReplaceComment->created_at > $moderatorWrapComment->created_at)) {
56-
$moderationType = ModerationTypeEnum::Replace;
57-
} elseif ($moderatorWrapComment !== null) {
58-
$moderationType = ModerationTypeEnum::Wrap;
59-
}
39+
$moderationType = $this->appearanceComment?->moderation_type ?? null;
6040

6141
// If there are no decorations to apply, just render the basic comment component.
6242
return view('livewire.comments.comment-component', [
6343
'comment' => $this->comment,
6444
'childComments' => $this->childComments,
6545
'moderationType' => $moderationType,
6646
'isInitiallyBlurred' => $this->isInitiallyBlurred,
67-
'replacedByCommentId' => $moderatorReplaceComment?->id,
68-
'wrappedByCommentId' => $moderatorWrapComment?->id,
69-
'blurredByCommentId' => $moderatorBlurComment?->id,
47+
'isRemoved' => $moderationType === ModerationTypeEnum::Remove && !auth()->user()?->hasRole(RoleNameEnum::MODERATOR->value),
48+
'appearanceCommentId' => $this->appearanceComment?->id,
49+
'blurCommentId' => $this->blurComment?->id,
7050
'isEditing' => $this->state === CommentStateEnum::Editing,
7151
'isFlagging' => $this->state === CommentStateEnum::Flagging,
7252
'isReplying' => $this->state === CommentStateEnum::Replying,
@@ -90,7 +70,7 @@ public function reloadChildComments(int $id, ?int $parentId): void
9070
{
9171
if ($parentId === $this->commentId) {
9272
$this->childComments = $this->commentRepository->getCommentsByParentId($parentId);
93-
unset($this->moderatorCommentsByType, $this->isInitiallyBlurred);
73+
unset($this->appearanceComment, $this->blurComment, $this->isInitiallyBlurred);
9474

9575
// Re-evaluate whether the comment should be blurred.
9676
$this->isBlurred = $this->isInitiallyBlurred;

app/Livewire/Comments/CommentFormComponent.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,13 @@ public function render(): View
119119
if ($this->isModerating) {
120120
$data['bodyLabel'] = trans('Original comment');
121121
$data['buttonText'] = trans(match ($this->moderationType) {
122+
ModerationTypeEnum::Comment => 'Comment as moderator',
122123
ModerationTypeEnum::Edit => 'Edit comment',
123-
ModerationTypeEnum::Remove => 'Remove comment',
124-
ModerationTypeEnum::Replace => 'Replace comment',
125-
ModerationTypeEnum::Wrap => 'Wrap comment',
126124
ModerationTypeEnum::Blur => 'Blur comment',
125+
ModerationTypeEnum::Wrap => 'Wrap comment',
126+
ModerationTypeEnum::Replace => 'Replace comment',
127+
ModerationTypeEnum::Remove => 'Remove comment',
128+
ModerationTypeEnum::Restore => 'Restore comment',
127129
default => 'Moderate',
128130
});
129131
} elseif ($this->isReplying) {
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Livewire\Comments;
6+
7+
use App\Enums\CommentStateEnum;
8+
use App\Traits\CommentComponentTrait;
9+
use App\Traits\CommentComponentStateTrait;
10+
use Illuminate\Contracts\View\View;
11+
use Livewire\Component;
12+
13+
final class CommentRemoval extends Component
14+
{
15+
use CommentComponentStateTrait;
16+
use CommentComponentTrait;
17+
18+
public function render(): View
19+
{
20+
return view('livewire.comments.comment-removal', [
21+
'comment' => $this->comment,
22+
'childComments' => $this->childComments,
23+
'moderatorComment' => $this->appearanceComment,
24+
'isModerating' => $this->state === CommentStateEnum::Moderating,
25+
]);
26+
}
27+
}

app/Livewire/Comments/CommentReplacement.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
namespace App\Livewire\Comments;
66

77
use App\Enums\CommentStateEnum;
8-
use App\Enums\ModerationTypeEnum;
98
use App\Traits\CommentComponentTrait;
109
use App\Traits\CommentComponentStateTrait;
1110
use Illuminate\Contracts\View\View;
@@ -18,12 +17,10 @@ final class CommentReplacement extends Component
1817

1918
public function render(): View
2019
{
21-
$moderatorComment = $this->moderatorCommentsByType?->get(ModerationTypeEnum::Replace->value);
22-
2320
return view('livewire.comments.comment-replacement', [
2421
'comment' => $this->comment,
2522
'childComments' => $this->childComments,
26-
'moderatorComment' => $moderatorComment,
23+
'moderatorComment' => $this->appearanceComment,
2724
'isModerating' => $this->state === CommentStateEnum::Moderating,
2825
]);
2926
}

app/Livewire/Comments/CommentWrapper.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
namespace App\Livewire\Comments;
66

7-
use App\Enums\ModerationTypeEnum;
87
use App\Traits\CommentComponentTrait;
98
use App\Traits\CommentComponentStateTrait;
109
use Illuminate\Contracts\View\View;
@@ -20,12 +19,10 @@ final class CommentWrapper extends Component
2019

2120
public function render(): View
2221
{
23-
$moderatorComment = $this->moderatorCommentsByType?->get(ModerationTypeEnum::Wrap->value);
24-
2522
return view('livewire.comments.comment-wrapper', [
2623
'comment' => $this->comment,
2724
'childComments' => $this->childComments,
28-
'moderatorComment' => $moderatorComment,
25+
'moderatorComment' => $this->appearanceComment,
2926
'isInitiallyBlurred' => $this->isInitiallyBlurred,
3027
]);
3128
}

app/Providers/AppServiceProvider.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44

55
namespace App\Providers;
66

7+
use App\Enums\RoleNameEnum;
78
use App\Traits\LoggingTrait;
89
use App\Traits\SubsiteTrait;
910
use App\Traits\UrlTrait;
1011
use Illuminate\Database\Eloquent\Model;
1112
use Illuminate\Database\Eloquent\Relations\Relation;
13+
use Illuminate\Support\Facades\Blade;
1214
use Illuminate\Support\ServiceProvider;
1315
use Psr\Container\ContainerExceptionInterface;
1416
use Psr\Container\NotFoundExceptionInterface;
@@ -30,6 +32,9 @@ public function boot(): void
3032
} catch (NotFoundExceptionInterface|ContainerExceptionInterface $exception) {
3133
$this->logError($exception);
3234
}
35+
Blade::if('moderator', function () {
36+
return auth()?->user()?->hasRole(RoleNameEnum::MODERATOR->value);
37+
});
3338

3439
Model::shouldBeStrict();
3540

app/Traits/CommentComponentTrait.php

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,58 @@ public function userFlagged(): bool
3636
return $this->comment?->userFlagged() ?? false;
3737
}
3838

39+
/**
40+
* Finds the most recent moderator remove, replace, or wrap comment.
41+
*
42+
* The effect of Reset is to override any previous appearance-modifying comment
43+
* and restore the default appearance for the original comment.
44+
*/
3945
#[Computed]
40-
public function moderatorCommentsByType(): ?Collection
41-
{
42-
return $this->childComments?->filter(
43-
fn($comment) =>
44-
$comment->moderation_type !== null &&
45-
$comment->moderation_type->value !== ModerationTypeEnum::Comment->value,
46-
)->keyBy(fn($comment) => $comment->moderation_type->value ?? 'none');
46+
public function appearanceComment(): ?Comment
47+
{
48+
$appearanceComment = $this->childComments?->last(
49+
fn($comment) => $comment->moderation_type !== null && match ($comment->moderation_type) {
50+
ModerationTypeEnum::Remove, ModerationTypeEnum::Replace, ModerationTypeEnum::Wrap, ModerationTypeEnum::Restore => true,
51+
default => false,
52+
},
53+
);
54+
55+
// If the last appearance-modifying comment is a Restore, return null.
56+
if ($appearanceComment && $appearanceComment->moderation_type === ModerationTypeEnum::Restore) {
57+
return null;
58+
}
59+
60+
return $appearanceComment;
61+
}
62+
63+
/**
64+
* Finds the most recent moderator blur comment.
65+
*
66+
* Blurring of comments can coexist with wrapping, but not with removal or replacement.
67+
* It is also reset by a later Restore comment.
68+
*/
69+
#[Computed]
70+
public function blurComment(): ?Comment
71+
{
72+
$blurComment = $this->childComments?->last(
73+
fn($comment) => $comment->moderation_type !== null && match ($comment->moderation_type) {
74+
ModerationTypeEnum::Blur, ModerationTypeEnum::Remove, ModerationTypeEnum::Replace, ModerationTypeEnum::Restore => true,
75+
default => false,
76+
},
77+
);
78+
79+
// If the last blur-modifying comment is not a Blur, return null.
80+
if ($blurComment && $blurComment->moderation_type !== ModerationTypeEnum::Blur) {
81+
return null;
82+
}
83+
84+
return $blurComment;
4785
}
4886

4987
#[Computed]
5088
public function isInitiallyBlurred(): bool
5189
{
52-
return $this->moderatorCommentsByType?->get(ModerationTypeEnum::Blur->value) !== null;
90+
return $this->blurComment !== null;
5391
}
5492

5593
protected CommentRepositoryInterface $commentRepository;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Illuminate\Database\Migrations\Migration;
6+
use Illuminate\Database\Schema\Blueprint;
7+
use Illuminate\Support\Facades\Schema;
8+
9+
return new class extends Migration {
10+
public function up(): void
11+
{
12+
Schema::table('comments', function (Blueprint $table) {
13+
$table->enum('moderation_type', ['blur', 'comment', 'edit', 'remove', 'replace', 'restore', 'wrap'])->nullable()->change();
14+
});
15+
}
16+
17+
public function down(): void
18+
{
19+
Schema::table('comments', function (Blueprint $table) {
20+
$table->enum('moderation_type', ['blur', 'comment', 'edit', 'remove', 'replace', 'wrap'])->nullable()->change();
21+
});
22+
}
23+
};

0 commit comments

Comments
 (0)