diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 0000000..b24fbd2 Binary files /dev/null and b/public/apple-touch-icon.png differ diff --git a/public/favicon-32.png b/public/favicon-32.png new file mode 100644 index 0000000..f39bd4d Binary files /dev/null and b/public/favicon-32.png differ diff --git a/public/icon-192.png b/public/icon-192.png new file mode 100644 index 0000000..9093b4a Binary files /dev/null and b/public/icon-192.png differ diff --git a/public/icon-512-maskable.png b/public/icon-512-maskable.png new file mode 100644 index 0000000..faab41e Binary files /dev/null and b/public/icon-512-maskable.png differ diff --git a/public/icon-512.png b/public/icon-512.png new file mode 100644 index 0000000..18118d8 Binary files /dev/null and b/public/icon-512.png differ diff --git a/public/manifest.webmanifest b/public/manifest.webmanifest new file mode 100644 index 0000000..f954994 --- /dev/null +++ b/public/manifest.webmanifest @@ -0,0 +1,16 @@ +{ + "name": "LocalESP · Locales Españoles", + "short_name": "LocalESP", + "description": "Directorio colaborativo de negocios locales por toda España", + "start_url": "/", + "scope": "/", + "display": "standalone", + "background_color": "#ffffff", + "theme_color": "#8B0000", + "lang": "es", + "icons": [ + { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" }, + { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" }, + { "src": "/icon-512-maskable.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" } + ] +} diff --git a/public/sw.js b/public/sw.js new file mode 100644 index 0000000..08e3309 --- /dev/null +++ b/public/sw.js @@ -0,0 +1,46 @@ +const CACHE_NAME = "localesp-shell-v1"; +const SHELL_ASSETS = ["/", "/manifest.webmanifest", "/icon-192.png", "/icon-512.png"]; + +self.addEventListener("install", (event) => { + event.waitUntil( + caches.open(CACHE_NAME).then((cache) => cache.addAll(SHELL_ASSETS)) + ); + self.skipWaiting(); +}); + +self.addEventListener("activate", (event) => { + event.waitUntil( + caches.keys().then((keys) => + Promise.all(keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k))) + ) + ); + self.clients.claim(); +}); + +// Network-first para la API (los datos deben estar frescos); cache-first +// para el resto del shell estático, para que la app abra offline. +self.addEventListener("fetch", (event) => { + const url = new URL(event.request.url); + + if (event.request.method !== "GET") return; + + if (url.pathname.startsWith("/api/")) { + event.respondWith( + fetch(event.request).catch(() => caches.match(event.request)) + ); + return; + } + + event.respondWith( + caches.match(event.request).then((cached) => { + const network = fetch(event.request) + .then((response) => { + const clone = response.clone(); + caches.open(CACHE_NAME).then((cache) => cache.put(event.request, clone)); + return response; + }) + .catch(() => cached); + return cached || network; + }) + ); +});