From 83ea4184b4b3169402fa7ccef16a8a59521f0f37 Mon Sep 17 00:00:00 2001 From: Bartek Date: Sat, 20 Jun 2026 15:11:49 +0200 Subject: [PATCH] Aplikacja kelnera jako PWA --- public/waiter/.htaccess | 9 +++ public/waiter/app.css | 13 +++- public/waiter/app.js | 59 +++++++++++++++--- public/waiter/icons/icon-192.png | Bin 0 -> 999 bytes public/waiter/icons/icon-512.png | Bin 0 -> 3091 bytes public/waiter/icons/icon-maskable-512.png | Bin 0 -> 2951 bytes public/waiter/index.php | 13 ++++ public/waiter/manifest.webmanifest | 33 +++++++++++ public/waiter/sw.js | 23 ++++++++ scripts/generate_waiter_pwa_icons.php | 69 ++++++++++++++++++++++ 10 files changed, 210 insertions(+), 9 deletions(-) create mode 100644 public/waiter/.htaccess create mode 100644 public/waiter/icons/icon-192.png create mode 100644 public/waiter/icons/icon-512.png create mode 100644 public/waiter/icons/icon-maskable-512.png create mode 100644 public/waiter/manifest.webmanifest create mode 100644 scripts/generate_waiter_pwa_icons.php diff --git a/public/waiter/.htaccess b/public/waiter/.htaccess new file mode 100644 index 0000000..4f2da59 --- /dev/null +++ b/public/waiter/.htaccess @@ -0,0 +1,9 @@ + + AddType application/manifest+json .webmanifest + + + + + Header set Cache-Control "no-cache" + + diff --git a/public/waiter/app.css b/public/waiter/app.css index b12d87d..a029392 100644 --- a/public/waiter/app.css +++ b/public/waiter/app.css @@ -87,7 +87,8 @@ body { 50% { opacity: 1; transform: scale(1.1); } } -.notify-banner { +.notify-banner, +.install-banner { display: flex; align-items: center; justify-content: space-between; @@ -99,13 +100,21 @@ body { margin-bottom: 16px; } -.notify-banner p { +.install-banner { + background: linear-gradient(135deg, #14532d 0%, #1e293b 100%); + border-color: #22c55e; +} + +.notify-banner p, +.install-banner p { margin-top: 4px; font-size: 0.82rem; color: #cbd5e1; line-height: 1.4; } +.notify-banner { + .btn { border: none; border-radius: 10px; diff --git a/public/waiter/app.js b/public/waiter/app.js index ec89bbb..9f0cd4d 100644 --- a/public/waiter/app.js +++ b/public/waiter/app.js @@ -7,6 +7,8 @@ const syncDot = document.getElementById('syncDot'); const syncLabel = document.getElementById('syncLabel'); const notifyBanner = document.getElementById('notifyBanner'); const enableNotifyBtn = document.getElementById('enableNotifyBtn'); +const installBanner = document.getElementById('installBanner'); +const installAppBtn = document.getElementById('installAppBtn'); const statPending = document.getElementById('statPending'); const statWaiter = document.getElementById('statWaiter'); @@ -18,6 +20,49 @@ let feedInitialized = false; let pollTimer = null; let lastPayload = ''; let swRegistration = null; +let deferredInstallPrompt = null; + +function isStandaloneDisplay() { + return window.matchMedia('(display-mode: standalone)').matches + || window.navigator.standalone === true; +} + +function updateInstallBanner() { + if (!installBanner) { + return; + } + + if (isStandaloneDisplay() || !deferredInstallPrompt) { + installBanner.classList.add('hidden'); + return; + } + + installBanner.classList.remove('hidden'); +} + +function setupInstallPrompt() { + window.addEventListener('beforeinstallprompt', (event) => { + event.preventDefault(); + deferredInstallPrompt = event; + updateInstallBanner(); + }); + + window.addEventListener('appinstalled', () => { + deferredInstallPrompt = null; + updateInstallBanner(); + }); +} + +async function promptInstallApp() { + if (!deferredInstallPrompt) { + return; + } + + deferredInstallPrompt.prompt(); + await deferredInstallPrompt.userChoice; + deferredInstallPrompt = null; + updateInstallBanner(); +} function escapeHtml(value) { return String(value ?? '') @@ -230,6 +275,10 @@ enableNotifyBtn?.addEventListener('click', () => { requestNotifications(); }); +installAppBtn?.addEventListener('click', () => { + promptInstallApp(); +}); + document.addEventListener('visibilitychange', () => { if (!document.hidden) { pollFeed(); @@ -237,13 +286,9 @@ document.addEventListener('visibilitychange', () => { }); (async function init() { + setupInstallPrompt(); + updateInstallBanner(); updateNotifyBanner(); - - if ('Notification' in window && Notification.permission === 'granted') { - await registerServiceWorker(); - } else if ('Notification' in window && Notification.permission === 'default') { - notifyBanner.classList.remove('hidden'); - } - + await registerServiceWorker(); startPolling(); })(); diff --git a/public/waiter/icons/icon-192.png b/public/waiter/icons/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..d23766652c9a4cea6908afa466e5544e5da97b16 GIT binary patch literal 999 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 OH6*^jHMTkiIs6Z2g1WN+ literal 0 HcmV?d00001 diff --git a/public/waiter/index.php b/public/waiter/index.php index 3fc2419..eb314d4 100644 --- a/public/waiter/index.php +++ b/public/waiter/index.php @@ -9,9 +9,14 @@ $vJs = publicAssetVersion($waiterDir, 'app.js'); + + + Kelner – wezwania + + @@ -27,6 +32,14 @@ $vJs = publicAssetVersion($waiterDir, 'app.js'); + +