Skip to content

Commit 1ca3676

Browse files
committed
Support flagging comments using Markable
1 parent d6c5ca4 commit 1ca3676

13 files changed

Lines changed: 254 additions & 122 deletions

File tree

app/Enums/LivewireEventEnum.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
enum LivewireEventEnum: string
88
{
99
case CommentDeleted = 'comment-deleted';
10+
case CommentFlagCancelled = 'comment-flag-cancelled';
1011
case CommentFlagDeleted = 'comment-flag-deleted';
1112
case CommentFlagged = 'comment-flagged';
1213
case CommentStored = 'comment-stored';
@@ -19,6 +20,7 @@ enum LivewireEventEnum: string
1920
case FlagDeleted = 'flag-deleted';
2021
case HideFlagCommentForm = 'hide-flag-comment-form';
2122
case PostDeleted = 'post-deleted';
23+
case PostFlagCancelled = 'post-flag-cancelled';
2224
case PostFlagDeleted = 'post-flag-deleted';
2325
case PostFlagged = 'post-flagged';
2426
case PostStored = 'post-stored';

app/Livewire/Comments/CommentComponent.php

Lines changed: 19 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
use App\Models\User;
1212
use App\Traits\CommentComponentTrait;
1313
use Illuminate\Contracts\View\View;
14-
use Illuminate\Support\Facades\DB;
1514
use Livewire\Attributes\On;
1615
use Livewire\Component;
1716

@@ -24,13 +23,12 @@ final class CommentComponent extends Component
2423
public string $body = '';
2524
public ?int $parentId = null;
2625
public int $flagCount = 0;
27-
public string $flagIconFilename = 'flag';
28-
public string $flagButtonText = '';
2926
public bool $userFlagged = false;
3027

3128
// State
3229
public bool $isEditing = false;
3330
public bool $isFlagging = false;
31+
public bool $isFlagLoading = false;
3432
public bool $isReplying = false;
3533

3634
public Comment $comment;
@@ -52,42 +50,16 @@ public function mount(Comment $comment, Post $post): void
5250
$this->user = auth()->user() ?? null;
5351

5452
$this->updateFlagCount();
55-
$this->hasUserFlagged();
56-
57-
$this->flagIconFilename = $this->getFlagIconFilename();
58-
$this->flagButtonText = $this->getFlagTitleText();
5953
}
6054

6155
public function render(): View
6256
{
6357
return view('livewire.comments.comment-component');
6458
}
6559

66-
private function getFlagIconFilename(): string
67-
{
68-
return $this->userFlagged ? 'flag-fill' : 'flag';
69-
}
70-
71-
private function getFlagTitleText(): string
72-
{
73-
return $this->userFlagged ? trans('Remove flag') : trans('Flag this comment');
74-
}
75-
76-
private function hasUserFlagged(): void
77-
{
78-
$userFlagCount = DB::table(table: 'markable_flags')
79-
->where(column: 'user_id', operator: '=', value: auth()->id())
80-
->where(column: 'markable_id', operator: '=', value: $this->comment->id)
81-
->where(column: 'markable_type', operator: 'LIKE', value: '%Comment%')
82-
->count();
83-
84-
$this->userFlagged = $userFlagCount > 0;
85-
}
86-
8760
#[On([
8861
LivewireEventEnum::CommentStored->value,
8962
LivewireEventEnum::CommentDeleted->value,
90-
LivewireEventEnum::CommentFlagged->value,
9163
LivewireEventEnum::CommentUpdated->value,
9264
LivewireEventEnum::EscapeKeyClicked->value,
9365
])]
@@ -98,27 +70,36 @@ public function closeForm(): void
9870
]);
9971

10072
$this->stopEditing();
101-
$this->stopFlagging();
10273
$this->stopReplying();
10374
}
10475

10576
private function updateFlagCount(): void
10677
{
107-
$this->flagCount = Flag::count($this->comment);
78+
$this->flagCount = $this->comment->flagCount();
79+
$this->userFlagged = $this->comment->userFlagged();
10880
}
10981

110-
#[On('comment-flagged.{comment.id}')]
111-
public function addUserFlag(): void
82+
public function addUserFlag(int $id): void
11283
{
113-
\Log::debug('CommentComponent::addUserFlag');
84+
if ($id !== $this->comment->id) {
85+
return;
86+
}
87+
11488
$this->userFlagged = true;
115-
$this->flagCount++;
89+
// Requery as flag may have just been edited, not added
90+
$this->updateFlagCount();
91+
$this->stopFlagging();
11692
}
11793

118-
#[On('comment-flag-deleted.{comment.id}')]
119-
public function removeUserFlag(): void
94+
public function removeUserFlag(int $id): void
12095
{
96+
if ($id !== $this->comment->id) {
97+
return;
98+
}
99+
121100
$this->userFlagged = false;
122-
$this->flagCount--;
101+
// Requery as technically multiple flags could exist (though they shouldn't)
102+
$this->updateFlagCount();
103+
$this->stopFlagging();
123104
}
124105
}

app/Livewire/Flags/FlagComponent.php

Lines changed: 88 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
use App\Traits\AuthStatusTrait;
1212
use App\Traits\LoggingTrait;
1313
use App\Traits\TypeTrait;
14-
use Exception;
1514
use Illuminate\Contracts\View\View;
15+
use Livewire\Attributes\Locked;
1616
use Livewire\Component;
1717
use Maize\Markable\Exceptions\InvalidMarkValueException;
1818

@@ -26,25 +26,42 @@ final class FlagComponent extends Component
2626
private const string MODEL_PATH = 'app\\models\\';
2727

2828
public Comment|Post $model;
29+
public Flag|null $userFlag = null;
30+
31+
#[Locked]
32+
public int $modelId;
33+
#[Locked]
34+
public string $type;
35+
#[Locked]
2936
public int $flagCount = 0;
37+
#[Locked]
3038
public string $iconFilename = 'flag';
31-
public string $note = '';
39+
#[Locked]
40+
public string $titleText;
41+
#[Locked]
3242
public array $flagReasons = [];
43+
44+
// Actual values we interact with
45+
public string $note = '';
3346
public string $selectedReason = '';
34-
public bool $showForm = false;
3547
public bool $showNoteField = false;
36-
public string $titleText;
37-
public string $type;
38-
public bool $userFlagged = false;
48+
public bool $formClosed = false;
3949

4050
public function mount(
4151
Comment|Post $model,
4252
): void {
4353
$configReasons = config('markable.allowed_values.flag', []);
44-
$this->flagReasons = is_array($configReasons) ? $configReasons : [];
4554

55+
$this->flagReasons = is_array($configReasons) ? array_combine(
56+
array_map(fn($reason) => mb_strtolower(preg_replace('/[^\w]+/', '-', $reason)), $configReasons),
57+
$configReasons,
58+
) : [];
59+
60+
$this->formClosed = false;
4661
$this->model = $model;
62+
$this->modelId = $model->id;
4763
$this->type = $this->getType();
64+
$this->userFlag = $this->getUserFlag();
4865
$this->updateFlagData();
4966
}
5067

@@ -53,77 +70,98 @@ public function render(): View
5370
return view('livewire.flags.flag-component');
5471
}
5572

56-
public function flagReasonSelected(string $selectedReason): void
73+
public function isNoteVisibleForReason(string $reason): bool
5774
{
58-
if ($selectedReason === self::FLAG_WITH_NOTE) {
59-
$this->showNoteField = true;
60-
} else {
61-
$this->showNoteField = false;
62-
$this->reset('note');
63-
}
75+
return $reason === self::FLAG_WITH_NOTE;
76+
}
6477

65-
$this->selectedReason = $selectedReason;
78+
public function getUserFlag(): Flag | null
79+
{
80+
return Flag::where([
81+
'user_id' => auth()->id(),
82+
'markable_id' => $this->model->getKey(),
83+
'markable_type' => $this->model->getMorphClass(),
84+
])->first();
6685
}
6786

68-
public function updateFlagData(): void
87+
protected function updateFlagData(): void
6988
{
89+
$value = $this->userFlag?->value ?? '';
90+
$this->selectedReason = in_array($value, $this->flagReasons) ? $value : '';
91+
$this->note = $this->userFlag?->metadata['note'] ?? '';
92+
$this->showNoteField = $this->isNoteVisibleForReason($this->selectedReason);
7093
$this->setTitleText();
7194
}
7295

73-
public function store(): void
96+
protected function deleteUserFlag(): void
7497
{
75-
// $rules = (new StoreFlagRequest())->rules();
98+
Flag::where([
99+
'user_id' => auth()->id(),
100+
'markable_id' => $this->model->getKey(),
101+
'markable_type' => $this->model->getMorphClass(),
102+
])->get()->each->delete();
103+
}
76104

77-
// $this->validate($rules);
105+
public function store(): void
106+
{
78107
$metadata = [];
79108

80109
$selectedReason = mb_trim($this->selectedReason);
110+
$noteText = mb_trim($this->note);
81111

82-
try {
83-
if ($selectedReason === self::FLAG_WITH_NOTE && mb_strlen($this->note) > 0) {
84-
$metadata = ['note' => $this->note];
85-
}
86-
87-
$event = $this->type === 'comment' ?
88-
LivewireEventEnum::CommentFlagged->value :
89-
LivewireEventEnum::PostFlagged->value;
90-
91-
$this->dispatchEvent($event);
112+
if ($this->isNoteVisibleForReason($selectedReason) && mb_strlen($noteText) > 0) {
113+
$metadata = ['note' => $noteText];
114+
}
92115

93-
Flag::add($this->model, auth()->user(), $selectedReason, $metadata);
116+
// Just cancel the change if the form is unmodified
117+
if ($selectedReason === ($this->userFlag?->value ?? '') && $metadata === $this->userFlag?->metadata) {
118+
$this->cancel();
119+
return;
120+
}
94121

95-
$this->showForm = false;
122+
$event = $this->type === 'comment' ?
123+
LivewireEventEnum::CommentFlagged :
124+
LivewireEventEnum::PostFlagged;
96125

97-
$this->userFlagged = false;
126+
// Stop rendering while we are modifying data
127+
$this->formClosed = true;
98128

99-
$this->updateFlagData();
129+
$this->deleteUserFlag();
130+
try {
131+
$this->userFlag = Flag::add($this->model, auth()->user(), $selectedReason, $metadata);
100132
} catch (InvalidMarkValueException $exception) {
101133
$this->logError($exception);
102134
}
135+
$this->updateFlagData();
136+
137+
$this->dispatchEvent($event);
103138
}
104139

105140
public function delete(): void
106141
{
107-
try {
108-
$event = $this->type === 'comment' ?
109-
LivewireEventEnum::CommentFlagDeleted->value :
110-
LivewireEventEnum::PostFlagDeleted->value;
111-
112-
$this->dispatchEvent($event);
113142

114-
Flag::remove($this->model, auth()->user());
143+
// Stop rendering while we are modifying data
144+
$this->formClosed = true;
145+
$event = $this->type === 'comment' ?
146+
LivewireEventEnum::CommentFlagDeleted :
147+
LivewireEventEnum::PostFlagDeleted;
115148

116-
$this->userFlagged = false;
149+
$this->deleteUserFlag();
150+
$this->userFlag = null;
151+
$this->updateFlagData();
117152

118-
$this->updateFlagData();
119-
} catch (Exception $exception) {
120-
$this->logError($exception);
121-
}
153+
$this->dispatchEvent($event);
122154
}
123155

124-
public function toggleForm(): void
156+
public function cancel(): void
125157
{
126-
$this->showForm = !$this->showForm;
158+
$this->formClosed = true;
159+
160+
$event = $this->type === 'comment' ?
161+
LivewireEventEnum::CommentFlagCancelled :
162+
LivewireEventEnum::PostFlagCancelled;
163+
164+
$this->dispatchEvent($event);
127165
}
128166

129167
private function getType(): string
@@ -137,11 +175,11 @@ private function setTitleText(): void
137175
{
138176
$flagText = 'Flag this ' . $this->type;
139177

140-
$this->titleText = $this->userFlagged ? trans('Remove flag') : trans($flagText);
178+
$this->titleText = $this->userFlag ? trans('Remove or edit flag') : trans($flagText);
141179
}
142180

143-
private function dispatchEvent(string $event): void
181+
private function dispatchEvent(LivewireEventEnum $event): void
144182
{
145-
$this->dispatch($event, id: $this->model->id);
183+
$this->dispatch($event->value, id: $this->model->id);
146184
}
147185
}

app/Models/Comment.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,19 @@ public function favoriteCount(): int
9797

9898
public function flagCount(): int
9999
{
100-
return Flag::count($this);
100+
return Flag::where([
101+
'markable_id' => $this->getKey(),
102+
'markable_type' => $this->getMorphClass(),
103+
])->count();
104+
}
105+
106+
public function userFlagged(): bool
107+
{
108+
return Flag::where([
109+
'user_id' => auth()->id(),
110+
'markable_id' => $this->getKey(),
111+
'markable_type' => $this->getMorphClass(),
112+
])->exists();
101113
}
102114

103115
public function post(): BelongsTo

app/Models/Post.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,19 @@ public function favoriteCount(): int
155155

156156
public function flagCount(): int
157157
{
158-
return Flag::count($this);
158+
return Flag::where([
159+
'markable_id' => $this->getKey(),
160+
'markable_type' => $this->getMorphClass(),
161+
])->count();
162+
}
163+
164+
public function userFlagged(): bool
165+
{
166+
return Flag::where([
167+
'user_id' => auth()->id(),
168+
'markable_id' => $this->getKey(),
169+
'markable_type' => $this->getMorphClass(),
170+
])->exists();
159171
}
160172

161173
public function next(): Post|null

app/Traits/CommentComponentTrait.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,15 @@ public function stopEditing(): void
4848
public function startFlagging(): void
4949
{
5050
$this->isFlagging = true;
51+
$this->isFlagLoading = false;
5152
$this->stopEditing();
5253
$this->stopReplying();
5354
}
5455

5556
public function stopFlagging(): void
5657
{
5758
$this->isFlagging = false;
59+
$this->isFlagLoading = false;
5860
}
5961

6062
public function startReplying(): void

0 commit comments

Comments
 (0)