Skip to content
Merged
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
2 changes: 2 additions & 0 deletions app/Enums/ModerationTypeEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ enum ModerationTypeEnum: string
case Edit = 'edit';
case Remove = 'remove';
case Replace = 'replace';
case Restore = 'restore';
case Wrap = 'wrap';

public function label(): string
Expand All @@ -21,6 +22,7 @@ public function label(): string
self::Edit => 'Edit',
self::Remove => 'Remove',
self::Replace => 'Replace',
self::Restore => 'Restore',
self::Wrap => 'Wrap',
};
}
Expand Down
5 changes: 1 addition & 4 deletions app/Livewire/Comments/CommentBlur.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace App\Livewire\Comments;

use App\Enums\ModerationTypeEnum;
use App\Traits\CommentComponentTrait;
use App\Traits\CommentComponentStateTrait;
use Illuminate\Contracts\View\View;
Expand All @@ -20,12 +19,10 @@ final class CommentBlur extends Component

public function render(): View
{
$moderatorComment = $this->moderatorCommentsByType?->get(ModerationTypeEnum::Blur->value);

return view('livewire.comments.comment-blur', [
'comment' => $this->comment,
'childComments' => $this->childComments,
'blurMessage' => $moderatorComment?->body ?? '',
'blurMessage' => $this->blurComment?->body ?? '',
]);
}
}
32 changes: 6 additions & 26 deletions app/Livewire/Comments/CommentComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
use App\Enums\CommentStateEnum;
use App\Enums\LivewireEventEnum;
use App\Enums\ModerationTypeEnum;
use App\Enums\RoleNameEnum;
use App\Models\Comment;
use App\Traits\CommentComponentTrait;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Collection;
use Livewire\Attributes\Computed;
use Livewire\Attributes\On;
use Livewire\Component;

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

#[Computed]
public function isInitiallyBlurred(): bool
{
return $this->moderatorCommentsByType?->get(ModerationTypeEnum::Blur->value) !== null;
}

public function mount(int $commentId, ?Comment $comment, ?Collection $childComments): void
{
// On mount we expect the comment list to provide the comment model
Expand All @@ -42,31 +36,17 @@ public function mount(int $commentId, ?Comment $comment, ?Collection $childComme

public function render(): View
{
// If the comment has been replaced, just render the moderation message.
// TODO: if it is possible to have a top-level comment be marked with a
// moderation type, we may want to render it via this view. But it may
// also just be about changing the border for a regular rendering.
$moderatorReplaceComment = $this->moderatorCommentsByType?->get(ModerationTypeEnum::Replace->value);
$moderatorWrapComment = $this->moderatorCommentsByType?->get(ModerationTypeEnum::Wrap->value);
$moderatorBlurComment = $this->moderatorCommentsByType?->get(ModerationTypeEnum::Blur->value);
$moderationType = null;

if ($moderatorReplaceComment !== null &&
($moderatorWrapComment === null || $moderatorReplaceComment->created_at > $moderatorWrapComment->created_at)) {
$moderationType = ModerationTypeEnum::Replace;
} elseif ($moderatorWrapComment !== null) {
$moderationType = ModerationTypeEnum::Wrap;
}
$moderationType = $this->appearanceComment?->moderation_type ?? null;

// If there are no decorations to apply, just render the basic comment component.
return view('livewire.comments.comment-component', [
'comment' => $this->comment,
'childComments' => $this->childComments,
'moderationType' => $moderationType,
'isInitiallyBlurred' => $this->isInitiallyBlurred,
'replacedByCommentId' => $moderatorReplaceComment?->id,
'wrappedByCommentId' => $moderatorWrapComment?->id,
'blurredByCommentId' => $moderatorBlurComment?->id,
'isRemoved' => $moderationType === ModerationTypeEnum::Remove && !auth()->user()?->hasRole(RoleNameEnum::MODERATOR->value),
'appearanceCommentId' => $this->appearanceComment?->id,
'blurCommentId' => $this->blurComment?->id,
'isEditing' => $this->state === CommentStateEnum::Editing,
'isFlagging' => $this->state === CommentStateEnum::Flagging,
'isReplying' => $this->state === CommentStateEnum::Replying,
Expand All @@ -90,7 +70,7 @@ public function reloadChildComments(int $id, ?int $parentId): void
{
if ($parentId === $this->commentId) {
$this->childComments = $this->commentRepository->getCommentsByParentId($parentId);
unset($this->moderatorCommentsByType, $this->isInitiallyBlurred);
unset($this->appearanceComment, $this->blurComment, $this->isInitiallyBlurred);

// Re-evaluate whether the comment should be blurred.
$this->isBlurred = $this->isInitiallyBlurred;
Expand Down
8 changes: 5 additions & 3 deletions app/Livewire/Comments/CommentFormComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,13 @@ public function render(): View
if ($this->isModerating) {
$data['bodyLabel'] = trans('Original comment');
$data['buttonText'] = trans(match ($this->moderationType) {
ModerationTypeEnum::Comment => 'Comment as moderator',
ModerationTypeEnum::Edit => 'Edit comment',
ModerationTypeEnum::Remove => 'Remove comment',
ModerationTypeEnum::Replace => 'Replace comment',
ModerationTypeEnum::Wrap => 'Wrap comment',
ModerationTypeEnum::Blur => 'Blur comment',
ModerationTypeEnum::Wrap => 'Wrap comment',
ModerationTypeEnum::Replace => 'Replace comment',
ModerationTypeEnum::Remove => 'Remove comment',
ModerationTypeEnum::Restore => 'Restore comment',
default => 'Moderate',
});
} elseif ($this->isReplying) {
Expand Down
27 changes: 27 additions & 0 deletions app/Livewire/Comments/CommentRemoval.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace App\Livewire\Comments;

use App\Enums\CommentStateEnum;
use App\Traits\CommentComponentTrait;
use App\Traits\CommentComponentStateTrait;
use Illuminate\Contracts\View\View;
use Livewire\Component;

final class CommentRemoval extends Component
{
use CommentComponentStateTrait;
use CommentComponentTrait;

public function render(): View
{
return view('livewire.comments.comment-removal', [
'comment' => $this->comment,
'childComments' => $this->childComments,
'moderatorComment' => $this->appearanceComment,
'isModerating' => $this->state === CommentStateEnum::Moderating,
]);
}
}
5 changes: 1 addition & 4 deletions app/Livewire/Comments/CommentReplacement.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace App\Livewire\Comments;

use App\Enums\CommentStateEnum;
use App\Enums\ModerationTypeEnum;
use App\Traits\CommentComponentTrait;
use App\Traits\CommentComponentStateTrait;
use Illuminate\Contracts\View\View;
Expand All @@ -18,12 +17,10 @@ final class CommentReplacement extends Component

public function render(): View
{
$moderatorComment = $this->moderatorCommentsByType?->get(ModerationTypeEnum::Replace->value);

return view('livewire.comments.comment-replacement', [
'comment' => $this->comment,
'childComments' => $this->childComments,
'moderatorComment' => $moderatorComment,
'moderatorComment' => $this->appearanceComment,
'isModerating' => $this->state === CommentStateEnum::Moderating,
]);
}
Expand Down
5 changes: 1 addition & 4 deletions app/Livewire/Comments/CommentWrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace App\Livewire\Comments;

use App\Enums\ModerationTypeEnum;
use App\Traits\CommentComponentTrait;
use App\Traits\CommentComponentStateTrait;
use Illuminate\Contracts\View\View;
Expand All @@ -20,12 +19,10 @@ final class CommentWrapper extends Component

public function render(): View
{
$moderatorComment = $this->moderatorCommentsByType?->get(ModerationTypeEnum::Wrap->value);

return view('livewire.comments.comment-wrapper', [
'comment' => $this->comment,
'childComments' => $this->childComments,
'moderatorComment' => $moderatorComment,
'moderatorComment' => $this->appearanceComment,
'isInitiallyBlurred' => $this->isInitiallyBlurred,
]);
}
Expand Down
5 changes: 5 additions & 0 deletions app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

namespace App\Providers;

use App\Enums\RoleNameEnum;
use App\Traits\LoggingTrait;
use App\Traits\SubsiteTrait;
use App\Traits\UrlTrait;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
Expand All @@ -30,6 +32,9 @@ public function boot(): void
} catch (NotFoundExceptionInterface|ContainerExceptionInterface $exception) {
$this->logError($exception);
}
Blade::if('moderator', function () {
return auth()?->user()?->hasRole(RoleNameEnum::MODERATOR->value);
});

Model::shouldBeStrict();

Expand Down
54 changes: 46 additions & 8 deletions app/Traits/CommentComponentTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,58 @@ public function userFlagged(): bool
return $this->comment?->userFlagged() ?? false;
}

/**
* Finds the most recent moderator remove, replace, or wrap comment.
*
* The effect of Reset is to override any previous appearance-modifying comment
* and restore the default appearance for the original comment.
*/
#[Computed]
public function moderatorCommentsByType(): ?Collection
{
return $this->childComments?->filter(
fn($comment) =>
$comment->moderation_type !== null &&
$comment->moderation_type->value !== ModerationTypeEnum::Comment->value,
)->keyBy(fn($comment) => $comment->moderation_type->value ?? 'none');
public function appearanceComment(): ?Comment
{
$appearanceComment = $this->childComments?->last(
fn($comment) => $comment->moderation_type !== null && match ($comment->moderation_type) {
ModerationTypeEnum::Remove, ModerationTypeEnum::Replace, ModerationTypeEnum::Wrap, ModerationTypeEnum::Restore => true,
default => false,
},
);

// If the last appearance-modifying comment is a Restore, return null.
if ($appearanceComment && $appearanceComment->moderation_type === ModerationTypeEnum::Restore) {
return null;
}

return $appearanceComment;
}

/**
* Finds the most recent moderator blur comment.
*
* Blurring of comments can coexist with wrapping, but not with removal or replacement.
* It is also reset by a later Restore comment.
*/
#[Computed]
public function blurComment(): ?Comment
{
$blurComment = $this->childComments?->last(
fn($comment) => $comment->moderation_type !== null && match ($comment->moderation_type) {
ModerationTypeEnum::Blur, ModerationTypeEnum::Remove, ModerationTypeEnum::Replace, ModerationTypeEnum::Restore => true,
default => false,
},
);

// If the last blur-modifying comment is not a Blur, return null.
if ($blurComment && $blurComment->moderation_type !== ModerationTypeEnum::Blur) {
return null;
}

return $blurComment;
}

#[Computed]
public function isInitiallyBlurred(): bool
{
return $this->moderatorCommentsByType?->get(ModerationTypeEnum::Blur->value) !== null;
return $this->blurComment !== null;
}

protected CommentRepositoryInterface $commentRepository;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

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('comments', function (Blueprint $table) {
$table->enum('moderation_type', ['blur', 'comment', 'edit', 'remove', 'replace', 'restore', 'wrap'])->nullable()->change();
});
}

public function down(): void
{
Schema::table('comments', function (Blueprint $table) {
$table->enum('moderation_type', ['blur', 'comment', 'edit', 'remove', 'replace', 'wrap'])->nullable()->change();
});
}
};
12 changes: 12 additions & 0 deletions resources/sass/modules/_posts.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
border-radius: 0.25rem;
}

.comment.moderator-removed {
display: none;
}

.comment>.moderator-removed,
.comment>.moderator-replaced,
.comment>.moderator-hidden>summary,
.comment .comment-container {
Expand Down Expand Up @@ -48,9 +53,16 @@

.moderator-message {
border: 1px solid light-dark(var(--base-color), var(--yellow-green));
}

.moderator-message:not(.moderator-removed) .moderator-content {
color: light-dark(var(--base-color), var(--yellow-green));
}

.moderator-message.moderator-removed {
border: 1px solid var(--is-danger);
}

details.moderator-hidden {
padding: 0;
border-radius: inherit;
Expand Down
4 changes: 4 additions & 0 deletions resources/sass/themes/modules/_themeable.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ body {
background-color: light-dark(var(--very-light-gray), var(--dark-base-color));
}

.comment:has(>.moderator-removed) {
background-color: inherit;
}

.main-contents>.post {
background-color: inherit;
}
Expand Down
Loading