2020-01-16
zelder
2020-01-17
16/01
2020

Push, Notification, Service Worker, Background Sync, etc.


Краткое вступление
Background Sync - асинхронная отправка события от клиента, вне работы браузера (страницы сайта). Например, нет связи с интернетом, а клиент нажал отправить сообщение и закрыл вкладку с сайтом. Мы зарегистрировали это событие, и как только будет доступ к интернету - отправляем его. Даже когда закрыта страница сайта. Для работы используется Service Worker.
Notification - сообщения для клиента, даже когда страница сайта закрыта. Требуются права доступа (разрешения) от клиента на получение их. Для работы используется Service Worker.
Service Worker - скрипт, который выполняет ваш браузер в фоне, отдельно (вне зависимости) от страницы сайта. Требуется HTTPS.
Push - получение событий от сервера к клиенту.


Service Worker и Background Sync

Пример отправки события (сообщения от клиента серверу).
Документация по Background Sync и по Service Worker.

скрипт на странице сайта:
var pac_sw_tag_first = "pac::sw::tag::myFirstSync";
var register = function() {
    if ('serviceWorker' in navigator) {
        // регистрируем SW
        navigator.serviceWorker.register('/sw.js') // ! важен путь
        // если OK
        .then(function(reg) { 
            return reg.sync.getTags();
        }, function(err) {
            console.log('ServiceWorker registration failed: ', err);
        })
        // проверяем тэги, если уже есть наш (единственный), то останавливаемся
        .then(function(tags) {
            if (tags.includes(pac_sw_tag_first)) { log("There's already a background sync pending"); return; }
            return navigator.serviceWorker.ready;
        })
        // регистрация нашего тэга (события)
        .then(function(reg) {
            if (reg) return reg.sync.register(pac_sw_tag_first); // собственно, просим ServiceWorker "отсылать событие"
            else return;
        })
        .catch(function(err) {
            log('It broke: ' + err.message);
        });
    }
else {
//! в идеале необходимо реализовать метод 'doSomeStuff' со своей логикой, на случай, если браузер не поддерживает SW
}
}

// слушаем событие на кнопке, где-то на странице сайта (в этом примере)
document.querySelector('.register').addEventListener('click', function(event) {
    event.preventDefault();
    register();
});

Реализация Service Worker
скрипт sw.js - который загрузит в себя отдельно браузер для выполнения в фоне
self.addEventListener('install', function(event) {
    self.skipWaiting();
});
self.addEventListener('sync', function(event) {
    var reg = self.registration;
    if (event.tag == 'pac::sw::tag::myFirstSync') {
        event.waitUntil(doSomeStuff(reg, event.tag)); // ! тут необходимо реализовать метод 'doSomeStuff' - который и будет непосредственно отправка данных
    }
});

Notification

Оповещения. Их использование разумно в некоторых случаях (но не ограничено ими):
- когда сервер хочет сообщить о новых событиях приложения (новое сообщение)
- когда действие на сайте (длительное) наконец-то завершилось
- когда событие от клиента отправилось на сервер (в случае, когда не было связи и использовался Background Sync)
- когда приложение должно работать в рамках всех вкладок браузера, и есть необходимость оповестить при работе в другой вкладке

Минимальный пример (не для реального использования):
// функция проверки прав на Нотификацию и отправка Нотификации
var register_logic = function() {
// запрашиваем права на Нотификацию
new Promise(function(resolve, reject) {
// умеет ли браузер Нотификацию
if (!"Notification" in window) {
reject(Error("This browser does not support notifications."));
} else {
// проверяем права
Notification.requestPermission(function(result) {
if (result !== 'granted') return reject(Error("Denied notification permission"));
resolve();
});
}
})
// ждем SW
.then(function() {
return navigator.serviceWorker.ready;
})
// регистрируем наше событие для ServiceWorker, который в фоне выполнит логику с Нотификацией (реализация в sw.js)
.then(function(reg) {
return reg.sync.register("my_notify_tag");
})
.catch(function(err) {
log(err.message);
});
}


window.addEventListener('load', function() {
// регистрируем SW
navigator.serviceWorker.register('sw.js')
// проверка прав и регистрация нашего события
.then(function(reg) {
register_logic(); // дальше другая ветка событий (Нотификация), нам ее ждать не надо
})
.catch(function(err) {
log(err.message);
});
});

Реализация sw.js.
// слушаем события по своим Нотификациям (ответы от пользователя)
self.onnotificationclick = function(event) {
//if (event.notification.tag === 'my_notify_tag') { // этот SW итак от нашего приложения
event.notification.close(); // закрываем свое сообщение
// дальше наша логика на основе ответа от клиента
if (event.action === 'yes') {  
console.log("yes"); 
// если открыт браузер на желаемой странице, то фокусируемся на ней, иначе открываем
var wantUrl = "/some/url";
event.waitUntil(
clients.matchAll({
type: "window"
})
.then(function(clientList) {
for (var i = 0; i < clientList.length; i++) { // проверяем все вкладки
var client = clientList[i];
if (client.url == wantUrl && 'focus' in client) return client.focus(); // фокусируемся
}
if (clients.openWindow) {
return clients.openWindow(wantUrl); // просто открываем с нашей страницей
}
})
);
}
else if (event.action === 'no') {  
console.log("no"); 
}
//}
};

var doSomeStuff = function(reg, tagName) {
// опции для нотификации
var opt = {
body: "Ты уже готов отсыпать $1,000,000 для меня...",
icon: "/Assets/images/emoji/train.png",
vibrate: [200, 100, 200, 100, 200, 100, 400],
tag: tagName,
requireInteraction: false,
actions: [
{ "action": "yes", "title": "😃 Да", "icon": "/Assets/images/emoji/good.png" },
{ "action": "no", "title": "🙈 Позже", "icon": "/Assets/images/emoji/bad.png" }
]
};
// показываем Нотификацию
reg.showNotification("Yooo!", opt);
}

self.addEventListener('install', function(event) {
self.skipWaiting();
});
self.addEventListener('sync', function(event) {
var reg = self.registration;
if (event.tag == 'my_notify_tag') {
event.waitUntil(doSomeStuff(reg, event.tag));
}
});

Push

Документация Push API,  Push Notifications on the Open Web и Web Push Notifications: Timely, Relevant, and Precise.
Примеры реализации.
TODO:





.