Veremos algunos patrones de implementación comunes para notificaciones push web.
Esto implicará usar algunas APIs diferentes que estén disponibles en el service worker.
Evento de cierre de notificación
En la última sección, vimos cómo escuchar eventos de notificationclick
.
También se llama a un evento notificationclose
si el usuario descarta uno de tus
notificaciones (es decir, en lugar de hacer clic en la notificación, el usuario hace clic en la cruz o desliza el dedo sobre
notificación).
Por lo general, este evento se usa para que las estadísticas hagan un seguimiento de la participación de los usuarios con notificaciones.
self.addEventListener('notificationclose', function (event) {
const dismissedNotification = event.notification;
const promiseChain = notificationCloseAnalytics();
event.waitUntil(promiseChain);
});
Agrega datos a una notificación
Cuando se recibe un mensaje push, es común tener datos que solo es útil si el usuario hizo clic en la notificación. Por ejemplo, la URL que se debe abrir cuando se hace clic en una notificación.
La forma más sencilla de tomar datos de un evento de envío y adjuntarlos a un
la notificación es agregar un parámetro data
al objeto de opciones que se pasa
en showNotification()
, de la siguiente manera:
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);
Dentro de un controlador de clics, se puede acceder a los datos con 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('');
Abrir una ventana
Una de las respuestas más comunes a una notificación es abrir una
ventana o pestaña a una URL específica. Podemos hacerlo con el
clients.openWindow()
en la API de Cloud.
En nuestro evento notificationclick
, ejecutaríamos un código como el siguiente:
const examplePage = '/demos/notification-examples/example-page.html';
const promiseChain = clients.openWindow(examplePage);
event.waitUntil(promiseChain);
En la siguiente sección, veremos cómo verificar si la página a la que queremos dirigir al usuario está ya están abiertas o no. De esta manera, podemos enfocar la pestaña abierta en lugar de abrir una nueva. pestañas.
Enfocar una ventana existente
Cuando sea posible, debemos enfocar una ventana en lugar de abrir una nueva ventana cada vez que el usuario hace clic en una notificación.
Antes de que veamos cómo lograr esto, vale la pena destacar que solo es posible para las páginas de tu origen. Esto se debe a que podemos solo ver las páginas abiertas que pertenecen a nuestro sitio. Esto evita los desarrolladores no puedan ver todos los sitios que visitan sus usuarios.
Tomando el ejemplo anterior, modificaremos el código para ver si
/demos/notification-examples/example-page.html
ya está abierto.
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);
Revisemos el código paso a paso.
Primero, analizamos nuestra página de ejemplo con la API de URL. Este es un buen truco que aprendí de Jeff
Posnick Llamar a new URL()
con el objeto location
hará lo siguiente:
Devuelve una URL absoluta si la cadena pasada es relativa (es decir, /
se convertirá en
https://example.com/
).
Hacemos que la URL sea absoluta para poder hacerla coincidir con las URL de ventana más adelante.
const urlToOpen = new URL(examplePage, self.location.origin).href;
Luego, obtenemos una lista de los objetos WindowClient
, que es la lista de los
pestañas y ventanas abiertas actualmente. (Recuerda que estas son solo las pestañas de origen).
const promiseChain = clients.matchAll({
type: 'window',
includeUncontrolled: true,
});
Las opciones que se pasan a matchAll
informan al navegador que solo queremos
para buscar "ventana" tipo de clientes (es decir, buscar pestañas y ventanas)
y excluir a los trabajadores web). includeUncontrolled
nos permite buscar
todas las pestañas de tu origen que no están controladas por el servicio actual
worker, es decir, el service worker que ejecuta este código. Por lo general,
siempre se desea que includeUncontrolled
sea verdadero cuando se llame a matchAll()
.
Capturamos la promesa que se muestra como promiseChain
para poder pasarla al
event.waitUntil()
más adelante, lo que mantiene activo nuestro service worker.
Cuando se resuelve la promesa matchAll()
, iteramos a través de los clientes que se muestran durante la ventana y
comparar sus URLs con la que queremos abrir. Si encontramos una coincidencia, nos enfocamos en
cliente, lo que atraerá la atención de los usuarios a esa ventana. El enfoque se hace con el
matchingClient.focus()
llamada.
Si no podemos encontrar un cliente que coincida, abriremos una nueva ventana, como en la sección anterior.
.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);
}
});
Combinación de notificaciones
Vimos que agregar una etiqueta a una notificación habilita un comportamiento en el que la notificación existente con la misma etiqueta se reemplaza.
Sin embargo, puedes ser más sofisticado con el cierre de las notificaciones mediante la API de notificaciones. Piensa en una app de chat, en la que el desarrollador podría querer una nueva notificación para mostrar un mensaje similar a "Tienes dos mensajes de Mateo" en lugar de solo mostrar la última mensaje.
Puedes hacerlo o manipular las notificaciones actuales de otras formas, con el registration.getNotifications() API que te otorga acceso a todas las notificaciones visibles actualmente de tu app web.
Veamos cómo podemos usar esta API para implementar el ejemplo de chat.
En nuestra app de chat, supongamos que cada notificación tiene algunos datos que incluyen un nombre de usuario.
Lo primero que debemos hacer es encontrar
las notificaciones abiertas para un usuario con un
nombre de usuario. Obtendremos registration.getNotifications()
, haremos un bucle y verificaremos el
notification.data
para un nombre de usuario específico:
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;
});
El siguiente paso es reemplazar esta notificación por una nueva.
En esta app de mensajes falsos, haremos un seguimiento de los mensajes nuevos agregando un recuento a la nueva de la notificación y aumentarlos con cada notificación nueva.
.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 aparece una notificación, aumentamos el recuento de mensajes y configuramos
el título y el cuerpo de la notificación según corresponda. Si
no hay notificaciones, creamos una nueva con un newMessageCount
de 1.
El resultado es que el primer mensaje se vería así:
Una segunda notificación las contraería de la siguiente manera:
Lo bueno de este enfoque es que si el usuario es testigo notificaciones aparecen una encima de la otra, se verá y se sentirá más coherente no basta con reemplazar la notificación con el último mensaje.
La excepción a la regla
He indicado que debes mostrar una notificación cuando recibas un mensaje push, y esto es verdadero la mayor parte del tiempo. Una situación en la que no tienes que mostrar una notificación es cuando el usuario tiene tu sitio abierto y enfocado.
En tu evento push, puedes verificar si necesitas mostrar una notificación o no examinar los clientes de la ventana y buscar una ventana enfocada.
El código para obtener todas las ventanas y buscar una ventana enfocada se ve de la siguiente manera:
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;
});
}
Usamos clients.matchAll()
para obtener todos nuestros clientes de ventana y, luego, los repasamos verificando el parámetro focused
.
En nuestro evento push, usaríamos esta función para decidir si necesitamos mostrar una notificación:
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);
Enviar un mensaje a una página desde un evento push
Notamos que puedes omitir mostrar una notificación si el usuario está actualmente en tu sitio. Sin embargo, ¿qué sucede si de todas formas deseas que el usuario sepa que ocurrió un evento, pero se con las manos demasiado pesadas?
Un enfoque es enviar un mensaje desde el service worker a la página, de esta manera Puede mostrar una notificación o una actualización al usuario para informarle del evento. Esto es útil para situaciones en las que una notificación sutil en la página es mejor y más amigable para el usuario.
Digamos que recibimos un mensaje push, comprobamos que la app web está enfocada entonces podemos "publicar un mensaje" a cada página abierta, así:
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);
En cada una de las páginas, agregamos un evento de mensaje para escuchar los mensajes. objeto de escucha:
navigator.serviceWorker.addEventListener('message', function (event) {
console.log('Received a message from service worker: ', event.data);
});
En este objeto de escucha de mensajes, puedes hacer lo que quieras, mostrar una IU personalizada en tu página o ignorar completamente el mensaje.
Si no defines un objeto de escucha de mensajes en tu página web, mensajes del service worker no hará nada.
Cómo almacenar en caché una página y abrir una ventana
Una situación que está fuera del alcance de esta guía, pero que vale la pena discutir es que puedes mejorar la UX general de tu aplicación web almacenando en caché las páginas web que esperas que visiten los usuarios haciendo clic en tu notificación.
Para ello, debes configurar tu service worker para controlar los eventos fetch
.
pero, si implementas un objeto de escucha de eventos fetch
, asegúrate de tomar
aprovéchalo en tu evento push
almacenando la página y los recursos en caché
necesitarás antes de mostrar la notificación.
Compatibilidad del navegador
El evento notificationclose
Clients.openWindow()
ServiceWorkerRegistration.getNotifications()
clients.matchAll()
Para obtener más información, consulta esta introducción a los service workers publicación.
Próximos pasos
- Descripción general de las notificaciones push web
- Cómo funciona el envío
- Cómo suscribir a un usuario
- UX de permisos
- Envía mensajes con bibliotecas push web
- Protocolo de envío web
- Maneja eventos de envío
- Cómo mostrar una notificación
- Comportamiento de las notificaciones
- Patrones de notificación comunes
- Preguntas frecuentes sobre las notificaciones push
- Problemas comunes e informar errores