Files
padmaja/components/PWAInstallPrompt.tsx
2026-01-17 14:17:42 +05:30

225 lines
7.4 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import { useState, useEffect } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
import { X, Download, Smartphone } from 'lucide-react'
interface BeforeInstallPromptEvent extends Event {
prompt(): Promise<void>
userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>
}
export default function PWAInstallPrompt() {
const [deferredPrompt, setDeferredPrompt] = useState<BeforeInstallPromptEvent | null>(null)
const [showPrompt, setShowPrompt] = useState(false)
const [isInstalled, setIsInstalled] = useState(false)
useEffect(() => {
// Check if app is already installed
const checkInstalled = () => {
const isStandalone = window.matchMedia('(display-mode: standalone)').matches
const isInWebAppiOS = (window.navigator as any).standalone === true
const isAndroidInstalled = document.referrer.includes('android-app://')
setIsInstalled(isStandalone || isInWebAppiOS || isAndroidInstalled)
}
checkInstalled()
const handleBeforeInstallPrompt = (e: Event) => {
e.preventDefault()
setDeferredPrompt(e as BeforeInstallPromptEvent)
// Don't show prompt immediately, wait a bit for user engagement
setTimeout(() => {
if (!isInstalled && !localStorage.getItem('pwa-install-dismissed')) {
setShowPrompt(true)
}
}, 10000) // Show after 10 seconds
}
const handleAppInstalled = () => {
setShowPrompt(false)
setIsInstalled(true)
setDeferredPrompt(null)
}
window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt)
window.addEventListener('appinstalled', handleAppInstalled)
return () => {
window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt)
window.removeEventListener('appinstalled', handleAppInstalled)
}
}, [isInstalled])
const handleInstallClick = async () => {
if (!deferredPrompt) return
try {
await deferredPrompt.prompt()
const choiceResult = await deferredPrompt.userChoice
if (choiceResult.outcome === 'accepted') {
console.log('User accepted the install prompt')
} else {
console.log('User dismissed the install prompt')
}
setDeferredPrompt(null)
setShowPrompt(false)
} catch (error) {
console.error('Error showing install prompt:', error)
}
}
const handleDismiss = () => {
setShowPrompt(false)
localStorage.setItem('pwa-install-dismissed', 'true')
// Show again after 7 days
setTimeout(() => {
localStorage.removeItem('pwa-install-dismissed')
}, 7 * 24 * 60 * 60 * 1000)
}
if (isInstalled || !showPrompt || !deferredPrompt) {
return null
}
return (
<AnimatePresence>
<motion.div
initial={{ opacity: 0, y: 100 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 100 }}
className="fixed bottom-4 left-4 right-4 z-50 max-w-sm mx-auto"
>
<Card className="bg-gradient-to-r from-green-500 to-emerald-600 text-white border-0 shadow-2xl">
<CardContent className="p-4">
<div className="flex items-start space-x-3">
<div className="flex-shrink-0">
<div className="w-12 h-12 bg-white/20 rounded-full flex items-center justify-center">
<Smartphone className="h-6 w-6 text-white" />
</div>
</div>
<div className="flex-1 min-w-0">
<h3 className="text-sm font-semibold text-white mb-1">
Install Padmaaja Rasooi App
</h3>
<p className="text-xs text-green-100 mb-3">
Get the full experience with offline access, faster loading, and push notifications.
</p>
<div className="flex space-x-2">
<Button
onClick={handleInstallClick}
size="sm"
variant="secondary"
className="flex-1 text-green-700 hover:text-green-800"
>
<Download className="h-3 w-3 mr-1" />
Install
</Button>
<Button
onClick={handleDismiss}
size="sm"
variant="ghost"
className="text-white hover:bg-white/20"
>
Later
</Button>
</div>
</div>
<Button
onClick={handleDismiss}
size="sm"
variant="ghost"
className="flex-shrink-0 text-white hover:bg-white/20 p-1"
>
<X className="h-4 w-4" />
</Button>
</div>
</CardContent>
</Card>
</motion.div>
</AnimatePresence>
)
}
// Alternative iOS Safari install instructions
export function IOSInstallPrompt() {
const [showIOS, setShowIOS] = useState(false)
useEffect(() => {
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent)
const isInStandaloneMode = (window.navigator as any).standalone === true
const hasPromptBeenShown = localStorage.getItem('ios-install-prompt-shown')
if (isIOS && !isInStandaloneMode && !hasPromptBeenShown) {
setTimeout(() => setShowIOS(true), 15000) // Show after 15 seconds on iOS
}
}, [])
const handleDismiss = () => {
setShowIOS(false)
localStorage.setItem('ios-install-prompt-shown', 'true')
}
if (!showIOS) return null
return (
<AnimatePresence>
<motion.div
initial={{ opacity: 0, y: 100 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 100 }}
className="fixed bottom-4 left-4 right-4 z-50 max-w-sm mx-auto"
>
<Card className="bg-blue-600 text-white border-0 shadow-2xl">
<CardContent className="p-4">
<div className="flex items-start space-x-3">
<div className="flex-shrink-0">
<div className="w-12 h-12 bg-white/20 rounded-full flex items-center justify-center">
<Smartphone className="h-6 w-6 text-white" />
</div>
</div>
<div className="flex-1 min-w-0">
<h3 className="text-sm font-semibold text-white mb-1">
Add to Home Screen
</h3>
<p className="text-xs text-blue-100 mb-2">
Tap the share button <span className="inline-block w-4 h-4 bg-white/20 rounded text-center text-xs"></span> below and select "Add to Home Screen"
</p>
<Button
onClick={handleDismiss}
size="sm"
variant="secondary"
className="text-blue-700 hover:text-blue-800"
>
Got it
</Button>
</div>
<Button
onClick={handleDismiss}
size="sm"
variant="ghost"
className="flex-shrink-0 text-white hover:bg-white/20 p-1"
>
<X className="h-4 w-4" />
</Button>
</div>
</CardContent>
</Card>
</motion.div>
</AnimatePresence>
)
}