supersam/src/hooks/usePushNotifications.js

123 lines
3.7 KiB
JavaScript

import React from "react";
import { supabase, hasSupabaseConfig } from "../supabaseClient";
const VAPID_PUBLIC_KEY = "BBHRiMfIGQf3EjnnPQ6QS-jS2oset-YMmufJSvw-0a974ULxLaxQqZkfB0ldz-tqH6B9WAIh9Et86i0ezjuedmU";
function urlBase64ToUint8Array(base64String) {
const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding).replace(/\-/g, "+").replace(/_/g, "/");
const rawData = window.atob(base64);
return Uint8Array.from(rawData, (c) => c.charCodeAt(0));
}
export async function subscribeUserToPush(userId) {
if (!hasSupabaseConfig || !userId) return null;
if (!("serviceWorker" in navigator) || !("PushManager" in window)) {
console.warn("Push notifications not supported");
return null;
}
const registration = await navigator.serviceWorker.ready;
let subscription = await registration.pushManager.getSubscription();
if (!subscription) {
try {
subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY),
});
} catch (err) {
console.error("Push subscription failed:", err);
return null;
}
}
const { data: { session } } = await supabase.auth.getSession();
if (!session?.access_token) return null;
const res = await fetch(`${supabase.supabaseUrl}/functions/v1/subscribe-push`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${session.access_token}`,
},
body: JSON.stringify({ subscription: subscription.toJSON() }),
});
if (!res.ok) {
console.error("Failed to save push subscription:", await res.text());
return null;
}
return subscription;
}
export async function unsubscribeUserFromPush() {
if (!("serviceWorker" in navigator)) return;
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.getSubscription();
if (!subscription) return;
const endpoint = subscription.endpoint;
await subscription.unsubscribe();
const { data: { session } } = await supabase.auth.getSession();
if (session?.access_token) {
await fetch(`${supabase.supabaseUrl}/functions/v1/unsubscribe-push`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${session.access_token}`,
},
body: JSON.stringify({ endpoint }),
});
}
}
export function usePushNotifications(userId) {
const [isSupported, setIsSupported] = React.useState(false);
const [isSubscribed, setIsSubscribed] = React.useState(false);
const [isLoading, setIsLoading] = React.useState(false);
React.useEffect(() => {
const supported = "serviceWorker" in navigator && "PushManager" in window && "Notification" in window;
setIsSupported(supported);
if (!supported) return;
navigator.serviceWorker.ready.then((reg) => {
reg.pushManager.getSubscription().then((sub) => {
setIsSubscribed(!!sub);
});
});
}, []);
const subscribe = React.useCallback(async () => {
if (!isSupported || !userId) return;
setIsLoading(true);
try {
const permission = await Notification.requestPermission();
if (permission !== "granted") {
setIsSubscribed(false);
return;
}
const sub = await subscribeUserToPush(userId);
setIsSubscribed(!!sub);
} finally {
setIsLoading(false);
}
}, [isSupported, userId]);
const unsubscribe = React.useCallback(async () => {
setIsLoading(true);
try {
await unsubscribeUserFromPush();
setIsSubscribed(false);
} finally {
setIsLoading(false);
}
}, []);
return { isSupported, isSubscribed, isLoading, subscribe, unsubscribe };
}