Files
chinese-family-tree-2/components/pwa/pwa-installer.tsx
T
freedakgmail c033e46782 0.0.0.4
2025-11-22 23:49:24 +08:00

147 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. 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 { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Download, X, Smartphone } from "lucide-react"
import { Alert, AlertDescription } from "@/components/ui/alert"
export function PWAInstaller() {
const [deferredPrompt, setDeferredPrompt] = useState<any>(null)
const [isInstallable, setIsInstallable] = useState(false)
const [isInstalled, setIsInstalled] = useState(false)
const [showPrompt, setShowPrompt] = useState(false)
useEffect(() => {
// 检查是否已安装
if (typeof window !== "undefined") {
const isStandalone = window.matchMedia('(display-mode: standalone)').matches
const isIOSStandalone = (window.navigator as any).standalone === true
if (isStandalone || isIOSStandalone) {
setIsInstalled(true)
return
}
}
// 监听 beforeinstallprompt 事件
const handleBeforeInstallPrompt = (e: Event) => {
e.preventDefault()
setDeferredPrompt(e)
setIsInstallable(true)
// 延迟显示提示(给用户一些时间浏览应用)
setTimeout(() => {
const hasSeenPrompt = localStorage.getItem('pwa-install-prompt-seen')
if (!hasSeenPrompt) {
setShowPrompt(true)
}
}, 30000) // 30秒后显示
}
// 监听安装完成事件
const handleAppInstalled = () => {
setIsInstalled(true)
setShowPrompt(false)
setIsInstallable(false)
}
if (typeof window !== "undefined") {
window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt)
window.addEventListener('appinstalled', handleAppInstalled)
return () => {
window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt)
window.removeEventListener('appinstalled', handleAppInstalled)
}
}
}, [])
const handleInstallClick = async () => {
if (!deferredPrompt) return
// 显示安装提示
deferredPrompt.prompt()
// 等待用户响应
const { outcome } = await deferredPrompt.userChoice
if (outcome === 'accepted') {
console.log('用户接受了安装')
} else {
console.log('用户拒绝了安装')
}
setDeferredPrompt(null)
setShowPrompt(false)
localStorage.setItem('pwa-install-prompt-seen', 'true')
}
const handleDismiss = () => {
setShowPrompt(false)
localStorage.setItem('pwa-install-prompt-seen', 'true')
}
if (isInstalled) {
return null
}
if (!isInstallable) {
return null
}
return (
<>
{/* 浮动安装提示 */}
{showPrompt && (
<div className="fixed bottom-4 right-4 z-50 max-w-sm animate-in slide-in-from-bottom-5">
<Card className="shadow-lg border-2 border-primary">
<CardHeader className="pb-3">
<div className="flex items-start justify-between">
<div className="flex items-center gap-2">
<Smartphone className="h-5 w-5 text-primary" />
<CardTitle className="text-base"></CardTitle>
</div>
<Button
variant="ghost"
size="icon"
className="h-6 w-6"
onClick={handleDismiss}
>
<X className="h-4 w-4" />
</Button>
</div>
<CardDescription className="text-xs">
访
</CardDescription>
</CardHeader>
<CardContent className="pt-0">
<Button
onClick={handleInstallClick}
className="w-full"
size="sm"
>
<Download className="mr-2 h-4 w-4" />
</Button>
</CardContent>
</Card>
</div>
)}
{/* 设置页面的安装按钮 */}
{!showPrompt && (
<Alert className="cursor-pointer hover:bg-muted/50 transition-colors" onClick={handleInstallClick}>
<Download className="h-4 w-4" />
<AlertDescription className="flex items-center justify-between">
<span></span>
<Button size="sm" variant="outline">
</Button>
</AlertDescription>
</Alert>
)}
</>
)
}