tangled
alpha
login
or
join now
bad-example.com
/
spacedust-utils
6
fork
atom
demos for spacedust
6
fork
atom
overview
issues
pulls
pipelines
minimal api server landing
bad-example.com
8 months ago
0084745c
68593ed6
+17
-162
2 changed files
expand all
collapse all
unified
split
server
web-content
index.html
service-worker.js
+17
-133
server/web-content/index.html
···
1
1
<!doctype html>
2
2
-
<style>
3
3
-
#error-message {
4
4
-
color: tomato;
5
5
-
}
6
6
-
</style>
7
7
-
8
8
-
hiiiiii
9
9
-
10
10
-
<label><input id="did" placeholder="did:plc:..." /> DID</label>
11
11
-
<button id="subscribe">subscribe</button>
12
12
-
13
13
-
<p id="error-message"></p>
14
14
-
15
15
-
16
16
-
<script>
17
17
-
const err = m => {
18
18
-
document.getElementById('error-message').textContent = m;
19
19
-
throw new Error(m);
20
20
-
};
21
21
-
const clearErr = () => {
22
22
-
document.getElementById('error-message').textContent = '';
23
23
-
};
24
24
-
25
25
-
if (!('serviceWorker' in navigator)) err('service worker not supported');
26
26
-
27
27
-
if (!('PushManager' in window)) err('push not supported');
28
28
-
29
29
-
function urlBase64ToUint8Array(base64String) {
30
30
-
var padding = '='.repeat((4 - base64String.length % 4) % 4);
31
31
-
var base64 = (base64String + padding)
32
32
-
.replace(/\-/g, '+')
33
33
-
.replace(/_/g, '/');
34
34
-
35
35
-
var rawData = window.atob(base64);
36
36
-
var outputArray = new Uint8Array(rawData.length);
37
37
-
38
38
-
for (var i = 0; i < rawData.length; ++i) {
39
39
-
outputArray[i] = rawData.charCodeAt(i);
40
40
-
}
41
41
-
return outputArray;
42
42
-
}
43
43
-
44
44
-
function registerServiceWorker() {
45
45
-
return navigator.serviceWorker
46
46
-
.register('/service-worker.js')
47
47
-
.then(function (registration) {
48
48
-
console.log('Service worker successfully registered.');
49
49
-
return registration;
50
50
-
})
51
51
-
.catch(function (err) {
52
52
-
console.error('Unable to register service worker.', err);
53
53
-
});
54
54
-
}
55
55
-
56
56
-
function askPermission() {
57
57
-
return new Promise(function (resolve, reject) {
58
58
-
const permissionResult = Notification.requestPermission(function (result) {
59
59
-
resolve(result);
60
60
-
});
61
61
-
62
62
-
if (permissionResult) {
63
63
-
permissionResult.then(resolve, reject);
64
64
-
}
65
65
-
}).then(function (permissionResult) {
66
66
-
if (permissionResult !== 'granted') {
67
67
-
throw new Error("We weren't granted permission.");
68
68
-
}
69
69
-
});
70
70
-
}
71
71
-
72
72
-
function subscribeUserToPush() {
73
73
-
return navigator.serviceWorker
74
74
-
.register('/service-worker.js')
75
75
-
.then(function (registration) {
76
76
-
const subscribeOptions = {
77
77
-
userVisibleOnly: true,
78
78
-
applicationServerKey: urlBase64ToUint8Array(PUBKEY),
79
79
-
};
80
80
-
81
81
-
return registration.pushManager.subscribe(subscribeOptions);
82
82
-
})
83
83
-
.then(function (pushSubscription) {
84
84
-
console.log(
85
85
-
'Received PushSubscription: ',
86
86
-
JSON.stringify(pushSubscription),
87
87
-
);
88
88
-
return pushSubscription;
89
89
-
});
90
90
-
}
91
91
-
92
92
-
document.getElementById('subscribe').addEventListener('click', async () => {
93
93
-
clearErr();
94
94
-
95
95
-
const did = document.getElementById('did').value;
96
96
-
if (!did.startsWith('did:')) err('should start with `did:`');
97
97
-
98
98
-
let perm;
99
99
-
try {
100
100
-
perm = await askPermission();
101
101
-
} catch (err) {
102
102
-
console.error(err);
103
103
-
err('notification permissions are needed to give you notifications');
104
104
-
}
105
105
-
106
106
-
let sub;
107
107
-
try {
108
108
-
sub = await subscribeUserToPush();
109
109
-
} catch (err) {
110
110
-
console.error(err);
111
111
-
err('failed to subscribe for notification');
112
112
-
}
113
113
-
114
114
-
let res;
115
115
-
try {
116
116
-
res = await fetch('/subscribe', {
117
117
-
method: 'POST',
118
118
-
body: JSON.stringify({ did, sub }),
119
119
-
headers: {
120
120
-
'Content-Type': 'application/json',
121
121
-
},
122
122
-
});
123
123
-
} catch (err) {
124
124
-
console.error(err);
125
125
-
err('failed to create subscription with backend');
126
126
-
}
127
127
-
128
128
-
if (!res.ok) {
129
129
-
console.error(await res.text());
130
130
-
err('not-ok response trying to create backend subscription');
131
131
-
}
132
132
-
});
133
133
-
134
134
-
</script>
2
2
+
<html lang="en">
3
3
+
<head>
4
4
+
<meta charset="utf-8">
5
5
+
<title>notifications api</title>
6
6
+
</head>
7
7
+
<body>
8
8
+
<h1>hello!</h1>
9
9
+
<p>this is the api server for <a href="https://spacedust.microcosm.blue/">spacedust</a> demos!</p>
10
10
+
<p>the source code for this server is on github at <a href="https://github.com/at-microcosm/spacedust-utils/tree/main/server">at-microcosm/spacedust-utils</a>.</p>
11
11
+
<h3>demos:</h3>
12
12
+
<ul>
13
13
+
<li>
14
14
+
<p><a href="https://notifications.microcosm.blue">universal atproto notifications</a> with spacedust and webpush</p>
15
15
+
</li>
16
16
+
</ul>
17
17
+
</body>
18
18
+
</html>
-29
server/web-content/service-worker.js
···
1
1
-
function urlBase64ToUint8Array(base64String) {
2
2
-
var padding = '='.repeat((4 - base64String.length % 4) % 4);
3
3
-
var base64 = (base64String + padding)
4
4
-
.replace(/\-/g, '+')
5
5
-
.replace(/_/g, '/');
6
6
-
7
7
-
var rawData = window.atob(base64);
8
8
-
var outputArray = new Uint8Array(rawData.length);
9
9
-
10
10
-
for (var i = 0; i < rawData.length; ++i) {
11
11
-
outputArray[i] = rawData.charCodeAt(i);
12
12
-
}
13
13
-
return outputArray;
14
14
-
}
15
15
-
16
16
-
self.addEventListener('push', function(event) {
17
17
-
const { title, body } = event.data.json();
18
18
-
19
19
-
// Display notification or handle data
20
20
-
// Example: show a notification
21
21
-
// const title = 'New Notification';
22
22
-
// const body = event.data.text();
23
23
-
// const icon = '/images/icon.png';
24
24
-
// const tag = 'simple-push-demo-notification-tag';
25
25
-
26
26
-
event.waitUntil(self.registration.showNotification(title, { body }));
27
27
-
28
28
-
// TODO: resubscribe to notifs to try to stay alive
29
29
-
});