Thông báo cho bạn về các thay đổi đối với thông báo

Matt Gaunt

Trước hết, tôi rất tiếc vì tiêu đề không phù hợp đó, nhưng tôi không thể làm vậy.

Trong Chrome 44, Notfication.dataServiceWorkerRegistration.getNotifications() được thêm vào và mở / đơn giản hoá một số trường hợp sử dụng phổ biến khi xử lý các thông báo bằng thông báo đẩy.

Dữ liệu thông báo

Notification.data cho phép bạn liên kết đối tượng JavaScript với một Thông báo.

Về cơ bản, khái niệm thông báo đẩy là khi nhận được một thông báo đẩy, bạn có thể tạo một thông báo chứa một số dữ liệu, sau đó, trong sự kiện notificationclick (nhấp vào thông báo), bạn có thể nhận thông báo đã được nhấp và lấy dữ liệu của thông báo đó.

Ví dụ: tạo một đối tượng dữ liệu và thêm đối tượng đó vào các tuỳ chọn thông báo của bạn như sau:

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

Có nghĩa là chúng ta có thể lấy thông tin trong sự kiện notificationclick:

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

Trước đây, bạn phải lưu trữ dữ liệu trong IndexDB hoặc đặt nội dung nào đó vào cuối URL của biểu tượng, eek.

ServiceWorkerRegistration.getNotifications()

Một yêu cầu phổ biến của các nhà phát triển phụ trách việc xử lý thông báo đẩy là có nhiều quyền kiểm soát hơn đối với các thông báo mà họ hiển thị.

Ví dụ về trường hợp sử dụng: ứng dụng trò chuyện trong đó người dùng gửi nhiều tin nhắn và người nhận hiện nhiều thông báo. Lý tưởng nhất là ứng dụng web có thể nhận thấy rằng bạn có nhiều thông báo chưa được xem và thu gọn thành một thông báo duy nhất.

Nếu không có getNotifications(), tốt nhất bạn nên thay thế thông báo trước đó bằng thông báo mới nhất. Với getNotifications(), bạn có thể "thu gọn" thông báo nếu đã hiển thị thông báo, nhờ đó mang lại trải nghiệm người dùng tốt hơn nhiều.

Ví dụ về cách nhóm các thông báo lại với nhau.

Mã để thực hiện việc này tương đối đơn giản. Bên trong sự kiện đẩy, hãy gọi ServiceWorkerSubscription.getNotifications() để nhận một loạt các Thông báo hiện tại, từ đó quyết định hành vi phù hợp, cho dù đó là thu gọn tất cả thông báo hay bằng cách sử dụng 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);
    }));
    }
});

Điều đầu tiên cần làm nổi bật với đoạn mã này là chúng ta lọc các thông báo bằng cách chuyển một đối tượng bộ lọc đến getNotifications(). Điều này có nghĩa là chúng ta có thể nhận danh sách các thông báo cho một thẻ cụ thể (trong ví dụ này về một cuộc trò chuyện cụ thể).

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

Sau đó, chúng ta sẽ xem qua các thông báo hiển thị và kiểm tra xem có số lượng thông báo liên kết với thông báo đó hay không và tăng dần dựa trên thông báo đó. Bằng cách này, nếu có một thông báo cho người dùng biết có 2 thông báo chưa đọc, chúng ta sẽ chỉ ra rằng có 3 thông báo chưa đọc khi một thông báo đẩy mới xuất hiện.

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

Xin lưu ý rằng bạn cần gọi close() trên thông báo để đảm bảo thông báo đó bị xoá khỏi danh sách thông báo. Đây là một lỗi trong Chrome vì mỗi thông báo được thay thế bằng thông báo tiếp theo vì sử dụng cùng một thẻ. Hiện tại, thông tin thay thế này chưa được phản ánh trong mảng được trả về qua getNotifications().

Đây chỉ là một ví dụ về getNotifications() và như bạn có thể hình dung, API này sẽ mở ra một loạt các trường hợp sử dụng khác.

NotificationOptions.vibrate

Kể từ Chrome 45, bạn có thể chỉ định mẫu rung khi tạo thông báo. Trên các thiết bị hỗ trợ API rung (hiện chỉ có Chrome dành cho Android), tính năng này cho phép bạn tuỳ chỉnh mẫu rung sẽ được dùng khi thông báo hiển thị.

Mẫu rung có thể là một mảng số hoặc một số duy nhất được coi là mảng số một. Các giá trị trong mảng biểu thị thời gian tính bằng mili giây, với các chỉ mục chẵn (0, 2, 4, ...) là khoảng thời gian rung và chỉ mục lẻ là khoảng thời gian tạm dừng trước lần rung tiếp theo.

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

Yêu cầu tính năng phổ biến còn lại

Một yêu cầu về tính năng phổ biến còn lại từ các nhà phát triển là khả năng đóng thông báo sau một khoảng thời gian nhất định, hoặc khả năng gửi thông báo đẩy với mục đích chỉ đóng thông báo nếu thông báo đó hiển thị.

Hiện tại, không có cách nào để bạn có thể thực hiện việc này và không có cách nào trong thông số kỹ thuật cho phép việc đó :( nhưng nhóm kỹ thuật của Chrome biết trường hợp sử dụng này.

Thông báo trên Android

Trên máy tính, bạn có thể tạo một thông báo bằng mã sau:

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

Tính năng này chưa từng được hỗ trợ trên Android do các hạn chế của nền tảng: cụ thể là Chrome không thể hỗ trợ các lệnh gọi lại trên đối tượng Thông báo, chẳng hạn như nhấp. Tuy nhiên, trình mô phỏng được dùng trên máy tính để hiển thị thông báo cho các ứng dụng web mà bạn có thể đang mở.

Lý do duy nhất tôi đề cập là vì ban đầu, một tính năng đơn giản như phát hiện tính năng dưới đây sẽ giúp bạn hỗ trợ máy tính và không gây ra lỗi nào trên Android:

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

Tuy nhiên, với hỗ trợ thông báo đẩy hiện có trên Chrome dành cho Android, bạn có thể tạo thông báo từ ServiceWorker chứ không phải từ trang web, nghĩa là tính năng này phát hiện không còn phù hợp nữa. Nếu cố gắng tạo một thông báo trên Chrome dành cho Android, bạn sẽ nhận được thông báo lỗi sau:

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

Hiện tại, cách tốt nhất để phát hiện tính năng cho Android và máy tính là làm như sau:

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

Bạn có thể sử dụng các hàm này như sau:

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