いろいろ試行錯誤しましたが、PWAでのPush通知はちょっと難しいと判断しfirebaseを使っての通知にすることとしました。
#動作結果
先に結果から
webにアクセスして通知を許可すると、こんな感じで通知されます。
ページを閉じていても通知が届きます(Windows10)
画面右下にこんな感じで通知されます
通知リストにもこんな感じで出てきます
#index.html
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>xxxxxxxxxxx Scheduler</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="manifest" href="/manifest.json">
<!-- ios用tag -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="PWA">
<link rel="apple-touch-icon" href="./img/app-icon-120x120.png" sizes="120x120">
<link rel="apple-touch-startup-image" href="./img/zeni_splash.png" media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/siimple@3.1.1/dist/siimple.min.css" />
<link href="https://fonts.googleapis.com/css?family=Poiret+One" rel="stylesheet">
<link type="text/css" rel="stylesheet" href="./css/bigapple.css">
<!-- update the version number as needed -->
<script defer src="/__/firebase/5.8.3/firebase-app.js"></script>
<!-- include only the Firebase features as you need -->
<script defer src="/__/firebase/5.8.3/firebase-auth.js"></script>
<script defer src="/__/firebase/5.8.3/firebase-database.js"></script>
<script defer src="/__/firebase/5.8.3/firebase-messaging.js"></script>
<script defer src="/__/firebase/5.8.3/firebase-storage.js"></script>
<!-- initialize the SDK after all desired features are loaded -->
<script defer src="/__/firebase/init.js"></script>
<script type="text/javascript">
// service workerが有効なら、service-worker.js を登録します
if ('serviceWorker' in navigator) {
console.log("service-worker ON");
navigator.serviceWorker.register('./firebase-messaging-sw.js').then(function() { console.log('Service Worker Registered'); });
}
</script>
</head>
<body>
<!-- HEADER -->
<div class="ens-title-area">
<div class="ens-title">
<img class="ens-logo" src="./img/xxxxxxxxxxxxxxx.png" alt="ロゴ"/>
EVENT NOTICE SERVICE
</div>
</div>
<!-- MAIN CONTENTS -->
<div class="wrapper">
<div class="page_title"><p><span>xxxxxxxxxxx Scheduler</span></p></div>
<div class="bg_wrapper">
<div class="contents_wrapper">
<div id="canvas"></div>
<div class="form_area">
<div>
<label for="event_title">イベント名:</label><input type="text" id="event_title">
</div>
<div>
<label for="event_date">イベント日:</label><input type="date" id="event_date">
</div>
</div>
<div id="btn_area" class="btn_area">
<button class="enable_click" id="get_btn" onclick="">イベント取得</button>
<button class="enable_click" id="write_btn" onclick="">イベント追加</button>
</div>
</div>
</div>
<!-- FOOTER -->
<div class="ens-footer">Copycenter 2018 xxxxx Limited.</div>
</div>
<script src="./js/jquery.js"></script>
<script src="./js/firebase.js"></script>
index.html
<script>
var config = {
apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
authDomain: "xxxxxxxxxxxxxxxxxxxxxxx",
databaseURL: "xxxxxxxxxxxxxxxxxxxxxxxxx",
projectId: "xxxxxxxxxxxxxx",
storageBucket: "xxxxxxxxxxxxxxxxxxxxxx",
messagingSenderId: "xxxxxxxxxxxxxxxxxxx"
};
firebase.initializeApp(config);
// firestoreインスタンスの生成
var db = firebase.firestore();
// タイムスタンプの設定を記述
var setting = { timestampsInSnapshots:true };
db.settings(setting);
// メッセージングの初期化
var messaging = firebase.messaging();
// 画面表示時の通知
messaging.onMessage(function(payload) {
console.log("Message received. ", payload);
alert(payload.notification.title+"\r\n"
+payload.notification.body+"\r\n"
+payload.notification.icon);
});
$(function(){
//プッシュ通知パーミッション取得
messaging.requestPermission()
.then(function() {
//ユーザー毎のトークンを取得して画面に表示する
messaging.getToken()
.then(function(token) {
console.log('Token refreshed.');
console.log(token);
db.collection("tokens").add({
token: token
});
document.getElementById("txtIIToken").value = token;
})
.catch(function(err) {
console.log('Unable to retrieve refreshed token ', err);
});
})
.catch(function(err) {
console.log('Unable to get permission to notify.', err);
});
$("#write_btn").bind("click touchstart", function(){
// フォームから値を取得
var event_title = document.getElementById("event_title").value;
var event_date = document.getElementById("event_date").value;
event_date = modEventDate(event_date);
// 日付を取得
var now = getNowDate();
// イベント追加
addEvent(db, "events", event_title, event_date, now);
// フォームの初期化
document.getElementById("event_title").value = "";
document.getElementById("event_date").value = "";
db.collection("tokens").get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
console.log(doc.get("token"));
// イベント通知
$.ajax({
url:'https://fcm.googleapis.com/fcm/send',
type:"POST",
headers:{
"Authorization":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"Content-Type":"application/json"
},
contentType: 'application/json',
dataType: "json",
data:JSON.stringify({
notification: {
title: "イベント通知", // 通知タイトル
body: "新しいイベントが追加されました。", // 通知内容
click_action: "https://xxxxxxxxxxxxxx.firebaseapp.com/", // クリック時の動き
icon: "./img/icon.png" // 通知アイコン
},
to: doc.get("token") // 送信対象
}),
success:function(json) {
console.log("success");
console.log(json);
},
error: function() {},
complete: function() {}
});
});
});
});
// イベント取得クリック時
$("#get_btn").bind("click touchstart", function(){
// 初期化
document.getElementById("canvas").innerHTML = ""
// イベント全件取得
db.collection("events").get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
document.getElementById("canvas").innerHTML +=
doc.get('event_date');;
document.getElementById("canvas").innerHTML +=
doc.get('event_name'); + " <br /> ";
document.getElementById("canvas").innerHTML += "<hr />";
});
});
});
});
// イベント追加関数
function addEvent(db, doc, name, date, now){
db.collection(doc).add({
event_name: name,
event_date: date,
});
}
// 日付フォーマット変更処理
function modEventDate(date){
var result = date.replace("-", "/");
// "-"が存在する限り繰り返し
while(result !== date) {
date = date.replace("-", "/");
result = result.replace("-", "/");
}
//TODO:スマホからだとyyyymdになるためyyyymmddに変換
// 無理矢理だが今は動けば良いので機会があれば修正する。
var tmpDate
tmpDate = new Date(date);
var yyyy = tmpDate.getFullYear();
var mm = ("00" + (tmpDate.getMonth()+1)).slice(-2);
var dd = ("00" + tmpDate.getDate()).slice(-2);
var res = yyyy + "/" + mm + "/" + dd;
return res;
}
// 現在時刻の取得関数
function getNowDate(){
var date = new Date();
var yyyy = date.getFullYear();
var mm = ("00" + (date.getMonth()+1)).slice(-2);
var dd = ("00" + date.getDate()).slice(-2);
var res = yyyy + "/" + mm + "/" + dd;
return res;
}
</script>
#firebase-messaging-sw.js
firebase-messaging-sw.js
importScripts('https://www.gstatic.com/firebasejs/3.7.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.7.0/firebase-messaging.js');
// service-worker.js
self.addEventListener('install', function(e) {
console.log('[ServiceWorker] Install');
});
self.addEventListener('activate', function(e) {
console.log('[ServiceWorker] Activate');
});
// 現状では、この処理を書かないとService Workerが有効と判定されないようです
self.addEventListener('fetch', function(event) {});
firebase.initializeApp({
'messagingSenderId': 'xxxxxxxxxxxxxx'
});
// メッセージングの初期化
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
const notificationTitle = payload.notification.title;
const notificationOptions = {
body: payload.notification.body,
icon: payload.notification.icon
};
return self.registration.showNotification(notificationTitle,notificationOptions);
});
#manifest.json
manifest.json
{
"name": "PWA Sample",
"short_name": "PWA",
"background_color": "#fc980c",
"icons": [
{
"src": "./img/BIG_APPLE.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "./img/BIG_APPLE_144.png",
"sizes": "144x144",
"type": "image/png"
}
],
"start_url": "https://xxxxxxxxxxxx.firebaseapp.com/",
"display": "standalone",
"theme_color": "#2F3BA2",
"gcm_sender_id": "xxxxxxxxxxxxx" // firebase送信者は固定
}