This commit is contained in:
2026-03-20 21:17:26 +01:00
parent 1a62fcb81d
commit 29c3ff8572
229 changed files with 13147 additions and 2577 deletions

View File

@@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
namespace App\Services;
use App\Models\Notification;
use App\Models\User;
use App\Support\AvatarUrl;
use Illuminate\Support\Collection;
final class NotificationService
{
public function listForUser(User $user, int $page = 1, int $perPage = 20): array
{
$resolvedPage = max(1, $page);
$resolvedPerPage = max(1, min(50, $perPage));
$notifications = $user->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,
];
}
}