-
Notifications
You must be signed in to change notification settings - Fork 90
/
app.js
251 lines (222 loc) · 8.39 KB
/
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
document.addEventListener('DOMContentLoaded', () => {
const applicationServerKey =
'BMBlr6YznhYMX3NgcWIDRxZXs0sh7tCv7_YCsWcww0ZCv9WGg-tRCXfMEHTiBPCksSqeve1twlbmVAZFv7GSuj0';
let isPushEnabled = false;
const pushButton = document.querySelector('#push-subscription-button');
if (!pushButton) {
return;
}
pushButton.addEventListener('click', function() {
if (isPushEnabled) {
push_unsubscribe();
} else {
push_subscribe();
}
});
if (!('serviceWorker' in navigator)) {
console.warn('Service workers are not supported by this browser');
changePushButtonState('incompatible');
return;
}
if (!('PushManager' in window)) {
console.warn('Push notifications are not supported by this browser');
changePushButtonState('incompatible');
return;
}
if (!('showNotification' in ServiceWorkerRegistration.prototype)) {
console.warn('Notifications are not supported by this browser');
changePushButtonState('incompatible');
return;
}
// Check the current Notification permission.
// If its denied, the button should appears as such, until the user changes the permission manually
if (Notification.permission === 'denied') {
console.warn('Notifications are denied by the user');
changePushButtonState('incompatible');
return;
}
navigator.serviceWorker.register('serviceWorker.js').then(
() => {
console.log('[SW] Service worker has been registered');
push_updateSubscription();
},
e => {
console.error('[SW] Service worker registration failed', e);
changePushButtonState('incompatible');
}
);
function changePushButtonState(state) {
switch (state) {
case 'enabled':
pushButton.disabled = false;
pushButton.textContent = 'Disable Push notifications';
isPushEnabled = true;
break;
case 'disabled':
pushButton.disabled = false;
pushButton.textContent = 'Enable Push notifications';
isPushEnabled = false;
break;
case 'computing':
pushButton.disabled = true;
pushButton.textContent = 'Loading...';
break;
case 'incompatible':
pushButton.disabled = true;
pushButton.textContent = 'Push notifications are not compatible with this browser';
break;
default:
console.error('Unhandled push button state', state);
break;
}
}
function urlBase64ToUint8Array(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;
}
function checkNotificationPermission() {
return new Promise((resolve, reject) => {
if (Notification.permission === 'denied') {
return reject(new Error('Push messages are blocked.'));
}
if (Notification.permission === 'granted') {
return resolve();
}
if (Notification.permission === 'default') {
return Notification.requestPermission().then(result => {
if (result !== 'granted') {
reject(new Error('Bad permission result'));
} else {
resolve();
}
});
}
return reject(new Error('Unknown permission'));
});
}
function push_subscribe() {
changePushButtonState('computing');
return checkNotificationPermission()
.then(() => navigator.serviceWorker.ready)
.then(serviceWorkerRegistration =>
serviceWorkerRegistration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(applicationServerKey),
})
)
.then(subscription => {
// Subscription was successful
// create subscription on your server
return push_sendSubscriptionToServer(subscription, 'POST');
})
.then(subscription => subscription && changePushButtonState('enabled')) // update your UI
.catch(e => {
if (Notification.permission === 'denied') {
// The user denied the notification permission which
// means we failed to subscribe and the user will need
// to manually change the notification permission to
// subscribe to push messages
console.warn('Notifications are denied by the user.');
changePushButtonState('incompatible');
} else {
// A problem occurred with the subscription; common reasons
// include network errors or the user skipped the permission
console.error('Impossible to subscribe to push notifications', e);
changePushButtonState('disabled');
}
});
}
function push_updateSubscription() {
navigator.serviceWorker.ready
.then(serviceWorkerRegistration => serviceWorkerRegistration.pushManager.getSubscription())
.then(subscription => {
changePushButtonState('disabled');
if (!subscription) {
// We aren't subscribed to push, so set UI to allow the user to enable push
return;
}
// Keep your server in sync with the latest endpoint
return push_sendSubscriptionToServer(subscription, 'PUT');
})
.then(subscription => subscription && changePushButtonState('enabled')) // Set your UI to show they have subscribed for push messages
.catch(e => {
console.error('Error when updating the subscription', e);
});
}
function push_unsubscribe() {
changePushButtonState('computing');
// To unsubscribe from push messaging, you need to get the subscription object
navigator.serviceWorker.ready
.then(serviceWorkerRegistration => serviceWorkerRegistration.pushManager.getSubscription())
.then(subscription => {
// Check that we have a subscription to unsubscribe
if (!subscription) {
// No subscription object, so set the state
// to allow the user to subscribe to push
changePushButtonState('disabled');
return;
}
// We have a subscription, unsubscribe
// Remove push subscription from server
return push_sendSubscriptionToServer(subscription, 'DELETE');
})
.then(subscription => subscription.unsubscribe())
.then(() => changePushButtonState('disabled'))
.catch(e => {
// We failed to unsubscribe, this can lead to
// an unusual state, so it may be best to remove
// the users data from your data store and
// inform the user that you have done so
console.error('Error when unsubscribing the user', e);
changePushButtonState('disabled');
});
}
function push_sendSubscriptionToServer(subscription, method) {
const key = subscription.getKey('p256dh');
const token = subscription.getKey('auth');
const contentEncoding = (PushManager.supportedContentEncodings || ['aesgcm'])[0];
return fetch('push_subscription.php', {
method,
body: JSON.stringify({
endpoint: subscription.endpoint,
publicKey: key ? btoa(String.fromCharCode.apply(null, new Uint8Array(key))) : null,
authToken: token ? btoa(String.fromCharCode.apply(null, new Uint8Array(token))) : null,
contentEncoding,
}),
}).then(() => subscription);
}
/**
* START send_push_notification
* this part handles the button that calls the endpoint that triggers a notification
* in the real world, you wouldn't need this, because notifications are typically sent from backend logic
*/
const sendPushButton = document.querySelector('#send-push-button');
if (!sendPushButton) {
return;
}
sendPushButton.addEventListener('click', () =>
navigator.serviceWorker.ready
.then(serviceWorkerRegistration => serviceWorkerRegistration.pushManager.getSubscription())
.then(subscription => {
if (!subscription) {
alert('Please enable push notifications');
return;
}
const contentEncoding = (PushManager.supportedContentEncodings || ['aesgcm'])[0];
const jsonSubscription = subscription.toJSON();
fetch('send_push_notification.php', {
method: 'POST',
body: JSON.stringify(Object.assign(jsonSubscription, { contentEncoding })),
});
})
);
/**
* END send_push_notification
*/
});