Schémas de notification courants

Nous allons examiner quelques schémas d'implémentation courants pour la fonctionnalité Web push.

Pour ce faire, vous devrez utiliser différentes API disponibles dans le service worker.

Événement de fermeture de notification

Dans la section précédente, nous avons vu comment écouter les événements notificationclick.

Un événement notificationclose est également appelé si l'utilisateur ignore l'un de vos (en d'autres termes, au lieu de cliquer sur la notification, l'utilisateur clique sur la croix ou notification d'absence).

Cet événement est généralement utilisé à des fins d'analyse afin de suivre l'engagement des utilisateurs avec les notifications.

self.addEventListener('notificationclose', function (event) {
  const dismissedNotification = event.notification;

  const promiseChain = notificationCloseAnalytics();
  event.waitUntil(promiseChain);
});

Ajouter des données à une notification

Lors de la réception d'un message push, il est courant d'avoir des données uniquement utile si l'utilisateur a cliqué sur la notification. Par exemple, l'URL à ouvrir lorsqu'un utilisateur clique sur une notification.

Le moyen le plus simple de récupérer des données d'un événement push et de les associer à un la notification consiste à ajouter un paramètre data à l'objet options transmis. dans showNotification(), comme ceci:

const options = {
  body:
    'This notification has data attached to it that is printed ' +
    "to the console when it's clicked.",
  tag: 'data-notification',
  data: {
    time: new Date(Date.now()).toString(),
    message: 'Hello, World!',
  },
};
registration.showNotification('Notification with Data', options);

Dans un gestionnaire de clics, les données sont accessibles avec event.notification.data.

const notificationData = event.notification.data;
console.log('');
console.log('The notification data has the following parameters:');
Object.keys(notificationData).forEach((key) => {
  console.log(`  ${key}: ${notificationData[key]}`);
});
console.log('');

Ouvrir une fenêtre

L'une des réponses les plus courantes à une notification une fenêtre ou un onglet spécifique. Pour ce faire, clients.openWindow() API.

Dans notre événement notificationclick, nous exécutons du code comme celui-ci:

const examplePage = '/demos/notification-examples/example-page.html';
const promiseChain = clients.openWindow(examplePage);
event.waitUntil(promiseChain);

Dans la section suivante, nous verrons comment vérifier si la page vers laquelle nous voulons rediriger l'utilisateur déjà ouvert ou non. De cette façon, nous pouvons sélectionner l'onglet ouvert au lieu d'en ouvrir de nouveaux onglets.

Sélectionner une fenêtre existante

Lorsque c'est possible, nous devons sélectionner une fenêtre plutôt que d'en ouvrir une nouvelle chaque fois que l'utilisateur clique sur une notification.

Avant de voir comment y parvenir, il convient de souligner que n'est possible que pour les pages de votre origine. C’est parce que nous pouvons voir uniquement les pages ouvertes qui appartiennent à notre site. Cela permet d'éviter les développeurs de voir tous les sites que leurs utilisateurs consultent.

Pour reprendre l'exemple précédent, nous allons modifier le code pour voir /demos/notification-examples/example-page.html est déjà ouvert.

const urlToOpen = new URL(examplePage, self.location.origin).href;

const promiseChain = clients
  .matchAll({
    type: 'window',
    includeUncontrolled: true,
  })
  .then((windowClients) => {
    let matchingClient = null;

    for (let i = 0; i < windowClients.length; i++) {
      const windowClient = windowClients[i];
      if (windowClient.url === urlToOpen) {
        matchingClient = windowClient;
        break;
      }
    }

    if (matchingClient) {
      return matchingClient.focus();
    } else {
      return clients.openWindow(urlToOpen);
    }
  });

event.waitUntil(promiseChain);

Examinons le code.

Nous commençons par analyser notre exemple de page à l'aide de l'API URL. C'est une super astuce que j'ai trouvée auprès de Jeff Posnick. L'appel de new URL() avec l'objet location permet renvoie une URL absolue si la chaîne transmise est relative (par exemple, / devient https://example.com/).

L'URL est absolue afin de pouvoir la faire correspondre ultérieurement à l'URL de la fenêtre.

const urlToOpen = new URL(examplePage, self.location.origin).href;

Nous obtenons ensuite la liste des objets WindowClient, qui correspond à la liste des onglets et fenêtres actuellement ouverts. (N'oubliez pas que ces onglets ne concernent que votre origine.)

const promiseChain = clients.matchAll({
  type: 'window',
  includeUncontrolled: true,
});

Les options transmises dans matchAll informent le navigateur que nous ne voulons pour rechercher "fenêtre" (recherchez simplement les onglets et les fenêtres et exclure les nœuds de calcul Web). includeUncontrolled nous permet de rechercher tous les onglets de votre origine qui ne sont pas contrôlés par le service actuel (c'est-à-dire le service worker exécutant ce code). Généralement, vous souhaite toujours que includeUncontrolled soit défini sur "true" lors de l'appel de matchAll().

Nous capturons la promesse renvoyée en tant que promiseChain afin de la transmettre à event.waitUntil() par la suite, ce qui maintiendra notre service worker actif.

Lorsque la promesse matchAll() est résolue, nous itérez les clients de fenêtre renvoyés et comparer leurs URL à celle que nous voulons ouvrir. Si nous trouvons une correspondance, ce qui attirera l'attention des utilisateurs sur cette fenêtre. Pour se concentrer, matchingClient.focus() appel.

Si nous ne trouvons pas de client correspondant, nous ouvrons une nouvelle fenêtre, comme dans la section précédente.

.then((windowClients) => {
  let matchingClient = null;

  for (let i = 0; i < windowClients.length; i++) {
    const windowClient = windowClients[i];
    if (windowClient.url === urlToOpen) {
      matchingClient = windowClient;
      break;
    }
  }

  if (matchingClient) {
    return matchingClient.focus();
  } else {
    return clients.openWindow(urlToOpen);
  }
});

Fusion des notifications

Nous avons vu que l'ajout d'une balise à une notification active un comportement notification existante comportant la même balise est remplacée.

Vous pouvez toutefois affiner votre recherche en réduisant les notifications à l'aide des boutons API Notifications. Prenons l'exemple d'une application de chat, pour laquelle le développeur peut souhaiter recevoir une nouvelle notification. afficher un message semblable à "Vous avez deux messages de Matt" au lieu de vous contenter d'afficher .

Vous pouvez le faire ou manipuler les notifications actuelles d'autres manières, à l'aide de la registration.getNotifications() qui vous donne accès à toutes les notifications actuellement visibles pour votre application Web.

Voyons comment utiliser cette API pour implémenter l'exemple de chat.

Dans notre application de chat, nous supposons que chaque notification contient des données, dont un nom d'utilisateur.

La première chose à faire est de rechercher toutes les notifications ouvertes pour un utilisateur ayant un nom d'utilisateur. Nous obtenons registration.getNotifications() et nous la mettons en boucle pour vérifier notification.data pour un nom d'utilisateur spécifique:

const promiseChain = registration.getNotifications().then((notifications) => {
  let currentNotification;

  for (let i = 0; i < notifications.length; i++) {
    if (notifications[i].data && notifications[i].data.userName === userName) {
      currentNotification = notifications[i];
    }
  }

  return currentNotification;
});

L'étape suivante consiste à remplacer cette notification par une nouvelle.

Dans cette application de messagerie fictive, nous allons suivre le nombre de nouveaux messages en ajoutant un nombre à notre nouveau données d'une notification et l'incrémenter à chaque nouvelle notification.

.then((currentNotification) => {
  let notificationTitle;
  const options = {
    icon: userIcon,
  }

  if (currentNotification) {
    // We have an open notification, let's do something with it.
    const messageCount = currentNotification.data.newMessageCount + 1;

    options.body = `You have ${messageCount} new messages from ${userName}.`;
    options.data = {
      userName: userName,
      newMessageCount: messageCount
    };
    notificationTitle = `New Messages from ${userName}`;

    // Remember to close the old notification.
    currentNotification.close();
  } else {
    options.body = `"${userMessage}"`;
    options.data = {
      userName: userName,
      newMessageCount: 1
    };
    notificationTitle = `New Message from ${userName}`;
  }

  return registration.showNotification(
    notificationTitle,
    options
  );
});

Si une notification est actuellement affichée, nous incrémentons le nombre de messages et définissons le le titre de la notification et le corps du message en conséquence. S'il y a il n'y a aucune notification, nous créons une notification avec un newMessageCount de 1.

Le résultat est que le premier message ressemblerait à ceci:

Première notification sans fusion.

Dans une deuxième notification, les notifications seraient réduites comme suit:

Deuxième notification avec fusion.

L'avantage de cette approche est que si l'utilisateur est témoin notifications qui s'affichent l'une sur l'autre, votre apparence est plus cohérente. que de simplement remplacer la notification par le dernier message.

L'exception à la règle

J'ai indiqué que vous devez afficher une notification lorsque vous recevez un message push. true la plupart du temps. Le seul scénario dans lequel vous n'avez pas besoin d'afficher une notification est lorsque que l'utilisateur ait votre site ouvert et ciblé.

Dans votre événement push, vous pouvez vérifier si vous devez afficher une notification ou non en en examinant les clients de fenêtres et en recherchant une fenêtre ciblée.

Le code permettant d'obtenir toutes les fenêtres et de rechercher une fenêtre sélectionnée se présente comme suit:

function isClientFocused() {
  return clients
    .matchAll({
      type: 'window',
      includeUncontrolled: true,
    })
    .then((windowClients) => {
      let clientIsFocused = false;

      for (let i = 0; i < windowClients.length; i++) {
        const windowClient = windowClients[i];
        if (windowClient.focused) {
          clientIsFocused = true;
          break;
        }
      }

      return clientIsFocused;
    });
}

Nous utilisons clients.matchAll() pour obtenir tous nos clients de fenêtre, puis nous les exécutons en boucle en vérifiant le paramètre focused.

Dans notre événement push, nous utiliserons cette fonction pour décider si nous devons afficher une notification:

const promiseChain = isClientFocused().then((clientIsFocused) => {
  if (clientIsFocused) {
    console.log("Don't need to show a notification.");
    return;
  }

  // Client isn't focused, we need to show a notification.
  return self.registration.showNotification('Had to show a notification.');
});

event.waitUntil(promiseChain);

Envoyer un message à une page à partir d'un événement push

Nous avons constaté que vous pouviez ignorer l'affichage d'une notification si l'utilisateur est actuellement sur votre site. Toutefois, que faire si vous voulez toujours informer l'utilisateur qu'un événement s'est produit, mais qu'une notification est trop lourdes ?

Une approche consiste à envoyer un message du service worker vers la page. La page Web peut afficher une notification ou une mise à jour pour l'informer de l'événement. Ceci est utile pour les cas où une notification subtile sur la page est plus agréable et plus conviviale pour l'utilisateur.

Disons que nous avons reçu une poussée, que nous avons vérifié que notre application web est actuellement ciblée, alors nous pouvons « publier un message » à chaque page ouverte, comme ceci:

const promiseChain = isClientFocused().then((clientIsFocused) => {
  if (clientIsFocused) {
    windowClients.forEach((windowClient) => {
      windowClient.postMessage({
        message: 'Received a push message.',
        time: new Date().toString(),
      });
    });
  } else {
    return self.registration.showNotification('No focused windows', {
      body: 'Had to show a notification instead of messaging each page.',
    });
  }
});

event.waitUntil(promiseChain);

Dans chacune des pages, nous écoutons les messages en ajoutant un événement de message écouteur:

navigator.serviceWorker.addEventListener('message', function (event) {
  console.log('Received a message from service worker: ', event.data);
});

Dans cet écouteur de messages, vous pouvez faire ce que vous voulez, afficher une UI personnalisée sur ou ignorer complètement le message.

Notez également que si vous ne définissez pas d'écouteur de messages sur votre page Web, les messages du service worker n'ont aucun effet.

Mettre en cache une page et ouvrir une fenêtre

Un scénario qui sort du cadre de ce guide, mais qui mérite d'être discuté, est que vous pouvez améliorer l'expérience utilisateur globale de votre application Web en mettant en cache les pages Web que les utilisateurs devraient consulter après en cliquant sur la notification.

Votre service worker doit être configuré pour gérer les événements fetch. Toutefois, si vous implémentez un écouteur d'événements fetch, assurez-vous de prendre pour en profiter dans votre événement push en mettant en cache la page et les composants. dont vous aurez besoin avant d'afficher la notification.

Compatibilité du navigateur

L'événement notificationclose

Navigateurs pris en charge

  • Chrome: 50 <ph type="x-smartling-placeholder">
  • Edge: 17 <ph type="x-smartling-placeholder">
  • Firefox: 44 <ph type="x-smartling-placeholder">
  • Safari: 16. <ph type="x-smartling-placeholder">

Source

Clients.openWindow()

Navigateurs pris en charge

  • Chrome: 40 <ph type="x-smartling-placeholder">
  • Edge: 17 <ph type="x-smartling-placeholder">
  • Firefox: 44 <ph type="x-smartling-placeholder">
  • Safari: 11.1. <ph type="x-smartling-placeholder">

Source

ServiceWorkerRegistration.getNotifications()

Navigateurs pris en charge

  • Chrome: 40 <ph type="x-smartling-placeholder">
  • Edge: 17 <ph type="x-smartling-placeholder">
  • Firefox: 44 <ph type="x-smartling-placeholder">
  • Safari: 16. <ph type="x-smartling-placeholder">

Source

clients.matchAll()

Navigateurs pris en charge

  • Chrome: 42 <ph type="x-smartling-placeholder">
  • Edge: 17 <ph type="x-smartling-placeholder">
  • Firefox: 54 <ph type="x-smartling-placeholder">
  • Safari: 11.1. <ph type="x-smartling-placeholder">

Source

Pour en savoir plus, consultez cette présentation des service workers. post.

Étapes suivantes

Ateliers de programmation