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
@@ -0,0 +1,218 @@
"use client"
import { useState, useEffect } from "react"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Switch } from "@/components/ui/switch"
import { Label } from "@/components/ui/label"
import { Bell, BellOff, Check, X } from "lucide-react"
import { requestNotificationPermission, checkUpcomingAnniversaries } from "@/lib/notifications"
import { useFamily } from "@/context/family-context"
import { Alert, AlertDescription } from "@/components/ui/alert"
export function NotificationSettings() {
const { treeData } = useFamily()
const [notificationEnabled, setNotificationEnabled] = useState(false)
const [permission, setPermission] = useState<NotificationPermission>("default")
const [isClient, setIsClient] = useState(false)
const [upcomingEvents, setUpcomingEvents] = useState<{
birthdays: Array<{ member: any; daysUntil: number }>
deathdays: Array<{ member: any; daysUntil: number }>
}>({ birthdays: [], deathdays: [] })
useEffect(() => {
// 标记为客户端
setIsClient(true)
// 检查通知权限
if (typeof window !== "undefined" && "Notification" in window) {
setPermission(Notification.permission)
setNotificationEnabled(Notification.permission === "granted")
}
// 检查即将到来的纪念日
const members = Object.values(treeData.members)
const upcoming = checkUpcomingAnniversaries(members, 7)
setUpcomingEvents(upcoming)
}, [treeData])
const handleEnableNotifications = async () => {
const granted = await requestNotificationPermission()
if (granted) {
setNotificationEnabled(true)
setPermission("granted")
// 发送测试通知
new Notification("通知已启用", {
body: "您将收到家族纪念日提醒",
icon: "/icon.svg",
})
} else {
setPermission(Notification.permission)
}
}
const handleDisableNotifications = () => {
setNotificationEnabled(false)
alert("请在浏览器设置中禁用此网站的通知权限")
}
return (
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Bell className="h-5 w-5" />
</CardTitle>
<CardDescription>
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{!isClient ? (
<div className="text-center py-4 text-muted-foreground">
...
</div>
) : !(typeof window !== "undefined" && "Notification" in window) ? (
<Alert>
<AlertDescription>
</AlertDescription>
</Alert>
) : (
<>
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label htmlFor="notifications"></Label>
<p className="text-sm text-muted-foreground">
</p>
</div>
<Switch
id="notifications"
checked={notificationEnabled}
onCheckedChange={(checked) => {
if (checked) {
handleEnableNotifications()
} else {
handleDisableNotifications()
}
}}
/>
</div>
{permission === "denied" && (
<Alert variant="destructive">
<X className="h-4 w-4" />
<AlertDescription>
</AlertDescription>
</Alert>
)}
{permission === "granted" && (
<Alert>
<Check className="h-4 w-4" />
<AlertDescription>
访
</AlertDescription>
</Alert>
)}
{notificationEnabled && (
<Button
variant="outline"
size="sm"
onClick={() => {
new Notification("测试通知", {
body: "这是一条测试通知",
icon: "/icon.svg",
})
}}
>
</Button>
)}
</>
)}
</CardContent>
</Card>
{/* 即将到来的纪念日 */}
{(upcomingEvents.birthdays.length > 0 || upcomingEvents.deathdays.length > 0) && (
<Card>
<CardHeader>
<CardTitle>7</CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{upcomingEvents.birthdays.length > 0 && (
<div>
<h4 className="font-medium mb-2 flex items-center gap-2">
🎂
</h4>
<div className="space-y-2">
{upcomingEvents.birthdays.map(({ member, daysUntil }) => (
<div
key={member.id}
className="flex items-center justify-between p-3 bg-muted/50 rounded-lg"
>
<div>
<p className="font-medium">{member.fullName}</p>
<p className="text-sm text-muted-foreground">
{new Date(member.birthDate).toLocaleDateString('zh-CN', {
month: 'long',
day: 'numeric'
})}
</p>
</div>
<div className="text-right">
<p className="text-sm font-medium text-primary">
{daysUntil === 1 ? '明天' : `${daysUntil}天后`}
</p>
</div>
</div>
))}
</div>
</div>
)}
{upcomingEvents.deathdays.length > 0 && (
<div>
<h4 className="font-medium mb-2 flex items-center gap-2">
🕯
</h4>
<div className="space-y-2">
{upcomingEvents.deathdays.map(({ member, daysUntil }) => (
<div
key={member.id}
className="flex items-center justify-between p-3 bg-muted/50 rounded-lg"
>
<div>
<p className="font-medium">{member.fullName}</p>
<p className="text-sm text-muted-foreground">
{new Date(member.deathDate!).toLocaleDateString('zh-CN', {
month: 'long',
day: 'numeric'
})}
</p>
</div>
<div className="text-right">
<p className="text-sm font-medium text-muted-foreground">
{daysUntil === 1 ? '明天' : `${daysUntil}天后`}
</p>
</div>
</div>
))}
</div>
</div>
)}
</div>
</CardContent>
</Card>
)}
</div>
)
}