¿Conoce esas pequeñas ventanas de notificación que aparecen en la esquina superior derecha (Mac) o inferior derecha (Windows) cuando, por ejemplo, se carga un nuevo artículo en nuestro blog favorito o un nuevo video en YouTube? Esas son notificaciones automáticas.
Parte de la magia de estas notificaciones es que pueden aparecer incluso cuando no estemos en ese sitio web para brindarnos esa información (después de que la hayas aprobado). En dispositivos móviles, donde sea compatible, incluso puede cerrar el navegador y aún así obtenerlos.
Serie de artículos:
- Configuración & Firebase (¡Usted está aquí!)
- El back end
Notificación push en una Mac en Chrome
Una notificación consta del logotipo del navegador para que el usuario sepa de qué software proviene, un título, la URL del sitio web desde el que se envió, una breve descripción y un icono personalizado.
Vamos a explorar cómo implementar notificaciones push. Dado que se basa en Service Workers, consulte estos puntos de partida si no está familiarizado con él o con la funcionalidad general de la API Push:
- Google explica los conceptos básicos de Push API
- Programar Plus para trabajadores de servicios
Que vamos a crear
Vista previa de nuestro sitio web de demostración de notificaciones push
Para probar nuestro sistema de notificaciones, crearemos una página con:
- un botón de suscripción
- un formulario para agregar publicaciones
- una lista de todas las publicaciones publicadas anteriormente
Aquí se puede encontrar un repositorio en Github con el código completo y una vista previa del proyecto:
Ver sitio de demostración
Y un video de cómo funciona:
Reuniendo todas las herramientas
Eres libre de elegir el sistema de back-end que más te convenga. Fui con Firebase ya que ofrece una API especial que hace que la implementación de un servicio de notificaciones push sea relativamente fácil.
Nosotros necesitamos:
- Un trabajador de servicios (y por lo tanto HTTPS)
- Un manifiesto
- Firebase Realtime Database para almacenar nuestras publicaciones
- Firebase Cloud Messaging, que nos permite “entregar mensajes de manera confiable y sin costo”
- Firebase Cloud Functions que observa los cambios en nuestra base de datos y envía las notificaciones
En esta parte, solo nos centraremos en el front-end, incluido el Service Worker y el manifiesto, pero para usar Firebase, también deberá registrarse y crear un nuevo proyecto.
Implementación de la lógica de suscripción
HTML
Tenemos un botón para suscribirse que se habilita if 'serviceWorker' in navigator
. Debajo de eso, un formulario simple y una lista de publicaciones:
<button id="push-button" disabled>Subscribe</button>
<form action="#">
<input id="input-title">
<label for="input-title">Post Title</label>
<button type="submit" id="add-post">Add Post</button>
</form>
<ul id="list"></ul>
Implementando Firebase
Para hacer uso de Firebase, necesitamos implementar algunos scripts.
<script src="https://www.gstatic.com/firebasejs/4.1.3/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/4.1.3/firebase-database.js"></script>
<script src="https://www.gstatic.com/firebasejs/4.1.3/firebase-messaging.js"></script>
Ahora podemos inicializar Firebase usando las credenciales dadas en Configuración del proyecto → General. La identificación del remitente se puede encontrar en Configuración del proyecto → Mensajería en la nube. La configuración está oculta detrás del icono de engranaje en la esquina superior izquierda.
firebase.initializeApp({
apiKey: '<API KEY>',
authDomain: '<PROJECT ID>.firebaseapp.com',
databaseURL: 'https://<PROJECT ID>.firebaseio.com',
projectId: '<PROJECT ID>',
storageBucket: '<PROJECT ID>.appspot.com',
messagingSenderId: '<SENDER ID>'
})
Registro de trabajador de servicio
Firebase ofrece su propia configuración de trabajador de servicio mediante la creación de un archivo llamado `firebase-messaging-sw.js` que contiene toda la funcionalidad para manejar notificaciones push. Pero, por lo general, necesita que su Service Worker haga más que eso. Entonces con el useServiceWorker
Podemos decirle a Firebase que también use nuestro propio archivo `service-worker.js`.
Ahora podemos crear un userToken
y un isSubscribed
variable que se utilizará más adelante.
const messaging = firebase.messaging(),
database = firebase.database(),
pushBtn = document.getElementById('push-button')
let userToken = null,
isSubscribed = false
window.addEventListener('load', () => {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js')
.then(registration => {
messaging.useServiceWorker(registration)
initializePush()
})
.catch(err => console.log('Service Worker Error', err))
} else {
pushBtn.textContent="Push not supported."
}
})
Inicializar configuración push
Fíjate en la función initializePush()
después del registro del trabajador de servicio. Comprueba si el usuario actual ya está suscrito buscando un token en localStorage
. Si hay un token, cambia el texto del botón y guarda el token en una variable.
function initializePush() {
userToken = localStorage.getItem('pushToken')
isSubscribed = userToken !== null
updateBtn()
pushBtn.addEventListener('click', () => {
pushBtn.disabled = true
if (isSubscribed) return unsubscribeUser()
return subscribeUser()
})
}
Aquí también manejamos el evento de clic en el botón de suscripción. Deshabilitamos el botón al hacer clic para evitar múltiples desencadenantes.
Actualizar el botón de suscripción
Para reflejar el estado de suscripción actual, debemos ajustar el texto y el estilo del botón. También podemos verificar si el usuario no permitió las notificaciones automáticas cuando se le solicite.
function updateBtn() {
if (Notification.permission === 'denied') {
pushBtn.textContent="Subscription blocked"
return
}
pushBtn.textContent = isSubscribed ? 'Unsubscribe' : 'Subscribe'
pushBtn.disabled = false
}
Suscribir usuario
Digamos que el usuario nos visita por primera vez en un navegador moderno, por lo que aún no está suscrito. Además, se admiten Service Workers y Push API. Cuando hace clic en el botón, el subscribeUser()
se dispara la función.
function subscribeUser() {
messaging.requestPermission()
.then(() => messaging.getToken())
.then(token => {
updateSubscriptionOnServer(token)
isSubscribed = true
userToken = token
localStorage.setItem('pushToken', token)
updateBtn()
})
.catch(err => console.log('Denied', err))
}
Aquí pedimos permiso para enviar notificaciones push al usuario escribiendo messaging.requestPermission()
.
El navegador que solicita permiso para enviar notificaciones automáticas.
Si el usuario bloquea esta solicitud, el botón se ajusta de la forma en que lo implementamos en el updateBtn()
función. Si el usuario permite esta solicitud, se genera un nuevo token, que se guarda tanto en una variable como en localStorage
. El token está siendo guardado en nuestra base de datos por updateSubscriptionOnServer()
.
Ahorre suscripción en nuestra base de datos
Si el usuario ya estaba suscrito, apuntamos a la referencia de base de datos correcta donde guardamos los tokens (en este caso device_ids
), busque el token que el usuario ya ha proporcionado antes y elimínelo.
De lo contrario, queremos guardar el token. Con .once('value')
, recibimos los valores clave y podemos verificar si el token ya está allí. Esto sirve como segunda protección para la búsqueda en localStorage
en initializePush()
ya que el token puede eliminarse de allí debido a varias razones. No queremos que el usuario reciba múltiples notificaciones con el mismo contenido.
function updateSubscriptionOnServer(token) {
if (isSubscribed) {
return database.ref('device_ids')
.equalTo(token)
.on('child_added', snapshot => snapshot.ref.remove())
}
database.ref('device_ids').once('value')
.then(snapshots => {
let deviceExists = false
snapshots.forEach(childSnapshot => {
if (childSnapshot.val() === token) {
deviceExists = true
return console.log('Device already registered.');
}
})
if (!deviceExists) {
console.log('Device subscribed');
return database.ref('device_ids').push(token)
}
})
}
Darse de baja de usuario
Si el usuario hace clic en el botón después de suscribirse nuevamente, su token se elimina. Reiniciamos nuestro userToken
y isSubscribed
variables, así como eliminar el token de localStorage
y actualiza nuestro botón de nuevo.
function unsubscribeUser() {
messaging.deleteToken(userToken)
.then(() => {
updateSubscriptionOnServer(userToken)
isSubscribed = false
userToken = null
localStorage.removeItem('pushToken')
updateBtn()
})
.catch(err => console.log('Error unsubscribing', err))
}
Para que el trabajador del servicio sepa que usamos Firebase, importamos los scripts a `service-worker.js` antes que nada.
importScripts('https://www.gstatic.com/firebasejs/4.1.3/firebase-app.js')
importScripts('https://www.gstatic.com/firebasejs/4.1.3/firebase-database.js')
importScripts('https://www.gstatic.com/firebasejs/4.1.3/firebase-messaging.js')
Necesitamos inicializar Firebase nuevamente ya que el Service Worker no puede acceder a los datos dentro de nuestro archivo `main.js`.
firebase.initializeApp({
apiKey: "<API KEY>",
authDomain: "<PROJECT ID>.firebaseapp.com",
databaseURL: "https://<PROJECT ID>.firebaseio.com",
projectId: "<PROJECT ID>",
storageBucket: "<PROJECT ID>.appspot.com",
messagingSenderId: "<SENDER ID>"
})
Debajo de eso, agregamos todos los eventos relacionados con el manejo de la ventana de notificación. En este ejemplo, cerramos la notificación y abrimos un sitio web después de hacer clic en él.
self.addEventListener('notificationclick', event => {
event.notification.close()
event.waitUntil(
self.clients.openWindow('https://artofmyself.com')
)
})
Otro ejemplo sería la sincronización de datos en segundo plano. Lea el artículo de Google sobre eso.
Mostrar mensajes cuando esté en el sitio
Cuando estamos suscritos a notificaciones de nuevas publicaciones pero ya estamos visitando el blog en el mismo momento en que se publica una nueva publicación, no recibimos una notificación.
Una forma de solucionar esto es mostrando un tipo diferente de mensaje en el sitio como un pequeño snack bar en la parte inferior.
Para interceptar la carga útil del mensaje, llamamos al onMessage
método en Firebase Messaging.
El estilo de este ejemplo utiliza Material Design Lite.
<div id="snackbar" class="mdl-js-snackbar mdl-snackbar">
<div class="mdl-snackbar__text"></div>
<button class="mdl-snackbar__action" type="button"></button>
</div>
import 'material-design-lite'
messaging.onMessage(payload => {
const snackbarContainer = document.querySelector('#snackbar')
let data = {
message: payload.notification.title,
timeout: 5000,
actionHandler() {
location.reload()
},
actionText: 'Reload'
}
snackbarContainer.MaterialSnackbar.showSnackbar(data)
})
Agregar un manifiesto
El último paso para esta parte de la serie es agregar el ID de remitente de mensajería en la nube de Google al archivo `manifest.json`. Esta identificación garantiza que Firebase pueda enviar mensajes a nuestra aplicación. Si aún no tiene un manifiesto, cree uno y agregue lo siguiente. No cambies el valor.
{
"gcm_sender_id": "103953800507"
}
Ahora estamos todos configurados en el front-end. Lo que queda es crear nuestra base de datos real y las funciones para observar los cambios en la base de datos en el próximo artículo.
Serie de artículos:
- Configuración & Firebase (¡Usted está aquí!)
- El back end