推播通知行為主要分成視覺、後續事件兩大部分,前幾天的文章開箱了:
這篇文章主要會更進一步解說視覺上可以進行設定的參數,以及收到通知後的行為,關於行為相關的參數如下。
{
"//": "行為相關參數",
"tag": "<String>",
"renotify": "<Boolean>",
"data": "<Anything>",
"requireInteraction": "<boolean>",
"silent": "<Boolean>",
"//": "視覺與行為參數",
"actions": "<Array of Strings>"
}
如果擔心覺得太抽象,歡迎先用以下連結的通知產生器先試玩:
https://tests.peter.sh/notification-generator/
這次的 Demo 連結如下也歡迎各位大大試玩看看:
https://linyencheng-push-notification.herokuapp.com/
除了單純 UI 顯示相關的參數以外,接下來介紹一些會影響流程的相關參數:
Tag 這個設定是方便讓訊息不要一直疊加,只要有同個 Tag 的新訊息就會關掉舊的並用新的取代,要注意的是後面取代的通知都不會觸發裝置的聲音或是震動。
const title = "Notification 1 of 3";
const options = {
body: "With 'tag' of 'message-group-1'",
tag: "message-group-1",
};
registration.showNotification(title, options);
因為 Tag 的實作不會觸發聲音或是震動,在聊天軟體中的某些情況下需要再次被叮咚,所以在 tag 的基礎上增加了 renotify
這個配置。
const title = "Notification 2 of 2";
const options = {
tag: "renotify",
renotify: true,
};
registration.showNotification(title, options);
停用震動、鈴聲硬體通知。
const title = "Silent Notification";
const options = {
silent: true,
};
registration.showNotification(title, options);
需要等使用者針對通知互動後才會關閉通知,Android 沒有這個問題但 Windows 7 桌面版本的通知會在一定時間後消失,直到 Windows 10 才會在通知列,所以有了這個配置。
const title = "Require Interaction Notification";
const options = {
body: "With \"requireInteraction: 'true'\".",
requireInteraction: true,
};
registration.showNotification(title, options);
透過定義 actions
就能夠讓通知帶有按鈕,將通知加上按鈕後,就可以針對按鈕補上後續的事件,以這次小編實作的功能來說,就是以下的配置。
self.registration.showNotification(data.title, {
image: "https://linyencheng.github.io/img/404-bg.jpg",
icon: "https://linyencheng.github.io/img/icon_wechat.png",
vibrate: [200, 100, 200, 100, 400],
body: "嗨,我是彥成,喜歡爬山的前端工程師,有個部落格叫前端三分鐘 :)",
actions: [
{ action: "know-more", title: "了解更多" },
{ action: "fans", title: "按讚粉專" },
],
});
實作後實際的畫面
經過相關進階設定後,就是針對相關的事件進行後續的處理和優化:
透過 service worker 去監聽點擊的事件做後續處理。
self.addEventListener("notificationclick", function (event) {
const clickedNotification = event.notification;
});
點擊通知後開啟視窗並開啟網頁。
const examplePage = "/hello.html";
const promiseChain = clients.openWindow(examplePage);
event.waitUntil(promiseChain);
如果 URL 已經在瀏覽器中被開啟就直接到那個頁面。
const urlToOpen = new URL(examplePage, self.location.origin).href;
const promiseChain = clients
.matchAll({
type: "window",
includeUncontrolled: true,
})
.then((windowClients) => {
let matchingClient = null;
for (let i = 0; i < windowClients.length; i++) {
const windowClient = windowClients[i];
if (windowClient.url === urlToOpen) {
matchingClient = windowClient;
break;
}
}
if (matchingClient) {
return matchingClient.focus();
} else {
return clients.openWindow(urlToOpen);
}
});
event.waitUntil(promiseChain);
關閉通知也有相關事件。
self.addEventListener("notificationclose", function (event) {
const dismissedNotification = event.notification;
});
假設今天加入了 requireInteraction
通知就會常駐,若是通知一直疊加上去也是非常惱人,這時候就可以透過合併通知的設計來優化用戶體驗。
const promiseChain = registration.getNotifications().then((notifications) => {
let currentNotification;
for (let i = 0; i < notifications.length; i++) {
if (notifications[i].data && notifications[i].data.userName === userName) {
currentNotification = notifications[i];
}
}
return currentNotification;
});
if (currentNotification) {
currentNotification.close();
}
這次小編實作的程式碼如下:
self.addEventListener("install", function (event) {
self.skipWaiting();
});
self.addEventListener("push", (event) => {
const data = event.data.json();
switch (data.title) {
case "前端三分鐘":
self.registration.showNotification(data.title, {
image: "https://linyencheng.github.io/img/404-bg.jpg",
icon: "https://linyencheng.github.io/img/icon_wechat.png",
vibrate: [200, 100, 200, 100, 400],
body: "嗨,我是彥成,喜歡爬山的前端工程師,有個部落格叫前端三分鐘 :)",
actions: [
{ action: "know-more", title: "了解更多" },
{ action: "fans", title: "按讚粉專" },
],
});
break;
default:
self.registration.showNotification(data.title, {
icon: "https://linyencheng.github.io/img/icon_wechat.png",
body: "恭喜! 成功註冊推播通知",
});
break;
}
});
self.addEventListener("notificationclick", function (event) {
let url;
if (!event.action) {
console.log("Notification Click.");
return;
}
switch (event.action) {
case "know-more":
url = "https://linyencheng.github.io/";
break;
case "fans":
url = "https://www.facebook.com/linyencheng.tw";
break;
default:
console.log(`Unknown action clicked: '${event.action}'`);
break;
}
event.notification.close(); // Android 需要觸發關閉
event.waitUntil(
clients.matchAll({ type: "window" }).then((windowClients) => {
// 看有沒有開過了
for (var i = 0; i < windowClients.length; i++) {
var client = windowClients[i];
// 有的話就 Focus 就好了
if (client.url === url && "focus" in client) {
return client.focus();
}
}
// 開新的頁面
if (clients.openWindow) {
return clients.openWindow(url);
}
})
);
});