|
121 | 121 | // ── Install Banner UI ────────────────────────────────────────────────────── |
122 | 122 |
|
123 | 123 | function _showInstallBanner() { |
124 | | - if (document.getElementById('pwa-install-banner')) return; // already shown |
125 | | - |
126 | | - const banner = document.createElement('div'); |
127 | | - banner.id = 'pwa-install-banner'; |
128 | | - banner.setAttribute('role', 'banner'); |
129 | | - banner.setAttribute('aria-label', 'Install BiblioDrift app'); |
130 | | - banner.innerHTML = ` |
131 | | - <div class="pwa-banner-content"> |
132 | | - <img src="../assets/images/biblioDrift_favicon.png" |
133 | | - alt="BiblioDrift icon" class="pwa-banner-icon"> |
134 | | - <div class="pwa-banner-text"> |
135 | | - <strong>Install BiblioDrift.</strong> |
136 | | - <span>Read offline, anytime.</span> |
| 124 | + let banner = document.getElementById('pwa-install-banner'); |
| 125 | + if (!banner) { |
| 126 | + banner = document.createElement('div'); |
| 127 | + banner.id = 'pwa-install-banner'; |
| 128 | + banner.setAttribute('role', 'banner'); |
| 129 | + banner.setAttribute('aria-label', 'Install BiblioDrift app'); |
| 130 | + banner.innerHTML = ` |
| 131 | + <div class="pwa-banner-content"> |
| 132 | + <img src="../assets/images/biblioDrift_favicon.png" |
| 133 | + alt="BiblioDrift icon" class="pwa-banner-icon"> |
| 134 | + <div class="pwa-banner-text"> |
| 135 | + <strong>Install BiblioDrift.</strong> |
| 136 | + <span>Read offline, anytime.</span> |
| 137 | + </div> |
137 | 138 | </div> |
138 | | - </div> |
139 | | - <div class="pwa-banner-actions"> |
140 | | - <button class="pwa-install-btn" id="pwa-install-btn" |
141 | | - aria-label="Install BiblioDrift as an app"> |
142 | | - Install |
143 | | - </button> |
144 | | - <button class="pwa-dismiss-btn" id="pwa-dismiss-btn" |
145 | | - aria-label="Dismiss install prompt"> |
146 | | - <i class="fa-solid fa-xmark"></i> |
147 | | - </button> |
148 | | - </div> |
149 | | - `; |
150 | | - |
151 | | - const footer = document.querySelector(".main-footer"); |
152 | | - if (footer) { |
153 | | - footer.parentNode.insertBefore(banner, footer); |
154 | | - } else { |
155 | | - const main = document.querySelector("main"); |
156 | | - if (main) main.appendChild(banner); |
157 | | - else document.body.appendChild(banner); |
| 139 | + <div class="pwa-banner-actions"> |
| 140 | + <button class="pwa-install-btn" id="pwa-install-btn" |
| 141 | + aria-label="Install BiblioDrift as an app"> |
| 142 | + Install |
| 143 | + </button> |
| 144 | + <button class="pwa-dismiss-btn" id="pwa-dismiss-btn" |
| 145 | + aria-label="Dismiss install prompt"> |
| 146 | + <i class="fa-solid fa-xmark"></i> |
| 147 | + </button> |
| 148 | + </div> |
| 149 | + `; |
| 150 | + |
| 151 | + const footer = document.querySelector(".main-footer"); |
| 152 | + if (footer) { |
| 153 | + footer.parentNode.insertBefore(banner, footer); |
| 154 | + } else { |
| 155 | + const main = document.querySelector("main"); |
| 156 | + if (main) main.appendChild(banner); |
| 157 | + else document.body.appendChild(banner); |
| 158 | + } |
158 | 159 | } |
159 | 160 |
|
160 | 161 | // Animate in |
161 | 162 | requestAnimationFrame(() => banner.classList.add('pwa-banner-visible')); |
162 | 163 |
|
163 | | - document.getElementById('pwa-install-btn').addEventListener('click', async () => { |
164 | | - if (!_deferredPrompt) return; |
165 | | - _deferredPrompt.prompt(); |
166 | | - const { outcome } = await _deferredPrompt.userChoice; |
167 | | - _deferredPrompt = null; |
168 | | - _hideInstallBanner(); |
169 | | - console.log(`[PWA] Install prompt outcome: ${outcome}`); |
170 | | - }); |
| 164 | + const installBtn = document.getElementById('pwa-install-btn'); |
| 165 | + if (installBtn && !installBtn.dataset.listenerAttached) { |
| 166 | + installBtn.dataset.listenerAttached = 'true'; |
| 167 | + installBtn.addEventListener('click', async () => { |
| 168 | + if (!_deferredPrompt) return; |
| 169 | + _deferredPrompt.prompt(); |
| 170 | + const { outcome } = await _deferredPrompt.userChoice; |
| 171 | + _deferredPrompt = null; |
| 172 | + _hideInstallBanner(); |
| 173 | + console.log(`[PWA] Install prompt outcome: ${outcome}`); |
| 174 | + }); |
| 175 | + } |
171 | 176 |
|
172 | | - document.getElementById('pwa-dismiss-btn').addEventListener('click', () => { |
173 | | - sessionStorage.setItem('pwa-install-dismissed', '1'); |
174 | | - _hideInstallBanner(); |
175 | | - }); |
| 177 | + const dismissBtn = document.getElementById('pwa-dismiss-btn'); |
| 178 | + if (dismissBtn && !dismissBtn.dataset.listenerAttached) { |
| 179 | + dismissBtn.dataset.listenerAttached = 'true'; |
| 180 | + dismissBtn.addEventListener('click', () => { |
| 181 | + sessionStorage.setItem('pwa-install-dismissed', '1'); |
| 182 | + _hideInstallBanner(); |
| 183 | + }); |
| 184 | + } |
176 | 185 | } |
177 | 186 |
|
178 | 187 | function _hideInstallBanner() { |
|
0 commit comments