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

מאט גאונט

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

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

נתוני התראות

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

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

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

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() כדי לקבל מגוון התראות עדכניות, ומשם קובעים את ההתנהגות הנכונה – בין אם מדובר בכיווץ כל ההתראות או באמצעות התראות.

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

ב-Chrome 45 אפשר להגדיר דפוס רטט כשיוצרים התראה. אפשרות זו מאפשרת להתאים אישית את דפוס הרטט שבו ייעשה שימוש כשההתראה תוצג במכשירים שתומכים ב-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
});

שאר הבקשות לתכונות נפוצות

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

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

התראות Android

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

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

אפשרות זו מעולם לא נתמכה ב-Android עקב הגבלות בפלטפורמה: באופן ספציפי, Chrome לא יכול לתמוך בקריאות חוזרות (callback) באובייקט ההודעה, כמו "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();
    }