feat: add persistent mobile notifications
This commit is contained in:
@@ -6,6 +6,7 @@ use App\Enums\FollowStatus;
|
||||
use App\Http\Requests\FollowRequest;
|
||||
use App\Http\Resources\FollowResource;
|
||||
use App\Models\User;
|
||||
use App\Notifications\NewFollowerNotification;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
@@ -27,7 +28,14 @@ class FollowController extends Controller
|
||||
// Default to Accepted as is_private doesn't exist in the DB yet
|
||||
$status = FollowStatus::Accepted;
|
||||
|
||||
$authUser->following()->syncWithPivotValues([$user->id], ['status' => $status], false);
|
||||
$changes = $authUser->following()->syncWithPivotValues([$user->id], ['status' => $status], false);
|
||||
|
||||
if ($changes['attached'] !== []) {
|
||||
$user->notify(
|
||||
(new NewFollowerNotification($authUser))
|
||||
->locale($user->preferredLocale()),
|
||||
);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => __('api.follows.followed'),
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Resources\NotificationResource;
|
||||
use App\Notifications\MobileNotification;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
|
||||
class NotificationController extends Controller
|
||||
{
|
||||
public function index(Request $request): AnonymousResourceCollection
|
||||
{
|
||||
$perPage = max(1, min($request->integer('per_page', 20), 60));
|
||||
$notifications = $request->user()
|
||||
->notifications()
|
||||
->whereIn('type', MobileNotification::TYPES)
|
||||
->latest()
|
||||
->paginate($perPage);
|
||||
|
||||
return NotificationResource::collection($notifications)->additional([
|
||||
'unreadCount' => $this->unreadCountFor($request),
|
||||
]);
|
||||
}
|
||||
|
||||
public function unreadCount(Request $request): JsonResponse
|
||||
{
|
||||
return response()->json([
|
||||
'data' => [
|
||||
'count' => $this->unreadCountFor($request),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function markAsRead(Request $request, string $notification): NotificationResource
|
||||
{
|
||||
$databaseNotification = $request->user()
|
||||
->notifications()
|
||||
->whereIn('type', MobileNotification::TYPES)
|
||||
->findOrFail($notification);
|
||||
|
||||
$databaseNotification->markAsRead();
|
||||
|
||||
return NotificationResource::make($databaseNotification->fresh());
|
||||
}
|
||||
|
||||
public function markAllAsRead(Request $request): JsonResponse
|
||||
{
|
||||
$updated = $request->user()
|
||||
->unreadNotifications()
|
||||
->whereIn('type', MobileNotification::TYPES)
|
||||
->update(['read_at' => now()]);
|
||||
|
||||
return response()->json([
|
||||
'data' => [
|
||||
'updated' => $updated,
|
||||
],
|
||||
'message' => __('api.notifications.all_read'),
|
||||
]);
|
||||
}
|
||||
|
||||
private function unreadCountFor(Request $request): int
|
||||
{
|
||||
return $request->user()
|
||||
->unreadNotifications()
|
||||
->whereIn('type', MobileNotification::TYPES)
|
||||
->count();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\UpdateNotificationPreferencesRequest;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class NotificationPreferenceController extends Controller
|
||||
{
|
||||
public function show(Request $request): JsonResponse
|
||||
{
|
||||
return response()->json([
|
||||
'data' => $this->preferences($request),
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(UpdateNotificationPreferencesRequest $request): JsonResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
$validated = $request->validated();
|
||||
|
||||
$user->forceFill([
|
||||
'social_notifications_enabled' => $validated['socialNotificationsEnabled']
|
||||
?? $user->social_notifications_enabled,
|
||||
'engagement_reminders_enabled' => $validated['engagementRemindersEnabled']
|
||||
?? $user->engagement_reminders_enabled,
|
||||
])->save();
|
||||
|
||||
return response()->json([
|
||||
'data' => $this->preferences($request),
|
||||
'message' => __('api.notifications.preferences_updated'),
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return array{socialNotificationsEnabled: bool, engagementRemindersEnabled: bool} */
|
||||
private function preferences(Request $request): array
|
||||
{
|
||||
return [
|
||||
'socialNotificationsEnabled' => (bool) $request->user()->social_notifications_enabled,
|
||||
'engagementRemindersEnabled' => (bool) $request->user()->engagement_reminders_enabled,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateNotificationPreferencesRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return $this->user() !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'socialNotificationsEnabled' => ['sometimes', 'required', 'boolean'],
|
||||
'engagementRemindersEnabled' => ['sometimes', 'required', 'boolean'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class NotificationResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
$data = is_array($this->data) ? $this->data : [];
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'type' => $data['type'] ?? $this->type,
|
||||
'category' => $data['category'] ?? 'account',
|
||||
'title' => $data['title'] ?? __('api.notifications.default_title'),
|
||||
'body' => $data['body'] ?? '',
|
||||
'url' => $data['url'] ?? null,
|
||||
'readAt' => $this->read_at?->toISOString(),
|
||||
'createdAt' => $this->created_at?->toISOString(),
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user