From 0273fbfddd3661f0029fa13e0fe441d8abf0ae1b Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 28 Apr 2026 17:15:28 -0600 Subject: [PATCH] feat: notificaciones en tiempo real para social (likes, comentarios, solicitudes) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Notifier helper: guarda en BD + broadcast en un solo llamado - AppNotification: notificación genérica para canal database - FriendshipController: notifica al recibir solicitud y al aceptarla - PostController: notifica al autor cuando le dan like o comentan (excepto en propio post) Co-Authored-By: Claude Sonnet 4.6 --- app/Http/Controllers/FriendshipController.php | 38 +++++++++++++----- app/Http/Controllers/PostController.php | 40 ++++++++++++++++--- app/Notifications/AppNotification.php | 35 ++++++++++++++++ app/Support/Notifier.php | 25 ++++++++++++ 4 files changed, 121 insertions(+), 17 deletions(-) create mode 100644 app/Notifications/AppNotification.php create mode 100644 app/Support/Notifier.php diff --git a/app/Http/Controllers/FriendshipController.php b/app/Http/Controllers/FriendshipController.php index 8df5db85..1cb4dfd9 100644 --- a/app/Http/Controllers/FriendshipController.php +++ b/app/Http/Controllers/FriendshipController.php @@ -4,12 +4,11 @@ namespace App\Http\Controllers; use App\Models\Friendship; use App\Models\User; -use App\Notifications\FriendRequestNotification; +use App\Support\Notifier; use Illuminate\Http\Request; class FriendshipController extends Controller { - // Solicitudes pendientes recibidas public function index() { $requests = auth()->user() @@ -22,7 +21,6 @@ class FriendshipController extends Controller return view('friendships.index', compact('requests')); } - // Enviar solicitud public function send(User $user) { $me = auth()->user(); @@ -31,33 +29,45 @@ class FriendshipController extends Controller return back()->with('error', 'No puedes enviarte una solicitud a ti mismo.'); } - $existing = $me->friendshipWith($user); - if ($existing) { + if ($me->friendshipWith($user)) { return back()->with('error', 'Ya existe una relación con este usuario.'); } - $friendship = Friendship::create([ + Friendship::create([ 'sender_id' => $me->id, 'receiver_id' => $user->id, 'status' => 'pending', ]); - $user->notify(new FriendRequestNotification($me)); + Notifier::send( + recipient: $user, + title: $me->name . ' te envió una solicitud de amistad', + icon: 'fa-user-plus', + color: 'primary', + url: route('friendships.index'), + ); return back()->with('success', 'Solicitud de amistad enviada.'); } - // Aceptar solicitud public function accept(Friendship $friendship) { abort_unless($friendship->receiver_id === auth()->id(), 403); $friendship->update(['status' => 'accepted']); + $friendship->load('receiver'); + + Notifier::send( + recipient: $friendship->sender, + title: $friendship->receiver->name . ' aceptó tu solicitud de amistad', + icon: 'fa-user-check', + color: 'success', + url: route('social.profile', $friendship->receiver), + ); return back()->with('success', 'Solicitud aceptada.'); } - // Rechazar o cancelar solicitud public function destroy(Friendship $friendship) { $me = auth()->id(); @@ -71,7 +81,7 @@ class FriendshipController extends Controller return back()->with('success', 'Solicitud eliminada.'); } - // AJAX: enviar/cancelar solicitud desde búsqueda o perfil + // AJAX desde búsqueda/perfil public function toggle(User $user) { $me = auth()->user(); @@ -93,7 +103,13 @@ class FriendshipController extends Controller 'status' => 'pending', ]); - $user->notify(new FriendRequestNotification($me)); + Notifier::send( + recipient: $user, + title: $me->name . ' te envió una solicitud de amistad', + icon: 'fa-user-plus', + color: 'primary', + url: route('friendships.index'), + ); return response()->json(['status' => 'pending']); } diff --git a/app/Http/Controllers/PostController.php b/app/Http/Controllers/PostController.php index 75e893aa..1e357c66 100644 --- a/app/Http/Controllers/PostController.php +++ b/app/Http/Controllers/PostController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers; use App\Models\Post; use App\Models\PostComment; use App\Models\PostLike; +use App\Support\Notifier; use Illuminate\Http\Request; use Illuminate\Support\Facades\Storage; @@ -12,9 +13,9 @@ class PostController extends Controller { public function index() { - $me = auth()->user(); + $me = auth()->user(); $friendIds = $me->friendIds(); - $ids = array_merge($friendIds, [$me->id]); + $ids = array_merge($friendIds, [$me->id]); $posts = Post::whereIn('user_id', $ids) ->with(['author', 'likes', 'comments.author']) @@ -51,15 +52,28 @@ class PostController extends Controller public function like(Post $post) { - $userId = auth()->id(); - $liked = PostLike::where('user_id', $userId)->where('post_id', $post->id)->first(); + $me = auth()->user(); + $liked = PostLike::where('user_id', $me->id)->where('post_id', $post->id)->first(); if ($liked) { $liked->delete(); $liked = false; } else { - PostLike::create(['user_id' => $userId, 'post_id' => $post->id]); + PostLike::create(['user_id' => $me->id, 'post_id' => $post->id]); $liked = true; + + // Notify post author (not if liking own post) + if ($post->user_id !== $me->id) { + $post->load('author'); + Notifier::send( + recipient: $post->author, + title: $me->name . ' le dio Me gusta a tu publicación', + body: $post->body ? \Str::limit($post->body, 60) : '', + icon: 'fa-heart', + color: 'danger', + url: route('feed') . '#post-' . $post->id, + ); + } } return response()->json([ @@ -72,14 +86,28 @@ class PostController extends Controller { $request->validate(['body' => 'required|string|max:500']); + $me = auth()->user(); $comment = PostComment::create([ - 'user_id' => auth()->id(), + 'user_id' => $me->id, 'post_id' => $post->id, 'body' => $request->body, ]); $comment->load('author'); + // Notify post author (not if commenting on own post) + if ($post->user_id !== $me->id) { + $post->load('author'); + Notifier::send( + recipient: $post->author, + title: $me->name . ' comentó en tu publicación', + body: \Str::limit($request->body, 60), + icon: 'fa-comment', + color: 'info', + url: route('feed') . '#post-' . $post->id, + ); + } + return response()->json([ 'id' => $comment->id, 'body' => $comment->body, diff --git a/app/Notifications/AppNotification.php b/app/Notifications/AppNotification.php new file mode 100644 index 00000000..bf68a01f --- /dev/null +++ b/app/Notifications/AppNotification.php @@ -0,0 +1,35 @@ + $this->title, + 'body' => $this->body, + 'url' => $this->url, + 'icon' => $this->icon, + 'color' => $this->color, + ]; + } +} diff --git a/app/Support/Notifier.php b/app/Support/Notifier.php new file mode 100644 index 00000000..0e3976b1 --- /dev/null +++ b/app/Support/Notifier.php @@ -0,0 +1,25 @@ +notify(new AppNotification($title, $body, $url, $icon, $color)); + + // Broadcast in real-time to the bell dropdown + broadcast(new NewNotification($recipient->id, $title, $body, $icon, $color, $url)); + } +}