Skip to content

Commit 3cc4bf3

Browse files
committed
Add MeetingNotifier component for meeting alerts
Introduces MeetingNotifier, a React component that checks for upcoming meetings and displays toast notifications to users within 10 minutes of meeting start. Notifications include meeting details and a quick join action, with periodic refresh and notification deduplication.
1 parent a424fc2 commit 3cc4bf3

1 file changed

Lines changed: 111 additions & 0 deletions

File tree

components/meeting-notifier.tsx

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
"use client"
2+
3+
import { useEffect, useState } from "react"
4+
import { toast } from "sonner"
5+
import { Calendar, Clock, ArrowRight } from "lucide-react"
6+
import { useRouter } from "next/navigation"
7+
import { getMeetings, type Meeting } from "@/lib/database"
8+
import { getCurrentUser } from "@/lib/features/auth"
9+
10+
export function MeetingNotifier() {
11+
const router = useRouter()
12+
const [meetings, setMeetings] = useState<Meeting[]>([])
13+
14+
// Load meetings on mount
15+
useEffect(() => {
16+
const fetchMeetings = async () => {
17+
const user = getCurrentUser()
18+
if (!user) return
19+
20+
try {
21+
const data = await getMeetings(user.id)
22+
setMeetings(data)
23+
} catch (error) {
24+
console.error("Failed to fetch meetings for notifications", error)
25+
}
26+
}
27+
28+
fetchMeetings()
29+
30+
// Refresh meetings every 5 minutes to keep up to date
31+
const refreshInterval = setInterval(fetchMeetings, 5 * 60 * 1000)
32+
return () => clearInterval(refreshInterval)
33+
}, [])
34+
35+
// Check for upcoming meetings every minute
36+
useEffect(() => {
37+
const checkUpcomingMeetings = () => {
38+
const now = new Date()
39+
const notifiedKey = "notified_meetings"
40+
const notified = JSON.parse(localStorage.getItem(notifiedKey) || "[]") as string[]
41+
42+
meetings.forEach(meeting => {
43+
// Skip if already notified
44+
if (notified.includes(meeting.id)) return
45+
46+
// Combine date and time
47+
// Note: meeting.date is YYYY-MM-DD string, we might need to be careful with timezones
48+
// Assuming meeting.date and meeting.time are stored in local time or consistent UTC
49+
// The previous file showed meeting.date as a string.
50+
// Let's construct a date object.
51+
// If meeting.date is a full ISO string (from DB), we just use it.
52+
// In DB definition (lib/database.ts): date: string
53+
// In page.tsx it was splitting it. DB usually returns ISO string for timestamptz.
54+
// Let's try to parse it safely.
55+
56+
let meetingTime: Date
57+
if (meeting.date.includes("T")) {
58+
meetingTime = new Date(meeting.date)
59+
} else {
60+
// Fallback if it's just date string, though our DB typically stores full ISO
61+
meetingTime = new Date(meeting.date)
62+
}
63+
64+
const diffInMinutes = (meetingTime.getTime() - now.getTime()) / (1000 * 60)
65+
66+
// Notify if meeting is starting in 0-10 minutes
67+
if (diffInMinutes > 0 && diffInMinutes <= 10) {
68+
// Show notification
69+
toast(
70+
<div className="flex flex-col gap-2 w-full">
71+
<div className="flex items-start justify-between">
72+
<span className="font-bold text-sm">Upcoming Meeting</span>
73+
<span className="text-xs text-muted-foreground whitespace-nowrap">
74+
in {Math.ceil(diffInMinutes)} min
75+
</span>
76+
</div>
77+
<p className="text-sm font-medium leading-tight">{meeting.title}</p>
78+
<div className="flex items-center gap-2 text-xs text-muted-foreground mt-1">
79+
<Clock className="h-3 w-3" />
80+
{meetingTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
81+
</div>
82+
</div>,
83+
{
84+
duration: 10000, // Show for 10 seconds
85+
position: "top-right",
86+
action: {
87+
label: "Join",
88+
onClick: () => router.push("/dashboard/meeting")
89+
}
90+
}
91+
)
92+
93+
// Mark as notified
94+
const updatedNotified = [...notified, meeting.id]
95+
localStorage.setItem(notifiedKey, JSON.stringify(updatedNotified))
96+
}
97+
})
98+
}
99+
100+
// Initial check
101+
if (meetings.length > 0) {
102+
checkUpcomingMeetings()
103+
}
104+
105+
// Periodic check every minute
106+
const interval = setInterval(checkUpcomingMeetings, 60 * 1000)
107+
return () => clearInterval(interval)
108+
}, [meetings, router])
109+
110+
return null // This component doesn't render anything visible
111+
}

0 commit comments

Comments
 (0)