| import { useEffect, useMemo, useState } from 'react' |
|
|
| function isStandaloneDisplay() { |
| if (typeof window === 'undefined') return false |
| return ( |
| window.matchMedia?.('(display-mode: standalone)').matches || |
| window.navigator.standalone === true |
| ) |
| } |
|
|
| function detectPlatform() { |
| if (typeof navigator === 'undefined') return 'desktop' |
|
|
| const userAgent = navigator.userAgent || '' |
| const isIOS = |
| /iPad|iPhone|iPod/.test(userAgent) || |
| (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) |
|
|
| if (isIOS) return 'ios' |
| if (/Android/i.test(userAgent)) return 'android' |
| return 'desktop' |
| } |
|
|
| function getManualInstallInstructions(platform) { |
| if (platform === 'ios') { |
| return 'Open this app in Safari, tap Share, then choose Add to Home Screen.' |
| } |
| if (platform === 'android') { |
| return 'Open the browser menu and choose Install app or Add to Home screen.' |
| } |
| return 'Open the browser menu and choose Install OwnGPT, Install app, or Create shortcut.' |
| } |
|
|
| export function useInstallPrompt() { |
| const [deferredPrompt, setDeferredPrompt] = useState(null) |
| const [isInstalled, setIsInstalled] = useState(() => isStandaloneDisplay()) |
| const [platform, setPlatform] = useState(() => detectPlatform()) |
|
|
| useEffect(() => { |
| if (typeof window === 'undefined') return undefined |
|
|
| const handleBeforeInstallPrompt = (event) => { |
| event.preventDefault() |
| setDeferredPrompt(event) |
| } |
|
|
| const handleInstalled = () => { |
| setDeferredPrompt(null) |
| setIsInstalled(true) |
| } |
|
|
| const handleModeChange = () => { |
| setIsInstalled(isStandaloneDisplay()) |
| } |
|
|
| const mediaQuery = window.matchMedia?.('(display-mode: standalone)') |
| window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt) |
| window.addEventListener('appinstalled', handleInstalled) |
| mediaQuery?.addEventListener?.('change', handleModeChange) |
|
|
| setPlatform(detectPlatform()) |
| setIsInstalled(isStandaloneDisplay()) |
|
|
| return () => { |
| window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt) |
| window.removeEventListener('appinstalled', handleInstalled) |
| mediaQuery?.removeEventListener?.('change', handleModeChange) |
| } |
| }, []) |
|
|
| const manualInstructions = useMemo(() => getManualInstallInstructions(platform), [platform]) |
| const installState = isInstalled ? 'installed' : deferredPrompt ? 'ready' : 'manual' |
|
|
| const install = async () => { |
| if (isInstalled) { |
| return { status: 'installed', platform, instructions: manualInstructions } |
| } |
|
|
| if (!deferredPrompt) { |
| return { status: 'manual', platform, instructions: manualInstructions } |
| } |
|
|
| deferredPrompt.prompt() |
| const choice = await deferredPrompt.userChoice.catch(() => null) |
| setDeferredPrompt(null) |
|
|
| return { |
| status: choice?.outcome === 'accepted' ? 'accepted' : 'dismissed', |
| platform, |
| instructions: manualInstructions, |
| } |
| } |
|
|
| return { |
| install, |
| installState, |
| canPromptInstall: Boolean(deferredPrompt), |
| isInstalled, |
| platform, |
| manualInstructions, |
| } |
| } |
|
|