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

ראשית, סליחה על הכותרת הנוראית הזו, אבל לא הצלחתי.

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

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

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