notifications() ->latest() ->paginate($resolvedPerPage, ['*'], 'page', $resolvedPage); $actorIds = collect($notifications->items()) ->map(function (Notification $notification): ?int { $data = is_array($notification->data) ? $notification->data : []; return isset($data['actor_id']) ? (int) $data['actor_id'] : null; }) ->filter() ->unique() ->values(); $actors = $actorIds->isEmpty() ? collect() : User::query() ->with('profile:user_id,avatar_hash') ->whereIn('id', $actorIds->all()) ->get() ->keyBy('id'); return [ 'data' => collect($notifications->items()) ->map(fn (Notification $notification) => $this->mapNotification($notification, $actors)) ->values() ->all(), 'unread_count' => $user->unreadNotifications()->count(), 'meta' => [ 'total' => $notifications->total(), 'current_page' => $notifications->currentPage(), 'last_page' => $notifications->lastPage(), 'per_page' => $notifications->perPage(), ], ]; } public function markAllRead(User $user): void { $user->unreadNotifications()->update(['read_at' => now()]); } public function markRead(User $user, string $id): void { $notification = $user->notifications()->findOrFail($id); $notification->markAsRead(); } private function mapNotification(Notification $notification, Collection $actors): array { $data = is_array($notification->data) ? $notification->data : []; $actorId = isset($data['actor_id']) ? (int) $data['actor_id'] : null; $actor = $actorId ? $actors->get($actorId) : null; return [ 'id' => (string) $notification->id, 'type' => (string) ($data['type'] ?? $notification->type ?? 'notification'), 'message' => (string) ($data['message'] ?? 'New activity'), 'url' => $data['url'] ?? null, 'created_at' => $notification->created_at?->toIso8601String(), 'time_ago' => $notification->created_at?->diffForHumans(), 'read' => $notification->read_at !== null, 'actor' => $actor ? [ 'id' => (int) $actor->id, 'name' => $actor->name, 'username' => $actor->username, 'avatar_url' => AvatarUrl::forUser((int) $actor->id, $actor->profile?->avatar_hash, 64), 'profile_url' => $actor->username ? '/@' . $actor->username : null, ] : null, ]; } }