ホームページ制作/SEO対策なら株式会社サイバーブレーンへ

 03-5961-5681 平日10:00~19:00

 2017-10-02

HTTPサーバーからのプッシュ通知について調べてみた【その2】

WEBのプッシュ通知はgoogleカレンダーでも当たり前のように実装されていて便利な機能ですが、いざ実装しようとすると仕様の理解と暗号方式の理解が必要になり、すべてを自前で用意するよりは、各種言語用のライブラリーを使うのが近道です。
しかし、いざ動作しないとかのトラブルに遭遇すると原因の究明が難しくなります。
そこでこの機能を解説しているサイトやRFCを読みながら理解を進めることになります。
まずはクライアント側のJavaScript実装で非常に参考になるサイトがこちらになります。

解説サイトのサンプルにあるmain.jsをまとめてみる

まずは、提供されているサンプルコードをダウンロードしてmain.jsを少し書き換えたコードが以下になります。

/*
 * TEST WEB Push API
 */
document.addEventListener("DOMContentLoaded", function () {
    const applicationServerPublicKey = 'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U';

    //var pushButton = document.getElementsByClassName('.js-push-btn');
    const pushButton = document.querySelector('.js-push-btn');
    console.log(pushButton);

    let isSubscribed = false;
    let swRegistration = null;

    function urlB64ToUint8Array(base64String) {
        const padding = '='.repeat((4 - base64String.length % 4) % 4);
        const base64 = (base64String + padding)
                .replace(/\-/g, '+')
                .replace(/_/g, '/');

        const rawData = window.atob(base64);
        const outputArray = new Uint8Array(rawData.length);

        for (let i = 0; i < rawData.length; ++i) {
            outputArray[i] = rawData.charCodeAt(i);
        }
        return outputArray;
    }

//-----------------------------------------------
//サポートしていればサービスワーカーを登録する
//-----------------------------------------------
    if ('serviceWorker' in navigator && 'PushManager' in window) {
        console.log('Service Worker and Push is supported');

        navigator.serviceWorker.register('sw.js')
                .then(function (swReg) {
                    console.log('Service Worker is registered', swReg);

                    swRegistration = swReg;
                    initialiseUI();
                })
                .catch(function (error) {
                    console.error('Service Worker Error', error);
                });
    } else {
        console.warn('Push messaging is not supported');
        pushButton.textContent = 'Push Not Supported';
    }

//ユーザーが現在登録されているかどうかを確認
    function initialiseUI() {
        pushButton.addEventListener('click', function () {
            pushButton.disabled = true;//プッシュの登録に時間がかかっているためにユーザーが再度クリックすることのないように、まず、ボタンを無効にします。

            if (isSubscribed) {//ユーザーの登録を解除
                unsubscribeUser();
            } else {
//ユーザーが現在登録されていなければ subscribeUser() で登録する。
                subscribeUser();
            }
        });

        // Set the initial subscription value
        swRegistration.pushManager.getSubscription()
                .then(function (subscription) {
                    isSubscribed = !(subscription === null);

                    //updateSubscriptionOnServer(subscription);

                    if (isSubscribed) {
                        console.log('User IS subscribed.');
                    } else {
                        console.log('User is NOT subscribed.');
                    }

                    updateBtn();
                });
    }
//
    function updateBtn() {
        //ユーザが通知を許可しなかった場合。
        // パーミッションが denied の場合、ユーザーを登録できず、これ以上何も実行できないため、
        // ボタンを恒久的に無効にすることが最適な方法
        if (Notification.permission === 'denied') {
            pushButton.textContent = 'Push Messaging Blocked.';
            pushButton.disabled = true;
            updateSubscriptionOnServer(null);
            return;
        }

        if (isSubscribed) {//既に登録されている。
            pushButton.textContent = 'Disable Push Messaging';
			document.getElementById('msgtoggle').innerHTML='下記のボタンをクリックして通知を解除してください。';
        } else {// 登録していない
            pushButton.textContent = 'Enable Push Messaging';
			document.getElementById('msgtoggle').innerHTML='下記のボタンをクリックして通知受信を許可してください。';
        }

        pushButton.disabled = false;
    }

    function unsubscribeUser() {
        swRegistration.pushManager.getSubscription()
                .then(function (subscription) {
                    if (subscription) {
                        //登録があるので、解除する
                        return subscription.unsubscribe();
                    }
                })
                .catch(function (error) {
                    console.log('Error unsubscribing', error);
                })
                .then(function () {
                    updateSubscriptionOnServer(null,true);

                    console.log('User is unsubscribed.');
                    isSubscribed = false;

                    updateBtn();
                });
    }

    function subscribeUser() {
        //アプリケーション サーバーの公開鍵(base 64 の安全な URL エンコード)を取得し、
        //UInt8Array に変換
        const applicationServerKey = urlB64ToUint8Array(applicationServerPublicKey);

        //Service Worker の pushManager で subscribe() メソッドを呼び出し、
        //アプリケーション サーバーの公開鍵と値 userVisibleOnly: true を渡します
        //userVisibleOnly パラメータは、基本的に、プッシュが送信されるたびに通知を表示するというアドミッション
        swRegistration.pushManager.subscribe({
            userVisibleOnly: true,
            applicationServerKey: applicationServerKey
        })
                .then(function (subscription) {
                    console.log('User is subscribed:', subscription);

                    updateSubscriptionOnServer(subscription);

                    isSubscribed = true;

                    updateBtn();
                })
                .catch(function (err) {
                    console.log('Failed to subscribe the user: ', err);
                    updateBtn();
                });
    }

//updateSubscriptionOnServer メソッドは、実際のアプリではバックエンドに登録を送信するメソッドですが、
//コードラボでは後で役に立つように UI に登録を出力します
    function updateSubscriptionOnServer(subscription,un) {
        // TODO: Send subscription to application server

        const subscriptionJson = document.querySelector('.js-subscription-json');
        const subscriptionDetails =
                document.querySelector('.js-subscription-details');

        if (subscription) {
            var jStr = JSON.stringify(subscription);
            postJson(jStr);
            subscriptionJson.textContent = jStr;
            subscriptionDetails.classList.remove('is-invisible');
        } else {
            subscriptionDetails.classList.add('is-invisible');
			/* セッションで管理して削除する必要があるのかどうか? */
			if (un){
				postJson(null);
			}
			
        }
    }

    function postJson(jStr) {

        var http = new XMLHttpRequest();
        http.onreadystatechange = function () {
            if (http.readyState == 4) { // 通信の完了時
                if (http.status == 200) { // 通信の成功時
                    console.log(http.responseText);
                } else {
                    //登録失敗
                }
            } else {
                //s "通信中..."
            }
        }

        http.open('POST', './ajax.php', true);
	    http.setRequestHeader('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
		if (jStr){
			http.send('jsonStr=' + encodeURIComponent(jStr));
		} else {
			http.send('unset=true');
		}
        

    }
});

Chromeを使って通知を送る実験

このサンプルではサーバー側の通知処理サンプルはありませんので、実験では、Chromeの開発ツールを使ってpush 通知をしています。
ChromeのDeveloper ToolsのApplicationメニューからService Workersを選択します。それが以下の画像です。

この中の”Push”をクリックすると通知ウィンドウが出て来ます。
このサンプルコードを利用してこちらで実際に試して見ました。
*このサンプルで「通知を無効化」してもDeveloper ToolsのPush通知は実行できてしまいます。

次回は、これを利用してサーバー側の実装を追記して紹介したいと思います。

~この記事の著者~

動機づけ施策に関するご相談はこちら

 03-5961-5681 平日10:00~19:00

メールフォーム

あなたが
サイトに
求める要素
あなたがサイトに求める0要素