Lors d'un article précédent nous avons créé notre première PWA, mais nous n'avons pas été jusqu'au bout du concept. L'intérêt du PWA, c'est d'agir comme une application mobile, d'être installé sur le téléphone, de gérer le off-line et surtout d'envoyer des push notifications. Les notifications sont un élément essentiel de l'engagement de l'utilisateur, elles permettent de faire un rappel et de communiquer avec nos utilisateurs. Nous allons donc finaliser le dernier tutoriel en mettant en place un système simple de push notification, en utilisant
Firebase pour stocker nos tokens utilisateurs.
Pour aller plus vite, nous vous invitons à récupérer le projet https://github.com/CaptainJojo/pwa-parisjs qui contient une PWA prête à l'emploi.
git clone https://github.com/CaptainJojo/pwa-parisjs
cd pwa-parisjs
git checkout manifest
npm install
npm start
A partir de la vous devez avoir accès à votre PWA à l'adresse suivante localhost:8080. Si ce n'est pas fait, vous devez installer Lighthouse, ce qui vous permettra de valider que vous avez bien une PWA.
Avant de se lancer dans l'envoi d'une push notification, nous allons passer par la configuration. Et oui ! Ce n'est pas magique, nous allons demander à Google l'autorisation.
Nous allons sur Firebase pour créer un projet.
Nous vous laissons choisir le nom du projet. Une fois sur le dashboard, vous devez cliquer sur la petite roue puis "Paramètres du projet".
Dans l'onglet "Cloud Messaging" vous trouverez votre bonheur en récupérant l'ID de l'expéditeur.
Dans le manifest.json, disponible dans le dossier public de l'application, vous devez ajouter en fin de fichier le "gsm_sender_id" avec comme valeur l'ID de l'expéditeur.
{
"name": "Lemonde",
"short_name": "Lemonde",
"icons": [{
"src": "images/touch/icon-128x128.png",
"sizes": "128x128",
"type": "image/png"
}, {
"src": "images/touch/apple-touch-icon.png",
"sizes": "152x152",
"type": "image/png"
}, {
"src": "images/touch/ms-touch-icon-144x144-precomposed.png",
"sizes": "144x144",
"type": "image/png"
}, {
"src": "images/touch/chrome-touch-icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}],
"start_url": "/",
"display": "standalone",
"background_color": "#3E4EB8",
"theme_color": "#2F3BA2",
"gcm_sender_id": "262283691358"
}
Nous allons maintenant demander à l'utilisateur d'accepter les notifications. C'est très simple, il vous faut ajouter dans le fichier public/register.js ce qui suit :
if('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js', { scope: '/' }).then(function() {
return navigator.serviceWorker.ready;
}).then(function(registration) {
registration.pushManager.subscribe({userVisibleOnly: true}).then(function(sub) {
var endpointSections = sub.endpoint.split('/');
var subscriptionId = endpointSections[endpointSections.length - 1];
console.log('endpoint:', subscriptionId);
});
});
navigator.serviceWorker.ready.then(function(registration) {
console.log('Service Worker Ready');
});
}
Normalement, si vous relancez le serveur, vous devez avoir une demande pour accepter les notifications.
Vous devez aussi voir dans votre console un message du type :
endpoint: cV2kP3sOb24:APA91bHfZgFSPQ3CXyG9LejWdq9jOT-WqQpvK4peX9ZZtrfsHCf6OPEvDegjsTF3uXj-Qsf4jOJ5O8rwdEm9fjKZbqerzcZUjDmsiaRcqmxuOOkpuEqPca31Dlh-6g0YgkvnLccIPz_Z
Il s'agit du token du device, c'est à partir de celui-ci que nous pourrons envoyer une push notification.
Comme nous voulons faire quelque chose de propre (même s'il ne s'agit que d'un tutoriel), nous allons utiliser Firebase pour stocker les tokens utilisateurs. Pour cela rien de plus simple, vous retournez sur la console Firebase et dans l'onglet "Authentification" vous cliquez sur "configuration web".
L'installation du script se fait dans le code html, donc public/home.html et public/article/alorscettearticle.html.
<html>;
<head>;
<meta charset=utf-8/>;
<meta name="theme-color" content="#2F3BA2">;
<meta name="viewport" content="width=device-width, initial-scale=1">;
<link rel="manifest" href="/public/manifest.json">;
</head>;
<body>;
<script src="https://www.gstatic.com/firebasejs/3.5.2/firebase.js">;</script>;
<script src="/register.js">;</script>;
<link rel="stylesheet" href="/main.css">;
<link rel="stylesheet" href="/async.css">;
L'initialization, nous la mettrons dans public/register.js.
// Initialize Firebase
var config = {
apiKey: "AIzaSyBQxLWhe7UNMnxWlO88Ybzf93Qv_3HLeQA",
authDomain: "pwa-parisjs.firebaseapp.com",
databaseURL: "https://pwa-parisjs.firebaseio.com",
storageBucket: "pwa-parisjs.appspot.com",
messagingSenderId: "262283691358"
};
firebase.initializeApp(config);
if('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js', { scope: '/' }).then(function() {
return navigator.serviceWorker.ready;
}).then(function(registration) {
registration.pushManager.subscribe({userVisibleOnly: true}).then(function(sub) {
var endpointSections = sub.endpoint.split('/');
var subscriptionId = endpointSections[endpointSections.length - 1];
console.log('endpoint:', subscriptionId);
});
});
navigator.serviceWorker.ready.then(function(registration) {
console.log('Service Worker Ready');
});
}
Maintenant que Firebase est installé, nous n'avons plus qu'à mettre en "database" le token de l'utilisateur. Vous trouverez toute les aides dont vous avez besoin dans la documentation Firebase.
Nous allons donc ajouter le fichier public/register.js
// Initialize Firebase
var config = {
apiKey: "AIzaSyBQxLWhe7UNMnxWlO88Ybzf93Qv_3HLeQA",
authDomain: "pwa-parisjs.firebaseapp.com",
databaseURL: "https://pwa-parisjs.firebaseio.com",
storageBucket: "pwa-parisjs.appspot.com",
messagingSenderId: "262283691358"
};
firebase.initializeApp(config);
if('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js', { scope: '/' }).then(function() {
return navigator.serviceWorker.ready;
}).then(function(registration) {
registration.pushManager.subscribe({userVisibleOnly: true}).then(function(sub) {
var endpointSections = sub.endpoint.split('/');
var subscriptionId = endpointSections[endpointSections.length - 1];
var newKey = firebase.database().ref().child('token').push().key;
firebase.database().ref('token/' + newKey).set({subscriptionId: subscriptionId});
console.log('endpoint:', subscriptionId);
});
});
navigator.serviceWorker.ready.then(function(registration) {
console.log('Service Worker Ready');
});
}
Avant de lancer le serveur, vous devez ouvrir les droits à Firebase afin qu'il puisse écrire dans le database. Dans l'onglet "Database" puis "Règles" il faut mettre :
{
"rules": {
".read": true,
".write": true
}
}
Si vous relancez le serveur, vous devez voir dans l'onglet "Database" de Firebase un token dans la BDD.
Bon, maintenant que nous mettons les tokens dans une base de données nous allons préparer le message qui s'affichera lors d'une push notification. Pour aller vite nous allons ajouter le code suivant en fin du fichier public/sw.js :
console.log('Started', self);
self.addEventListener('install', function(event) {
self.skipWaiting();
console.log('Installed', event);
});
self.addEventListener('activate', function(event) {
console.log('Activated', event);
});
self.addEventListener('push', function(event) {
console.log('Push message', event);
var title = 'Le push de test :)';
event.waitUntil(
self.registration.showNotification(title, {
body: 'Bravo tu l\'as reçu',
icon: 'images/icon.png',
tag: 'my-tag'
}));
});
C'est presque fini ! Nous allons créer une url "/sender" qui nous permettra d'envoyer les notifications à tous les tokens que nous avons en base. Pour cela nous allons utiliser les modules request et firebase (version npm). Voici le nouveau package.json :
{
"name": "pwa-parisjs",
"version": "0.0.1",
"description": "A Progressive Web App",
"main": "app.js",
"scripts": {
"start": "node app.js",
"sw": "gulp sw-precache"
},
"dependencies": {
"express": "^4.14.0",
"firebase": "^3.5.1",
"request": "^2.75.0"
},
"devDependencies": {
"gulp": "^3.9.1",
"sw-precache": "^3.2.0"
}
}
Dans le fichier app.js, nous initialisons Firebase. Vous allez avoir besoin d'un fichier de clé serveur. Vous allez cliquer sur la roue dans Firebase puis "Autorisation". Vous êtes alors redirigé sur une autre console.
Puis dans Comptes de service, vous créez un nouveau compte de service.
Un fichier json sera alors téléchargé, il vous suffit de l'ajouter à la racine de votre projet.
Dans le fichier app.js nous allons ajouter la route /sender qui envoie la requête de demande de push en prenant l'ensemble des tokens.
var path = require('path');
var express = require('express');
var app = express();
var firebase = require('firebase');
var request = require('request');
firebase.initializeApp({
databaseURL: 'https://pwa-parisjs.firebaseio.com/', // A trouver dans l'onglet Database
serviceAccount: 'pwa-parisjs-cf0bc079ee69.json' // Nom du fichier json a votre racine
});
app.use(express.static(path.join(__dirname, 'public'), {index: false}))
app.get('/sender', function(req, res){
var allToken = firebase.database().ref('/token');
Promise.all([allToken.once('value')]).then(function(resp) {
var allToken = resp[0].val();
tokenRep = '';
Object.keys(allToken).forEach(function(uid) {
var token = allToken[uid];
request({
url: 'https://android.googleapis.com/gcm/send',
method: 'POST',
headers: {
'Content-Type' :' application/json',
'Authorization': 'key=AIzaSyDV-KhCa9dM-bSg_r23GUwpRJqBw6qrJIc', // Clé serveur disponible dans les paramètres du projet Firebase
},
body: JSON.stringify(
{
"registration_ids" : [token.subscriptionId]
}
)
}, function(error, response, body) {
});
});
}).catch(function(error) {
console.log('Failed to start weekly top posts emailer:', error);
});
res.send('ok');
});
app.use('/', function (req, res) {
res.sendFile(__dirname + '/public/home.html');
});
app.listen(8080, function () {
console.log('Example app listening on port 8080!');
});
Attention ! La clé Authorization se trouve dans le premier onglet que l'on a ouvert.
Si tout est bon, quand vous relancez le serveur et que vous allez sur / puis /sender vous avez la notification. Si ce n'est pas le cas supprimez le cache de votre application dans la console chrome onglet application.
Encore une fois ce code est vraiment un tutoriel, je vous invite donc à faire des issues pour la moindre question. Le code final est ici.