iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 23
1
Modern Web

你應該要知道的新一代Web技術---漸進式網頁(PWA)系列 第 23

[Day23] 實作PWA推播通知(Part2)

昨天在實作「顯示通知」時,除了設定body這個option之外,其實還有很多其他選項可以設定 (不過這裡要先說這些額外的設定不一定每個browser都有支援QQ):

function displayConfirmNotification() {
    if('serviceWorker' in navigator) {
        var options = {
            body: '您已成功訂閱我們的推播服務!',
            icon: '/src/images/icons/app-icon-96x96.png',
            image: '/src/images/sf-boat.jpg',
            dir: 'ltr',
            lang: 'zh-TW',   // BCP 47
            vibrate: [100, 50, 200],
            badge: '/src/images/icons/app-icon-96x96.png',
            tag: 'confirm-notification',
            renotify: true,
            actions: [
                { action: 'confirm', title: '收到', icon: '/src/images/icons/app-icon-96x96.png' },
                { action: 'cancel', title: '取消', icon: '/src/images/icons/app-icon-96x96.png' }
            ]
        }
        navigator.serviceWorker.ready.then(function(swreg) {
            swreg.showNotification('成功訂閱!! (from Service Worker)', options);
        });
    }
}

我一個個說明一下每個選項代表的意義:

  • icon:在顯示通知左方的小圖示,往往會是網頁的logo。
  • image:這裡我先寫死一張固定的圖片,之後我會拿用戶發佈的新貼文圖片作為通知的image。
  • dir:其實在web manifest中有設定過,定義通知的「文字顯示方向」,預設為ltr(left to right)。
  • lang:根據IETF的BCP47來去定義「文字的語言代號」。
  • vibrate:在手機上顯示通知時的震動模式,可以輸入多個時間到陣列中。
  • badge:在手機top bar上的「標記圖示」(等等看截圖就會比較清楚是什麼了XDD)。
  • tag:把它看成是「通知的ID值」,若今天我們的PWA有多種不同的推播通知,但都使用同一個tag的話,就只有最新的那則通知會被顯示。
  • renotify:定義是否在舊通知被新通知替換掉時通知用戶。
  • actions:定義一連串「用戶跟該通知進行互動的方式」。陣列中每個element都是一個javascript object,每個object中可以定義一些屬性:
    • action:定義用戶要執行的動作,confirm代表用戶確認收到該則通知並且可能想看該則通知的詳細資訊,cancel代表用戶不想看到該則通知。
    • title:執行動作的名稱。像這邊我把confirm的動作命名為「收到」,cancel為「取消」。
    • icon:顯示通知上要執行動作的圖示。

來看一下顯示通知的結果吧:


針對顯示通知的actions,我們可以在servie worker中針對用戶的動作進行相對的處理,也就是說當用戶收到通知按下收到鈕後我們可以再做進一步的邏輯處理(像是跳回我的PWA網站之類的):

來看一下在sw.js中要怎寫吧:

self.addEventListener('notificationclick', function(event) {
    var notification = event.notification;
    var action = event.action;

    console.log(notification);
    
    if(action === 'confirm') {
        console.log('Confirm was chosen');
        notification.close();
    } else {
        console.log(action);
        notification.close();
    }
});

在service worker中開始監聽notificationclick event,當用戶按下不同的動作鈕後,就會進行不同的處理。根據我剛剛設定的actions(有confirm和cacnel兩種動作),目前我只印出log來表示service worker確實有監聽到用戶對推播通知進行的動作。

再來,如果用戶都沒有按下動作鈕,只是把這則通知關掉呢?service worker也可以監聽到這類型的事件,不過要監聽的事件跟剛剛的不太一樣:

self.addEventListener('notificationclose', function(event) {
    console.log('Notification was closed', event);
})

回顧一下一開始的推播流程圖,我已經完成Display Notification,接下來要實作push messages的部分。

可以發現push分成很多個步驟,第一步當然是必須取得用戶訂閱(subscription)的資訊(不管是新建一個或是從server那邊取得)。

在我的PWA project中,使用情境是當用戶按下訂閱鈕後,我就會為用戶建立一個訂閱,來看要怎麼在app.js中實作「創建新的訂閱」吧:

首先在原本的askForNotificationPermission() funciton中的displayConfirmNotification()註解掉,並改為下列的函式:(要這樣做因為我希望用戶允許通知後,先為他註冊一個訂閱,最後才顯示通知)

function askForNotificationPermission() {
    Notification.requestPermission(function(result) {
        console.log('User Choice', result);
        if(result !== 'granted') {
            console.log('No notification permission granted!');
        } else {
            configurePushSub();
            // displayConfirmNotification();
        }
    });
}
function configurePushSub() {
    if(!('serviceWorker' in navigator)) {
        return;
    }
    var reg;
    navigator.serviceWorker.ready.then(function(swreg) {
        reg = swreg;
        return swreg.pushManager.getSubscription();
    }).then(function(sub) {
        if(sub === null) {
            // Create a new subscription
            reg.pushManager.subscribe({
                userVisibleOnly: true
            });
        } else {
            // We have a subscription
        }
    })
}

跟前面流程圖相同我們必須透過service worker來創建,所以要先取得已經註冊完成的service worker(這裡把它命名為swreg),接著使用pushManager.getSubscription()這個method來取得訂閱資訊,它會回傳一個promise。我們可以根據回傳的訂閱資訊來進行相對應的處理。如果回傳是null的話,代表用戶尚未創建訂閱,透過pushManager.subscribe() method幫他創建一個。

之前我有提到向瀏覽器供應商(Google、Mozilla)創建一個訂閱時,它會回傳一個API endpoint url,之後我的server會將要推播的貼文資訊傳送到這個API。

不過為了避免任何人知道這個url後,就可以向我的用戶推播垃圾訊息,這裡我們需要一個防護的機制,也就是subscribe() method中輸入的參數。

我將userVisibleOnly這個property設為true,代表從我的server發出推播訊息時,只有該用戶會看到。不過這還沒解決剛剛的問題RRRR/images/emoticon/emoticon02.gif

較好的解決方法我的server在push message時,先使用「簽章」加密再傳送到瀏覽器供應商的API Endpoint,如此就可以證明傳送到push server的確是我的server而不是其他Hacker。

這裡當然有現成的library讓我們來使用囉,叫作VAPID。不過這是明天的內容了XDD

Day23 結束!! /images/emoticon/emoticon07.gif


上一篇
[Day22] 實作PWA推播通知(Part1)
下一篇
[Day24] 實作PWA推播通知(Part3)
系列文
你應該要知道的新一代Web技術---漸進式網頁(PWA)29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言