demos for spacedust
1import { useContext, useEffect, useState } from 'react';
2import { PushServerContext } from '../../context';
3import { urlBase64ToUint8Array } from '../../utils';
4
5async function subscribePushServer(sub) {
6 const host = import.meta.env.VITE_NOTIFICATIONS_HOST;
7 const res = await fetch(`${host}/subscribe`, {
8 method: 'POST',
9 headers: {'Content-Type': 'application/json'},
10 body: JSON.stringify({ sub }),
11 credentials: 'include',
12 });
13 if (!res.ok) {
14 const msg = await res.text();
15 console.error('failed to sub', msg);
16 throw new Error('failed to subscribe server');
17 }
18}
19
20export function WithPushSubscription({ children }) {
21 const pushServerPubkey = useContext(PushServerContext);
22 const [status, setStatus] = useState('pending');
23
24 useEffect(() => {
25 let cancelled = false;
26 let updateSubscription;
27 (async () => {
28 try {
29 // idk how to do a cancellable async fn, so we just check after each step
30 // if we should be cancelling, in case this keeps running after unmount
31
32 const registration = await navigator.serviceWorker.getRegistration();
33 // try to update the sw in case a user keeps a tab open for a long time
34 updateSubscription = setInterval(() => registration.update(), 4 * 60 * 60 * 1000); // every 4h
35 if (cancelled) return;
36
37 // check for an existing subscription
38 let subscription = await registration.pushManager.getSubscription();
39 if (cancelled) return;
40
41 // create a new sub if none exist
42 if (!subscription) {
43 subscription = await registration.pushManager.subscribe({
44 userVisibleOnly: true,
45 applicationServerKey: urlBase64ToUint8Array(pushServerPubkey),
46 });
47 if (cancelled) return;
48 }
49
50 // finally, send the deets to our push server backend
51 await subscribePushServer(subscription);
52
53 } catch (e) {
54 console.error('failed to subscribe', e);
55 setStatus('failed');
56 return;
57 }
58
59 // finally finally
60 setStatus('subscribed');
61 })();
62 return () => {
63 cancelled = true;
64 clearInterval(updateSubscription);
65 };
66 }, [pushServerPubkey]);
67
68 if (status === 'pending') {
69 return <p>Setting up push notifications…</p>
70 }
71
72 if (status === 'failed') {
73 return <p>Sorry, something went wrong setting up push notifications</p>;
74 }
75
76 return children
77}