From 7f9bbdc4f745959f76c826cab2b8950f1eec6455 Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Mon, 26 Jan 2026 00:46:27 -0500 Subject: [PATCH] up --- index.php | 46 ++++++++++++++++++++++++++++++++++++++++++++++ manifest.json | 20 ++++++++++++++++++++ service-worker.js | 43 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 manifest.json create mode 100644 service-worker.js diff --git a/index.php b/index.php index f17f908..7b0fff2 100644 --- a/index.php +++ b/index.php @@ -39,6 +39,11 @@ try { + + + + + @@ -97,6 +102,11 @@ try { + + + +
+
@@ -1237,6 +1247,42 @@ try { openSidebar = function() { console.debug('[index] openSidebar()'); _openSidebar(); }; closeSidebar = function() { console.debug('[index] closeSidebar()'); _closeSidebar(); }; + // PWA: register service worker and handle install prompt + let deferredPrompt = null; + const pwaBtn = document.getElementById('pwa-install-btn'); + const pwaWrap = document.getElementById('pwa-install-wrap'); + + window.addEventListener('beforeinstallprompt', (e) => { + e.preventDefault(); + deferredPrompt = e; + if (pwaBtn) { + pwaBtn.style.display = 'inline-block'; + if (pwaWrap) pwaWrap.classList.remove('d-none'); + } + }); + + if (pwaBtn) { + pwaBtn.addEventListener('click', async () => { + if (!deferredPrompt) return; + deferredPrompt.prompt(); + const { outcome } = await deferredPrompt.userChoice; + console.debug('PWA install result:', outcome); + if (outcome === 'accepted') { + pwaBtn.style.display = 'none'; + } + deferredPrompt = null; + }); + } + + window.addEventListener('appinstalled', () => { + console.debug('PWA installed'); + if (pwaBtn) pwaBtn.style.display = 'none'; + try { if (NotificationManager && NotificationManager.showToast) NotificationManager.showToast({ message: 'App instalada correctamente' }); } catch(e){} + }); + + if ('serviceWorker' in navigator) { + navigator.serviceWorker.register('/service-worker.js').then(reg => console.debug('SW registered', reg)).catch(e => console.warn('SW registration failed', e)); + } // Close sidebar when a nav item is selected (mobile UX improvement) document.querySelectorAll('.nav-link[data-tab]').forEach(link => { link.addEventListener('click', () => { diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..664bd0a --- /dev/null +++ b/manifest.json @@ -0,0 +1,20 @@ +{ + "name": "WhatsApp Bot Manager", + "short_name": "WA Bot", + "start_url": "/index.php", + "display": "standalone", + "background_color": "#ffffff", + "theme_color": "#10b981", + "icons": [ + { + "src": "data:image/svg+xml;utf8,WB", + "sizes": "192x192", + "type": "image/svg+xml" + }, + { + "src": "data:image/svg+xml;utf8,WB", + "sizes": "512x512", + "type": "image/svg+xml" + } + ] +} diff --git a/service-worker.js b/service-worker.js new file mode 100644 index 0000000..67c97ea --- /dev/null +++ b/service-worker.js @@ -0,0 +1,43 @@ +const CACHE_NAME = 'whatsapp-static-v1'; +const OFFLINE_URL = '/index.php'; +const ASSETS_TO_CACHE = [ + OFFLINE_URL, + '/assets/css/styles.css', + '/assets/js/app_simple.js', + '/manifest.json' +]; + +self.addEventListener('install', (event) => { + self.skipWaiting(); + event.waitUntil( + caches.open(CACHE_NAME).then((cache) => cache.addAll(ASSETS_TO_CACHE)) + ); +}); + +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(); +}); + +self.addEventListener('fetch', (event) => { + if (event.request.method !== 'GET') return; + event.respondWith( + caches.match(event.request).then(cached => { + if (cached) return cached; + return fetch(event.request).then(resp => { + // cache same-origin GET responses + try { + if (resp && resp.status === 200 && resp.type === 'basic') { + const clone = resp.clone(); + caches.open(CACHE_NAME).then(cache => cache.put(event.request, clone)); + } + } catch (e) {} + return resp; + }).catch(() => caches.match(OFFLINE_URL)); + }) + ); +});