UNPKG

react-pwa-hazhtech

Version:

A React library for Progressive Web App (PWA) functionality with install prompts and status tracking

1 lines 11.1 kB
{"version":3,"sources":["../src/PWAProvider.tsx","../src/usePWA.ts"],"names":["PWAContext","createContext","PWAProvider","children","onInstallPromptAvailable","onAppInstalled","deferredPrompt","setDeferredPrompt","useState","isInstalled","setIsInstalled","isInstallable","setIsInstallable","isStandalone","setIsStandalone","useEffect","checkStandalone","standalone","mediaQuery","handleChange","handleBeforeInstall","e","handleInstalled","promptInstall","useCallback","result","error","jsx","triggerPWAInstall","resolve","isPWAInstallAvailable","isPWAMode","usePWA","useContext"],"mappings":"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","file":"index.mjs","sourcesContent":["import React, { createContext, useEffect, useState, useCallback } from 'react';\n\nexport interface PWAContextProps {\n isInstalled: boolean;\n isInstallable: boolean;\n promptInstall: () => Promise<{ outcome: 'accepted' | 'dismissed' } | null>;\n installPromptEvent: BeforeInstallPromptEvent | null;\n isStandalone: boolean;\n}\n\nexport const PWAContext = createContext<PWAContextProps>({\n isInstalled: false,\n isInstallable: false,\n promptInstall: async () => null,\n installPromptEvent: null,\n isStandalone: false,\n});\n\ndeclare global {\n interface WindowEventMap {\n beforeinstallprompt: BeforeInstallPromptEvent;\n appinstalled: Event;\n }\n \n interface BeforeInstallPromptEvent extends Event {\n prompt: () => Promise<void>;\n userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>;\n }\n}\n\nexport interface PWAProviderProps {\n children: React.ReactNode;\n onInstallPromptAvailable?: (event: BeforeInstallPromptEvent) => void;\n onAppInstalled?: () => void;\n}\n\nexport const PWAProvider: React.FC<PWAProviderProps> = ({ \n children, \n onInstallPromptAvailable,\n onAppInstalled \n}) => {\n const [deferredPrompt, setDeferredPrompt] = useState<BeforeInstallPromptEvent | null>(null);\n const [isInstalled, setIsInstalled] = useState<boolean>(false);\n const [isInstallable, setIsInstallable] = useState<boolean>(false);\n const [isStandalone, setIsStandalone] = useState<boolean>(false);\n\n // Check if app is running in standalone mode\n useEffect(() => {\n const checkStandalone = () => {\n const standalone = window.matchMedia('(display-mode: standalone)').matches ||\n (window.navigator as any).standalone ||\n document.referrer.includes('android-app://');\n setIsStandalone(standalone);\n setIsInstalled(standalone);\n };\n\n checkStandalone();\n \n // Listen for changes in display mode\n const mediaQuery = window.matchMedia('(display-mode: standalone)');\n const handleChange = () => checkStandalone();\n \n if (mediaQuery.addEventListener) {\n mediaQuery.addEventListener('change', handleChange);\n } else {\n // Fallback for older browsers\n mediaQuery.addListener(handleChange);\n }\n\n return () => {\n if (mediaQuery.removeEventListener) {\n mediaQuery.removeEventListener('change', handleChange);\n } else {\n mediaQuery.removeListener(handleChange);\n }\n };\n }, []);\n\n useEffect(() => {\n const handleBeforeInstall = (e: BeforeInstallPromptEvent) => {\n e.preventDefault();\n setDeferredPrompt(e);\n setIsInstallable(true);\n onInstallPromptAvailable?.(e);\n };\n\n const handleInstalled = () => {\n setIsInstalled(true);\n setIsInstallable(false);\n setDeferredPrompt(null);\n onAppInstalled?.();\n };\n\n window.addEventListener('beforeinstallprompt', handleBeforeInstall);\n window.addEventListener('appinstalled', handleInstalled);\n\n return () => {\n window.removeEventListener('beforeinstallprompt', handleBeforeInstall);\n window.removeEventListener('appinstalled', handleInstalled);\n };\n }, [onInstallPromptAvailable, onAppInstalled]);\n\n const promptInstall = useCallback(async (): Promise<{ outcome: 'accepted' | 'dismissed' } | null> => {\n if (!deferredPrompt) {\n console.warn('Install prompt is not available. Make sure the app meets PWA criteria.');\n return null;\n }\n\n try {\n await deferredPrompt.prompt();\n const result = await deferredPrompt.userChoice;\n \n if (result.outcome === 'accepted') {\n setIsInstalled(true);\n setIsInstallable(false);\n }\n \n setDeferredPrompt(null);\n return result;\n } catch (error) {\n console.error('Error during install prompt:', error);\n throw error;\n }\n }, [deferredPrompt]);\n\n return (\n <PWAContext.Provider value={{ \n isInstalled, \n isInstallable,\n promptInstall, \n installPromptEvent: deferredPrompt,\n isStandalone\n }}>\n {children}\n </PWAContext.Provider>\n );\n};\n\n// Utility function to trigger PWA install without using the hook\nexport const triggerPWAInstall = async (): Promise<{ outcome: 'accepted' | 'dismissed' } | null> => {\n // Check if we're in a browser environment\n if (typeof window === 'undefined') {\n console.warn('triggerPWAInstall: Not in browser environment');\n return null;\n }\n\n // Check if the app is already installed\n const isStandalone = window.matchMedia('(display-mode: standalone)').matches ||\n (window.navigator as any).standalone ||\n document.referrer.includes('android-app://');\n \n if (isStandalone) {\n console.warn('triggerPWAInstall: App is already installed');\n return null;\n }\n\n return new Promise((resolve) => {\n const handleBeforeInstall = async (e: BeforeInstallPromptEvent) => {\n e.preventDefault();\n \n try {\n await e.prompt();\n const result = await e.userChoice;\n resolve(result);\n } catch (error) {\n console.error('Error during install prompt:', error);\n resolve(null);\n }\n };\n\n // Check if there's already a deferred prompt available\n if ((window as any).deferredPrompt) {\n handleBeforeInstall((window as any).deferredPrompt);\n } else {\n // Listen for the beforeinstallprompt event\n window.addEventListener('beforeinstallprompt', handleBeforeInstall, { once: true });\n \n // Timeout after 5 seconds if no install prompt is available\n setTimeout(() => {\n window.removeEventListener('beforeinstallprompt', handleBeforeInstall);\n console.warn('triggerPWAInstall: Install prompt not available within timeout');\n resolve(null);\n }, 5000);\n }\n });\n};\n\n// Utility function to check if PWA install is available\nexport const isPWAInstallAvailable = (): boolean => {\n if (typeof window === 'undefined') return false;\n \n const isStandalone = window.matchMedia('(display-mode: standalone)').matches ||\n (window.navigator as any).standalone ||\n document.referrer.includes('android-app://');\n \n return !isStandalone && (\n (window as any).deferredPrompt !== undefined ||\n 'serviceWorker' in navigator\n );\n};\n\n// Utility function to check if app is running as PWA\nexport const isPWAMode = (): boolean => {\n if (typeof window === 'undefined') return false;\n \n return window.matchMedia('(display-mode: standalone)').matches ||\n (window.navigator as any).standalone ||\n document.referrer.includes('android-app://');\n};","import { useContext } from 'react';\nimport { PWAContext } from './PWAProvider';\n\nexport const usePWA = () => useContext(PWAContext);"]}