If a user logs out from a device the expectation should be that device no longer having any association with the user's account. Any existing push notifications should thefore be removed on server. For this reason tie push notifications to a session, and remove them when the session is deleted.
119 lines
3.3 KiB
Vue
119 lines
3.3 KiB
Vue
<template>
|
|
<section>
|
|
Notifications are: <b>{{ subscription ? "Enabled" : "Disabled" }}</b>
|
|
<br />
|
|
<button
|
|
:disabled="unsupported"
|
|
@click="onClick"
|
|
>
|
|
{{ unsupported === undefined ? "Checking for support" : null }}
|
|
{{ unsupported === true ? "Notifications are not supported :(." : null }}
|
|
{{ unsupported === false ? (subscription ? "Disable notifications" : "Enable notifications") : null }}
|
|
</button>
|
|
<details>
|
|
<summary>Debug</summary>
|
|
<pre><code>{{ JSON.stringify(subscription?.toJSON(), undefined, 4) ?? "No subscription set" }}</code></pre>
|
|
</details>
|
|
</section>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
function notificationUnsupported() {
|
|
return (
|
|
!("serviceWorker" in navigator)
|
|
|| !("PushManager" in window)
|
|
|| !("showNotification" in ServiceWorkerRegistration.prototype)
|
|
)
|
|
}
|
|
|
|
async function registerAndSubscribe(
|
|
vapidPublicKey: string,
|
|
onSubscribe: (subs: PushSubscription | null ) => void,
|
|
) {
|
|
try {
|
|
await navigator.serviceWorker.register("/sw.js");
|
|
await subscribe(vapidPublicKey, onSubscribe);
|
|
} catch (err) {
|
|
console.error("Failed to register service worker:", err);
|
|
}
|
|
}
|
|
|
|
async function subscribe(
|
|
vapidPublicKey: string,
|
|
onSubscribe: (subs: PushSubscription | null) => void
|
|
) {
|
|
try {
|
|
const registration = await navigator.serviceWorker.ready;
|
|
const subscription = await registration.pushManager.subscribe({
|
|
userVisibleOnly: true,
|
|
applicationServerKey: vapidPublicKey,
|
|
});
|
|
console.log("Got subscription object", subscription.toJSON());
|
|
await submitSubscription(subscription);
|
|
onSubscribe(subscription);
|
|
} catch (err) {
|
|
console.error("Failed to subscribe:" , err);
|
|
}
|
|
}
|
|
|
|
async function unsubscribe(
|
|
subscription: PushSubscription,
|
|
onUnsubscribed: () => void,
|
|
) {
|
|
const body = JSON.stringify({ subscription });
|
|
await subscription.unsubscribe();
|
|
const res = await fetch("/api/unsubscribe", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body,
|
|
});
|
|
const result = await res.json();
|
|
console.log("/api/unsubscribe returned", result);
|
|
onUnsubscribed();
|
|
}
|
|
|
|
async function submitSubscription(subscription: PushSubscription) {
|
|
const res = await fetch("/api/subscribe", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({ subscription }),
|
|
});
|
|
const result = await res.json();
|
|
console.log("/api/subscribe returned", result);
|
|
}
|
|
|
|
async function getSubscription(
|
|
onSubscribe: (subs: PushSubscription | null) => void,
|
|
) {
|
|
const registration = await navigator.serviceWorker.ready;
|
|
const subscription = await registration.pushManager.getSubscription();
|
|
if (subscription) {
|
|
onSubscribe(subscription);
|
|
}
|
|
}
|
|
|
|
const unsupported = ref<boolean | undefined>(undefined);
|
|
const subscription = ref<PushSubscription | null>(null);
|
|
const runtimeConfig = useRuntimeConfig();
|
|
const { refresh: refreshSession } = useAccountSession();
|
|
|
|
async function onClick() {
|
|
if (!subscription.value)
|
|
await registerAndSubscribe(runtimeConfig.public.vapidPublicKey, (subs) => { subscription.value = subs })
|
|
else
|
|
await unsubscribe(subscription.value, () => { subscription.value = null })
|
|
refreshSession();
|
|
}
|
|
|
|
onMounted(() => {
|
|
unsupported.value = notificationUnsupported()
|
|
if (unsupported.value) {
|
|
return;
|
|
}
|
|
getSubscription(subs => { subscription.value = subs });
|
|
})
|
|
</script>
|