// Notificaciones: toasts dentro de la app + soporte de Web Push nativo // mediante el Service Worker de mensajería en /sw.js. import { toast } from "sonner"; // Clave pública VAPID (configurable con VITE_VAPID_KEY). export const VAPID_KEY = import.meta.env["VITE_VAPID_KEY"] ?? "wLVf8PBUOuPW8Zn5l_ksTTiaf9wb3OSSDaJAuxKPru0"; /** Alias retrocompatible. */ export const PUBLIC_VAPID_KEY = VAPID_KEY; export function urlBase64ToUint8Array(base64String: string): Uint8Array { const padding = "=".repeat((4 - (base64String.length % 4)) % 4); const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/"); const raw = atob(base64); const salida = new Uint8Array(raw.length); for (let i = 0; i < raw.length; i += 1) salida[i] = raw.charCodeAt(i); return salida; } export async function registrarServiceWorker(): Promise { if (typeof window === "undefined" || !("serviceWorker" in navigator)) return null; try { return await navigator.serviceWorker.register("/sw.js", { scope: "/" }); } catch (error) { console.warn("No se pudo registrar el Service Worker de notificaciones", error); return null; } } export async function pedirPermisoNotificaciones(): Promise { if (typeof window === "undefined" || !("Notification" in window)) return "unsupported"; if (Notification.permission === "granted") return "granted"; try { return await Notification.requestPermission(); } catch { return "denied"; } } export type ResultadoPush = | { ok: true; suscripcion: PushSubscription } | { ok: false; motivo: "unsupported" | "denied" | "sin-clave" | "error" }; export async function suscribirNotificacionesPush(): Promise { if (typeof window === "undefined" || !("serviceWorker" in navigator) || !("PushManager" in window)) { return { ok: false, motivo: "unsupported" }; } const permiso = await pedirPermisoNotificaciones(); if (permiso !== "granted") return { ok: false, motivo: "denied" }; if (!VAPID_KEY || VAPID_KEY.startsWith("REEMPLAZA")) { return { ok: false, motivo: "sin-clave" }; } try { const registration = (await navigator.serviceWorker.getRegistration("/sw.js")) ?? (await registrarServiceWorker()); if (!registration) return { ok: false, motivo: "error" }; const activo = await navigator.serviceWorker.ready; const existente = await activo.pushManager.getSubscription(); const suscripcion = existente ?? (await activo.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(VAPID_KEY) as BufferSource, })); console.log("Push Subscription JSON:", JSON.stringify(suscripcion)); await fetch("/api/guardar-suscripcion", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(suscripcion.toJSON()), }); return { ok: true, suscripcion }; } catch (error) { console.warn("No se pudo suscribir a notificaciones push", error); return { ok: false, motivo: "error" }; } } export function notificar(titulo: string, cuerpo: string) { toast.success(titulo, { description: cuerpo }); if (typeof window !== "undefined" && "Notification" in window && Notification.permission === "granted") { try { new Notification(titulo, { body: cuerpo, icon: "/icons/icon-192.png" }); } catch { /* silencioso: algunos navegadores exigen Service Worker */ } } }