Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

phayes0289's avatar

How to Open a Web Notification within a Progressive Web App

I have my Web Notifications displaying correctly, but they only open the app and bring the user to the home page of the web app.

When the web notification appears on the user's device, I want to bring the user to a specific URL instead of just the home page. I have defined the $returnUrl in the NewCadIncidentNotify.php notification file which is the URL I want to bring them to when they click on the notification.

This is my SendCadIncidentNotification listener.

    public function handle(CadIncidentCreated $event)
    {
        // Log the data from the newly created record
        $log = Log::channel('cad_incident_notifications');
        $log->info('New CadIncident record created', [
            'id' => $event->cadIncident->id,
            'data' => $event->cadIncident->toArray(),
        ]);


        // Retrieve all subscribed users    
        $emailRecipients = User::where('id', '1')->get(); // Get recipients for email notifications
        // Send the notification to all subscribed users
        Notification::send($emailRecipients, new NewCadIncidentEmail($event->cadIncident));

        // Retrieve all subscribed users
        // $subscribedUsers = PushSubscription::all(); // Assuming you want to notify all subscribed users
        $notifiableUsers = User::has('pushSubscriptions')->get();
        // Send the notification to all subscribed users
        // Notification::send($subscribedUsers, new NewCadIncidentNotify($event->cadIncident));
        Notification::send($notifiableUsers, new NewCadIncidentNotify($event->cadIncident));
    }

This is my NewCadIncidentNotify notification

  public function toWebPush($notifiable, $notification)
    {

        $iAddress = $this->cadIncident->location_address . ' ' . $this->cadIncident->location_apartment . ' ' . $this->cadIncident->location_city . ' ' . $this->cadIncident->location_state;
        $returnUrl = env('APP_URL') . '/911-communications/incidents/' . $this->cadIncident->id;
        $userId = $notifiable->id; // Assuming the user ID is accessible this way
        Log::info('push notification to: ' . $userId . ' - ' . $this->cadIncident->incident_type . ' - ' . $iAddress . ' - ' . $returnUrl);

        return (new WebPushMessage)
            ->title($this->cadIncident->incident_type)
            ->body($iAddress);
        // ->action('View Incident', $returnUrl);
    }

This is serviceworker.js file

// FireOps Service Worker Last Updated: 2/13/2024 @2200
var staticCacheName = "pwa-v" + new Date().getTime();
var filesToCache = [
  "/offline",
  "/css/app.css",
  "/js/app.js",
  "/media/images/favicons/icon-72x72.png",
  "/media/images/favicons/icon-96x96.png",
  "/media/images/favicons/icon-128x128.png",
  "/media/images/favicons/icon-144x144.png",
  "/media/images/favicons/icon-152x152.png",
  "/media/images/favicons/icon-192x192.png",
  "/media/images/favicons/icon-384x384.png",
  "/media/images/favicons/icon-512x512.png",
];

// Cache on install
self.addEventListener("install", (event) => {
  this.skipWaiting();
  event.waitUntil(
    caches.open(staticCacheName).then((cache) => {
      return cache.addAll(filesToCache);
    })
  );
});

// Clear cache on activate
self.addEventListener("activate", (event) => {
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames
          .filter((cacheName) => cacheName.startsWith("pwa-"))
          .filter((cacheName) => cacheName !== staticCacheName)
          .map((cacheName) => caches.delete(cacheName))
      );
    })
  );
});

// Serve from Cache
self.addEventListener("fetch", (event) => {
  event.respondWith(
    caches
      .match(event.request)
      .then((response) => {
        return response || fetch(event.request);
      })
      .catch(() => {
        return caches.match("offline");
      })
  );
});

// Subscribe to push notifications
self.addEventListener("pushsubscriptionchange", function (event) {
  event.waitUntil(
    self.registration.pushManager
      .subscribe(event.newSubscription)
      .then(function (subscription) {
        // Send subscription to server for storage
        sendSubscriptionToServer(subscription);
      })
  );
});

self.addEventListener("push", function (event) {
  let data = { title: "Default title", body: "Default body" };

  if (event.data) {
    data = event.data.json(); // Parse the incoming data as JSON
  }

  const title = data.title || "Incident Alert";
  const body = data.body || "Default payload";
  const icon = "/media/images/favicons/icon-128x128.png";
  const badge = "/media/images/favicons/icon-128x128.png";
  const actions = data.actions || []; // Parse actions if any

  const options = {
    body: body,
    icon: icon,
    badge: badge,
    silent: false, // If notifications should be SILENT, set to true
    actions: actions, // Add actions to the notification options if they exist
  };

  event.waitUntil(self.registration.showNotification(title, options));
});

// Handle notification click event
self.addEventListener("notificationclick", function (event) {
  event.notification.close();
  // Get the current app URL
  var appUrl = location.origin;
  // Handle notification click by opening the current app URL
  event.waitUntil(clients.openWindow(appUrl));
});

Any help in explaining to me how I bring a user to a specific URL within a Progressive Web App when the notification is clicked will be greatly appreciated.

0 likes
1 reply
LaryAI's avatar
Level 58

To open a specific URL when a web notification is clicked within a Progressive Web App (PWA), you need to modify the notificationclick event listener in your service worker (serviceworker.js) to open the desired URL instead of the default app URL.

Here's how you can modify the notificationclick event listener to open a specific URL:

// Handle notification click event
self.addEventListener("notificationclick", function (event) {
  event.notification.close();
  // Check if the notification data has a URL to open
  var notificationData = event.notification.data;
  var urlToOpen = notificationData && notificationData.url ? notificationData.url : location.origin;

  // Open the specific URL in a new window/tab
  event.waitUntil(
    clients.matchAll({
      type: "window"
    }).then(function (windowClients) {
      // Check if there is already a window/tab open with the target URL
      for (var i = 0; i < windowClients.length; i++) {
        var client = windowClients[i];
        // If so, just focus it.
        if (client.url === urlToOpen && 'focus' in client) {
          return client.focus();
        }
      }
      // If not, then open the new URL
      if (clients.openWindow) {
        return clients.openWindow(urlToOpen);
      }
    })
  );
});

In your NewCadIncidentNotify notification, you need to include the URL in the notification payload. You can do this by adding a data attribute to the WebPushMessage object:

public function toWebPush($notifiable, $notification)
{
    // ... your existing code ...

    $returnUrl = env('APP_URL') . '/911-communications/incidents/' . $this->cadIncident->id;

    return (new WebPushMessage)
        ->title($this->cadIncident->incident_type)
        ->body($iAddress)
        ->data(['url' => $returnUrl]); // Include the URL in the notification data
}

Make sure that when you send the notification from your server, the notification payload includes the data attribute with the url property set to the specific URL you want to open.

With these changes, when the user clicks on the notification, the service worker will handle the click event and open the specific URL provided in the notification data. If the app is already open in a tab with that URL, it will focus on that tab; otherwise, it will open a new tab with the specified URL.

Please or to participate in this conversation.