* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0 */ namespace App\Http\Livewire; use App\Achievements\UserMade100Comments; use App\Achievements\UserMade200Comments; use App\Achievements\UserMade300Comments; use App\Achievements\UserMade400Comments; use App\Achievements\UserMade500Comments; use App\Achievements\UserMade50Comments; use App\Achievements\UserMade600Comments; use App\Achievements\UserMade700Comments; use App\Achievements\UserMade800Comments; use App\Achievements\UserMade900Comments; use App\Achievements\UserMadeComment; use App\Achievements\UserMadeTenComments; use App\Models\Article; use App\Models\Collection; use App\Models\Playlist; use App\Models\Ticket; use App\Models\Torrent; use App\Models\TorrentRequest; use App\Models\User; use App\Notifications\NewComment; use App\Notifications\NewCommentTag; use App\Repositories\ChatRepository; use App\Traits\CastLivewireProperties; use Illuminate\Support\Facades\Notification; use Livewire\Attributes\Computed; use Livewire\Attributes\Validate; use Livewire\Component; use Livewire\WithPagination; class Comments extends Component { use CastLivewireProperties; use WithPagination; protected ChatRepository $chatRepository; public ?User $user; public null|Article|Collection|Playlist|Ticket|Torrent|TorrentRequest $model; public bool $anon = false; public int $perPage = 10; /** * @var array */ protected $listeners = [ 'refresh' => '$refresh', ]; #[Validate('required')] public string $newCommentState = ''; /** * @var array */ protected $validationAttributes = [ 'newCommentState' => 'comment', ]; final public function boot(ChatRepository $chatRepository): void { $this->chatRepository = $chatRepository; } final public function mount(): void { $this->user = auth()->user(); } final public function updating(string $field, mixed &$value): void { $this->castLivewireProperties($field, $value); } /** * @return array */ final public function taggedUsers(): array { preg_match_all('/@([\w\-]+)/', $this->newCommentState, $matches); return $matches[1]; } final public function loadMore(): void { $this->perPage += 10; } /// TODO: Find a better data structure to avoid this mess of exception cases final public function postComment(): void { // Authorization abort_unless($this->model instanceof Ticket || ($this->user->can_comment ?? $this->user->group->can_comment), 403, __('comment.rights-revoked')); abort_if($this->model instanceof Torrent && $this->model->status !== Torrent::APPROVED, 403, __('comment.torrent-status')); // Validation $this->validate(); $comment = $this->model->comments()->create([ 'content' => $this->newCommentState, 'user_id' => auth()->id(), 'anon' => $this->anon, ]); // New Comment Notification switch (true) { case $this->model instanceof Ticket: $ticket = $this->model; if ($this->user->id !== $ticket->staff_id && $ticket->staff_id !== null) { User::find($ticket->staff_id)?->notify(new NewComment($this->model, $comment)); } if ($this->user->id !== $ticket->user_id) { User::find($ticket->user_id)?->notify(new NewComment($this->model, $comment)); } break; case $this->model instanceof Article: case $this->model instanceof Playlist: case $this->model instanceof TorrentRequest: case $this->model instanceof Torrent: if ($this->user->id !== $this->model->user_id) { User::find($this->model->user_id)?->notify(new NewComment($this->model, $comment)); } break; } // User Tagged Notification $users = User::whereIn('username', $this->taggedUsers())->get(); Notification::sendNow($users, new NewCommentTag($this->model, $comment)); if (!$this->model instanceof Ticket) { // Auto Shout $username = $comment->anon ? 'An anonymous user' : '[url='.href_profile($this->user).']'.$this->user->username.'[/url]'; switch (true) { case $this->model instanceof Article: $this->chatRepository->systemMessage($username.' has left a comment on Article [url='.href_article($this->model).']'.$this->model->title.'[/url]'); break; case $this->model instanceof Collection: $this->chatRepository->systemMessage($username.' has left a comment on Collection [url='.href_collection($this->model).']'.$this->model->name.'[/url]'); break; case $this->model instanceof Playlist: $this->chatRepository->systemMessage($username.' has left a comment on Playlist [url='.href_playlist($this->model).']'.$this->model->name.'[/url]'); break; case $this->model instanceof TorrentRequest: $this->chatRepository->systemMessage($username.' has left a comment on Torrent Request [url='.href_request($this->model).']'.$this->model->name.'[/url]'); break; case $this->model instanceof Torrent: $this->chatRepository->systemMessage($username.' has left a comment on Torrent [url='.href_torrent($this->model).']'.$this->model->name.'[/url]'); break; } // Achievements if (!$comment->anon) { $this->user->unlock(new UserMadeComment()); $this->user->addProgress(new UserMadeTenComments(), 1); $this->user->addProgress(new UserMade50Comments(), 1); $this->user->addProgress(new UserMade100Comments(), 1); $this->user->addProgress(new UserMade200Comments(), 1); $this->user->addProgress(new UserMade300Comments(), 1); $this->user->addProgress(new UserMade400Comments(), 1); $this->user->addProgress(new UserMade500Comments(), 1); $this->user->addProgress(new UserMade600Comments(), 1); $this->user->addProgress(new UserMade700Comments(), 1); $this->user->addProgress(new UserMade800Comments(), 1); $this->user->addProgress(new UserMade900Comments(), 1); } } $this->reset('newCommentState'); $this->gotoPage(1); } /** * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator<\App\Models\Comment> */ #[Computed] final public function comments(): \Illuminate\Contracts\Pagination\LengthAwarePaginator { return $this->model ->comments() ->with(['user:id,username,group_id,image,title', 'user.group', 'children.user:id,username,group_id,image,title', 'children.user.group']) ->parent() ->latest() ->paginate($this->perPage); } final public function render(): \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Contracts\Foundation\Application { return view('livewire.comments', [ 'comments' => $this->comments, ]); } }