0.0.0.4
This commit is contained in:
@@ -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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user