diff --git a/app/Enums/LivewireEventEnum.php b/app/Enums/LivewireEventEnum.php
index 2e09c662..7806b95e 100644
--- a/app/Enums/LivewireEventEnum.php
+++ b/app/Enums/LivewireEventEnum.php
@@ -7,6 +7,7 @@
enum LivewireEventEnum: string
{
case CommentDeleted = 'comment-deleted';
+ case CommentFlagCancelled = 'comment-flag-cancelled';
case CommentFlagDeleted = 'comment-flag-deleted';
case CommentFlagged = 'comment-flagged';
case CommentStored = 'comment-stored';
@@ -19,6 +20,7 @@ enum LivewireEventEnum: string
case FlagDeleted = 'flag-deleted';
case HideFlagCommentForm = 'hide-flag-comment-form';
case PostDeleted = 'post-deleted';
+ case PostFlagCancelled = 'post-flag-cancelled';
case PostFlagDeleted = 'post-flag-deleted';
case PostFlagged = 'post-flagged';
case PostStored = 'post-stored';
diff --git a/app/Livewire/Comments/CommentComponent.php b/app/Livewire/Comments/CommentComponent.php
index cf65ed09..a546d46f 100644
--- a/app/Livewire/Comments/CommentComponent.php
+++ b/app/Livewire/Comments/CommentComponent.php
@@ -11,7 +11,6 @@
use App\Models\User;
use App\Traits\CommentComponentTrait;
use Illuminate\Contracts\View\View;
-use Illuminate\Support\Facades\DB;
use Livewire\Attributes\On;
use Livewire\Component;
@@ -24,13 +23,12 @@ final class CommentComponent extends Component
public string $body = '';
public ?int $parentId = null;
public int $flagCount = 0;
- public string $flagIconFilename = 'flag';
- public string $flagButtonText = '';
public bool $userFlagged = false;
// State
public bool $isEditing = false;
public bool $isFlagging = false;
+ public bool $isFlagLoading = false;
public bool $isReplying = false;
public Comment $comment;
@@ -52,10 +50,6 @@ public function mount(Comment $comment, Post $post): void
$this->user = auth()->user() ?? null;
$this->updateFlagCount();
- $this->hasUserFlagged();
-
- $this->flagIconFilename = $this->getFlagIconFilename();
- $this->flagButtonText = $this->getFlagTitleText();
}
public function render(): View
@@ -63,31 +57,9 @@ public function render(): View
return view('livewire.comments.comment-component');
}
- private function getFlagIconFilename(): string
- {
- return $this->userFlagged ? 'flag-fill' : 'flag';
- }
-
- private function getFlagTitleText(): string
- {
- return $this->userFlagged ? trans('Remove flag') : trans('Flag this comment');
- }
-
- private function hasUserFlagged(): void
- {
- $userFlagCount = DB::table(table: 'markable_flags')
- ->where(column: 'user_id', operator: '=', value: auth()->id())
- ->where(column: 'markable_id', operator: '=', value: $this->comment->id)
- ->where(column: 'markable_type', operator: 'LIKE', value: '%Comment%')
- ->count();
-
- $this->userFlagged = $userFlagCount > 0;
- }
-
#[On([
LivewireEventEnum::CommentStored->value,
LivewireEventEnum::CommentDeleted->value,
- LivewireEventEnum::CommentFlagged->value,
LivewireEventEnum::CommentUpdated->value,
LivewireEventEnum::EscapeKeyClicked->value,
])]
@@ -98,27 +70,36 @@ public function closeForm(): void
]);
$this->stopEditing();
- $this->stopFlagging();
$this->stopReplying();
}
private function updateFlagCount(): void
{
- $this->flagCount = Flag::count($this->comment);
+ $this->flagCount = $this->comment->flagCount();
+ $this->userFlagged = $this->comment->userFlagged();
}
- #[On('comment-flagged.{comment.id}')]
- public function addUserFlag(): void
+ public function addUserFlag(int $id): void
{
- \Log::debug('CommentComponent::addUserFlag');
+ if ($id !== $this->comment->id) {
+ return;
+ }
+
$this->userFlagged = true;
- $this->flagCount++;
+ // Requery as flag may have just been edited, not added
+ $this->updateFlagCount();
+ $this->stopFlagging();
}
- #[On('comment-flag-deleted.{comment.id}')]
- public function removeUserFlag(): void
+ public function removeUserFlag(int $id): void
{
+ if ($id !== $this->comment->id) {
+ return;
+ }
+
$this->userFlagged = false;
- $this->flagCount--;
+ // Requery as technically multiple flags could exist (though they shouldn't)
+ $this->updateFlagCount();
+ $this->stopFlagging();
}
}
diff --git a/app/Livewire/Flags/FlagComponent.php b/app/Livewire/Flags/FlagComponent.php
index 2470d3a4..765b1b70 100644
--- a/app/Livewire/Flags/FlagComponent.php
+++ b/app/Livewire/Flags/FlagComponent.php
@@ -11,8 +11,8 @@
use App\Traits\AuthStatusTrait;
use App\Traits\LoggingTrait;
use App\Traits\TypeTrait;
-use Exception;
use Illuminate\Contracts\View\View;
+use Livewire\Attributes\Locked;
use Livewire\Component;
use Maize\Markable\Exceptions\InvalidMarkValueException;
@@ -26,25 +26,42 @@ final class FlagComponent extends Component
private const string MODEL_PATH = 'app\\models\\';
public Comment|Post $model;
+ public Flag|null $userFlag = null;
+
+ #[Locked]
+ public int $modelId;
+ #[Locked]
+ public string $type;
+ #[Locked]
public int $flagCount = 0;
+ #[Locked]
public string $iconFilename = 'flag';
- public string $note = '';
+ #[Locked]
+ public string $titleText;
+ #[Locked]
public array $flagReasons = [];
+
+ // Actual values we interact with
+ public string $note = '';
public string $selectedReason = '';
- public bool $showForm = false;
public bool $showNoteField = false;
- public string $titleText;
- public string $type;
- public bool $userFlagged = false;
+ public bool $formClosed = false;
public function mount(
Comment|Post $model,
): void {
$configReasons = config('markable.allowed_values.flag', []);
- $this->flagReasons = is_array($configReasons) ? $configReasons : [];
+ $this->flagReasons = is_array($configReasons) ? array_combine(
+ array_map(fn($reason) => mb_strtolower(preg_replace('/[^\w]+/', '-', $reason)), $configReasons),
+ $configReasons,
+ ) : [];
+
+ $this->formClosed = false;
$this->model = $model;
+ $this->modelId = $model->id;
$this->type = $this->getType();
+ $this->userFlag = $this->getUserFlag();
$this->updateFlagData();
}
@@ -53,77 +70,98 @@ public function render(): View
return view('livewire.flags.flag-component');
}
- public function flagReasonSelected(string $selectedReason): void
+ public function isNoteVisibleForReason(string $reason): bool
{
- if ($selectedReason === self::FLAG_WITH_NOTE) {
- $this->showNoteField = true;
- } else {
- $this->showNoteField = false;
- $this->reset('note');
- }
+ return $reason === self::FLAG_WITH_NOTE;
+ }
- $this->selectedReason = $selectedReason;
+ public function getUserFlag(): Flag | null
+ {
+ return Flag::where([
+ 'user_id' => auth()->id(),
+ 'markable_id' => $this->model->getKey(),
+ 'markable_type' => $this->model->getMorphClass(),
+ ])->first();
}
- public function updateFlagData(): void
+ protected function updateFlagData(): void
{
+ $value = $this->userFlag?->value ?? '';
+ $this->selectedReason = in_array($value, $this->flagReasons) ? $value : '';
+ $this->note = $this->userFlag?->metadata['note'] ?? '';
+ $this->showNoteField = $this->isNoteVisibleForReason($this->selectedReason);
$this->setTitleText();
}
- public function store(): void
+ protected function deleteUserFlag(): void
{
- // $rules = (new StoreFlagRequest())->rules();
+ Flag::where([
+ 'user_id' => auth()->id(),
+ 'markable_id' => $this->model->getKey(),
+ 'markable_type' => $this->model->getMorphClass(),
+ ])->get()->each->delete();
+ }
- // $this->validate($rules);
+ public function store(): void
+ {
$metadata = [];
$selectedReason = mb_trim($this->selectedReason);
+ $noteText = mb_trim($this->note);
- try {
- if ($selectedReason === self::FLAG_WITH_NOTE && mb_strlen($this->note) > 0) {
- $metadata = ['note' => $this->note];
- }
-
- $event = $this->type === 'comment' ?
- LivewireEventEnum::CommentFlagged->value :
- LivewireEventEnum::PostFlagged->value;
-
- $this->dispatchEvent($event);
+ if ($this->isNoteVisibleForReason($selectedReason) && mb_strlen($noteText) > 0) {
+ $metadata = ['note' => $noteText];
+ }
- Flag::add($this->model, auth()->user(), $selectedReason, $metadata);
+ // Just cancel the change if the form is unmodified
+ if ($selectedReason === ($this->userFlag?->value ?? '') && $metadata === $this->userFlag?->metadata) {
+ $this->cancel();
+ return;
+ }
- $this->showForm = false;
+ $event = $this->type === 'comment' ?
+ LivewireEventEnum::CommentFlagged :
+ LivewireEventEnum::PostFlagged;
- $this->userFlagged = false;
+ // Stop rendering while we are modifying data
+ $this->formClosed = true;
- $this->updateFlagData();
+ $this->deleteUserFlag();
+ try {
+ $this->userFlag = Flag::add($this->model, auth()->user(), $selectedReason, $metadata);
} catch (InvalidMarkValueException $exception) {
$this->logError($exception);
}
+ $this->updateFlagData();
+
+ $this->dispatchEvent($event);
}
public function delete(): void
{
- try {
- $event = $this->type === 'comment' ?
- LivewireEventEnum::CommentFlagDeleted->value :
- LivewireEventEnum::PostFlagDeleted->value;
-
- $this->dispatchEvent($event);
- Flag::remove($this->model, auth()->user());
+ // Stop rendering while we are modifying data
+ $this->formClosed = true;
+ $event = $this->type === 'comment' ?
+ LivewireEventEnum::CommentFlagDeleted :
+ LivewireEventEnum::PostFlagDeleted;
- $this->userFlagged = false;
+ $this->deleteUserFlag();
+ $this->userFlag = null;
+ $this->updateFlagData();
- $this->updateFlagData();
- } catch (Exception $exception) {
- $this->logError($exception);
- }
+ $this->dispatchEvent($event);
}
- public function toggleForm(): void
+ public function cancel(): void
{
- $this->showForm = !$this->showForm;
+ $this->formClosed = true;
+
+ $event = $this->type === 'comment' ?
+ LivewireEventEnum::CommentFlagCancelled :
+ LivewireEventEnum::PostFlagCancelled;
+
+ $this->dispatchEvent($event);
}
private function getType(): string
@@ -137,11 +175,11 @@ private function setTitleText(): void
{
$flagText = 'Flag this ' . $this->type;
- $this->titleText = $this->userFlagged ? trans('Remove flag') : trans($flagText);
+ $this->titleText = $this->userFlag ? trans('Remove or edit flag') : trans($flagText);
}
- private function dispatchEvent(string $event): void
+ private function dispatchEvent(LivewireEventEnum $event): void
{
- $this->dispatch($event, id: $this->model->id);
+ $this->dispatch($event->value, id: $this->model->id);
}
}
diff --git a/app/Models/Comment.php b/app/Models/Comment.php
index bfb8c4df..75910c5b 100644
--- a/app/Models/Comment.php
+++ b/app/Models/Comment.php
@@ -97,7 +97,19 @@ public function favoriteCount(): int
public function flagCount(): int
{
- return Flag::count($this);
+ return Flag::where([
+ 'markable_id' => $this->getKey(),
+ 'markable_type' => $this->getMorphClass(),
+ ])->count();
+ }
+
+ public function userFlagged(): bool
+ {
+ return Flag::where([
+ 'user_id' => auth()->id(),
+ 'markable_id' => $this->getKey(),
+ 'markable_type' => $this->getMorphClass(),
+ ])->exists();
}
public function post(): BelongsTo
diff --git a/app/Models/Post.php b/app/Models/Post.php
index f2eae3df..2f76bf97 100644
--- a/app/Models/Post.php
+++ b/app/Models/Post.php
@@ -155,7 +155,19 @@ public function favoriteCount(): int
public function flagCount(): int
{
- return Flag::count($this);
+ return Flag::where([
+ 'markable_id' => $this->getKey(),
+ 'markable_type' => $this->getMorphClass(),
+ ])->count();
+ }
+
+ public function userFlagged(): bool
+ {
+ return Flag::where([
+ 'user_id' => auth()->id(),
+ 'markable_id' => $this->getKey(),
+ 'markable_type' => $this->getMorphClass(),
+ ])->exists();
}
public function next(): Post|null
diff --git a/app/Traits/CommentComponentTrait.php b/app/Traits/CommentComponentTrait.php
index 9067c4f4..0f4e7c14 100644
--- a/app/Traits/CommentComponentTrait.php
+++ b/app/Traits/CommentComponentTrait.php
@@ -48,6 +48,7 @@ public function stopEditing(): void
public function startFlagging(): void
{
$this->isFlagging = true;
+ $this->isFlagLoading = false;
$this->stopEditing();
$this->stopReplying();
}
@@ -55,6 +56,7 @@ public function startFlagging(): void
public function stopFlagging(): void
{
$this->isFlagging = false;
+ $this->isFlagLoading = false;
}
public function startReplying(): void
diff --git a/public_html/images/icons/bars-rotate-fade.svg b/public_html/images/icons/bars-rotate-fade.svg
new file mode 100644
index 00000000..bdcd8fcf
--- /dev/null
+++ b/public_html/images/icons/bars-rotate-fade.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/sass/modules/_forms.scss b/resources/sass/modules/_forms.scss
index 9890da9d..06f8b489 100644
--- a/resources/sass/modules/_forms.scss
+++ b/resources/sass/modules/_forms.scss
@@ -19,7 +19,7 @@ main input[type='url'] {
}
textarea.comment-textarea {
- resize: block;
+ resize: vertical;
}
input[type='radio'] {
@@ -60,6 +60,12 @@ input[type='radio']:focus {
min-height: 10rem;
}
+label.radio-button-label {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+
.radio-button-label:focus-within {
color: var(--dark-base-color);
}
@@ -68,10 +74,22 @@ input[type='radio']:focus {
margin: 0 0 1rem 0;
}
-.fantastic {
+.flag-form fieldset legend {
margin-bottom: 1rem;
}
+.flag-form label:has(input[type='radio']) {
+ margin-bottom: 0.5rem;
+}
+
+.flag-form label:has(input[type='radio'].flag-reason-fantastic) {
+ margin-bottom: 1rem;
+}
+
+.flag-form textarea.flag-note {
+ resize: vertical;
+}
+
.input-icon-wrap {
display: flex;
flex-direction: row;
diff --git a/resources/sass/modules/_posts.scss b/resources/sass/modules/_posts.scss
index 30e8f2b1..7e62f35e 100644
--- a/resources/sass/modules/_posts.scss
+++ b/resources/sass/modules/_posts.scss
@@ -1,7 +1,22 @@
-
.comment,
.post {
margin: 0 0 2rem 0;
padding: 0.75rem;
border-radius: 0.25rem;
}
+
+.button:not(.loading)>span.icon:not(.loading-icon) {
+ display: inline;
+}
+
+.button:not(.loading)>span.icon.loading-icon {
+ display: none;
+}
+
+.button.loading>span.icon:not(.loading-icon) {
+ display: none;
+}
+
+.button.loading>span.icon.loading-icon {
+ display: inline;
+}
\ No newline at end of file
diff --git a/resources/views/livewire/comments/comment-component.blade.php b/resources/views/livewire/comments/comment-component.blade.php
index 32b548ec..7c97d34d 100644
--- a/resources/views/livewire/comments/comment-component.blade.php
+++ b/resources/views/livewire/comments/comment-component.blade.php
@@ -39,8 +39,11 @@
@if ($isFlagging === true)
@endif
diff --git a/resources/views/livewire/comments/comment-show-component.blade.php b/resources/views/livewire/comments/comment-show-component.blade.php
index da9368e1..cc782831 100644
--- a/resources/views/livewire/comments/comment-show-component.blade.php
+++ b/resources/views/livewire/comments/comment-show-component.blade.php
@@ -32,7 +32,7 @@ class="button footer-button"
@auth
- @if ($userFlagged === true)
+ @if ($userFlag !== null)