push通知

vue3 laravel9

参考
https://zenn.dev/nnahito/articles/fd2c8b0ad0d19a

# 公開鍵を取得

以下より
Public KeyとPrivate Keyの値をコピー。

https://web-push-codelab.glitch.me/


# 受信側

```public/sw.js
// プッシュ通知を受け取ったときのイベント
self.addEventListener('push', function (event) {
    const title = 'Push通知テスト';
    const options = {
        body: event.data.text(), // サーバーからのメッセージ
        tag: title, // タイトル
        icon: 'img/32x32.png', // アイコン
        badge: 'img/32x32.png' // アイコン
    };

    event.waitUntil(self.registration.showNotification(title, options));
});

// プッシュ通知をクリックしたときのイベント
self.addEventListener('notificationclick', function (event) {
    event.notification.close();

    event.waitUntil(
        // プッシュ通知をクリックしたときにブラウザを起動して表示するURL
        clients.openWindow('https://renkatu.club/')
    );
});

// Service Worker インストール時に実行される
// キャッシュするファイルとかをここで登録する
self.addEventListener('install', (event) => {
    console.log('service worker install ...');
});

```




```public/manifest.json
{
    "name": "タイトルですよ",
    "short_name": "タイトル",
    "icons": [
        {
            "src": "/img/32x32.png",
            "sizes": "32x32",
            "type": "image/png"
        }
    ],
    "start_url": "/",
    "display": "standalone",
    "theme_color": "#e91e63"
}
```


以下を追記

```vue.blade.php
<head>
//略
    <link rel="manifest" href="/manifest.json">
    <script defer src="sw.js"></script>
</head>
```






```sample.vue
<template>
    <a @click="allowWebPush">webpush登録します</a>
</template>

<script lang="ts" setup>

import { ref , onMounted ,inject } from 'vue';

/**
 * サービスワーカーの登録
 */
self.addEventListener('load', async () => {
    if ('serviceWorker' in navigator) {
        window.sw = await navigator.serviceWorker.register('/sw.js', {scope: '/'});
    }
});


/**
 * WebPushを許可する仕組み
 */
async function allowWebPush() {

    if ('Notification' in window) {
        let permission = Notification.permission;

        if (permission === 'denied') {
            alert('Push通知が拒否されているようです。ブラウザの設定からPush通知を有効化してください');
            return false;
        } else if (permission === 'granted') {
            alert('すでにWebPushを許可済みです');
            return false;
        }
    }
    // 取得したPublicKey
    const appServerKey = 'YOURKEY';
    const applicationServerKey = urlB64ToUint8Array(appServerKey);

    // push managerにサーバーキーを渡し、トークンを取得
    let subscription = undefined;
    try {
        subscription = await window.sw.pushManager.subscribe({
            userVisibleOnly: true,
            applicationServerKey
        });
    } catch (e) {
        alert('Push通知機能が拒否されたか、エラーが発生しましたので、Push通知は送信されません。');
        return false;
    }


    // 必要なトークンを変換して取得（これが重要！！！）
    const key = subscription.getKey('p256dh');
    const token = subscription.getKey('auth');
    const request = {
        endpoint: subscription.endpoint,
        userPublicKey: btoa(String.fromCharCode.apply(null, new Uint8Array(key))),
        userAuthToken: btoa(String.fromCharCode.apply(null, new Uint8Array(token)))
    };

    console.log(request);
}



/**
 * トークンを変換するときに使うロジック
 * @param {*} base64String
 */
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;
}


```

上記で、'allowWebPush'をクリックするとコンソールに

```
endpoint:
userPublicKey:
userAuthToken:
```

が表示されるので、メモする。

受信側は以上でOK！


# 送信側

laravelに最初から入ってるみたいだが、一応インスコ。

```
composer require minishlink/web-push
```


```HogesController.php

use Minishlink\WebPush\WebPush;
use Minishlink\WebPush\Subscription;

public static function test(){
    const VAPID_SUBJECT = 'ここにあなたのWebサイトのURL（http://localhost:5000/ とか https://nnahito.com/ とか）';
    const PUBLIC_KEY = '公開鍵（ https://web-push-codelab.glitch.me/ で取得したやつ ）';
    const PRIVATE_KEY = '秘密鍵（ https://web-push-codelab.glitch.me/ で取得したやつ ）';
    
    // push通知認証用のデータ　chromeのコンソールに表示されるやつ
    $subscription = Subscription::create([
        'endpoint' => '「必要なトークンを変換して取得（これが重要！！！）」で取得されたendpoint',
        'publicKey' => '「必要なトークンを変換して取得（これが重要！！！）」で取得されたuserPublicKey',
        'authToken' => '「必要なトークンを変換して取得（これが重要！！！）」で取得されたuserAuthToken',
    ]);
    
    // ブラウザに認証させる
    $auth = [
        'VAPID' => [
            'subject' => VAPID_SUBJECT,
            'publicKey' => PUBLIC_KEY,
            'privateKey' => PRIVATE_KEY,
        ]
    ];
    
    $webPush = new WebPush($auth);
    
    $report = $webPush->sendOneNotification(
        $subscription,
        'push通知の本文だよ！'
    );
    
    $endpoint = $report->getRequest()->getUri()->__toString();
    
    if ($report->isSuccess()) {
        echo '送信成功！';
    } else {
        echo '送信失敗やで';
    }
}

```

これで、通知成功と表示されたらOK。

# あれ？通知成功しているのに出ない

windows11
```
スタートを右クリック
> システム
> 通知
> 通知がオンになっていることを確認する
```


# ios での挙動と注意

①webサイトを開く (chrome、safari どちらもOK)
②ホーム画面に追加する
③ホーム画面より、アプリ化されたアイコンをタップ
④webpush登録ボタンを押させる

（ホーム画面に追加しても、ブラウザの登録ボタンは反応しない）

notification ページを作るのであれば、そこにwebpush登録ボタンをつけ、manifest.json で hoge.com/notification みたいにして、そこを登録させるのが良いだろう。





テスト時は
トップページに登録するボタンを付けておいたほうが良い。

manifest.jsonで設定可能？
（hoge.com/sample とか他の階層でやると動かない。というかホームアプリに追加できない）

アプリを削除すると購読終了になる。

# push.js は？

push.jsは、サーバーサイドから送信できないようなので使わない。