התראות על שינויים בהתראות

קודם כול, סליחה על השם הנוראי, אבל לא יכולתי שלא.

ב-Chrome 44 נוספו Notfication.data ו-ServiceWorkerRegistration.getNotifications(), שמאפשרים לכם להשתמש בהתראות עם הודעות Push בדרכים חדשות או פשוטות יותר.

נתוני ההתראות

Notification.data מאפשר לשייך אובייקט JavaScript ל-Notification.

בעיקרון, כשאתם מקבלים הודעת דחיפה, אתם יכולים ליצור התראה עם נתונים מסוימים, ואז באירוע notificationclick תוכלו לקבל את ההתראה שנלחצה עליה ולקבל את הנתונים שלה.

לדוגמה, יוצרים אובייקט נתונים ומוסיפים אותו לאפשרויות ההתראות כך:

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

המשמעות היא שאנחנו יכולים לקבל את המידע באירוע notificationclick:

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

לפני כן, היה צריך להסתיר נתונים ב-IndexDB או להוסיף משהו בסוף של כתובת ה-URL של הסמל – איכס.

ServiceWorkerRegistration.getNotifications()

בקשה נפוצה של מפתחים שעובדים עם התראות דחיפה היא לקבל שליטה טובה יותר על ההתראות שהם מציגים.

דוגמה לתרחיש שימוש היא אפליקציית צ'אט שבה משתמש שולח כמה הודעות ונמען מקבל כמה התראות. באופן אידיאלי, אפליקציית האינטרנט תוכל לזהות שיש לכם כמה התראות שלא צפיתם בהן ולקבץ אותן להתראה אחת.

בלי getNotifications()‎, האפשרות הטובה ביותר היא להחליף את ההתראה הקודמת בהודעה האחרונה. באמצעות getNotifications()‎ אפשר "לכווץ" את ההתראות אם כבר מוצגת התראה – וכך לשפר את חוויית המשתמש.

דוגמה לקיבוץ התראות.

הקוד לביצוע הפעולה הזו פשוט יחסית. באירוע ה-push, צריך להפעיל את ServiceWorkerRegistration.getNotifications() כדי לקבל מערך של ההתראות הנוכחיות, ומשם להחליט מהו ההתנהגות המתאימה – אם לכווץ את כל ההתראות או להשתמש ב-Notification.tag.

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

הדבר הראשון שחשוב לציין לגבי קטע הקוד הזה הוא שאנחנו מסננים את ההתראות על ידי העברת אובייקט מסנן ל-getNotifications(). כלומר, אנחנו יכולים לקבל רשימת התראות לגבי תג ספציפי (בדוגמה הזו, לגבי שיחה ספציפית).

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

לאחר מכן אנחנו בודקים את ההתראות שגלויות ומחפשים אם יש להן מספר התראות משויך, ומגדילים את המספר על סמך זה. כך, אם יש התראה אחת שמציינת למשתמש שיש שתי הודעות שלא נקראו, נציין שיש שלוש הודעות שלא נקראו כשהתראה חדשה תגיע.

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

חשוב לציין שצריך להפעיל את ההתראה באמצעות close() כדי לוודא שהיא תוסר מרשימת ההתראות. זוהי באג ב-Chrome, כי כל התראה מוחלפת על ידי התראה הבאה כי נעשה שימוש באותו תג. בשלב הזה, ההחלפה הזו לא משתקפת במערך המוחזר מ-getNotifications().

זו רק דוגמה אחת ל-getNotifications(), וכפי שאתם יכולים לדמיין, ה-API הזה מאפשר מגוון רחב של תרחישים אחרים לדוגמה.

NotificationOptions.vibrate

החל מגרסה 45 של Chrome, אפשר לציין דפוס רטט כשיוצרים התראה. במכשירים שתומכים ב-Vibration API – בשלב זה רק ב-Chrome ל-Android – תוכלו להתאים אישית את דפוס הרטט שיופעל כשהתראה תוצג.

דפוס רטט יכול להיות מערך של מספרים או מספר יחיד שנחשב כמערך של מספר אחד. הערכים במערך מייצגים זמנים באלפיות שנייה, כאשר האינדקסים הזוגיים (0, 2, 4 וכו') מייצגים את משך הרטט, והאינדקסים האי-זוגיים מייצגים את משך ההשהיה לפני הרטט הבא.

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

בקשות נפוצות נוספות לתכונות

הבקשה הנפוצה היחידה שעוד נשארה מהמפתחים היא היכולת לסגור התראה אחרי פרק זמן מסוים, או היכולת לשלוח התראה ב-push כדי לסגור התראה אם היא גלויה.

בשלב הזה אין דרך לעשות זאת ואין דבר במפרט שמאפשר זאת :( אבל צוות המהנדסים של Chrome מודע לתרחיש לדוגמה הזה.

התראות ב-Android

במחשב, אפשר ליצור התראה באמצעות הקוד הבא:

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

אף פעם לא הייתה תמיכה בכך ב-Android בגלל הגבלות בפלטפורמה: באופן ספציפי, Chrome לא יכול לתמוך בקריאות החוזרות (callbacks) באובייקט Notification, כמו onclick. אבל במחשב הוא משמש להצגת התראות מאפליקציות אינטרנט שעשויות להיות פתוחות כרגע.

הסיבה היחידה לכך היא שבמקור, זיהוי תכונות פשוט כמו זה שבהמשך יעזור לך לתמוך במחשב ולא יגרום לשגיאות ב-Android:

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

עם זאת, עכשיו יש תמיכה בהתראות דחיפה ב-Chrome ל-Android, כך שאפשר ליצור התראות מ-ServiceWorker, אבל לא מדף אינטרנט. לכן, זיהוי התכונה הזו כבר לא רלוונטי. אם תנסו ליצור התראה ב-Chrome ל-Android, תופיע הודעת השגיאה הבאה:

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

בשלב הזה, הדרך הטובה ביותר לזהות תכונות ב-Android ובמחשב היא לבצע את הפעולות הבאות:

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

אפשר להשתמש בזה כך:

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