Remove DOM access from webpush

This commit is contained in:
Pavel Djundik 2019-11-04 16:11:17 +02:00
parent 8972242863
commit b164e95290
2 changed files with 62 additions and 70 deletions

View file

@ -232,11 +232,21 @@
id="pushNotifications" id="pushNotifications"
type="button" type="button"
class="btn" class="btn"
:disabled="$store.state.pushNotificationState !== 'supported'" :disabled="
data-text-alternate="Unsubscribe from push notifications" $store.state.pushNotificationState !== 'supported' &&
$store.state.pushNotificationState !== 'subscribed'
"
@click="onPushButtonClick" @click="onPushButtonClick"
> >
Subscribe to push notifications <template v-if="$store.state.pushNotificationState === 'subscribed'">
Unsubscribe from push notifications
</template>
<template v-else-if="$store.state.pushNotificationState === 'loading'">
Loading
</template>
<template v-else>
Subscribe to push notifications
</template>
</button> </button>
<div v-if="$store.state.pushNotificationState === 'nohttps'" class="error"> <div v-if="$store.state.pushNotificationState === 'nohttps'" class="error">
<strong>Warning</strong>: Push notifications are only supported over <strong>Warning</strong>: Push notifications are only supported over
@ -257,9 +267,9 @@
</div> </div>
<div class="col-sm-12"> <div class="col-sm-12">
<label class="opt"> <label class="opt">
<!-- TODO: handle enabling/disabling notifications -->
<input <input
id="desktopNotifications" id="desktopNotifications"
:checked="$store.state.settings.desktopNotifications"
type="checkbox" type="checkbox"
name="desktopNotifications" name="desktopNotifications"
/> />

View file

@ -1,20 +1,22 @@
"use strict"; "use strict";
const $ = require("jquery");
const storage = require("./localStorage"); const storage = require("./localStorage");
const socket = require("./socket"); const socket = require("./socket");
const store = require("./store").default; const store = require("./store").default;
let pushNotificationsButton;
let clientSubscribed = null; let clientSubscribed = null;
let applicationServerKey; let applicationServerKey;
if ("serviceWorker" in navigator) { if ("serviceWorker" in navigator) {
navigator.serviceWorker.addEventListener("message", (event) => { navigator.serviceWorker.addEventListener("message", (event) => {
if (event.data && event.data.type === "open") { if (event.data && event.data.type === "open") {
$("#sidebar") const channelTarget = document.querySelector(
.find(`.chan[data-target="#${event.data.channel}"]`) "#sidebar .chan[data-target='#" + event.data.channel + "']"
.trigger("click"); );
if (channelTarget) {
channelTarget.click();
}
} }
}); });
} }
@ -27,22 +29,18 @@ module.exports.configurePushNotifications = (subscribedOnServer, key) => {
// If client has push registration but the server knows nothing about it, // If client has push registration but the server knows nothing about it,
// this subscription is broken and client has to register again // this subscription is broken and client has to register again
if (clientSubscribed === true && subscribedOnServer === false) { if (clientSubscribed === true && subscribedOnServer === false) {
pushNotificationsButton.prop("disabled", true); store.commit("pushNotificationState", "loading");
navigator.serviceWorker.ready navigator.serviceWorker.ready
.then((registration) => registration.pushManager.getSubscription()) .then((registration) => registration.pushManager.getSubscription())
.then((subscription) => subscription && subscription.unsubscribe()) .then((subscription) => subscription && subscription.unsubscribe())
.then((successful) => { .then((successful) => {
if (successful) { store.commit("pushNotificationState", successful ? "supported" : "unsupported");
alternatePushButton().prop("disabled", false);
}
}); });
} }
}; };
module.exports.initialize = () => { module.exports.initialize = () => {
pushNotificationsButton = $("#pushNotifications");
if (!isAllowedServiceWorkersHost()) { if (!isAllowedServiceWorkersHost()) {
store.commit("pushNotificationState", "nohttps"); store.commit("pushNotificationState", "nohttps");
return; return;
@ -59,13 +57,12 @@ module.exports.initialize = () => {
} }
return registration.pushManager.getSubscription().then((subscription) => { return registration.pushManager.getSubscription().then((subscription) => {
store.commit("pushNotificationState", "supported");
clientSubscribed = !!subscription; clientSubscribed = !!subscription;
if (clientSubscribed) { store.commit(
alternatePushButton(); "pushNotificationState",
} clientSubscribed ? "subscribed" : "supported"
);
}); });
}) })
.catch(() => { .catch(() => {
@ -75,55 +72,48 @@ module.exports.initialize = () => {
}; };
module.exports.onPushButton = () => { module.exports.onPushButton = () => {
// TODO: move dom logic to Settings.vue store.commit("pushNotificationState", "loading");
pushNotificationsButton = $("#pushNotifications");
pushNotificationsButton.prop("disabled", true);
navigator.serviceWorker.ready navigator.serviceWorker.ready
.then((registration) => .then((registration) =>
registration.pushManager registration.pushManager.getSubscription().then((existingSubscription) => {
.getSubscription() if (existingSubscription) {
.then((existingSubscription) => { socket.emit("push:unregister");
if (existingSubscription) {
socket.emit("push:unregister");
return existingSubscription.unsubscribe(); return existingSubscription.unsubscribe().then(() => {
} store.commit("pushNotificationState", "supported");
});
}
return registration.pushManager return registration.pushManager
.subscribe({ .subscribe({
applicationServerKey: urlBase64ToUint8Array(applicationServerKey), applicationServerKey: urlBase64ToUint8Array(applicationServerKey),
userVisibleOnly: true, userVisibleOnly: true,
}) })
.then((subscription) => { .then((subscription) => {
const rawKey = subscription.getKey ? subscription.getKey("p256dh") : ""; const rawKey = subscription.getKey ? subscription.getKey("p256dh") : "";
const key = rawKey const key = rawKey
? window.btoa(String.fromCharCode(...new Uint8Array(rawKey))) ? window.btoa(String.fromCharCode(...new Uint8Array(rawKey)))
: ""; : "";
const rawAuthSecret = subscription.getKey const rawAuthSecret = subscription.getKey
? subscription.getKey("auth") ? subscription.getKey("auth")
: ""; : "";
const authSecret = rawAuthSecret const authSecret = rawAuthSecret
? window.btoa(String.fromCharCode(...new Uint8Array(rawAuthSecret))) ? window.btoa(String.fromCharCode(...new Uint8Array(rawAuthSecret)))
: ""; : "";
socket.emit("push:register", { socket.emit("push:register", {
token: storage.get("token"), token: storage.get("token"),
endpoint: subscription.endpoint, endpoint: subscription.endpoint,
keys: { keys: {
p256dh: key, p256dh: key,
auth: authSecret, auth: authSecret,
}, },
});
return true;
}); });
})
.then((successful) => { store.commit("pushNotificationState", "subscribed");
if (successful) { });
alternatePushButton().prop("disabled", false); })
}
})
) )
.catch(() => { .catch(() => {
store.commit("pushNotificationState", "unsupported"); store.commit("pushNotificationState", "unsupported");
@ -132,14 +122,6 @@ module.exports.onPushButton = () => {
return false; return false;
}; };
function alternatePushButton() {
const text = pushNotificationsButton.text();
return pushNotificationsButton
.text(pushNotificationsButton.data("text-alternate"))
.data("text-alternate", text);
}
function urlBase64ToUint8Array(base64String) { function urlBase64ToUint8Array(base64String) {
const padding = "=".repeat((4 - (base64String.length % 4)) % 4); const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/"); const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");