'use client' import { useEffect, useState } from 'react' import { useTranslations } from 'next-intl' interface ServiceWorkerContextType { isInstalling: boolean isInstalled: boolean isUpdateAvailable: boolean updateServiceWorker: () => Promise installPrompt: () => Promise canInstall: boolean } export function ServiceWorkerProvider({ children }: { children: React.ReactNode }) { const [isInstalling, setIsInstalling] = useState(false) const [isInstalled, setIsInstalled] = useState(false) const [isUpdateAvailable, setIsUpdateAvailable] = useState(false) const [canInstall, setCanInstall] = useState(false) const [deferredPrompt, setDeferredPrompt] = useState(null) const [registration, setRegistration] = useState(null) const t = useTranslations('pwa') useEffect(() => { if (typeof window === 'undefined' || !('serviceWorker' in navigator)) { console.log('[PWA] Service Worker not supported') return } registerServiceWorker() setupInstallPrompt() // Cleanup function return () => { if (registration) { registration.removeEventListener('updatefound', handleUpdateFound) } } }, []) const registerServiceWorker = async () => { try { setIsInstalling(true) console.log('[PWA] Registering service worker...') const reg = await navigator.serviceWorker.register('/sw.js', { scope: '/', updateViaCache: 'none' }) setRegistration(reg) setIsInstalled(true) console.log('[PWA] Service worker registered successfully') // Check for updates reg.addEventListener('updatefound', handleUpdateFound) // Check if there's already an update waiting if (reg.waiting) { setIsUpdateAvailable(true) } // Listen for controlling service worker changes navigator.serviceWorker.addEventListener('controllerchange', () => { console.log('[PWA] Service worker controller changed') window.location.reload() }) } catch (error) { console.error('[PWA] Service worker registration failed:', error) } finally { setIsInstalling(false) } } const handleUpdateFound = () => { if (!registration) return const newWorker = registration.installing if (!newWorker) return console.log('[PWA] New service worker installing...') newWorker.addEventListener('statechange', () => { if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { console.log('[PWA] New service worker installed, update available') setIsUpdateAvailable(true) } }) } const updateServiceWorker = async () => { if (!registration || !registration.waiting) { console.log('[PWA] No update available') return } console.log('[PWA] Updating service worker...') registration.waiting.postMessage({ type: 'SKIP_WAITING' }) setIsUpdateAvailable(false) } const setupInstallPrompt = () => { if (typeof window === 'undefined') return // Listen for beforeinstallprompt event window.addEventListener('beforeinstallprompt', (e) => { console.log('[PWA] Install prompt available') e.preventDefault() setDeferredPrompt(e) setCanInstall(true) }) // Listen for app installed event window.addEventListener('appinstalled', () => { console.log('[PWA] App installed') setCanInstall(false) setDeferredPrompt(null) showNotification('App installed successfully!', 'success') }) // Check if already installed if (window.matchMedia && window.matchMedia('(display-mode: standalone)').matches) { console.log('[PWA] App is running in standalone mode') setCanInstall(false) } } const installPrompt = async () => { if (!deferredPrompt) { console.log('[PWA] No install prompt available') return } try { console.log('[PWA] Showing install prompt...') const result = await deferredPrompt.prompt() console.log('[PWA] Install prompt result:', result.outcome) if (result.outcome === 'accepted') { setCanInstall(false) setDeferredPrompt(null) } } catch (error) { console.error('[PWA] Install prompt failed:', error) } } const showNotification = (message: string, type: 'success' | 'error' | 'info' = 'info') => { // Show a toast notification (you can integrate with your existing notification system) console.log(`[PWA] ${type.toUpperCase()}: ${message}`) // Create a simple toast const toast = document.createElement('div') toast.style.cssText = ` position: fixed; top: 20px; right: 20px; background: ${type === 'success' ? '#4CAF50' : type === 'error' ? '#f44336' : '#2196F3'}; color: white; padding: 12px 20px; border-radius: 8px; z-index: 10000; font-family: system-ui, -apple-system, sans-serif; box-shadow: 0 4px 12px rgba(0,0,0,0.2); animation: slideIn 0.3s ease; ` toast.textContent = message // Add animation keyframes if not already added if (!document.querySelector('#pwa-toast-styles')) { const style = document.createElement('style') style.id = 'pwa-toast-styles' style.textContent = ` @keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @keyframes slideOut { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } } ` document.head.appendChild(style) } document.body.appendChild(toast) // Remove after 4 seconds setTimeout(() => { toast.style.animation = 'slideOut 0.3s ease' setTimeout(() => { document.body.removeChild(toast) }, 300) }, 4000) } // Provide context value const contextValue: ServiceWorkerContextType = { isInstalling, isInstalled, isUpdateAvailable, updateServiceWorker, installPrompt, canInstall } // For now, just provide the functions globally // You can later create a proper React context if needed useEffect(() => { if (typeof window !== 'undefined') { (window as any).pwa = contextValue } }, [contextValue]) return <>{children} } // Export hook for using PWA functionality export function usePWA(): ServiceWorkerContextType { if (typeof window === 'undefined') { return { isInstalling: false, isInstalled: false, isUpdateAvailable: false, updateServiceWorker: async () => {}, installPrompt: async () => {}, canInstall: false } } return (window as any).pwa || { isInstalling: false, isInstalled: false, isUpdateAvailable: false, updateServiceWorker: async () => {}, installPrompt: async () => {}, canInstall: false } }