implement web push

This commit is contained in:
2025-05-10 13:04:39 +01:00
parent f832b95d69
commit 44d744266a
11 changed files with 559 additions and 142 deletions

View File

@@ -1,39 +1,104 @@
const KEY_UPDATES_ENABLED = "updatesEnabled"
const KEY_SUBSCRIPTION = "subscription"
const canReceiveUpdates = "Notification" in window && "serviceWorker" in navigator
const getSummaryButton = document.getElementById("get-summary-btn")
const loc = getSummaryButton.dataset.loc
getSummaryButton.style.display = "none"
console.log("can receive updates?", canReceiveUpdates)
if (canReceiveUpdates) {
async function main() {
window.addEventListener("load", () => {
navigator.serviceWorker.register("/sw.js")
})
navigator.serviceWorker.ready.then((reg) => {
if (Notification.permission === "granted" && localStorage.getItem(KEY_UPDATES_ENABLED) === "true") {
getSummaryButton.innerText = "Stop updates"
reg.active.postMessage(loc)
} else {
getSummaryButton.innerText = "Get daily updates at 7am"
}
const reg = await navigator.serviceWorker.ready
getSummaryButton.addEventListener("click", async () => {
const currentlyEnabled = localStorage.getItem(KEY_UPDATES_ENABLED) === "true"
const worker = await navigator.serviceWorker.ready
if (currentlyEnabled) {
localStorage.removeItem(KEY_UPDATES_ENABLED)
worker.active.postMessage("cancel")
} else if (await Notification.requestPermission()) {
localStorage.setItem(KEY_UPDATES_ENABLED, "true")
worker.active.postMessage(loc)
} else {
console.log("notification denied")
}
})
const existingSubscriptionJson = localStorage.getItem(KEY_SUBSCRIPTION)
const existingSubscription = existingSubscriptionJson ? JSON.parse(existingSubscriptionJson) : null
getSummaryButton.style.display = "block"
})
if (existingSubscription?.locations?.includes(loc) ?? false) {
getSummaryButton.innerText = "Stop updates"
reg.active.postMessage(loc)
} else {
getSummaryButton.innerText = "Get daily updates at 7am"
}
getSummaryButton.addEventListener("click", onButtonClick)
getSummaryButton.style.display = "block"
}
async function onButtonClick() {
const reg = await navigator.serviceWorker.ready
const existingSubscriptionJson = localStorage.getItem(KEY_SUBSCRIPTION)
const existingSubscription = existingSubscriptionJson ? JSON.parse(existingSubscriptionJson) : null
const currentlyEnabled = existingSubscription?.locations?.includes(loc) ?? false
if (currentlyEnabled) {
await reg.pushManager.getSubscription().then((sub) => sub?.unsubscribe())
localStorage.removeItem(KEY_SUBSCRIPTION)
getSummaryButton.innerText = "Get daily updates at 7am"
} else {
const worker = await navigator.serviceWorker.ready
try {
const publicKey = await fetch("/vapid").then((res) => {
if (res.status === 200) {
return res.text()
}
throw new Error(`${res.status}`)
})
const pushSub = await worker.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: publicKey
}).catch((error) => {
console.error(error)
})
let newSubscription
if (existingSubscription) {
newSubscription = await fetch(`/registrations/${existingSubscription.id}`, {
method: "PATCH",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
subscription: pushSub,
locations: [...existingSubscription.locations, loc]
})
}).then((res) => {
if (res.status === 200) {
return res.json()
}
throw new Error(`${res.status}`)
})
} else {
newSubscription = await fetch("/registrations", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
subscription: pushSub,
locations: [loc]
})
}).then((res) => {
if (res.status === 200) {
return res.json()
}
throw new Error(`${res.status}`)
})
}
localStorage.setItem(KEY_SUBSCRIPTION, JSON.stringify(newSubscription))
getSummaryButton.innerText = "Stop updates"
} catch (error) {
console.log(error)
}
}
}
if (canReceiveUpdates) {
main()
}

View File

@@ -1,47 +1,17 @@
let ws
let shouldRetry = false
self.addEventListener('install', function (event) {
event.waitUntil(self.skipWaiting());
});
function start(location) {
shouldRetry = true
const port = self.location.port ? `:${self.location.port}` : ""
const socket = new WebSocket(`ws://${self.location.hostname}${port}/ws?location=${location}`)
socket.onopen = () => {
ws = socket
}
socket.onclose = () => {
reconnect()
}
socket.onerror = () => {
reconnect()
}
socket.onmessage = (event) => {
self.registration.showNotification("7am weather summary", {
body: event.data
})
}
}
self.addEventListener('activate', function (event) {
event.waitUntil(self.clients.claim());
});
function cancel() {
shouldRetry = false
if (ws) {
ws.close()
ws = null
self.addEventListener("push", (event) => {
if (event.data) {
event.waitUntil(
self.registration.showNotification("7am weather summary", {
body: event.data.text()
})
)
}
}
async function reconnect() {
while (shouldRetry) {
await new Promise((resolve) => {
setTimeout(resolve, 5 * 60 * 1000)
})
start(location)
}
}
self.addEventListener("message", (event) => {
if (event.data === "cancel") {
cancel()
} else {
start(event.data)
}
})
})