sozsoft-platform/ui/src/views/version/installPrompt.ts

88 lines
2.7 KiB
TypeScript
Raw Normal View History

2026-08-12 19:08:14 +00:00
/**
* 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<void>
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<InstallState>) {
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 }),
)
}