From 2c92b4b5f8a6a91483baf8052331fd3915fa7e52 Mon Sep 17 00:00:00 2001 From: Arthur Monney Date: Tue, 17 Dec 2024 23:03:57 +0100 Subject: [PATCH] feat: [LAR-112] add comment form on discussion --- app/Actions/Replies/CreateReply.php | 6 +- app/Actions/Replies/LikeReply.php | 2 +- .../Components/Discussion/Comment.php | 52 ++++++++++ .../Components/Discussion/Comments.php | 95 +++++++++++++++++++ app/Livewire/Discussions/AddComment.php | 55 ----------- app/Livewire/Discussions/Comment.php | 54 ----------- app/Livewire/Discussions/Comments.php | 46 --------- app/Livewire/Pages/Account/Profile.php | 12 --- .../Pages/Discussions/SingleDiscussion.php | 5 +- app/Policies/ReplyPolicy.php | 5 + composer.json | 1 + composer.lock | 77 ++++++++++++++- lang/en/global.php | 1 + lang/en/notifications.php | 2 + lang/en/pages/discussion.php | 2 + lang/fr/global.php | 1 + lang/fr/notifications.php | 2 + lang/fr/pages/discussion.php | 2 + .../views/components/layouts/header.blade.php | 4 +- resources/views/components/skeleton.blade.php | 12 --- .../components/skeletons/comment.blade.php | 8 ++ resources/views/discussions/edit.blade.php | 18 ---- resources/views/discussions/new.blade.php | 20 ---- .../components/discussion/comment.blade.php | 66 +++++++++++++ .../components/discussion/comments.blade.php | 39 ++++++++ .../discussions/add-comment.blade.php | 82 ---------------- .../livewire/discussions/comment.blade.php | 76 --------------- .../livewire/discussions/comments.blade.php | 27 ------ .../discussions/single-discussion.blade.php | 2 +- 29 files changed, 360 insertions(+), 414 deletions(-) create mode 100644 app/Livewire/Components/Discussion/Comment.php create mode 100644 app/Livewire/Components/Discussion/Comments.php delete mode 100644 app/Livewire/Discussions/AddComment.php delete mode 100644 app/Livewire/Discussions/Comment.php delete mode 100644 app/Livewire/Discussions/Comments.php delete mode 100644 resources/views/components/skeleton.blade.php create mode 100644 resources/views/components/skeletons/comment.blade.php delete mode 100644 resources/views/discussions/edit.blade.php delete mode 100644 resources/views/discussions/new.blade.php create mode 100644 resources/views/livewire/components/discussion/comment.blade.php create mode 100644 resources/views/livewire/components/discussion/comments.blade.php delete mode 100644 resources/views/livewire/discussions/add-comment.blade.php delete mode 100644 resources/views/livewire/discussions/comment.blade.php delete mode 100644 resources/views/livewire/discussions/comments.blade.php diff --git a/app/Actions/Replies/CreateReply.php b/app/Actions/Replies/CreateReply.php index 76a0e44d..ac3cd4c2 100644 --- a/app/Actions/Replies/CreateReply.php +++ b/app/Actions/Replies/CreateReply.php @@ -12,17 +12,17 @@ final class CreateReply { - public function handle(string $body, User $user, Model $model): Reply + public function __invoke(string $body, User $user, Model $model): Reply { $reply = new Reply(['body' => $body]); $reply->authoredBy($user); - $reply->to($model); + $reply->to($model); // @phpstan-ignore-line $reply->save(); $user->givePoint(new ReplyCreated($model, $user)); // On envoie un event pour une nouvelle réponse à tous les abonnés de la discussion - event(new CommentWasAdded($reply, $model)); + event(new CommentWasAdded($reply, $model)); // @phpstan-ignore-line return $reply; } diff --git a/app/Actions/Replies/LikeReply.php b/app/Actions/Replies/LikeReply.php index 212a76e8..6fd48277 100644 --- a/app/Actions/Replies/LikeReply.php +++ b/app/Actions/Replies/LikeReply.php @@ -10,7 +10,7 @@ final class LikeReply { - public function handle(User $user, Reply $reply, string $reaction = 'love'): void + public function __invoke(User $user, Reply $reply, string $reaction = 'love'): void { /** @var Reaction $react */ $react = Reaction::query()->where('name', $reaction)->first(); diff --git a/app/Livewire/Components/Discussion/Comment.php b/app/Livewire/Components/Discussion/Comment.php new file mode 100644 index 00000000..02c2a012 --- /dev/null +++ b/app/Livewire/Components/Discussion/Comment.php @@ -0,0 +1,52 @@ +comment->delete(); + + Notification::make() + ->title(__('notifications.discussion.delete_comment')) + ->success() + ->send(); + + $this->dispatch('comments.change'); + } + + public function toggleLike(): void + { + $this->authorize('like', $this->comment); + + app()->call(LikeReply::class, [ + 'user' => auth()->user(), + 'reply' => $this->comment, + ]); + } + + public function placeholder(): View + { + return view('components.skeletons.comment'); + } + + public function render(): View + { + return view('livewire.components.discussion.comment', [ + 'count' => $this->comment->getReactionsSummary()->sum('count'), + ]); + } +} diff --git a/app/Livewire/Components/Discussion/Comments.php b/app/Livewire/Components/Discussion/Comments.php new file mode 100644 index 00000000..540bdc54 --- /dev/null +++ b/app/Livewire/Components/Discussion/Comments.php @@ -0,0 +1,95 @@ +form->fill(); + } + + public function form(Form $form): Form + { + return $form + ->schema([ + Forms\Components\Textarea::make('body') + ->hiddenLabel() + ->placeholder(__('pages/discussion.placeholder')) + ->rows(3) + ->required(), + ]) + ->statePath('data'); + } + + public function save(): void + { + $this->validate(); + + app()->call(CreateReply::class, [ + 'body' => data_get($this->form->getState(), 'body'), + 'user' => Auth::user(), + 'model' => $this->discussion, + ]); + + Notification::make() + ->title(__('notifications.discussion.save_comment')) + ->success() + ->send(); + + $this->dispatch('comments.change'); + + $this->reset('data'); + } + + #[Computed] + #[On('comments.change')] + public function comments(): Collection + { + $replies = collect(); + + foreach ($this->discussion->replies->load(['allChildReplies', 'user']) as $reply) { + /** @var Reply $reply */ + if ($reply->allChildReplies->isNotEmpty()) { + foreach ($reply->allChildReplies as $childReply) { + $replies->add($childReply); + } + } + + $replies->add($reply); + } + + return $replies; + } + + public function render(): View + { + return view('livewire.components.discussion.comments'); + } +} diff --git a/app/Livewire/Discussions/AddComment.php b/app/Livewire/Discussions/AddComment.php deleted file mode 100644 index 8d2d7bab..00000000 --- a/app/Livewire/Discussions/AddComment.php +++ /dev/null @@ -1,55 +0,0 @@ -discussion = $discussion; - } - - public function saveComment(): void - { - $this->validate( - ['body' => 'required'], - ['body.required' => __('Votre commentaire ne peut pas être vide')] - ); - - $comment = app(CreateReply::class)->handle($this->body, Auth::user(), $this->discussion); // @phpstan-ignore-line - - $this->dispatch('$refresh')->self(); - - $this->dispatch('scrollToComment', ['id' => $comment->id]); - - Notification::make() - ->title(__('Votre commentaire a été ajouté!')) - ->success() - ->duration(5000) - ->send(); - - $this->reset(); - } - - public function render(): View - { - return view('livewire.discussions.add-comment'); - } -} diff --git a/app/Livewire/Discussions/Comment.php b/app/Livewire/Discussions/Comment.php deleted file mode 100644 index 1f1e0061..00000000 --- a/app/Livewire/Discussions/Comment.php +++ /dev/null @@ -1,54 +0,0 @@ -comment->delete(); - - Notification::make() - ->title(__('Votre commentaire a été supprimé.')) - ->success() - ->duration(5000) - ->send(); - - $this->dispatch('reloadComment'); - } - - public function toggleLike(): void - { - if (! Auth::check()) { - Notification::make() - ->title(__('Vous devez être connecté pour liker un commentaire.')) - ->danger() - ->duration(5000) - ->send(); - - return; - } - - app(LikeReply::class)->handle(auth()->user(), $this->comment); // @phpstan-ignore-line - - $this->dispatch('reloadComment')->self(); - } - - public function render(): View - { - return view('livewire.discussions.comment', [ - 'count' => $this->comment->getReactionsSummary()->sum('count'), - ]); - } -} diff --git a/app/Livewire/Discussions/Comments.php b/app/Livewire/Discussions/Comments.php deleted file mode 100644 index 5b80ed5f..00000000 --- a/app/Livewire/Discussions/Comments.php +++ /dev/null @@ -1,46 +0,0 @@ -discussion = $discussion; - } - - public function getCommentsProperty(): Collection - { - $replies = collect(); - - foreach ($this->discussion->replies->load(['allChildReplies', 'user']) as $reply) { - /** @var Reply $reply */ - if ($reply->allChildReplies->isNotEmpty()) { - foreach ($reply->allChildReplies as $childReply) { - $replies->add($childReply); - } - } - - $replies->add($reply); - } - - return $replies; - } - - #[On('reloadComments')] - public function render(): View - { - return view('livewire.discussions.comments'); - } -} diff --git a/app/Livewire/Pages/Account/Profile.php b/app/Livewire/Pages/Account/Profile.php index 52c26445..d7f0fdf6 100644 --- a/app/Livewire/Pages/Account/Profile.php +++ b/app/Livewire/Pages/Account/Profile.php @@ -12,18 +12,6 @@ final class Profile extends Component { public User $user; - public function mount(User $user): void - { - $this->user = $user->load([ - 'activities', - 'articles', - 'articles.tags', - 'discussions', - 'discussions.tags', - 'threads', - ]); - } - public function render(): View { return view('livewire.pages.account.profile', [ diff --git a/app/Livewire/Pages/Discussions/SingleDiscussion.php b/app/Livewire/Pages/Discussions/SingleDiscussion.php index 5fbb2a91..d39ea76f 100644 --- a/app/Livewire/Pages/Discussions/SingleDiscussion.php +++ b/app/Livewire/Pages/Discussions/SingleDiscussion.php @@ -37,7 +37,7 @@ public function mount(Discussion $discussion): void ->twitterSite('laravelcm') ->withUrl(); - $this->discussion = $discussion->load('tags'); + $this->discussion = $discussion->load('tags', 'replies', 'reactions', 'replies.user'); } public function editAction(): Action @@ -85,6 +85,7 @@ public function deleteAction(): Action public function render(): View { - return view('livewire.pages.discussions.single-discussion')->title($this->discussion->title); + return view('livewire.pages.discussions.single-discussion') + ->title($this->discussion->title); } } diff --git a/app/Policies/ReplyPolicy.php b/app/Policies/ReplyPolicy.php index 366a1468..cbd75faf 100644 --- a/app/Policies/ReplyPolicy.php +++ b/app/Policies/ReplyPolicy.php @@ -36,4 +36,9 @@ public function report(User $user, Reply $reply): bool { return $user->hasVerifiedEmail() && ! $reply->isAuthoredBy($user); } + + public function like(User $user, Reply $reply): bool + { + return $user->hasVerifiedEmail(); + } } diff --git a/composer.json b/composer.json index a3042ace..4955008c 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,7 @@ "archtechx/laravel-seo": "^0.10", "awcodes/filament-badgeable-column": "^2.3", "blade-ui-kit/blade-heroicons": "^2.4", + "codeat3/blade-phosphor-icons": "^2.0", "cyrildewit/eloquent-viewable": "^7.0", "doctrine/dbal": "^3.6.4", "dutchcodingcompany/livewire-recaptcha": "^1.0", diff --git a/composer.lock b/composer.lock index 106578b2..8889bac6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "bfdc09f1824bdb94242b92829f3141d5", + "content-hash": "982a2d074809764b1ecdbbfe8fe23a64", "packages": [ { "name": "abraham/twitteroauth", @@ -1398,6 +1398,77 @@ ], "time": "2023-12-11T17:09:12+00:00" }, + { + "name": "codeat3/blade-phosphor-icons", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/codeat3/blade-phosphor-icons.git", + "reference": "d7d7c027ce0a0ce2f5e5c68688595f0a816e7508" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/codeat3/blade-phosphor-icons/zipball/d7d7c027ce0a0ce2f5e5c68688595f0a816e7508", + "reference": "d7d7c027ce0a0ce2f5e5c68688595f0a816e7508", + "shasum": "" + }, + "require": { + "blade-ui-kit/blade-icons": "^1.1", + "illuminate/support": "^8.0|^9.0|^10.0|^11.0", + "php": "^7.4|^8.0" + }, + "require-dev": { + "codeat3/blade-icon-generation-helpers": "^0.8", + "codeat3/phpcs-styles": "^1.0", + "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0", + "phpunit/phpunit": "^9.0|^10.5|^11.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Codeat3\\BladePhosphorIcons\\BladePhosphorIconsServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Codeat3\\BladePhosphorIcons\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Swapnil Sarwe", + "homepage": "https://swapnilsarwe.com" + }, + { + "name": "Dries Vints", + "homepage": "https://driesvints.com" + } + ], + "description": "A package to easily make use of \"Phosphor Icons\" in your Laravel Blade views.", + "homepage": "https://github.com/codeat3/blade-phosphor-icons", + "keywords": [ + "blade", + "laravel", + "phosphor-icons" + ], + "support": { + "issues": "https://github.com/codeat3/blade-phosphor-icons/issues", + "source": "https://github.com/codeat3/blade-phosphor-icons/tree/2.2.0" + }, + "funding": [ + { + "url": "https://github.com/swapnilsarwe", + "type": "github" + } + ], + "time": "2024-04-08T19:02:17+00:00" + }, { "name": "composer/ca-bundle", "version": "1.5.4", @@ -17359,7 +17430,7 @@ ], "aliases": [], "minimum-stability": "dev", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": true, "prefer-lowest": false, "platform": { @@ -17367,6 +17438,6 @@ "ext-fileinfo": "*", "ext-json": "*" }, - "platform-dev": [], + "platform-dev": {}, "plugin-api-version": "2.6.0" } diff --git a/lang/en/global.php b/lang/en/global.php index cd249440..d8209dc0 100644 --- a/lang/en/global.php +++ b/lang/en/global.php @@ -101,5 +101,6 @@ 'joined' => 'Joined', 'website' => 'Website', 'characters' => ':number characters', + 'like' => ':count like', ]; diff --git a/lang/en/notifications.php b/lang/en/notifications.php index ffef9fb6..4a2a1a54 100644 --- a/lang/en/notifications.php +++ b/lang/en/notifications.php @@ -49,5 +49,7 @@ 'created' => 'Discussion was successfully created.', 'updated' => 'Discussion was successfully updated.', 'deleted' => 'Discussion was successfully deleted.', + 'save_comment' => 'Your comment has been saved', + 'delete_comment' => 'Your comment has been deleted', ], ]; diff --git a/lang/en/pages/discussion.php b/lang/en/pages/discussion.php index 530ed94c..150c0f44 100644 --- a/lang/en/pages/discussion.php +++ b/lang/en/pages/discussion.php @@ -21,6 +21,7 @@ 'comments_count' => 'Comments (:count)', 'convert_to_thread' => 'Convert to thread', 'confirm_conversion' => 'Confirm conversion', + 'confirm_comment_remove' => 'Are you sure you want to delete this comment?', 'text_confirmation' => 'Do you really want to turn this discussion into a topic?', 'converted_by_admin' => [ 'subject' => 'Discussion Converted to Thread by the administrator', @@ -38,5 +39,6 @@ 'thank_you_line' => 'Thank you for your participation!', ], 'min_discussion_length' => '160 characters maximum', + 'placeholder' => 'Your comment here...', ]; diff --git a/lang/fr/global.php b/lang/fr/global.php index 52482d92..6caea354 100644 --- a/lang/fr/global.php +++ b/lang/fr/global.php @@ -101,5 +101,6 @@ 'joined' => 'Inscrit', 'website' => 'Site internet', 'characters' => ':number caractères', + 'like' => ':count j\'aime', ]; diff --git a/lang/fr/notifications.php b/lang/fr/notifications.php index 1ab1cfaa..853214f1 100644 --- a/lang/fr/notifications.php +++ b/lang/fr/notifications.php @@ -49,5 +49,7 @@ 'created' => 'Votre discussion à été créée.', 'updated' => 'Votre discussion à été modifiée.', 'deleted' => 'Le sujet de discussion a été supprimé.', + 'save_comment' => 'Votre commentaire a été enregistré', + 'delete_comment' => 'Votre commentaire a été supprimé.', ], ]; diff --git a/lang/fr/pages/discussion.php b/lang/fr/pages/discussion.php index d1539f2c..3ff41588 100644 --- a/lang/fr/pages/discussion.php +++ b/lang/fr/pages/discussion.php @@ -21,6 +21,7 @@ 'comments_count' => 'Commentaires (:count)', 'convert_to_thread' => 'Convertir en sujet', 'confirm_conversion' => 'Confirmez la conversion', + 'confirm_comment_remove' => 'Êtes-vous sûr de vouloir supprimer ce commentaire ?', 'text_confirmation' => 'Voulez-vous vraiment transformer cette discussion en sujet de forum?', 'converted_by_admin' => [ 'subject' => 'Discussion convertie en sujet par l\'administrateur', @@ -38,5 +39,6 @@ 'thank_you_line' => 'Merci pour votre participation !', ], 'min_discussion_length' => 'Maximum de 160 caractères.', + 'placeholder' => 'Votre commentaire', ]; diff --git a/resources/views/components/layouts/header.blade.php b/resources/views/components/layouts/header.blade.php index b81f327d..e69d970f 100644 --- a/resources/views/components/layouts/header.blade.php +++ b/resources/views/components/layouts/header.blade.php @@ -69,7 +69,7 @@ class="absolute inset-x-0 top-0 origin-top-right transform p-2 transition" @click.away="open = false" style="display: none" > -
+
@@ -78,7 +78,7 @@ class="absolute inset-x-0 top-0 origin-top-right transform p-2 transition"
+
+
+ @endcan +
+ + + + +
+
+
diff --git a/resources/views/livewire/components/discussion/comments.blade.php b/resources/views/livewire/components/discussion/comments.blade.php new file mode 100644 index 00000000..ac283b48 --- /dev/null +++ b/resources/views/livewire/components/discussion/comments.blade.php @@ -0,0 +1,39 @@ +
+ @if ($this->comments->isNotEmpty()) + + @endif + + @auth +
+ + +
+ {{ $this->form }} + +
+ + {{ __('actions.save') }} + +
+
+
+ @else +

+ {{ __('global.need') }} + + {{ __('pages/auth.login.page_title') }} + + {{ __('global.or') }} + + {{ __('pages/auth.register.page_title') }} + + {{ __('pages/forum.collaborate_thread') }} +

+ @endauth +
diff --git a/resources/views/livewire/discussions/add-comment.blade.php b/resources/views/livewire/discussions/add-comment.blade.php deleted file mode 100644 index 64826786..00000000 --- a/resources/views/livewire/discussions/add-comment.blade.php +++ /dev/null @@ -1,82 +0,0 @@ -@php - $user = \Illuminate\Support\Facades\Auth::user(); -@endphp - -
-
-
- - - - - - -
-
- -
- - -
- @if ($isRoot) -

- {{ __('Veuillez vous assurer d\'avoir lu nos') }} - - {{ __('règles de conduite') }} - - {{ __('avant de répondre à ce fil de conversation.') }} -

- @endif - -
- @if ($isReply) - {{ __('Annuler') }} - @endif - - - - {{ __('Commenter') }} - -
-
-
- @guest -
-

- {{ __('Veuillez vous') }} - - {{ __('connecter') }} - - {{ __('ou') }} - - {{ __('créer un compte') }} - - {{ __('pour participer à cette conversation') }}. -

-
- @endguest -
diff --git a/resources/views/livewire/discussions/comment.blade.php b/resources/views/livewire/discussions/comment.blade.php deleted file mode 100644 index 1ed73302..00000000 --- a/resources/views/livewire/discussions/comment.blade.php +++ /dev/null @@ -1,76 +0,0 @@ -
  • -
    - @if ($comment->allChildReplies->isNotEmpty()) - - @endif - -
    -
    - -
    -
    -
    - - {{ $comment->user->name }} - - - {{ $comment->user->getPoints() }} XP - - - - - @can('update', $comment) -
    - -
    - -
    -
    - @endcan -
    - -
    - -
    -
    -
    -
    -
  • diff --git a/resources/views/livewire/discussions/comments.blade.php b/resources/views/livewire/discussions/comments.blade.php deleted file mode 100644 index a597b5ed..00000000 --- a/resources/views/livewire/discussions/comments.blade.php +++ /dev/null @@ -1,27 +0,0 @@ -
    - @if ($this->comments->isNotEmpty()) -
    -
      - @foreach ($this->comments as $comment) - - @endforeach -
    -
    - @endif - - @auth - - @else -

    - {{ __('global.need') }} - - {{ __('pages/auth.login.page_title') }} - - {{ __('global.or') }} - - {{ __('pages/auth.register.page_title') }} - - {{ __('pages/forum.collaborate_thread') }} -

    - @endauth -
    diff --git a/resources/views/livewire/pages/discussions/single-discussion.blade.php b/resources/views/livewire/pages/discussions/single-discussion.blade.php index e871b04b..1848d414 100644 --- a/resources/views/livewire/pages/discussions/single-discussion.blade.php +++ b/resources/views/livewire/pages/discussions/single-discussion.blade.php @@ -121,7 +121,7 @@ class="mx-auto mt-6 text-sm prose prose-sm prose-green max-w-none dark:prose-inv @endauth - +