diff --git a/components/app.js b/components/app.js index 4788788..ec5d74a 100644 --- a/components/app.js +++ b/components/app.js @@ -28,6 +28,21 @@ const baseConfig = { server: {}, }; +function urlBase64ToUint8Array(base64String) { + var padding = '='.repeat((4 - base64String.length % 4) % 4); + var base64 = (base64String + padding) + .replace(/\-/g, '+') + .replace(/_/g, '/'); + + var rawData = window.atob(base64); + var outputArray = new Uint8Array(rawData.length); + + for (var i = 0; i < rawData.length; ++i) { + outputArray[i] = rawData.charCodeAt(i); + } + return outputArray; +} + const configPromise = fetch("./config.json") .then((resp) => { if (resp.ok) { @@ -981,9 +996,37 @@ export default class App extends Component { affectedBuffers.push(prefix.name); } return affectedBuffers; + case irc.RPL_ISUPPORT: { + const params = msg.params; + for (const p of params) { + if (p.indexOf("VAPID") === 0) { + const value = p.replace("VAPID=", ""); + const vapidPubKey = urlBase64ToUint8Array(value); + navigator.serviceWorker.ready.then((registration) => { + return registration.pushManager.getSubscription() + .then((subscription) => { + if (subscription) { + return subscription; + } + return registration.pushManager.subscribe({ + userVisibleOnly: true, + applicationServerKey: vapidPubKey, + }); + }).then((subscription) => { + var data = subscription.toJSON(); + var keysStr = irc.formatTags(data.keys); + client.send({ + command: "WEBPUSH", + params: ["REGISTER", data.endpoint, keysStr], + }); + }); + }); + } + } + return []; + } case irc.RPL_YOURHOST: case irc.RPL_MYINFO: - case irc.RPL_ISUPPORT: case irc.RPL_ENDOFMOTD: case irc.ERR_NOMOTD: case irc.RPL_AWAY: diff --git a/lib/client.js b/lib/client.js index a1a969a..90e736e 100644 --- a/lib/client.js +++ b/lib/client.js @@ -25,6 +25,7 @@ const permanentCaps = [ "draft/read-marker", "soju.im/bouncer-networks", + "soju.im/webpush", ]; const RECONNECT_MIN_DELAY_MSEC = 10 * 1000; // 10s diff --git a/main.js b/main.js index 2b73e7d..93a7737 100644 --- a/main.js +++ b/main.js @@ -1,4 +1,6 @@ import { html, render } from "./lib/index.js"; import App from "./components/app.js"; +navigator.serviceWorker.register("./service-worker.js").catch(console.error); + render(html`<${App}/>`, document.body); diff --git a/service-worker.js b/service-worker.js new file mode 100644 index 0000000..6398c4e --- /dev/null +++ b/service-worker.js @@ -0,0 +1,6 @@ +self.addEventListener("push", (event) => { + var payload = event.data ? event.data.text() : "no payload"; + event.waitUntil(self.registration.showNotification("gamja service worker", { + body: payload, + })); +});