import { useAuth } from "@/_core/hooks/useAuth"; import { trpc } from "@/lib/trpc"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogFooter } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; import { Textarea } from "@/components/ui/textarea"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Separator } from "@/components/ui/separator"; import { toast } from "sonner"; import { useState, useEffect, useCallback, useMemo } from "react"; import { Bell, BellRing, Plus, Trash2, Clock, Calendar, CheckCircle2, XCircle, Settings, BellOff, Volume2 } from "lucide-react"; const DAY_NAMES = ["日", "一", "二", "三", "四", "五", "六"]; const REMINDER_TYPES = [ { value: "training", label: "训练提醒", icon: }, { value: "checkin", label: "打卡提醒", icon: }, { value: "analysis", label: "分析提醒", icon: }, ]; export default function Reminders() { const { user } = useAuth(); const [showCreate, setShowCreate] = useState(false); const [newReminder, setNewReminder] = useState({ reminderType: "training", title: "", message: "", timeOfDay: "08:00", daysOfWeek: [1, 2, 3, 4, 5] as number[], }); const utils = trpc.useUtils(); const { data: reminders, isLoading } = trpc.reminder.list.useQuery(undefined, { enabled: !!user }); const { data: notifications } = trpc.notification.list.useQuery(undefined, { enabled: !!user }); const { data: unreadCount } = trpc.notification.unreadCount.useQuery(undefined, { enabled: !!user }); const createReminder = trpc.reminder.create.useMutation({ onSuccess: () => { toast.success("提醒已创建"); setShowCreate(false); setNewReminder({ reminderType: "training", title: "", message: "", timeOfDay: "08:00", daysOfWeek: [1, 2, 3, 4, 5] }); utils.reminder.list.invalidate(); }, }); const deleteReminder = trpc.reminder.delete.useMutation({ onSuccess: () => { toast.success("提醒已删除"); utils.reminder.list.invalidate(); }, }); const toggleReminder = trpc.reminder.toggle.useMutation({ onSuccess: () => { utils.reminder.list.invalidate(); }, }); const markAllRead = trpc.notification.markAllRead.useMutation({ onSuccess: () => { utils.notification.list.invalidate(); utils.notification.unreadCount.invalidate(); toast.success("全部已读"); }, }); const markRead = trpc.notification.markRead.useMutation({ onSuccess: () => { utils.notification.list.invalidate(); utils.notification.unreadCount.invalidate(); }, }); const toggleDay = useCallback((day: number) => { setNewReminder(prev => ({ ...prev, daysOfWeek: prev.daysOfWeek.includes(day) ? prev.daysOfWeek.filter(d => d !== day) : [...prev.daysOfWeek, day].sort(), })); }, []); const handleCreate = () => { if (!newReminder.title.trim()) { toast.error("请输入提醒标题"); return; } if (newReminder.daysOfWeek.length === 0) { toast.error("请至少选择一天"); return; } createReminder.mutate(newReminder); }; // Browser notification permission const [notifPermission, setNotifPermission] = useState("default"); useEffect(() => { if ("Notification" in window) { setNotifPermission(Notification.permission); } }, []); const requestPermission = async () => { if ("Notification" in window) { const perm = await Notification.requestPermission(); setNotifPermission(perm); if (perm === "granted") { toast.success("通知权限已开启"); new Notification("Tennis Training Hub", { body: "训练提醒已开启!" }); } } }; // Check reminders and trigger browser notifications useEffect(() => { if (!reminders || notifPermission !== "granted") return; const checkInterval = setInterval(() => { const now = new Date(); const currentTime = `${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}`; const currentDay = now.getDay(); reminders.forEach((r: any) => { if (r.isActive && r.timeOfDay === currentTime) { const days = typeof r.daysOfWeek === "string" ? JSON.parse(r.daysOfWeek) : r.daysOfWeek; if (Array.isArray(days) && days.includes(currentDay)) { new Notification(r.title, { body: r.message || "该训练了!", icon: "/favicon.ico", }); } } }); }, 60000); // Check every minute return () => clearInterval(checkInterval); }, [reminders, notifPermission]); const activeReminders = useMemo(() => reminders?.filter((r: any) => r.isActive) || [], [reminders]); const inactiveReminders = useMemo(() => reminders?.filter((r: any) => !r.isActive) || [], [reminders]); if (isLoading) { return (
); } return (
{/* Header */}

训练提醒

设置定时提醒,保持训练节奏

{notifPermission !== "granted" && ( )} 创建训练提醒
setNewReminder(p => ({ ...p, title: e.target.value }))} />