// PWA install hint. Two flavours: // - Android Chrome: capture `beforeinstallprompt` and surface an install button // - iOS Safari: show a banner explaining "Share → Add to Home Screen" // Hidden once the app is launched in standalone mode, or after the user dismisses. (function () { const DISMISS_KEY = 'resdri:pwa-dismissed-at'; const DISMISS_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days function isStandalone() { return ( window.matchMedia && window.matchMedia('(display-mode: standalone)').matches ) || window.navigator.standalone === true; } function isIos() { return /iPhone|iPad|iPod/i.test(navigator.userAgent) && !window.MSStream; } function wasRecentlyDismissed() { const t = parseInt(localStorage.getItem(DISMISS_KEY) || '0', 10); return t && Date.now() - t < DISMISS_TTL_MS; } // ─ Android: capture event let deferredPrompt = null; window.addEventListener('beforeinstallprompt', (e) => { e.preventDefault(); deferredPrompt = e; window.dispatchEvent(new CustomEvent('resdri:install-available')); }); window.addEventListener('appinstalled', () => { deferredPrompt = null; localStorage.setItem(DISMISS_KEY, String(Date.now())); // stop reprompting }); // ─ Public API window.resdriPwa = { isStandalone, isIos, wasRecentlyDismissed, hasDeferredPrompt: () => !!deferredPrompt, async install() { if (!deferredPrompt) return false; deferredPrompt.prompt(); try { await deferredPrompt.userChoice; } catch {} deferredPrompt = null; return true; }, dismiss() { localStorage.setItem(DISMISS_KEY, String(Date.now())); }, }; })(); // ─ React component injected into the App tree function InstallHint() { const [available, setAvailable] = React.useState(false); const [dismissed, setDismissed] = React.useState(window.resdriPwa.wasRecentlyDismissed()); React.useEffect(() => { if (window.resdriPwa.isStandalone()) { setDismissed(true); return; } const onAvail = () => setAvailable(true); window.addEventListener('resdri:install-available', onAvail); // On iOS there's no beforeinstallprompt; surface the hint immediately. if (window.resdriPwa.isIos()) setAvailable(true); // Check after a moment in case the event already fired setTimeout(() => setAvailable(window.resdriPwa.hasDeferredPrompt() || window.resdriPwa.isIos()), 300); return () => window.removeEventListener('resdri:install-available', onAvail); }, []); if (dismissed || !available || window.resdriPwa.isStandalone()) return null; const ios = window.resdriPwa.isIos(); const dismiss = () => { window.resdriPwa.dismiss(); setDismissed(true); }; const install = async () => { const ok = await window.resdriPwa.install(); if (ok) setDismissed(true); }; return (