Primeiro, peço desculpas pelo título horrível, mas não consegui evitar.
No Chrome 44, Notfication.data e ServiceWorkerRegistration.getNotifications() são adicionados e abrem / simplificam alguns casos de uso comuns ao lidar com notificações com mensagens push.
Dados da notificação
Notification.data permite associar um objeto JavaScript a uma notificação.
Basicamente, quando você recebe uma mensagem push, pode criar uma notificação com alguns dados e, no evento de notificationclick, pode receber a notificação que foi clicada e os dados dela.
Por exemplo, crie um objeto de dados e adicione-o às opções de notificação como esta:
self.addEventListener('push', function(event) {
console.log('Received a push message', event);
var title = 'Yay a message.';
var body = 'We have received a push message.';
var icon = '/images/icon-192x192.png';
var tag = 'simple-push-demo-notification-tag';
var data = {
doge: {
wow: 'such amaze notification data'
}
};
event.waitUntil(
self.registration.showNotification(title, {
body: body,
icon: icon,
tag: tag,
data: data
})
);
});
Isso significa que podemos receber as informações no evento notificationclick:
self.addEventListener('notificationclick', function(event) {
var doge = event.notification.data.doge;
console.log(doge.wow);
});
Antes disso, era preciso armazenar dados no IndexDB ou colocar algo no final do URL do ícone, por exemplo.
ServiceWorkerRegistration.getNotifications()
Uma solicitação comum de desenvolvedores que trabalham com notificações push é ter melhor controle sobre as notificações que exibem.
Um exemplo de caso de uso seria um aplicativo de chat em que um usuário envia várias mensagens e o destinatário exibe várias notificações. O ideal é que o app da Web perceba que você tem várias notificações que não foram visualizadas e as junte em uma única notificação.
Sem getNotifications(), o melhor que você pode fazer é substituir a notificação anterior pela mensagem mais recente. Com getNotifications(), você pode "recolher" as notificações se uma delas já estiver visível, o que resulta em uma experiência do usuário muito melhor.
O código para fazer isso é relativamente simples. No evento push, chame ServiceWorkerRegistration.getNotifications() para receber uma matriz das notificações atuais e, a partir daí, decidir o comportamento correto, seja reduzindo todas as notificações ou usando a tag Notification.
function showNotification(title, body, icon, data) {
var notificationOptions = {
body: body,
icon: icon ? icon : 'images/touch/chrome-touch-icon-192x192.png',
tag: 'simple-push-demo-notification',
data: data
};
self.registration.showNotification(title, notificationOptions);
return;
}
self.addEventListener('push', function(event) {
console.log('Received a push message', event);
// Since this is no payload data with the first version
// of Push notifications, here we'll grab some data from
// an API and use it to populate a notification
event.waitUntil(
fetch(API_ENDPOINT).then(function(response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
// Throw an error so the promise is rejected and catch() is executed
throw new Error();
}
// Examine the text in the response
return response.json().then(function(data) {
var title = 'You have a new message';
var message = data.message;
var icon = 'images/notification-icon.png';
var notificationTag = 'chat-message';
var notificationFilter = {
tag: notificationTag
};
return self.registration.getNotifications(notificationFilter)
.then(function(notifications) {
if (notifications && notifications.length > 0) {
// Start with one to account for the new notification
// we are adding
var notificationCount = 1;
for (var i = 0; i < notifications.length; i++) {
var existingNotification = notifications[i];
if (existingNotification.data &&
existingNotification.data.notificationCount) {
notificationCount +=
existingNotification.data.notificationCount;
} else {
notificationCount++;
}
existingNotification.close();
}
message = 'You have ' + notificationCount +
' weather updates.';
notificationData.notificationCount = notificationCount;
}
return showNotification(title, message, icon, notificationData);
});
});
}).catch(function(err) {
console.error('Unable to retrieve data', err);
var title = 'An error occurred';
var message = 'We were unable to get the information for this ' +
'push message';
return showNotification(title, message);
})
);
});
self.addEventListener('notificationclick', function(event) {
console.log('On notification click: ', event);
if (Notification.prototype.hasOwnProperty('data')) {
console.log('Using Data');
var url = event.notification.data.url;
event.waitUntil(clients.openWindow(url));
} else {
event.waitUntil(getIdb().get(KEY_VALUE_STORE_NAME,
event.notification.tag).then(function(url) {
// At the moment you cannot open third party URL's, a simple trick
// is to redirect to the desired URL from a URL on your domain
var redirectUrl = '/redirect.html?redirect=' +
url;
return clients.openWindow(redirectUrl);
}));
}
});
A primeira coisa a destacar neste snippet de código é que filtramos nossas notificações transmitindo um objeto de filtro para getNotifications(). Isso significa que podemos receber uma lista de notificações para uma tag específica (neste exemplo, para uma conversa específica).
var notificationFilter = {
tag: notificationTag
};
return self.registration.getNotifications(notificationFilter)
Em seguida, analisamos as notificações visíveis, verificamos se há uma contagem de notificações associada a ela e incrementamos com base nisso. Dessa forma, se houver uma notificação informando ao usuário que há duas mensagens não lidas, queremos apontar que há três mensagens não lidas quando uma nova notificação push chegar.
var notificationCount = 1;
for (var i = 0; i < notifications.length; i++) {
var existingNotification = notifications[i];
if (existingNotification.data && existingNotification.data.notificationCount) {
notificationCount += existingNotification.data.notificationCount;
} else {
notificationCount++;
}
existingNotification.close();
}
Uma sutileza a ser destacada é que você precisa chamar close()
na notificação para
garantir que ela seja removida da lista de notificações. Esse é um bug no
Chrome, já que cada notificação é substituída pela próxima porque a mesma tag
é usada. No momento, essa substituição não está sendo refletida na matriz retornada
de getNotifications()
.
Esse é apenas um exemplo de getNotificações() e, como você pode imaginar, essa API abre uma variedade de outros casos de uso.
NotificationOptions.vibrate
A partir do Chrome 45, é possível especificar um padrão de vibração ao criar uma notificação. Em dispositivos com suporte à API Vibration (atualmente, apenas o Chrome para Android), é possível personalizar o padrão de vibração que será usado quando a notificação for exibida.
Um padrão de vibração pode ser uma matriz de números ou um único número tratado como uma matriz de um número. Os valores na matriz representam tempos em milissegundos, com os índices pares (0, 2, 4, ...) sendo o tempo de vibração, e os índices ímpares sendo o tempo de pausa antes da próxima vibração.
self.registration.showNotification('Buzz!', {
body: 'Bzzz bzzzz',
vibrate: [300, 100, 400] // Vibrate 300ms, pause 100ms, then vibrate 400ms
});
Solicitações de recursos comuns restantes
A única solicitação de recurso comum restante dos desenvolvedores é a capacidade de fechar uma notificação após um determinado período ou de enviar uma notificação push com o objetivo de apenas fechar uma notificação se ela estiver visível.
No momento, não há como fazer isso e nada na especificação permite isso :( mas a equipe de engenharia do Chrome está ciente desse caso de uso.
Notificações do Android
No computador, é possível criar uma notificação com o seguinte código:
new Notification('Hello', {body: 'Yay!'});
Isso nunca foi compatível com o Android devido a restrições da plataforma: especificamente, o Chrome não pode oferecer suporte aos callbacks no objeto Notification, como onclick. No entanto, ele é usado na área de trabalho para exibir notificações de apps da Web que você pode estar aberto no momento.
O único motivo para mencioná-lo é que, originalmente, uma detecção de recurso simples como a abaixo ajudaria a oferecer suporte ao computador e não causaria erros no Android:
if (!'Notification' in window) {
// Notifications aren't supported
return;
}
No entanto, com o suporte a notificações push no Chrome para Android, as notificações podem ser criadas em um ServiceWorker, mas não em uma página da Web. Isso significa que essa detecção de recurso não é mais adequada. Se tentar criar uma notificação no Chrome para Android, você receberá esta mensagem de erro:
_Uncaught TypeError: Failed to construct 'Notification': Illegal constructor.
Use ServiceWorkerRegistration.showNotification() instead_
No momento, a melhor maneira de detectar recursos para Android e computador é fazer o seguinte:
function isNewNotificationSupported() {
if (!window.Notification || !Notification.requestPermission)
return false;
if (Notification.permission == 'granted')
throw new Error('You must only call this \*before\* calling
Notification.requestPermission(), otherwise this feature detect would bug the
user with an actual notification!');
try {
new Notification('');
} catch (e) {
if (e.name == 'TypeError')
return false;
}
return true;
}
Ele pode ser usado assim:
if (window.Notification && Notification.permission == 'granted') {
// We would only have prompted the user for permission if new
// Notification was supported (see below), so assume it is supported.
doStuffThatUsesNewNotification();
} else if (isNewNotificationSupported()) {
// new Notification is supported, so prompt the user for permission.
showOptInUIForNotifications();
}