'use client'; import { useEffect, useState } from 'react'; // PWA install banner. Shown only after the user has completed ≥2 Reads — we // don't want to nag visitors before they've seen the product work. Trigger // counter is incremented elsewhere via incrementReadCount() in lib/reads.ts. type BeforeInstallPromptEvent = Event & { prompt: () => Promise; userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>; }; const READS_KEY = 'vyndr_reads_completed'; const DISMISSED_KEY = 'vyndr_install_dismissed'; const REQUIRED_READS = 2; const DISMISSAL_COOLDOWN_DAYS = 7; function isStandalone(): boolean { if (typeof window === 'undefined') return false; if (window.matchMedia('(display-mode: standalone)').matches) return true; // iOS Safari exposes navigator.standalone only for installed PWAs. const nav = window.navigator as Navigator & { standalone?: boolean }; return nav.standalone === true; } function isIOS(): boolean { if (typeof window === 'undefined') return false; const ua = window.navigator.userAgent; return /iPad|iPhone|iPod/.test(ua) && !(window as unknown as { MSStream?: unknown }).MSStream; } function readsCompleted(): number { if (typeof window === 'undefined') return 0; const raw = window.localStorage.getItem(READS_KEY); return raw ? parseInt(raw, 10) || 0 : 0; } function dismissedRecently(): boolean { if (typeof window === 'undefined') return false; const raw = window.localStorage.getItem(DISMISSED_KEY); if (!raw) return false; const ts = parseInt(raw, 10); if (!ts) return false; const ageDays = (Date.now() - ts) / (1000 * 60 * 60 * 24); return ageDays < DISMISSAL_COOLDOWN_DAYS; } export default function InstallPrompt() { const [deferred, setDeferred] = useState(null); const [visible, setVisible] = useState(false); const [iosHint, setIosHint] = useState(false); useEffect(() => { if (isStandalone()) return; if (readsCompleted() < REQUIRED_READS) return; if (dismissedRecently()) return; if (isIOS()) { // iOS doesn't fire beforeinstallprompt — show manual instructions. setIosHint(true); setVisible(true); return; } const onBeforeInstall = (e: Event) => { e.preventDefault(); setDeferred(e as BeforeInstallPromptEvent); setVisible(true); }; window.addEventListener('beforeinstallprompt', onBeforeInstall); return () => window.removeEventListener('beforeinstallprompt', onBeforeInstall); }, []); const handleInstall = async () => { if (!deferred) return; await deferred.prompt(); const choice = await deferred.userChoice; if (choice.outcome === 'dismissed') { window.localStorage.setItem(DISMISSED_KEY, String(Date.now())); } setVisible(false); setDeferred(null); }; const handleDismiss = () => { window.localStorage.setItem(DISMISSED_KEY, String(Date.now())); setVisible(false); }; if (!visible) return null; return (
Install VYNDR
{iosHint ? 'Tap the Share button, then "Add to Home Screen" for instant access.' : 'Add VYNDR to your home screen for instant access.'}
{!iosHint && ( )}
); }