This commit is contained in:
freedakgmail
2025-11-22 23:49:24 +08:00
parent b83382b604
commit c033e46782
3580 changed files with 1745279 additions and 2428 deletions
+146
View File
@@ -0,0 +1,146 @@
"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>
)}
</>
)
}
+18
View File
@@ -0,0 +1,18 @@
"use client"
import { useEffect } from "react"
import { registerServiceWorker } from "@/lib/register-sw"
import { PWAInstaller } from "./pwa-installer"
export function PWAProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
registerServiceWorker()
}, [])
return (
<>
{children}
<PWAInstaller />
</>
)
}