push notification api
This commit is contained in:
parent
8b8335eb32
commit
045b8d1f4c
@ -296,6 +296,33 @@ public function broadcastNotification(Request $request)
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
// Send FCM push notifications for broadcast
|
||||
if ($request->channel === 'push' || $request->channel === 'both') {
|
||||
$recipientsLower = strtolower($request->recipients);
|
||||
if (str_contains($recipientsLower, 'worker') || $recipientsLower === 'all') {
|
||||
$workers = \App\Models\Worker::whereNotNull('fcm_token')->get();
|
||||
foreach ($workers as $worker) {
|
||||
\App\Services\FCMService::sendPushNotification(
|
||||
$worker->fcm_token,
|
||||
$request->title,
|
||||
$request->message,
|
||||
['type' => 'broadcast']
|
||||
);
|
||||
}
|
||||
}
|
||||
if (str_contains($recipientsLower, 'employer') || $recipientsLower === 'all') {
|
||||
$employers = \App\Models\User::where('role', 'employer')->whereNotNull('fcm_token')->get();
|
||||
foreach ($employers as $employer) {
|
||||
\App\Services\FCMService::sendPushNotification(
|
||||
$employer->fcm_token,
|
||||
$request->title,
|
||||
$request->message,
|
||||
['type' => 'broadcast']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return back()->with('success', "Notification campaign broadcasted successfully to all {$request->recipients} users!");
|
||||
}
|
||||
|
||||
@ -523,13 +550,15 @@ public function storeAnnouncement(Request $request)
|
||||
'content' => $request->content,
|
||||
]);
|
||||
|
||||
\App\Models\Announcement::create([
|
||||
$ann = \App\Models\Announcement::create([
|
||||
'title' => $request->title,
|
||||
'body' => $body,
|
||||
'type' => 'Charity',
|
||||
'status' => 'approved',
|
||||
]);
|
||||
|
||||
$this->notifyUsersOfEvent($ann);
|
||||
|
||||
return back()->with('success', 'Charity Event created successfully.');
|
||||
}
|
||||
|
||||
@ -541,6 +570,8 @@ public function approveAnnouncement(Request $request, $id)
|
||||
$ann = \App\Models\Announcement::findOrFail($id);
|
||||
$ann->update(['status' => 'approved']);
|
||||
|
||||
$this->notifyUsersOfEvent($ann);
|
||||
|
||||
return back()->with('success', 'Charity Event approved successfully.');
|
||||
}
|
||||
|
||||
@ -572,4 +603,48 @@ public function deleteAnnouncement(Request $request, $id)
|
||||
|
||||
return back()->with('success', 'Charity Event deleted successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast FCM Push Notification for new Event/Announcement.
|
||||
*/
|
||||
protected function notifyUsersOfEvent($ann)
|
||||
{
|
||||
$title = "New Event: " . $ann->title;
|
||||
$body = $ann->body;
|
||||
|
||||
if (str_starts_with($ann->body, '{"type":"Charity"')) {
|
||||
$decoded = json_decode($ann->body, true);
|
||||
if ($decoded && isset($decoded['content'])) {
|
||||
$body = $decoded['content'];
|
||||
}
|
||||
}
|
||||
|
||||
// Notify all workers
|
||||
$workers = \App\Models\Worker::whereNotNull('fcm_token')->get();
|
||||
foreach ($workers as $worker) {
|
||||
\App\Services\FCMService::sendPushNotification(
|
||||
$worker->fcm_token,
|
||||
$title,
|
||||
$body,
|
||||
[
|
||||
'type' => 'announcement',
|
||||
'announcement_id' => $ann->id,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Notify all employers
|
||||
$employers = \App\Models\User::where('role', 'employer')->whereNotNull('fcm_token')->get();
|
||||
foreach ($employers as $employer) {
|
||||
\App\Services\FCMService::sendPushNotification(
|
||||
$employer->fcm_token,
|
||||
$title,
|
||||
$body,
|
||||
[
|
||||
'type' => 'announcement',
|
||||
'announcement_id' => $ann->id,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ public function login(Request $request)
|
||||
'email' => 'nullable|string',
|
||||
'mobile' => 'nullable|string',
|
||||
'password' => 'required|string',
|
||||
'fcm_token' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
$validator->after(function ($validator) use ($request) {
|
||||
@ -103,7 +104,11 @@ public function login(Request $request)
|
||||
], 403);
|
||||
}
|
||||
|
||||
$user->update(['api_token' => $apiToken]);
|
||||
$userUpdateData = ['api_token' => $apiToken];
|
||||
if ($request->has('fcm_token')) {
|
||||
$userUpdateData['fcm_token'] = $request->fcm_token;
|
||||
}
|
||||
$user->update($userUpdateData);
|
||||
if ($sponsor) {
|
||||
$sponsor->update([
|
||||
'api_token' => $apiToken,
|
||||
|
||||
@ -93,6 +93,7 @@ public function updateProfile(Request $request)
|
||||
'notifications' => 'required|boolean',
|
||||
'current_password' => 'nullable|required_with:new_password|string',
|
||||
'new_password' => 'nullable|string|min:8|confirmed',
|
||||
'fcm_token' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
@ -121,10 +122,14 @@ public function updateProfile(Request $request)
|
||||
$oldEmail = $employer->getOriginal('email') ?? $employer->email;
|
||||
|
||||
// Update user table
|
||||
$employer->update([
|
||||
$userData = [
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
]);
|
||||
];
|
||||
if ($request->has('fcm_token')) {
|
||||
$userData['fcm_token'] = $request->fcm_token;
|
||||
}
|
||||
$employer->update($userData);
|
||||
|
||||
// Sync with corresponding sponsor record if found
|
||||
$matchingSponsor = \App\Models\Sponsor::where('email', $oldEmail)->first();
|
||||
|
||||
@ -798,6 +798,19 @@ public function hireCandidate(Request $request, $id = null)
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch push notification to worker
|
||||
if ($worker && $worker->fcm_token) {
|
||||
\App\Services\FCMService::sendPushNotification(
|
||||
$worker->fcm_token,
|
||||
"Congratulations! You've been Hired",
|
||||
"Employer " . ($employer->name ?? "Employer") . " has hired you.",
|
||||
[
|
||||
'type' => 'hired',
|
||||
'employer_id' => $employer->id,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Candidate successfully marked as Hired!',
|
||||
|
||||
@ -441,6 +441,7 @@ public function login(Request $request)
|
||||
'phone' => 'required_without:email|nullable|string',
|
||||
'email' => 'required_without:phone|nullable|email',
|
||||
'password' => 'required|string',
|
||||
'fcm_token' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
@ -464,7 +465,11 @@ public function login(Request $request)
|
||||
}
|
||||
|
||||
$apiToken = Str::random(80);
|
||||
$worker->update(['api_token' => $apiToken]);
|
||||
$updateData = ['api_token' => $apiToken];
|
||||
if ($request->has('fcm_token')) {
|
||||
$updateData['fcm_token'] = $request->fcm_token;
|
||||
}
|
||||
$worker->update($updateData);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
|
||||
@ -271,6 +271,21 @@ public function sendMessage(Request $request, $id)
|
||||
}
|
||||
});
|
||||
|
||||
// Dispatch push notification to employer
|
||||
$employer = $conv->employer;
|
||||
if ($employer && $employer->fcm_token) {
|
||||
\App\Services\FCMService::sendPushNotification(
|
||||
$employer->fcm_token,
|
||||
"New Message from " . ($worker->name ?? "Worker"),
|
||||
$request->text ?: "Sent an attachment",
|
||||
[
|
||||
'conversation_id' => $conv->id,
|
||||
'sender_type' => 'worker',
|
||||
'sender_id' => $worker->id,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Message sent successfully.',
|
||||
|
||||
@ -351,6 +351,22 @@ public function respondToOffer(Request $request, $id)
|
||||
}
|
||||
});
|
||||
|
||||
// Dispatch push notification to employer
|
||||
$employer = $offer->employer;
|
||||
if ($employer && $employer->fcm_token) {
|
||||
$statusMessage = $responseStatus === 'accepted' ? 'accepted' : 'rejected';
|
||||
\App\Services\FCMService::sendPushNotification(
|
||||
$employer->fcm_token,
|
||||
"Job Offer " . ucfirst($statusMessage),
|
||||
"Worker " . ($worker->name ?? "Candidate") . " has " . $statusMessage . " your job offer.",
|
||||
[
|
||||
'type' => 'job_offer_response',
|
||||
'offer_id' => $offer->id,
|
||||
'status' => $responseStatus,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => "Job offer successfully " . $responseStatus . ".",
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
#[Fillable(['name', 'email', 'password', 'role', 'status', 'subscription_status', 'subscription_expires_at', 'api_token'])]
|
||||
#[Fillable(['name', 'email', 'password', 'role', 'status', 'subscription_status', 'subscription_expires_at', 'api_token', 'fcm_token'])]
|
||||
#[Hidden(['password', 'remember_token', 'api_token'])]
|
||||
class User extends Authenticatable
|
||||
{
|
||||
|
||||
@ -119,6 +119,20 @@ public static function sendPushNotification($token, $title, $body, array $data =
|
||||
'title' => $title,
|
||||
'body' => $body,
|
||||
],
|
||||
'android' => [
|
||||
'notification' => [
|
||||
'click_action' => 'FLUTTER_NOTIFICATION_CLICK',
|
||||
'sound' => 'default',
|
||||
],
|
||||
],
|
||||
'apns' => [
|
||||
'payload' => [
|
||||
'aps' => [
|
||||
'sound' => 'default',
|
||||
'badge' => 1,
|
||||
],
|
||||
],
|
||||
],
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('fcm_token')->nullable()->after('api_token');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('fcm_token');
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -202,4 +202,40 @@ public function test_unauthenticated_requests_are_blocked()
|
||||
$response = $this->getJson('/api/workers/conversations');
|
||||
$response->assertStatus(401);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test worker message triggers push notification.
|
||||
*/
|
||||
public function test_worker_message_triggers_push_notification()
|
||||
{
|
||||
\Illuminate\Support\Facades\Http::fake([
|
||||
'https://oauth2.googleapis.com/token' => \Illuminate\Support\Facades\Http::response(['access_token' => 'mocked-fcm-token'], 200),
|
||||
'https://fcm.googleapis.com/*' => \Illuminate\Support\Facades\Http::response(['name' => 'projects/migrant-fe5a7/messages/mock-id'], 200),
|
||||
]);
|
||||
|
||||
// Set fcm_token on employer
|
||||
$this->employer->update(['fcm_token' => 'employer-device-fcm-token-123']);
|
||||
|
||||
// Create conversation
|
||||
$conversation = Conversation::create([
|
||||
'employer_id' => $this->employer->id,
|
||||
'worker_id' => $this->worker->id,
|
||||
]);
|
||||
|
||||
// Reply request
|
||||
$response = $this->withHeaders([
|
||||
'Authorization' => 'Bearer ' . $this->token,
|
||||
])->postJson('/api/workers/conversations/' . $conversation->id . '/messages', [
|
||||
'text' => 'Testing push notification to employer!'
|
||||
]);
|
||||
|
||||
$response->assertStatus(201);
|
||||
|
||||
// Assert Http call was made to FCM endpoint
|
||||
\Illuminate\Support\Facades\Http::assertSent(function ($request) {
|
||||
return str_contains($request->url(), 'fcm.googleapis.com') &&
|
||||
$request['message']['token'] === 'employer-device-fcm-token-123' &&
|
||||
$request['message']['notification']['body'] === 'Testing push notification to employer!';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user