Webプッシュ通知にカスタムボタンが実装できるようになったので試してみた

昨日は新卒エンジニアの[ごっちん](http://qiita.com/g_ryotaro)が[ReactJSを使ってライフゲームを作る](http://qiita.com/g_ryotaro/items/8101fdc05eea799f0334)という面白い記事を書いてくれました。良い記事ですね。ReactJS→JavaScript→ServiceWorkerというJSつながりで、ServiceWorkerで出来るWebプッシュ通知の話をしたいと思います。

# Webプッシュ通知

ServiceWorkerによってブラウザで開いていなくても、プッシュ通知を送ることが出来るようになりました。まるでネイティブアプリみたいですよね。素晴らしい。プッシュ通知そのものについては過去に書いた[Webでプッシュ通知するサービスを個人開発で作ってみた＋ServiceWorkerPushAPIの実装方法まとめ](http://qiita.com/zaru/items/f6e821052abb1b18bb0b)を参考にしてみてください。

このServiceWorker、まだまだ仕様策定中ということもあって、じょじょに機能が追加されていってます（破壊的仕様変更もある）。そして、 **Chrome48（ベータ）** にてついに通知ダイアログにカスタムボタンを追加できるようになりました！

- [PC版 Chromeベータ](https://www.google.co.jp/chrome/browser/beta.html)
- [Android Chromeベータ](https://play.google.com/store/apps/details?id=com.chrome.beta&hl=ja)

## カスタムボタン

![push_custom_buttons.png](https://qiita-image-store.s3.amazonaws.com/0/2152/6623e7cc-adb6-25c6-59b6-6756cc6cd9cd.png "push_custom_buttons.png")

PC版のChromeではこんな感じで表示されます。

![android-push.png](https://qiita-image-store.s3.amazonaws.com/0/2152/ae2ba5cb-79cf-0b20-a834-a83923433f47.png)

Android版だとこんな感じ。

今のところ、最大で2つまでボタンが表示できるようです。本体のアクションと合わせると、3つのアクションが定義できることになります。単なる通知メッセージというよりは、インタラクティブな機能になるような予感がしています。

### ボタンの表示

カスタムボタンの表示には、プッシュ通知時にタイトルやメッセージと同時に`actions`プロパティで定義します。配列で、複数の定義ができます。

```javascript:serviceworker.js
self.registration.showNotification("プッシュ通知だよ😁", {
  body: "通知メッセージ👐",
  icon: "/icon.png",
  tag: "tag",
  actions: [
    {action: 'action1', title: "ボタンだよ⚡️"},
    {action: 'action2', title: "こっちもボタン🍑"}
  ]
});
```

### アクションの定義

アクションの定義は、単純に`notificationclick`のイベントリスナーで行います。渡ってきた`event`オブジェクトの中にクリックしたアクション名が入っているので、それで制御をします。簡単ですね！

```javascript:serviceworker.js
self.addEventListener('notificationclick', function(event) {
  event.notification.close();
  
  if (event.action === 'action1') {
    clients.openWindow("/action1");
  } else if (event.action === 'action2') {
    clients.openWindow("/action2");
  } else {
    clients.openWindow("/");
  }
}, false);
```

### 最小構成のサンプル

Webプッシュ通知は、GCMのプッシュ通知サーバを使用しなくても普通にServiceWorkerのメソッドを叩けば通知ダイアログを出せます。なので、デバッグするときとかは、この方法を使うと便利です。

```javascript:serviceworker.js
if (typeof window !== "undefined") {
    document.addEventListener('DOMContentLoaded', function() {
        navigator.serviceWorker.getRegistration().then(function (r) {
            navigator.serviceWorker.register('/serviceworker.js', { scope: '/' });
        });

        var push = document.getElementById('push');
        push.addEventListener('click', function() {
            navigator.serviceWorker.ready.then(function(sw) {
                return sw.showNotification("プッシュ通知だよ😁", {
                    body: "通知メッセージ👐",
                    icon: "/icon.png",
                    tag: "tag",
                    actions: [
                        {action: 'action1', title: "ボタンだよ⚡️"},
                        {action: 'action2', title: "こっちもボタン🍑"}
                    ]
                });
            });
        });
    });
}

if (typeof window === "undefined") {
    self.addEventListener('notificationclick', function(event) {
        event.notification.close();
        
        if (event.action === 'action1') {
            clients.openWindow("/action1");
        } else {
            clients.openWindow("/");
        }
    }, false);
}
```

```index.html
<!doctype>
<html>
<head>
<script src="/serviceworker.js"></script>
<link rel="manifest" href="/manifest.json">
</head>
<body>
  <button id="push">プッシュ！</button>
</body>
</html>
```

```manifest.json
{
    "name": "SiteNameExample",
    "short_name": "SiteNameExample",
    "start_url": "/",
    "display": "standalone",
    "gcm_sender_id": "プロジェクトID",
    "gcm_user_visible_only": true
}
```

## 最後に

Webプッシュ通知は、今やデイリーで3億5千万も通知が送られているそうです。まだまだ発展途上な機能ではありますが、今後Webサービスを作っていく中で外せない選択肢になると思います。実戦投入はまだとしても、どんなことが出来るのかはウォッチしていくのが良いと思います。気軽に使いたい場合は、[Webプッシュ通知サービスPushnate](https://pushnate.com/)をどうぞ。

明日はアプリエンジニア[サキさん](http://qiita.com/koitaro)の出番です。わくてか。