/** * Tarayıcının "uygulamayı yükle" akışı (PWA install). * * `beforeinstallprompt` sayfa yüklenirken erken tetiklenir; React ağacı mount * olmadan yakalanmazsa olay kaybolur ve buton hiç görünmez. Bu yüzden modül * import edilir edilmez dinleyiciler kurulur (dosya ~1 KB, entry'de kalması * sorun değil). */ type InstallPromptEvent = Event & { prompt: () => Promise userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }> } export type InstallState = { /** Tarayıcı kurulum teklifi verdi ve uygulama henüz kurulu değil. */ canInstall: boolean /** Uygulama standalone modda çalışıyor (yani kurulu). */ installed: boolean } let deferredPrompt: InstallPromptEvent | undefined const isStandalone = () => { try { return ( window.matchMedia('(display-mode: standalone)').matches || window.matchMedia('(display-mode: minimal-ui)').matches || (navigator as unknown as { standalone?: boolean }).standalone === true ) } catch { return false } } let state: InstallState = { canInstall: false, installed: isStandalone() } const listeners = new Set<(value: InstallState) => void>() export const getInstallState = () => state export const subscribeInstallState = (listener: (value: InstallState) => void) => { listeners.add(listener) return () => listeners.delete(listener) } function setState(patch: Partial) { state = { ...state, ...patch } listeners.forEach((listener) => listener(state)) } export const promptAppInstall = async (): Promise<'accepted' | 'dismissed' | 'unavailable'> => { const event = deferredPrompt if (!event) return 'unavailable' // Yakalanan olay tek kullanımlıktır. deferredPrompt = undefined setState({ canInstall: false }) try { await event.prompt() const { outcome } = await event.userChoice if (outcome === 'accepted') setState({ installed: true }) return outcome } catch (error) { console.warn('Uygulama kurulumu başlatılamadı.', error) return 'unavailable' } } if (typeof window !== 'undefined') { window.addEventListener('beforeinstallprompt', (event) => { // Varsayılan mini bar yerine uygulama içindeki butonu kullanıyoruz. event.preventDefault() deferredPrompt = event as InstallPromptEvent setState({ canInstall: !isStandalone(), installed: isStandalone() }) }) window.addEventListener('appinstalled', () => { deferredPrompt = undefined setState({ canInstall: false, installed: true }) }) window .matchMedia('(display-mode: standalone)') .addEventListener?.('change', (event) => setState({ installed: event.matches, canInstall: event.matches ? false : state.canInstall }), ) }