From 4ce6fdcc5acbf2afa73eff7c8937b33806ff1c3d Mon Sep 17 00:00:00 2001 From: nimbleghost <132819643+nimbleghost@users.noreply.github.com> Date: Thu, 8 Jun 2023 20:12:41 +0200 Subject: [PATCH] Implement http actions in service worker These are only supported in Chrome-based browsers via the service worker and not for regular desktop notifications. --- web/public/sw.js | 69 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 14 deletions(-) diff --git a/web/public/sw.js b/web/public/sw.js index 96e5bf51..39d60a8a 100644 --- a/web/public/sw.js +++ b/web/public/sw.js @@ -48,6 +48,13 @@ self.addEventListener("push", (event) => { const image = message.attachment?.name.match(/\.(png|jpe?g|gif|webp)$/i) ? message.attachment.url : undefined; + const actions = message.actions + ?.filter(({ action }) => action === "view" || action === "http") + .map(({ label }) => ({ + action: label, + title: label, + })); + await Promise.all([ (async () => { await db.notifications.add({ @@ -71,6 +78,7 @@ self.addEventListener("push", (event) => { image, data, timestamp: message.time * 1_000, + actions, }), ]); } else { @@ -88,8 +96,6 @@ self.addEventListener("push", (event) => { self.addEventListener("notificationclick", (event) => { console.log("[ServiceWorker] NotificationClick"); - event.notification.close(); - event.waitUntil( (async () => { const clients = await self.clients.matchAll({ type: "window" }); @@ -97,29 +103,64 @@ self.addEventListener("notificationclick", (event) => { const rootUrl = new URL(self.location.origin); const rootClient = clients.find((client) => client.url === rootUrl.toString()); - if (event.notification.data.event !== "message") { + if (event.notification.data?.event !== "message") { if (rootClient) { rootClient.focus(); } else { self.clients.openWindow(rootUrl); } + event.notification.close(); } else { const { message } = event.notification.data; - if (message.click) { + if (event.action) { + const action = event.notification.data.message.actions.find(({ label }) => event.action === label); + + if (action.action === "view") { + self.clients.openWindow(action.url); + + if (action.clear) { + event.notification.close(); + } + } else if (action.action === "http") { + try { + const response = await fetch(action.url, { + method: action.method ?? "POST", + headers: action.headers ?? {}, + body: action.body, + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status} ${response.statusText}`); + } + + if (action.clear) { + event.notification.close(); + } + } catch (e) { + console.error("[ServiceWorker] Error performing http action", e); + self.registration.showNotification(`Unsuccessful action ${action.label} (${action.action})`, { + body: e.message, + }); + } + } + } else if (message.click) { self.clients.openWindow(message.click); - return; - } - const topicUrl = new URL(message.topic, self.location.origin); - const topicClient = clients.find((client) => client.url === topicUrl.toString()); - - if (topicClient) { - topicClient.focus(); - } else if (rootClient) { - rootClient.focus(); + event.notification.close(); } else { - self.clients.openWindow(topicUrl); + const topicUrl = new URL(message.topic, self.location.origin); + const topicClient = clients.find((client) => client.url === topicUrl.toString()); + + if (topicClient) { + topicClient.focus(); + } else if (rootClient) { + rootClient.focus(); + } else { + self.clients.openWindow(topicUrl); + } + + event.notification.close(); } } })()