| 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 → PWA install (1-tap if available, else Chrome ⋮-menu instructions)
|
|---|
| 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>
|
|---|
| 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 |
|
|---|
| 33 | // Capture Chrome/Edge native install prompt so we can fire it from our modal.
|
|---|
| 34 | let deferredPrompt = null;
|
|---|
| 35 | window.addEventListener('beforeinstallprompt', (e) => {
|
|---|
| 36 | e.preventDefault();
|
|---|
| 37 | deferredPrompt = e;
|
|---|
| 38 | btn.classList.add('has-native-prompt');
|
|---|
| 39 | });
|
|---|
| 40 | window.addEventListener('appinstalled', () => {
|
|---|
| 41 | deferredPrompt = null;
|
|---|
| 42 | btn.style.display = 'none';
|
|---|
| 43 | });
|
|---|
| 44 |
|
|---|
| 45 | function showModal(innerHTML) {
|
|---|
| 46 | const m = document.createElement('div');
|
|---|
| 47 | m.className = 'install-modal';
|
|---|
| 48 | m.innerHTML = `
|
|---|
| 49 | <div class="install-modal-backdrop"></div>
|
|---|
| 50 | <div class="install-modal-panel" role="dialog" aria-labelledby="install-title" aria-modal="true">
|
|---|
| 51 | ${innerHTML}
|
|---|
| 52 | <div class="install-modal-actions">
|
|---|
| 53 | <button type="button" class="btn btn-ghost" data-close>Sluiten</button>
|
|---|
| 54 | </div>
|
|---|
| 55 | </div>
|
|---|
| 56 | `;
|
|---|
| 57 | document.body.appendChild(m);
|
|---|
| 58 | const close = () => m.remove();
|
|---|
| 59 | m.querySelector('.install-modal-backdrop').addEventListener('click', close);
|
|---|
| 60 | m.querySelector('[data-close]').addEventListener('click', close);
|
|---|
| 61 | document.addEventListener('keydown', function onEsc(e) {
|
|---|
| 62 | if (e.key === 'Escape') { close(); document.removeEventListener('keydown', onEsc); }
|
|---|
| 63 | });
|
|---|
| 64 |
|
|---|
| 65 | // If a [data-install-pwa] button is in the modal AND a deferredPrompt is
|
|---|
| 66 | // available: clicking triggers the native install dialog.
|
|---|
| 67 | const pwaBtn = m.querySelector('[data-install-pwa]');
|
|---|
| 68 | if (pwaBtn) {
|
|---|
| 69 | pwaBtn.addEventListener('click', () => {
|
|---|
| 70 | if (!deferredPrompt) { close(); return; }
|
|---|
| 71 | deferredPrompt.prompt();
|
|---|
| 72 | deferredPrompt.userChoice.finally(() => {
|
|---|
| 73 | deferredPrompt = null;
|
|---|
| 74 | close();
|
|---|
| 75 | });
|
|---|
| 76 | });
|
|---|
| 77 | }
|
|---|
| 78 | }
|
|---|
| 79 |
|
|---|
| 80 | function iosInstructions() {
|
|---|
| 81 | showModal(`
|
|---|
| 82 | <h3 id="install-title" style="margin:0 0 .5rem">📲 Installeer op iOS</h3>
|
|---|
| 83 | <p style="margin:0 0 1rem;opacity:.8">Om deze site als app op je home-scherm te zetten:</p>
|
|---|
| 84 | <ol class="install-steps">
|
|---|
| 85 | <li>Tik op het <strong>deel-icoon</strong>
|
|---|
| 86 | <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>
|
|---|
| 87 | onderaan in Safari
|
|---|
| 88 | </li>
|
|---|
| 89 | <li>Scroll tot je <strong>"Voeg toe aan beginscherm"</strong> ziet (soms achter "Meer…")</li>
|
|---|
| 90 | <li>Geef de app eventueel een naam en tik op <strong>Voeg toe</strong></li>
|
|---|
| 91 | </ol>
|
|---|
| 92 | <p style="margin:1rem 0 0;opacity:.7;font-size:.85rem">⚠️ Werkt alleen in Safari — niet in Chrome op iOS (Apple-beleid).</p>
|
|---|
| 93 | `);
|
|---|
| 94 | }
|
|---|
| 95 |
|
|---|
| 96 | function androidInstructions() {
|
|---|
| 97 | const nativeLine = deferredPrompt
|
|---|
| 98 | ? `<button type="button" class="install-choice-cta" data-install-pwa>📱 Installeer als PWA (1 tik)</button>`
|
|---|
| 99 | : `<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>`;
|
|---|
| 100 |
|
|---|
| 101 | showModal(`
|
|---|
| 102 | <h3 id="install-title" style="margin:0 0 .5rem">🤖 Installeer op Android</h3>
|
|---|
| 103 | <p style="margin:0 0 1rem;opacity:.8">Voeg deze site als app toe aan je beginscherm:</p>
|
|---|
| 104 | <ul class="install-steps" style="opacity:.85">
|
|---|
| 105 | <li>Instant — geen download of toestemmingen</li>
|
|---|
| 106 | <li>Updatet automatisch bij elke wijziging</li>
|
|---|
| 107 | <li>Minder opslag dan een losse app</li>
|
|---|
| 108 | </ul>
|
|---|
| 109 | ${nativeLine}
|
|---|
| 110 | <p style="margin:1rem 0 0;opacity:.7;font-size:.85rem">⚠️ Werkt het best in Chrome.</p>
|
|---|
| 111 | `);
|
|---|
| 112 | }
|
|---|
| 113 |
|
|---|
| 114 | function desktopInstructions() {
|
|---|
| 115 | if (deferredPrompt) {
|
|---|
| 116 | deferredPrompt.prompt();
|
|---|
| 117 | deferredPrompt.userChoice.finally(() => { deferredPrompt = null; });
|
|---|
| 118 | return;
|
|---|
| 119 | }
|
|---|
| 120 | showModal(`
|
|---|
| 121 | <h3 id="install-title" style="margin:0 0 .5rem">💻 Installeer op desktop</h3>
|
|---|
| 122 | <p style="margin:0 0 1rem;opacity:.8">In Chrome, Edge of een andere Chromium-browser:</p>
|
|---|
| 123 | <ol class="install-steps">
|
|---|
| 124 | <li>Zoek het <strong>installeer-icoon</strong>
|
|---|
| 125 | <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>
|
|---|
| 126 | in de adresbalk</li>
|
|---|
| 127 | <li>Klik erop en bevestig</li>
|
|---|
| 128 | </ol>
|
|---|
| 129 | <p style="margin:1rem 0 0;opacity:.7;font-size:.85rem">Firefox/Safari desktop ondersteunen PWA-install niet — gebruik een bookmark.</p>
|
|---|
| 130 | `);
|
|---|
| 131 | }
|
|---|
| 132 |
|
|---|
| 133 | btn.addEventListener('click', () => {
|
|---|
| 134 | if (isIOS) return iosInstructions();
|
|---|
| 135 | if (isAndroid) return androidInstructions();
|
|---|
| 136 | return desktopInstructions();
|
|---|
| 137 | });
|
|---|
| 138 | })();
|
|---|