refactor Notifications rc
This commit is contained in:
parent
041ee8fe62
commit
9f47de084c
@ -142,6 +142,22 @@ export function CalendarWidget() {
|
||||
}
|
||||
|
||||
setEvents(upcomingEvents.slice(0, 5)); // Keep only 5 for display
|
||||
|
||||
// Dispatch event for Outlook-style notifications (when events start)
|
||||
window.dispatchEvent(new CustomEvent('calendar-events-updated', {
|
||||
detail: {
|
||||
events: upcomingEvents.map(evt => ({
|
||||
id: evt.id,
|
||||
title: evt.title,
|
||||
start: evt.start,
|
||||
end: evt.end,
|
||||
isAllDay: evt.isAllDay,
|
||||
calendarName: evt.calendarName,
|
||||
calendarColor: evt.calendarColor,
|
||||
})),
|
||||
}
|
||||
}));
|
||||
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
console.error("Calendar Widget - Error in fetchUpcomingEvents:", err);
|
||||
@ -155,10 +171,12 @@ export function CalendarWidget() {
|
||||
fetchUpcomingEvents();
|
||||
|
||||
// Set up an interval to refresh events every 5 minutes
|
||||
const intervalId = setInterval(fetchUpcomingEvents, 300000);
|
||||
const intervalId = setInterval(() => {
|
||||
fetchUpcomingEvents();
|
||||
}, 300000);
|
||||
|
||||
return () => clearInterval(intervalId);
|
||||
}, [session, status]);
|
||||
}, [session, status, triggerNotification]);
|
||||
|
||||
const formatEventDate = (date: Date, isAllDay: boolean) => {
|
||||
let dateString = "";
|
||||
|
||||
@ -12,6 +12,7 @@ import { useRocketChatCalls } from "@/hooks/use-rocketchat-calls";
|
||||
import { IncomingCallNotification } from "@/components/incoming-call-notification";
|
||||
import { useEmailNotifications } from "@/hooks/use-email-notifications";
|
||||
import { useRocketChatMessageNotifications } from "@/hooks/use-rocketchat-message-notifications";
|
||||
import { useCalendarEventNotifications } from "@/hooks/use-calendar-event-notifications";
|
||||
import { OutlookNotification } from "@/components/outlook-notification";
|
||||
import { NotificationStack } from "@/components/notification-stack";
|
||||
|
||||
@ -257,6 +258,15 @@ export function LayoutWrapper({ children, isSignInPage, isAuthenticated }: Layou
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{eventNotification && (
|
||||
<OutlookNotification
|
||||
notification={eventNotification}
|
||||
onDismiss={() => {
|
||||
console.log('[LayoutWrapper] Calendar event notification dismissed');
|
||||
setEventNotification(null);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</NotificationStack>
|
||||
)}
|
||||
</AuthCheck>
|
||||
|
||||
153
hooks/use-calendar-event-notifications.ts
Normal file
153
hooks/use-calendar-event-notifications.ts
Normal file
@ -0,0 +1,153 @@
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { Calendar } from 'lucide-react';
|
||||
import { OutlookNotificationData } from '@/components/outlook-notification';
|
||||
import { format } from 'date-fns';
|
||||
import { fr } from 'date-fns/locale';
|
||||
|
||||
interface CalendarEvent {
|
||||
id: string;
|
||||
title: string;
|
||||
start: Date;
|
||||
end: Date;
|
||||
isAllDay: boolean;
|
||||
calendarName?: string;
|
||||
calendarColor?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to manage calendar event notifications and show Outlook-style notifications
|
||||
* when an event's start time arrives
|
||||
*/
|
||||
export function useCalendarEventNotifications() {
|
||||
const [eventNotification, setEventNotification] = useState<OutlookNotificationData | null>(null);
|
||||
const notifiedEventIdsRef = useRef<Set<string>>(new Set());
|
||||
const eventsRef = useRef<CalendarEvent[]>([]);
|
||||
const checkIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Listen for calendar events updates
|
||||
const handleEventsUpdate = (event: CustomEvent) => {
|
||||
const events = event.detail?.events || [];
|
||||
|
||||
if (!events || events.length === 0) {
|
||||
eventsRef.current = [];
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert events to CalendarEvent format
|
||||
const calendarEvents: CalendarEvent[] = events.map((evt: any) => ({
|
||||
id: evt.id,
|
||||
title: evt.title,
|
||||
start: new Date(evt.start),
|
||||
end: new Date(evt.end),
|
||||
isAllDay: evt.isAllDay || false,
|
||||
calendarName: evt.calendarName,
|
||||
calendarColor: evt.calendarColor,
|
||||
}));
|
||||
|
||||
eventsRef.current = calendarEvents;
|
||||
console.log('[useCalendarEventNotifications] Events updated', {
|
||||
count: calendarEvents.length,
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener('calendar-events-updated', handleEventsUpdate as EventListener);
|
||||
|
||||
// Check for events that are starting now (every minute)
|
||||
const checkForStartingEvents = () => {
|
||||
const now = new Date();
|
||||
const currentTime = now.getTime();
|
||||
|
||||
// Check events that start within the next 2 minutes (to catch events that just started)
|
||||
const upcomingWindow = 2 * 60 * 1000; // 2 minutes in milliseconds
|
||||
|
||||
const startingEvents = eventsRef.current.filter((event) => {
|
||||
// Skip if already notified
|
||||
if (notifiedEventIdsRef.current.has(event.id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const eventStartTime = event.start.getTime();
|
||||
const timeUntilStart = eventStartTime - currentTime;
|
||||
|
||||
// Event is starting now or within the next 2 minutes
|
||||
// And hasn't started more than 5 minutes ago (to avoid old notifications)
|
||||
return (
|
||||
timeUntilStart >= -5 * 60 * 1000 && // Not more than 5 minutes ago
|
||||
timeUntilStart <= upcomingWindow // Within next 2 minutes
|
||||
);
|
||||
});
|
||||
|
||||
if (startingEvents.length > 0) {
|
||||
// Sort by start time (earliest first)
|
||||
startingEvents.sort((a, b) => a.start.getTime() - b.start.getTime());
|
||||
|
||||
// Show notification for the first event starting
|
||||
const event = startingEvents[0];
|
||||
|
||||
console.log('[useCalendarEventNotifications] 📅 Event starting detected', {
|
||||
title: event.title,
|
||||
start: event.start,
|
||||
now: now,
|
||||
});
|
||||
|
||||
const timeStr = event.isAllDay
|
||||
? 'Toute la journée'
|
||||
: format(event.start, 'HH:mm', { locale: fr });
|
||||
|
||||
const notification: OutlookNotificationData = {
|
||||
id: `calendar-${event.id}-${Date.now()}`,
|
||||
source: 'calendar',
|
||||
title: 'Agenda',
|
||||
subtitle: event.isAllDay ? 'Événement aujourd\'hui' : 'Événement maintenant',
|
||||
message: `${event.title}${event.isAllDay ? '' : ` à ${timeStr}`}${event.calendarName ? ` (${event.calendarName})` : ''}`,
|
||||
icon: Calendar,
|
||||
iconColor: 'text-orange-600',
|
||||
iconBgColor: 'bg-orange-100',
|
||||
borderColor: 'border-orange-500',
|
||||
link: '/agenda',
|
||||
timestamp: event.start,
|
||||
autoDismiss: 30000, // 30 seconds for calendar events
|
||||
actions: [
|
||||
{
|
||||
label: 'Ouvrir',
|
||||
onClick: () => {
|
||||
window.location.href = '/agenda';
|
||||
},
|
||||
variant: 'default',
|
||||
className: 'bg-orange-600 hover:bg-orange-700 text-white',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
setEventNotification(notification);
|
||||
notifiedEventIdsRef.current.add(event.id);
|
||||
|
||||
// Clean up old notified events (older than 1 hour) to allow re-notification if needed
|
||||
setTimeout(() => {
|
||||
notifiedEventIdsRef.current.delete(event.id);
|
||||
}, 60 * 60 * 1000); // 1 hour
|
||||
}
|
||||
};
|
||||
|
||||
// Check immediately and then every minute
|
||||
checkForStartingEvents();
|
||||
checkIntervalRef.current = setInterval(checkForStartingEvents, 60000); // Every minute
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('calendar-events-updated', handleEventsUpdate as EventListener);
|
||||
if (checkIntervalRef.current) {
|
||||
clearInterval(checkIntervalRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleDismiss = () => {
|
||||
setEventNotification(null);
|
||||
};
|
||||
|
||||
return {
|
||||
eventNotification,
|
||||
setEventNotification: handleDismiss,
|
||||
};
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user