Daniel Zotti logo
About meBlogProjectsOpen Source
🍪 Cookie Policy
Blog
 15 Sept 2023
 javascript, browser-api

Notifiche nel browser

Quanto sarebbe comodo avere notifiche nei browser come in una normale app mobile?

Beh, ce le abbiamo già!

Ci sono solo alcuni concetti da padroneggiare prima di iniziare:

  1. Supporto browser
  2. Permessi per le notifiche
  3. Service Worker

Il browser lo supporta?

Per prima cosa dobbiamo verificare se il browser supporta Notification e serviceWorker. Senza queste funzionalità non possiamo gestire le notifiche!

Basta scrivere:

if (!("serviceWorker" in navigator)) {
  throw new Error("No Service Worker support");
}
if (!("Notification" in window)) {
  throw new Error("No Notification support");
}

NB: vale per qualsiasi feature del browser.

Permessi notifiche

Per attivare le notifiche su uno specifico dispositivo, bisogna chiedere all'utente se vuole riceverle.

Notification.requestPermission();

Comparirà un popup nel browser che chiede la risposta dell'utente...

Notification Permissions Request

...e la nostra applicazione deve gestire la scelta così:

Notification.requestPermission().then((permission) => {
  if (permission === "granted") {
    // L'utente ha cliccato su "Allow"
    // Il browser PUÒ inviare notifiche
  } else if (permission === "denied") {
    // L'utente ha cliccato su "Block"
    // Il browser NON PUÒ inviare notifiche
  } else if (permission === "default") {
    // L'utente ha chiuso il popup
    // possiamo chiedere i permessi di nuovo se vogliamo!
  }
});

Ok, ora potremmo essere pronti a inviare la prima notifica, ma prima parliamo di Service Worker!

Service Worker

Secondo MDN, i Service Worker agiscono essenzialmente come proxy server tra web app, browser e rete (quando disponibile).

In pratica, un ServiceWorker è un file JavaScript che gira in background su un thread separato. Deve prima essere registrato (installato), poi può comunicare con l'app (NB: https è obbligatorio!).

Perché ci serve un ServiceWorker per inviare notifiche? Perché gira in background anche quando la pagina dell'app non è aperta nel browser! E soprattutto perché ServiceWorkerRegistration ha un metodo showNotification usato per mostrare la notifica sul dispositivo.

Inviare una notifica

Dobbiamo solo aspettare che il service worker sia pronto e chiamare il metodo passando il titolo della notifica come primo parametro.

navigator.serviceWorker.ready.then((swRegistration) => {
  swRegistration.showNotification("My First notification!");
});

Queste poche righe mostreranno la notifica:

Notification popup on a Mac

Proprietà delle notifiche

Il metodo showNotification ha un secondo argomento che accetta un oggetto di opzioni per personalizzare la notifica:

  • icon: URL di un'immagine mostrata accanto al titolo della notifica
  • image: URL di un'immagine mostrata come contenuto della notifica
  • actions: array di azioni che diventano una lista di pulsanti cliccabili sotto al contenuto della notifica
  • action: stringa univoca che rappresenta l'ID dell'azione
  • title: stringa human readable da visualizzare
  • icon: URL dell'immagine accanto al titolo
  • body: stringa con contenuto aggiuntivo da mostrare nella notifica
  • badge: stringa con URL di un'immagine che rappresenta la notifica quando non c'è spazio sufficiente per mostrarla per intero (ad esempio nella barra notifiche Android)
  • data: dati arbitrari che vuoi associare alla notifica
  • tag: ID della notifica che consente di trovarla, sostituirla o rimuoverla via script quando necessario
  • silent: se true, niente vibrazione o suono
  • vibrate: array di numeri che rappresenta il pattern di vibrazione
  • ...e molte altre!

Notifica "come back"

Proviamo a creare insieme una notifica "come back" che compare quando l'app perde il focus e si chiude automaticamente quando torna in focus. Inoltre, se cliccata, riporta l'utente all'app.

document.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "hidden") {
    navigator.serviceWorker.ready.then(async (registration) => {
      await registration.showNotification("Come baaaaack!", {
        body: `Click here and come back to the website!`,
        silent: true,
        tag: "come-back", // richiesto se silent è true
      });
      registration.addEventListener("click", (e) => {
        e.preventDefault();
        window.parent.focus();
      });
    });
  } else {
    navigator.serviceWorker.ready.then((registration) => {
      registration
        .getNotifications({
          tag: "come-back",
        })
        .then((notifications) => {
          notifications.forEach((n) => {
            n.close();
          });
        });
    });
  }
});

Notifiche senza ServiceWorker

"Come?! Hai appena detto che i ServiceWorker sono necessari per inviare notifiche!!" Lo so... in realtà esiste un metodo alternativo che non coinvolge ServiceWorker, ma si basa su una feature deprecata! Il costruttore window.Notification non è segnato come deprecato in generale, ma è deprecato in Chrome su Android, quindi non funzionerà sui dispositivi mobile. Per questo ho preferito usare direttamente l'approccio con ServiceWorker.

Ti lascio comunque un esempio, può essere utile a chi non ha bisogno del supporto mobile.

Metodo "OLD": costruttore Notification

Ricreiamo la notifica "come back" usando il costruttore Notification.

let comeBackNotification;
document.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "hidden") {
    comeBackNotification = new Notification("Come baaaaack!");

    comeBackNotification.addEventListener("close", (e) => {
      console.log("Notification CLOSED!", e.target.data);
    });

    comeBackNotification.addEventListener("click", (e) => {
      console.log("Notification CLICKED!", e.target.data);
      e.preventDefault();
      window.parent.focus();
    });
  } else {
    comeBackNotification.close();
  }
});

Utile da sapere

  • Da Chrome 49, le notifiche non funzionano in incognito mode.
  • notification.vibrate non funziona su Android > 8.0
  • sw.js, se non ci serve, può anche essere un file vuoto!
  • Chrome per Android richiede la chiamata tramite registrazione service worker
  • iOS richiede che il sito venga prima aggiunto alla Home Screen

Wait Until

Una cosa importante sui service worker è che hai poco controllo su quando il loro codice verrà eseguito. È il browser a decidere quando avviarli e quando terminarli. L'unico modo per dire al browser di aspettare è passare una promise a event.waitUntil(). Così il browser terrà il service worker attivo finché la promise non sarà risolta o rifiutata.

// file: `sw,js`
self.addEventListener("push", function (event) {
  const promiseChain = self.registration.showNotification("Push Notification");
  event.waitUntil(promiseChain);
});

Push Notifications (da server remoto)

La differenza essenziale tra notifiche locali e push notifications è semplice:

  • Notifiche locali: pianificate localmente dall'app e consegnate dallo stesso dispositivo.

  • Push notifications: inviate da un server remoto che le recapita ai dispositivi su cui è installata l'app.

Non parlerò della parte server, quindi spiego come simulare una Push Notification con Chrome DevTools.

Simulare una Push Notification con Chrome DevTools

  • Apri Chrome DevTools
  • Vai nella tab Application
  • Seleziona Service Workers a sinistra
  • Scrivi un messaggio personalizzato a destra e clicca Push

Test Push Notification message on Chrome DevTools

Demo

Come sempre, ho creato una demo e un progetto GitHub.

Durante l'uso della demo, controlla la console per maggiori informazioni.