-
Notifications
You must be signed in to change notification settings - Fork 59
Expand file tree
/
Copy pathgetW3CDeviceDetails.ts
More file actions
104 lines (86 loc) · 3.22 KB
/
getW3CDeviceDetails.ts
File metadata and controls
104 lines (86 loc) · 3.22 KB
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
import { ActivationStateMachine } from 'plugins/push/pushactivation';
function toBase64Url(arrayBuffer: ArrayBuffer) {
const buffer = new Uint8Array(arrayBuffer.slice(0, arrayBuffer.byteLength));
return btoa(String.fromCharCode.apply(null, Array.from(buffer)));
}
function urlBase64ToBase64(base64String: string) {
const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
return base64;
}
function base64ToUint8Array(base64String: string) {
const rawData = window.atob(base64String);
const rawDataChars = [];
for (let i = 0; i < rawData.length; i++) {
rawDataChars.push(rawData[i].charCodeAt(0));
}
return Uint8Array.from(rawDataChars);
}
export async function getW3CPushDeviceDetails(machine: ActivationStateMachine) {
const GettingPushDeviceDetailsFailed = machine.GettingPushDeviceDetailsFailed;
const GotPushDeviceDetails = machine.GotPushDeviceDetails;
const { ErrorInfo, Defaults } = machine.client;
let permission: NotificationPermission = Notification.permission;
if (permission === 'default') {
try {
// throws exception in iOS
permission = await Notification.requestPermission();
} catch (err) {
machine.handleEvent(
new GettingPushDeviceDetailsFailed(
new ErrorInfo('Failed to request permission to send notifications', 400, 40000),
),
);
return;
}
}
if (permission !== 'granted') {
machine.handleEvent(
new GettingPushDeviceDetailsFailed(new ErrorInfo('User denied permission to send notifications', 400, 40000)),
);
return;
}
const swUrl = machine.client.options.pushServiceWorkerUrl;
if (!swUrl) {
machine.handleEvent(
new GettingPushDeviceDetailsFailed(new ErrorInfo('Missing ClientOptions.pushServiceWorkerUrl', 400, 40000)),
);
return;
}
try {
const worker = await navigator.serviceWorker.register(swUrl);
machine._pushManager = worker.pushManager;
const headers = Defaults.defaultGetHeaders(machine.client.options, { format: 'text' });
const appServerKey = (
await machine.client.rest.Resource.get(machine.client, '/push/publicVapidKey', headers, {}, null, true)
).body as string;
if (!worker.active) {
await navigator.serviceWorker.ready;
}
const subscription = await worker.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: base64ToUint8Array(urlBase64ToBase64(appServerKey)),
});
const endpoint = subscription.endpoint;
const [p256dh, auth] = [subscription.getKey('p256dh'), subscription.getKey('auth')];
if (!p256dh || !auth) {
throw new ErrorInfo('Public key not found', 50000, 500);
}
const device = machine.client.device();
device.push.recipient = {
transportType: 'web',
targetUrl: btoa(endpoint),
publicVapidKey: appServerKey,
encryptionKey: {
p256dh: toBase64Url(p256dh),
auth: toBase64Url(auth),
},
};
device.persist();
machine.handleEvent(new GotPushDeviceDetails());
} catch (err) {
machine.handleEvent(
new GettingPushDeviceDetailsFailed(new ErrorInfo('Failed to register service worker', 50000, 500, err as Error)),
);
}
}