at main 77 lines 2.4 kB view raw
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&hellip;</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}