iT邦幫忙

2021 iThome 鐵人賽

DAY 25
0

推播通知行為

推播通知行為主要分成視覺、後續事件兩大部分,前幾天的文章開箱了:

這篇文章主要會更進一步解說視覺上可以進行設定的參數,以及收到通知後的行為,關於行為相關的參數如下。

{
  "//": "行為相關參數",
  "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 的新訊息就會關掉舊的並用新的取代,要注意的是後面取代的通知都不會觸發裝置的聲音或是震動。

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 這個配置。

const title = "Notification 2 of 2";
const options = {
  tag: "renotify",
  renotify: true,
};
registration.showNotification(title, options);

靜音通知設定 (Silent)

停用震動、鈴聲硬體通知。

const title = "Silent Notification";
const options = {
  silent: true,
};
registration.showNotification(title, options);

通知互動設定 (Require Interaction)

需要等使用者針對通知互動後才會關閉通知,Android 沒有這個問題但 Windows 7 桌面版本的通知會在一定時間後消失,直到 Windows 10 才會在通知列,所以有了這個配置。

const title = "Require Interaction Notification";
const options = {
  body: "With \"requireInteraction: 'true'\".",
  requireInteraction: true,
};
registration.showNotification(title, options);

通知動作 (Actions)

透過定義 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: "按讚粉專" },
  ],
});

實作後實際的畫面

通知事件流程處理

經過相關進階設定後,就是針對相關的事件進行後續的處理和優化:

  • 點擊通知
    • 開啟視窗
    • Focus Tab
  • 關閉通知
  • 合併通知

點擊通知

透過 service worker 去監聽點擊的事件做後續處理。

self.addEventListener("notificationclick", function (event) {
  const clickedNotification = event.notification;
});

開啟視窗

點擊通知後開啟視窗並開啟網頁。

const examplePage = "/hello.html";
const promiseChain = clients.openWindow(examplePage);
event.waitUntil(promiseChain);

Focus Tab

如果 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 通知就會常駐,若是通知一直疊加上去也是非常惱人,這時候就可以透過合併通知的設計來優化用戶體驗。

  1. 先抓出現有的
  2. 把現有的關閉再發一則新的
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();
}

實作

這次小編實作的程式碼如下:

  1. 跳過等待直接生效
  2. 處理 push 事件,收到就顯示通知,並且在通知加上 aciton
  3. 處理 notificationclick 事件,並且針對不同 action 加上各自的事件
  4. 開啟不同的連結
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);
      }
    })
  );
});

上一篇
Progressive Web App 推播通知: 網站推播通知用戶端實作入門 (24)
下一篇
Progressive Web App 推播協定 (26)
系列文
前端三分鐘 X Progressive Web App 30 天製造解密30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言