diff --git a/components/email.tsx b/components/email.tsx index a07927e..0f5fdda 100644 --- a/components/email.tsx +++ b/components/email.tsx @@ -176,6 +176,7 @@ export function Email() { // Trigger notification if count changed if (currentUnreadCount !== lastUnreadCountRef.current) { + const previousCount = lastUnreadCountRef.current; lastUnreadCountRef.current = currentUnreadCount; // Prepare notification items (unread emails only, max 10) @@ -197,12 +198,28 @@ export function Email() { }; }); - // Trigger notification update + // Trigger notification update (for badge) await triggerNotification({ source: 'email', count: currentUnreadCount, items: notificationItems, }); + + // Dispatch event for Outlook-style notifications (only for new emails) + if (previousCount >= 0 && currentUnreadCount > previousCount) { + const newEmails = transformedEmails + .filter(e => !e.read) + .slice(0, currentUnreadCount - previousCount); // Only the new ones + + if (newEmails.length > 0) { + window.dispatchEvent(new CustomEvent('new-emails-detected', { + detail: { + emails: transformedEmails, + accountMap: accountMap, + } + })); + } + } } // Show error only if all accounts failed diff --git a/components/incoming-call-notification.tsx b/components/incoming-call-notification.tsx index 3d92c26..27109aa 100644 --- a/components/incoming-call-notification.tsx +++ b/components/incoming-call-notification.tsx @@ -1,9 +1,9 @@ "use client"; import { useState, useEffect } from 'react'; -import { Phone, PhoneOff, X } from 'lucide-react'; -import { Button } from '@/components/ui/button'; +import { Phone, PhoneOff } from 'lucide-react'; import { useRouter } from 'next/navigation'; +import { OutlookNotification, OutlookNotificationData } from '@/components/outlook-notification'; export interface IncomingCall { from: { @@ -30,7 +30,7 @@ export function IncomingCallNotification({ onReject, }: IncomingCallNotificationProps) { const router = useRouter(); - const [isVisible, setIsVisible] = useState(false); + const [notificationData, setNotificationData] = useState(null); useEffect(() => { if (call) { @@ -38,98 +38,62 @@ export function IncomingCallNotification({ from: call.from.name || call.from.username, roomId: call.roomId, }); - setIsVisible(true); + + const callerName = call.from.name || call.from.username || 'Inconnu'; - // Auto-dismiss after 30 seconds if user doesn't interact - const autoDismissTimer = setTimeout(() => { - console.log('[IncomingCallNotification] ⏰ Auto-dismissing after 30 seconds'); - setIsVisible(false); - onDismiss(); - }, 30000); // 30 seconds - - return () => { - clearTimeout(autoDismissTimer); + const notification: OutlookNotificationData = { + id: `call-${call.roomId}-${Date.now()}`, + source: 'call', + title: 'Parole', + subtitle: 'Appel entrant', + message: `Vous avez un appel de ${callerName}${call.roomName && call.roomName !== callerName ? ` dans ${call.roomName}` : ''}`, + icon: Phone, + iconColor: 'text-blue-600', + iconBgColor: 'bg-blue-100', + borderColor: 'border-blue-500', + timestamp: call.timestamp, + autoDismiss: 30000, // 30 seconds + actions: [ + { + label: 'Accepter', + onClick: () => { + onAccept(call.roomId); + router.push(`/parole?room=${call.roomId}`); + setNotificationData(null); + }, + variant: 'default', + className: 'bg-green-600 hover:bg-green-700 text-white', + icon: Phone, + }, + { + label: 'Raccrocher', + onClick: () => { + onReject(); + setNotificationData(null); + }, + variant: 'destructive', + icon: PhoneOff, + }, + ], }; - } else { - setIsVisible(false); - } - }, [call, onDismiss]); - if (!call || !isVisible) { + setNotificationData(notification); + } else { + setNotificationData(null); + } + }, [call, router, onAccept, onReject]); + + if (!notificationData) { return null; } - const handleAccept = () => { - onAccept(call.roomId); - // Navigate to parole page with room ID - router.push(`/parole?room=${call.roomId}`); - setIsVisible(false); - }; - - const handleReject = () => { - onReject(); - setIsVisible(false); - }; - - const handleDismiss = () => { - onDismiss(); - setIsVisible(false); - }; - - const callerName = call.from.name || call.from.username || 'Inconnu'; - return ( -
-
- {/* Header with Outlook-like style */} -
-
-
- -
-
-

Parole

-

Appel entrant

-
-
- -
- - {/* Call Info - Outlook style message */} -
-

- Vous avez un appel de {callerName} -

- {call.roomName && call.roomName !== callerName && ( -

Dans {call.roomName}

- )} -
- - {/* Actions - Outlook style buttons */} -
- - -
-
-
+ { + onDismiss(); + setNotificationData(null); + }} + /> ); } diff --git a/components/layout/layout-wrapper.tsx b/components/layout/layout-wrapper.tsx index 24f7466..63e48a2 100644 --- a/components/layout/layout-wrapper.tsx +++ b/components/layout/layout-wrapper.tsx @@ -10,6 +10,9 @@ import { useBackgroundImage } from "@/components/background-switcher"; import { clearAuthCookies, clearKeycloakCookies } from "@/lib/session"; import { useRocketChatCalls } from "@/hooks/use-rocketchat-calls"; import { IncomingCallNotification } from "@/components/incoming-call-notification"; +import { useEmailNotifications } from "@/hooks/use-email-notifications"; +import { OutlookNotification } from "@/components/outlook-notification"; +import { NotificationStack } from "@/components/notification-stack"; interface LayoutWrapperProps { children: React.ReactNode; @@ -208,32 +211,37 @@ export function LayoutWrapper({ children, isSignInPage, isAuthenticated }: Layou {!isSignInPage && isAuthenticated &&