From 193038a671ff94e9bc05a369d8f9be41474a13d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D1=82=D0=BE=D0=BD=20=D0=A2=D1=80=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Sat, 22 Aug 2026 23:14:25 +0300 Subject: [PATCH] fix: hide install button when PWA already installed - Button hidden by default; async isPwaInstalled() check (standalone mode, ponomar-installed flag, getInstalledRelatedApps) unhides only if installable - Click hint 'already installed' branch uses same multi-signal detection - manifest id '/' for getInstalledRelatedApps matching on desktop Chrome --- src/ui/settings-view.ts | 29 ++++++++++++++++++++++++++--- vite.config.ts | 1 + 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/ui/settings-view.ts b/src/ui/settings-view.ts index a9dff67..56549c2 100644 --- a/src/ui/settings-view.ts +++ b/src/ui/settings-view.ts @@ -253,7 +253,7 @@ export class SettingsView {
- @@ -416,9 +416,8 @@ export class SettingsView { } if (!hintEl) return; const ua = navigator.userAgent; - const isStandalone = window.matchMedia('(display-mode: standalone)').matches || (window.navigator as any).standalone === true; let hint: string; - if (isStandalone) { + if (await this.isPwaInstalled()) { hint = t.settings.installPwaAlreadyInstalled; } else if (/firefox/i.test(ua)) { hint = t.settings.installPwaHintFirefox; @@ -431,6 +430,15 @@ export class SettingsView { hintEl.classList.remove('hidden'); }); + // Show install button only when actually installable + (async () => { + const btn = document.getElementById('install-pwa'); + if (!btn || !('serviceWorker' in navigator)) return; + try { + if (!(await this.isPwaInstalled())) btn.classList.remove('hidden'); + } catch { /* stay hidden */ } + })(); + // Location const saveLatLng = () => { const lat = parseFloat((document.getElementById('latitude') as HTMLInputElement)?.value); @@ -729,4 +737,19 @@ export class SettingsView { preview.style.fontSize = `${this.settings.fontSize}px`; } } + + private async isPwaInstalled(): Promise { + if (window.matchMedia('(display-mode: standalone)').matches || (window.navigator as any).standalone === true) { + return true; + } + if (localStorage.getItem('ponomar-installed')) return true; + try { + const navAny = navigator as any; + if (typeof navAny.getInstalledRelatedApps === 'function') { + const apps = await navAny.getInstalledRelatedApps(); + if (Array.isArray(apps) && apps.length > 0) return true; + } + } catch { /* not supported */ } + return false; + } } diff --git a/vite.config.ts b/vite.config.ts index ea40800..1a5afbc 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -42,6 +42,7 @@ export default defineConfig({ ], }, manifest: { + id: '/', name: 'Ponomar — Orthodox Calendar', short_name: 'Ponomar', description: 'Orthodox Church calendar and liturgical information',