e78c959c49
Never checked the browser's actual push subscription on mount — state initialized to false unconditionally, so the button looked reset to "Enable notifications" on every page load even when notifications were genuinely still active. Now checks navigator.serviceWorker + getSubscription() on mount and initializes from that.
108 lines
3.0 KiB
TypeScript
108 lines
3.0 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect } from "react";
|
|
import { Bell, BellOff } from "lucide-react";
|
|
import { Button } from "@/components/ui/button";
|
|
import { toast } from "sonner";
|
|
|
|
export function PushSubscribeButton() {
|
|
const [subscribed, setSubscribed] = useState(false);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
// The button previously always started as "not subscribed" regardless of
|
|
// actual state — it never checked the browser's real push subscription on
|
|
// mount, so it looked reset every time this page loaded even when
|
|
// notifications were genuinely still enabled.
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
(async () => {
|
|
try {
|
|
if (!("serviceWorker" in navigator)) return;
|
|
const registration = await navigator.serviceWorker.ready;
|
|
const sub = await registration.pushManager.getSubscription();
|
|
if (!cancelled) setSubscribed(!!sub);
|
|
} catch {
|
|
// ignore — leave as not-subscribed if the check itself fails
|
|
} finally {
|
|
if (!cancelled) setLoading(false);
|
|
}
|
|
})();
|
|
return () => { cancelled = true; };
|
|
}, []);
|
|
|
|
async function handleSubscribe() {
|
|
if (Notification.permission === "denied") {
|
|
toast.error("Notifications blocked in browser settings.");
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
try {
|
|
const permission = await Notification.requestPermission();
|
|
if (permission !== "granted") return;
|
|
|
|
const registration = await navigator.serviceWorker.ready;
|
|
const sub = await registration.pushManager.subscribe({
|
|
userVisibleOnly: true,
|
|
applicationServerKey: process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY,
|
|
});
|
|
|
|
await fetch("/api/v1/push/subscribe", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(sub.toJSON()),
|
|
});
|
|
|
|
setSubscribed(true);
|
|
} catch (err) {
|
|
console.error(err);
|
|
toast.error("Failed to enable notifications.");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
async function handleUnsubscribe() {
|
|
setLoading(true);
|
|
try {
|
|
const registration = await navigator.serviceWorker.ready;
|
|
const sub = await registration.pushManager.getSubscription();
|
|
if (sub) {
|
|
await fetch("/api/v1/push/subscribe", {
|
|
method: "DELETE",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ endpoint: sub.endpoint }),
|
|
});
|
|
await sub.unsubscribe();
|
|
}
|
|
setSubscribed(false);
|
|
} catch (err) {
|
|
console.error(err);
|
|
toast.error("Failed to disable notifications.");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
disabled={loading}
|
|
onClick={subscribed ? handleUnsubscribe : handleSubscribe}
|
|
>
|
|
{subscribed ? (
|
|
<>
|
|
<BellOff className="mr-2 h-4 w-4" />
|
|
Notifications on
|
|
</>
|
|
) : (
|
|
<>
|
|
<Bell className="mr-2 h-4 w-4" />
|
|
Enable notifications
|
|
</>
|
|
)}
|
|
</Button>
|
|
);
|
|
}
|