diff --git a/frontend/index.html b/frontend/index.html index 440ce1e..69b6847 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -12,6 +12,8 @@ + + WealthySmart diff --git a/frontend/public/manifest.webmanifest b/frontend/public/manifest.webmanifest new file mode 100644 index 0000000..c2f9e9f --- /dev/null +++ b/frontend/public/manifest.webmanifest @@ -0,0 +1,29 @@ +{ + "name": "WealthySmart", + "short_name": "WealthySmart", + "description": "Smart personal finance management", + "start_url": "/", + "scope": "/", + "display": "standalone", + "background_color": "#0f172a", + "theme_color": "#0f172a", + "lang": "es-CR", + "icons": [ + { + "src": "/icons/icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/icons/icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "/icons/icon-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/frontend/public/sw.js b/frontend/public/sw.js index 5c8b7ce..3104549 100644 --- a/frontend/public/sw.js +++ b/frontend/public/sw.js @@ -1,4 +1,42 @@ +/* WealthySmart service worker: web push + PWA installability. + * (Replaces the old self-unregistering cleanup stub, which also meant + * `navigator.serviceWorker.ready` never resolved and push silently no-oped.) */ + self.addEventListener("install", () => self.skipWaiting()); -self.addEventListener("activate", async () => { - await self.registration.unregister(); + +self.addEventListener("activate", (event) => { + event.waitUntil(self.clients.claim()); +}); + +self.addEventListener("push", (event) => { + let data = { title: "WealthySmart", body: "", url: "/" }; + try { + data = { ...data, ...event.data.json() }; + } catch { + if (event.data) data.body = event.data.text(); + } + event.waitUntil( + self.registration.showNotification(data.title, { + body: data.body, + icon: "/icons/icon-192.png", + badge: "/icons/icon-192.png", + data: { url: data.url }, + }), + ); +}); + +self.addEventListener("notificationclick", (event) => { + event.notification.close(); + const url = event.notification.data?.url || "/"; + event.waitUntil( + self.clients.matchAll({ type: "window", includeUncontrolled: true }).then((clients) => { + for (const client of clients) { + if ("focus" in client) { + client.navigate(url); + return client.focus(); + } + } + return self.clients.openWindow(url); + }), + ); }); diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index c2a145c..cde6120 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -3,6 +3,13 @@ import { createRoot } from "react-dom/client"; import App from "./App"; import "./index.css"; +if ("serviceWorker" in navigator) { + // Required for both web push and PWA installability. + navigator.serviceWorker.register("/sw.js").catch((err) => { + console.warn("Service worker registration failed:", err); + }); +} + createRoot(document.getElementById("root")!).render(