source: Klonkt/src/assets/js/install-app.js@ 46f23fd

main
Last change on this file since 46f23fd was 7bc636b, checked in by Robin <robin@…>, 4 months ago

Initial commit — PrutFolio v1 source (pulled from Hetzner /srv/prutfolio)

  • Property mode set to 100644
File size: 8.3 KB
RevLine 
[7bc636b]1/**
2 * install-app.js — handles the "Installeer app" button in the footer.
3 *
4 * Behavior:
5 * - Hides itself if the user is already running the site as an installed PWA
6 * (display-mode: standalone) or iOS web-app.
7 * - On click, opens a modal with platform-specific instructions:
8 * iOS → "Voeg toe aan beginscherm" via Safari share sheet
9 * Android → choice between PWA install (1-tap if available) and APK download
10 * Desktop → instructions for Chrome/Edge install icon, or native prompt if available
11 * - Listens for `beforeinstallprompt` (Android Chrome / desktop Chromium) and
12 * deferreds it so the modal's "Install as PWA" button can fire it.
13 *
14 * Markup contract: <button data-pcms-install-app data-apk-url="/path/to.apk">
15 *
16 * CSS: .install-app-btn / .install-modal* / .install-choice* — defined in
17 * style.css (carried over from v9 import).
18 */
19(function() {
20 'use strict';
21 const btn = document.querySelector('[data-pcms-install-app]');
22 if (!btn) return;
23
24 // Already running standalone → no point showing the button.
25 const isStandalone = window.matchMedia('(display-mode: standalone)').matches
26 || window.navigator.standalone === true;
27 if (isStandalone) { btn.style.display = 'none'; return; }
28
29 const ua = navigator.userAgent;
30 const isIOS = /iPad|iPhone|iPod/.test(ua) && !window.MSStream;
31 const isAndroid = /Android/.test(ua);
32 const apkUrl = btn.dataset.apkUrl || '';
33
34 // Capture Chrome/Edge native install prompt so we can fire it from our modal.
35 let deferredPrompt = null;
36 window.addEventListener('beforeinstallprompt', (e) => {
37 e.preventDefault();
38 deferredPrompt = e;
39 btn.classList.add('has-native-prompt');
40 });
41 window.addEventListener('appinstalled', () => {
42 deferredPrompt = null;
43 btn.style.display = 'none';
44 });
45
46 function showModal(innerHTML) {
47 const m = document.createElement('div');
48 m.className = 'install-modal';
49 m.innerHTML = `
50 <div class="install-modal-backdrop"></div>
51 <div class="install-modal-panel" role="dialog" aria-labelledby="install-title" aria-modal="true">
52 ${innerHTML}
53 <div class="install-modal-actions">
54 <button type="button" class="btn btn-ghost" data-close>Sluiten</button>
55 </div>
56 </div>
57 `;
58 document.body.appendChild(m);
59 const close = () => m.remove();
60 m.querySelector('.install-modal-backdrop').addEventListener('click', close);
61 m.querySelector('[data-close]').addEventListener('click', close);
62 document.addEventListener('keydown', function onEsc(e) {
63 if (e.key === 'Escape') { close(); document.removeEventListener('keydown', onEsc); }
64 });
65
66 // If a [data-install-pwa] button is in the modal AND a deferredPrompt is
67 // available: clicking triggers the native install dialog.
68 const pwaBtn = m.querySelector('[data-install-pwa]');
69 if (pwaBtn) {
70 pwaBtn.addEventListener('click', () => {
71 if (!deferredPrompt) { close(); return; }
72 deferredPrompt.prompt();
73 deferredPrompt.userChoice.finally(() => {
74 deferredPrompt = null;
75 close();
76 });
77 });
78 }
79 }
80
81 function iosInstructions() {
82 showModal(`
83 <h3 id="install-title" style="margin:0 0 .5rem">📲 Installeer op iOS</h3>
84 <p style="margin:0 0 1rem;opacity:.8">Om deze site als app op je home-scherm te zetten:</p>
85 <ol class="install-steps">
86 <li>Tik op het <strong>deel-icoon</strong>
87 <svg width="16" height="16" viewBox="0 0 24 24" style="vertical-align:middle;margin:0 2px"><path d="M12 2L8 6h3v9h2V6h3l-4-4zm7 10v8H5v-8H3v8a2 2 0 002 2h14a2 2 0 002-2v-8h-2z" fill="currentColor"/></svg>
88 onderaan in Safari
89 </li>
90 <li>Scroll tot je <strong>"Voeg toe aan beginscherm"</strong> ziet (soms achter "Meer…")</li>
91 <li>Geef de app eventueel een naam en tik op <strong>Voeg toe</strong></li>
92 </ol>
93 <p style="margin:1rem 0 0;opacity:.7;font-size:.85rem">⚠️ Werkt alleen in Safari — niet in Chrome op iOS (Apple-beleid).</p>
94 `);
95 }
96
97 function androidInstructions() {
98 const nativeLine = deferredPrompt
99 ? `<button type="button" class="install-choice-cta" data-install-pwa>📱 Installeer als PWA (1 tik)</button>`
100 : `<p class="install-choice-note">Open deze site in <strong>Chrome</strong> en kies in het <strong>⋮-menu → "App installeren"</strong> (of "Aan beginscherm toevoegen").</p>`;
101
102 const apkBlock = apkUrl ? `
103 <div class="install-choice install-choice-apk">
104 <h4>🤖 Optie B — APK (native Android-app)</h4>
105 <div class="install-pros-cons">
106 <div class="install-pros">
107 <strong>Voordelen</strong>
108 <ul>
109 <li>Echte app in app-lade, naast WhatsApp/Gmail</li>
110 <li>Beste achtergrond-audio ondersteuning</li>
111 <li>Lock-screen controls werken optimaal</li>
112 <li>Blijft geïnstalleerd ongeacht browser-cache</li>
113 </ul>
114 </div>
115 <div class="install-cons">
116 <strong>Nadelen</strong>
117 <ul>
118 <li>Moet eenmalig "onbekende bronnen" toestaan</li>
119 <li>Geen auto-updates — nieuwe versie = APK opnieuw downloaden</li>
120 <li>Browser toont beveiligings-waarschuwing bij install</li>
121 <li>Alleen Android (Android 5+ / API 21+)</li>
122 </ul>
123 </div>
124 </div>
125 <a href="${apkUrl}" class="install-choice-cta" download>
126 <svg width="18" height="18" viewBox="0 0 24 24" style="vertical-align:middle;margin-right:.35rem"><path d="M5 20h14v-2H5v2zM19 9h-4V3H9v6H5l7 7 7-7z" fill="currentColor"/></svg>
127 Download APK
128 </a>
129 <ol class="install-steps" style="margin-top:.75rem">
130 <li>Tik op de gedownloade APK</li>
131 <li>Sta eenmalig <strong>"installeren van onbekende bronnen"</strong> toe voor je browser</li>
132 <li>Tik <strong>Installeer</strong> in de popup</li>
133 <li>Open de app vanuit je app-lade</li>
134 </ol>
135 </div>
136 ` : '';
137
138 showModal(`
139 <h3 id="install-title" style="margin:0 0 .5rem">🤖 Installeer op Android</h3>
140 <p style="margin:0 0 1.25rem;opacity:.8">Twee manieren. Kies wat bij je past:</p>
141
142 <div class="install-choice install-choice-pwa">
143 <h4>📱 Optie A — Home-screen shortcut (PWA)</h4>
144 <div class="install-pros-cons">
145 <div class="install-pros">
146 <strong>Voordelen</strong>
147 <ul>
148 <li>Instant — geen download of toestemmingen</li>
149 <li>Updates automatisch bij elke site-wijziging</li>
150 <li>Geen beveiligings-waarschuwing</li>
151 <li>Minder opslagruimte op je telefoon</li>
152 </ul>
153 </div>
154 <div class="install-cons">
155 <strong>Nadelen</strong>
156 <ul>
157 <li>Vereist Chrome als browser</li>
158 <li>Audio-in-achtergrond iets minder robuust dan native</li>
159 <li>Kan verdwijnen als je browser-data wist</li>
160 </ul>
161 </div>
162 </div>
163 ${nativeLine}
164 </div>
165
166 ${apkBlock}
167
168 <p style="margin:1rem 0 0;opacity:.65;font-size:.82rem">💡 Weet je niet wat te kiezen? Begin met <strong>Optie A (shortcut)</strong>. Te beperkt? Ga dan voor de APK.</p>
169 `);
170 }
171
172 function desktopInstructions() {
173 if (deferredPrompt) {
174 deferredPrompt.prompt();
175 deferredPrompt.userChoice.finally(() => { deferredPrompt = null; });
176 return;
177 }
178 showModal(`
179 <h3 id="install-title" style="margin:0 0 .5rem">💻 Installeer op desktop</h3>
180 <p style="margin:0 0 1rem;opacity:.8">In Chrome, Edge of een andere Chromium-browser:</p>
181 <ol class="install-steps">
182 <li>Zoek het <strong>installeer-icoon</strong>
183 <svg width="16" height="16" viewBox="0 0 24 24" style="vertical-align:middle;margin:0 2px"><path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z" fill="currentColor"/></svg>
184 in de adresbalk</li>
185 <li>Klik erop en bevestig</li>
186 </ol>
187 <p style="margin:1rem 0 0;opacity:.7;font-size:.85rem">Firefox/Safari desktop ondersteunen PWA-install niet — gebruik een bookmark.</p>
188 `);
189 }
190
191 btn.addEventListener('click', () => {
192 if (isIOS) return iosInstructions();
193 if (isAndroid) return androidInstructions();
194 return desktopInstructions();
195 });
196})();
Note: See TracBrowser for help on using the repository browser.