Benachrichtigung bei Änderungen an Benachrichtigungen

Matt Gaunt

Zuerst entschuldige ich mich für diesen schrecklichen Titel, aber ich konnte es nicht.

In Chrome 44 wurden Notfication.data und ServiceWorkerRegistration.getNotifications() hinzugefügt. Dadurch werden einige gängige Anwendungsfälle im Umgang mit Benachrichtigungen mit Push-Nachrichten vereinfacht.

Benachrichtigungsdaten

Mit „Notification.data“ können Sie ein JavaScript-Objekt mit einer Benachrichtigung verknüpfen.

Wenn Sie also eine Push-Nachricht erhalten, können Sie eine Benachrichtigung mit einigen Daten erstellen. Im Ereignis „notificationclick“ können Sie dann die Benachrichtigung abrufen, auf die geklickt wurde, und die zugehörigen Daten abrufen.

So erstellen Sie beispielsweise ein Datenobjekt und fügen es Ihren Benachrichtigungsoptionen hinzu:

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
    })
    );
});

Das bedeutet, dass wir die Informationen im Ereignis „notificationclick“ abrufen können:

self.addEventListener('notificationclick', function(event) {
    var doge = event.notification.data.doge;
    console.log(doge.wow);
});

Bisher mussten Sie Daten in IndexDB speichern oder etwas ans Ende der Symbol-URL anhängen – ganz schön umständlich.

ServiceWorkerRegistration.getNotifications()

Eine häufige Anforderung von Entwicklern, die mit Push-Benachrichtigungen arbeiten, ist eine bessere Kontrolle über die angezeigten Benachrichtigungen.

Ein Beispiel für einen Anwendungsfall wäre eine Chatanwendung, in der ein Nutzer mehrere Nachrichten sendet und der Empfänger mehrere Benachrichtigungen anzeigt. Idealerweise sollte die Webanwendung erkennen können, dass Sie mehrere Benachrichtigungen haben, die noch nicht angesehen wurden, und sie zu einer einzigen Benachrichtigung zusammenfassen.

Ohne getNotifications() können Sie die vorherige Benachrichtigung nur durch die neueste Nachricht ersetzen. Mit getNotifications() können Sie die Benachrichtigungen „minimieren“, wenn bereits eine Benachrichtigung angezeigt wird. Dies verbessert die Nutzerfreundlichkeit.

Beispiel für die Gruppierung von Benachrichtigungen

Der Code dafür ist relativ einfach. Rufen Sie in Ihrem Push-Ereignis ServiceWorkerRegistration.getNotifications() auf, um ein Array mit aktuellen Benachrichtigungen zu erhalten und von dort aus das richtige Verhalten festzulegen, also ob alle Benachrichtigungen minimiert oder das Notification.tag verwendet wird.

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);
    }));
    }
});

Bei diesem Code-Snippet ist vor allem wichtig, dass wir unsere Benachrichtigungen filtern, indem wir ein Filterobjekt an „getNotifications()“ übergeben. So können wir eine Liste mit Benachrichtigungen für ein bestimmtes Tag abrufen (in diesem Beispiel für eine bestimmte Unterhaltung).

var notificationFilter = {
    tag: notificationTag
};
return self.registration.getNotifications(notificationFilter)

Anschließend sehen wir uns die sichtbaren Benachrichtigungen an und prüfen, ob mit dieser Benachrichtigung eine Benachrichtigungszahl verknüpft ist. Daraufhin erhöhen wir die Anzahl entsprechend. Wenn der Nutzer also eine Benachrichtigung erhält, dass er zwei ungelesene Nachrichten hat, möchten wir ihn darauf hinweisen, dass er drei ungelesene Nachrichten hat, wenn eine neue Push-Nachricht eingeht.

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();
}

Wichtig: Sie müssen close() auf die Benachrichtigung anwenden, damit sie aus der Benachrichtigungsliste entfernt wird. Dies ist ein Fehler in Chrome, da jede Benachrichtigung durch die nächste ersetzt wird, da dasselbe Tag verwendet wird. Derzeit wird diese Ersetzung nicht im zurückgegebenen Array von getNotifications() widergespiegelt.

Dies ist nur ein Beispiel für getNotifications(). Wie Sie sich vorstellen können, eröffnet diese API eine Reihe weiterer Anwendungsfälle.

NotificationOptions.vibrate

Ab Chrome 45 können Sie beim Erstellen einer Benachrichtigung ein Vibrationsmuster angeben. Auf Geräten, die die Vibration API unterstützen (derzeit nur Chrome für Android), können Sie das Vibrationsmuster anpassen, das beim Anzeigen der Benachrichtigung verwendet wird.

Ein Vibrationsmuster kann entweder ein Zahlenarray oder eine einzelne Zahl sein, die als Array mit einer Zahl behandelt wird. Die Werte im Array geben die Zeit in Millisekunden an. Die geraden Indizes (0, 2, 4, ...) geben an, wie lange vibriert werden soll, und die ungeraden Indizes geben an, wie lange die Pause vor der nächsten Vibration sein soll.

self.registration.showNotification('Buzz!', {
    body: 'Bzzz bzzzz',
    vibrate: [300, 100, 400] // Vibrate 300ms, pause 100ms, then vibrate 400ms
});

Verbleibende häufige Anfragen zu Funktionen

Die einzige verbleibende häufige Funktionsanfrage von Entwicklern ist die Möglichkeit, eine Benachrichtigung nach einem bestimmten Zeitraum zu schließen oder eine Push-Benachrichtigung zu senden, um eine Benachrichtigung zu schließen, wenn sie sichtbar ist.

Leider ist das derzeit nicht möglich und auch in der Spezifikation ist das nicht vorgesehen. Das Chrome-Entwicklerteam ist sich dieses Anwendungsfalls aber bewusst.

Android-Benachrichtigungen

Auf dem Computer kannst du mit dem folgenden Code eine Benachrichtigung erstellen:

new Notification('Hello', {body: 'Yay!'});

Diese Funktion wurde unter Android aufgrund von Einschränkungen der Plattform nie unterstützt. Chrome kann insbesondere die Callbacks im Benachrichtigungsobjekt wie „onclick“ nicht unterstützen. Auf dem Computer wird es jedoch verwendet, um Benachrichtigungen für Web-Apps anzuzeigen, die Sie möglicherweise gerade geöffnet haben.

Ich erwähne das nur, weil ursprünglich eine einfache Funktionserkennung wie die unten beschriebene dazu beigetragen hat, die Desktopversion zu unterstützen und keine Fehler auf Android-Geräten zu verursachen:

if (!'Notification' in window) {
    // Notifications aren't supported
    return;
}

Da Push-Benachrichtigungen jetzt in Chrome für Android unterstützt werden, können Benachrichtigungen über einen ServiceWorker erstellt werden, aber nicht über eine Webseite. Daher ist diese Funktion nicht mehr geeignet. Wenn Sie versuchen, in Chrome für Android eine Benachrichtigung zu erstellen, erhalten Sie diese Fehlermeldung:

_Uncaught TypeError: Failed to construct 'Notification': Illegal constructor.
Use ServiceWorkerRegistration.showNotification() instead_

Die beste Methode für die Feature-Erkennung für Android und Computer ist derzeit Folgendes:

    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;
    }

So können Sie das verwenden:

    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();
    }
durch.