import { useState, useEffect } from 'react' interface BeforeInstallPromptEvent extends Event { prompt: () => Promise userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }> } interface PWAInstall { canInstall: boolean // Android/Chrome: native prompt available isIOS: boolean // iOS Safari: must show manual instructions isInstalled: boolean // Already running as installed PWA triggerInstall: () => Promise } export function usePWAInstall(): PWAInstall { const [deferredPrompt, setDeferredPrompt] = useState(null) const [isInstalled, setIsInstalled] = useState(false) const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !(window as unknown as { MSStream?: unknown }).MSStream useEffect(() => { // Detect if already installed (standalone mode) const mq = window.matchMedia('(display-mode: standalone)') const iosStandalone = (navigator as unknown as { standalone?: boolean }).standalone === true if (mq.matches || iosStandalone) { setIsInstalled(true) return } const handler = (e: Event) => { e.preventDefault() setDeferredPrompt(e as BeforeInstallPromptEvent) } window.addEventListener('beforeinstallprompt', handler) window.addEventListener('appinstalled', () => { setIsInstalled(true) setDeferredPrompt(null) }) return () => window.removeEventListener('beforeinstallprompt', handler) }, []) const triggerInstall = async () => { if (!deferredPrompt) return await deferredPrompt.prompt() const { outcome } = await deferredPrompt.userChoice if (outcome === 'accepted') { setIsInstalled(true) setDeferredPrompt(null) } } return { canInstall: !!deferredPrompt, isIOS, isInstalled, triggerInstall, } }