推播通知行為
推播通知行為主要分成視覺、後續事件兩大部分,前幾天的文章開箱了:
這篇文章主要會更進一步解說視覺上可以進行設定的參數,以及收到通知後的行為,關於行為相關的參數如下。
1 2 3 4 5 6 7 8 9 10 11
| { "//": "行為相關參數", "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)
- 重複通知 (Renotify)
- 通知動作 (Actions)
- 靜音通知設定 (Silent)
- 通知互動設定 (Require Interaction)
通知分組 (Tag)
Tag 這個設定是方便讓訊息不要一直疊加,只要有同個 Tag 的新訊息就會關掉舊的並用新的取代,要注意的是後面取代的通知都不會觸發裝置的聲音或是震動。
1 2 3 4 5 6
| const title = "Notification 1 of 3"; const options = { body: "With 'tag' of 'message-group-1'", tag: "message-group-1", }; registration.showNotification(title, options);
|
重複通知 (Renotify)
因為 Tag 的實作不會觸發聲音或是震動,在聊天軟體中的某些情況下需要再次被叮咚,所以在 tag 的基礎上增加了 renotify
這個配置。
1 2 3 4 5 6
| const title = "Notification 2 of 2"; const options = { tag: "renotify", renotify: true, }; registration.showNotification(title, options);
|
靜音通知設定 (Silent)
停用震動、鈴聲硬體通知。
1 2 3 4 5
| const title = "Silent Notification"; const options = { silent: true, }; registration.showNotification(title, options);
|
通知互動設定 (Require Interaction)
需要等使用者針對通知互動後才會關閉通知,Android 沒有這個問題但 Windows 7 桌面版本的通知會在一定時間後消失,直到 Windows 10 才會在通知列,所以有了這個配置。
1 2 3 4 5 6
| const title = "Require Interaction Notification"; const options = { body: "With \"requireInteraction: 'true'\".", requireInteraction: true, }; registration.showNotification(title, options);
|
通知動作 (Actions)
透過定義 actions
就能夠讓通知帶有按鈕,將通知加上按鈕後,就可以針對按鈕補上後續的事件,以這次小編實作的功能來說,就是以下的配置。
1 2 3 4 5 6 7 8 9 10
| 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 去監聽點擊的事件做後續處理。
1 2 3
| self.addEventListener("notificationclick", function (event) { const clickedNotification = event.notification; });
|
開啟視窗
點擊通知後開啟視窗並開啟網頁。
1 2 3
| const examplePage = "/hello.html"; const promiseChain = clients.openWindow(examplePage); event.waitUntil(promiseChain);
|
Focus Tab
如果 URL 已經在瀏覽器中被開啟就直接到那個頁面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 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);
|
關閉通知
關閉通知也有相關事件。
1 2 3
| self.addEventListener("notificationclose", function (event) { const dismissedNotification = event.notification; });
|
合併通知
假設今天加入了 requireInteraction
通知就會常駐,若是通知一直疊加上去也是非常惱人,這時候就可以透過合併通知的設計來優化用戶體驗。
- 先抓出現有的
- 把現有的關閉再發一則新的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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(); }
|
實作
這次小編實作的程式碼如下:
- 跳過等待直接生效
- 處理 push 事件,收到就顯示通知,並且在通知加上 aciton
- 處理 notificationclick 事件,並且針對不同 action 加上各自的事件
- 開啟不同的連結
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| 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(); event.waitUntil( clients.matchAll({ type: "window" }).then((windowClients) => { for (var i = 0; i < windowClients.length; i++) { var client = windowClients[i]; if (client.url === url && "focus" in client) { return client.focus(); } } if (clients.openWindow) { return clients.openWindow(url); } }) ); });
|
喜歡這篇文章,請幫忙拍拍手喔 🤣