diff --git a/components/email.tsx b/components/email.tsx index 0f5fdda..54cbe34 100644 --- a/components/email.tsx +++ b/components/email.tsx @@ -207,15 +207,24 @@ export function Email() { // Dispatch event for Outlook-style notifications (only for new emails) if (previousCount >= 0 && currentUnreadCount > previousCount) { + // Get only the newly arrived emails (the difference) const newEmails = transformedEmails .filter(e => !e.read) .slice(0, currentUnreadCount - previousCount); // Only the new ones if (newEmails.length > 0) { + console.log('[Email Widget] 📧 Dispatching new emails event', { + newEmailsCount: newEmails.length, + previousCount, + currentCount: currentUnreadCount, + }); + window.dispatchEvent(new CustomEvent('new-emails-detected', { detail: { emails: transformedEmails, accountMap: accountMap, + previousCount, + currentCount: currentUnreadCount, } })); } diff --git a/components/layout/layout-wrapper.tsx b/components/layout/layout-wrapper.tsx index 8c2041f..d5dc383 100644 --- a/components/layout/layout-wrapper.tsx +++ b/components/layout/layout-wrapper.tsx @@ -11,6 +11,7 @@ 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 { useRocketChatMessageNotifications } from "@/hooks/use-rocketchat-message-notifications"; import { OutlookNotification } from "@/components/outlook-notification"; import { NotificationStack } from "@/components/notification-stack"; @@ -244,6 +245,15 @@ export function LayoutWrapper({ children, isSignInPage, isAuthenticated }: Layou }} /> )} + {messageNotification && ( + { + console.log('[LayoutWrapper] RocketChat message notification dismissed'); + setMessageNotification(null); + }} + /> + )} )} diff --git a/components/parole.tsx b/components/parole.tsx index 57d47c6..6fb80cd 100644 --- a/components/parole.tsx +++ b/components/parole.tsx @@ -87,6 +87,7 @@ export function Parole() { // Si le count a changé (ou premier chargement), déclencher notification if (currentUnreadCount !== lastUnreadCountRef.current) { + const previousCount = lastUnreadCountRef.current; lastUnreadCountRef.current = currentUnreadCount; // Préparer les items pour les notifications (messages, max 10) @@ -104,12 +105,35 @@ export function Parole() { }, })); - // Déclencher notification update + // Déclencher notification update (for badge) await triggerNotification({ source: 'rocketchat', count: currentUnreadCount, items: notificationItems, }); + + // Dispatch event for Outlook-style notifications (only for new messages) + // We detect new messages by comparing message count, not unread count + // because new messages might be read immediately + if (previousCount >= 0 && data.messages.length > 0) { + // Get the most recent messages (first ones in the array, sorted by date desc) + // We'll let the hook determine which are truly new + const totalMessages = data.messages.length; + + console.log('[Parole Widget] 💬 Dispatching messages event', { + totalMessages, + previousUnreadCount: previousCount, + currentUnreadCount, + }); + + window.dispatchEvent(new CustomEvent('new-messages-detected', { + detail: { + messages: data.messages, + previousCount: previousCount, + currentCount: currentUnreadCount, + } + })); + } } setMessages(data.messages); diff --git a/hooks/use-email-notifications.ts b/hooks/use-email-notifications.ts index 69f2e50..b6a4e89 100644 --- a/hooks/use-email-notifications.ts +++ b/hooks/use-email-notifications.ts @@ -10,64 +10,82 @@ export function useEmailNotifications() { const lastEmailIdsRef = useRef>(new Set()); const notificationQueueRef = useRef([]); const isShowingRef = useRef(false); + const isInitializedRef = useRef(false); useEffect(() => { // Listen for new emails via custom event const handleNewEmails = (event: CustomEvent) => { const emails = event.detail?.emails || []; const accountMap = event.detail?.accountMap || new Map(); + const previousCount = event.detail?.previousCount ?? -1; + const currentCount = event.detail?.currentCount ?? 0; if (!emails || emails.length === 0) return; - // Find new emails (not in lastEmailIdsRef) - const newEmails = emails.filter((email: any) => { - const emailId = email.id; - if (!lastEmailIdsRef.current.has(emailId) && !email.read) { - lastEmailIdsRef.current.add(emailId); - return true; - } - return false; - }); + // On first load, just store all email IDs without showing notifications + if (!isInitializedRef.current) { + console.log('[useEmailNotifications] Initializing - storing existing email IDs without notifications'); + lastEmailIdsRef.current = new Set(emails.map((e: any) => e.id)); + isInitializedRef.current = true; + return; + } - // Update lastEmailIdsRef with all current emails - lastEmailIdsRef.current = new Set(emails.map((e: any) => e.id)); + // Only show notifications if the count increased (new emails arrived) + // AND we have a previous count to compare with + if (previousCount >= 0 && currentCount > previousCount) { + // Find new emails (not in lastEmailIdsRef) - these are the ones that just arrived + const newEmails = emails + .filter((email: any) => { + const emailId = email.id; + return !lastEmailIdsRef.current.has(emailId) && !email.read; + }) + .slice(0, 5); // Limit to 5 most recent new emails - // If there are new unread emails, queue them for notification - if (newEmails.length > 0) { - newEmails.forEach((email: any) => { - const account = accountMap.get(email.accountId); - const notification: OutlookNotificationData = { - id: `email-${email.id}-${Date.now()}`, - source: 'email', - title: 'Courrier', - subtitle: 'Nouvel email', - message: `${email.subject || 'Sans objet'} - De ${email.fromName || email.from.split('@')[0]}`, - icon: Mail, - iconColor: 'text-green-600', - iconBgColor: 'bg-green-100', - borderColor: 'border-green-500', - link: '/courrier', - timestamp: new Date(email.date), - autoDismiss: 10000, // 10 seconds for emails - actions: [ - { - label: 'Ouvrir', - onClick: () => { - window.location.href = '/courrier'; - }, - variant: 'default', - className: 'bg-green-600 hover:bg-green-700 text-white', - }, - ], - }; + // Update lastEmailIdsRef with all current emails + lastEmailIdsRef.current = new Set(emails.map((e: any) => e.id)); + + // If there are new unread emails, queue them for notification + if (newEmails.length > 0) { + console.log('[useEmailNotifications] 📧 New emails detected:', newEmails.length); - notificationQueueRef.current.push(notification); - }); + newEmails.forEach((email: any) => { + const account = accountMap.get(email.accountId); + const notification: OutlookNotificationData = { + id: `email-${email.id}-${Date.now()}`, + source: 'email', + title: 'Courrier', + subtitle: 'Nouvel email', + message: `${email.subject || 'Sans objet'} - De ${email.fromName || email.from.split('@')[0]}`, + icon: Mail, + iconColor: 'text-green-600', + iconBgColor: 'bg-green-100', + borderColor: 'border-green-500', + link: '/courrier', + timestamp: new Date(email.date), + autoDismiss: 10000, // 10 seconds for emails + actions: [ + { + label: 'Ouvrir', + onClick: () => { + window.location.href = '/courrier'; + }, + variant: 'default', + className: 'bg-green-600 hover:bg-green-700 text-white', + }, + ], + }; + + notificationQueueRef.current.push(notification); + }); - // Show the first notification if none is currently showing - if (!isShowingRef.current && notificationQueueRef.current.length > 0) { - showNextNotification(); + // Show the first notification if none is currently showing + if (!isShowingRef.current && notificationQueueRef.current.length > 0) { + showNextNotification(); + } } + } else { + // Just update the email IDs without showing notifications + lastEmailIdsRef.current = new Set(emails.map((e: any) => e.id)); } }; diff --git a/hooks/use-rocketchat-message-notifications.ts b/hooks/use-rocketchat-message-notifications.ts new file mode 100644 index 0000000..855bbde --- /dev/null +++ b/hooks/use-rocketchat-message-notifications.ts @@ -0,0 +1,128 @@ +import { useState, useEffect, useRef } from 'react'; +import { MessageSquare } from 'lucide-react'; +import { OutlookNotificationData } from '@/components/outlook-notification'; + +/** + * Hook to manage RocketChat message notifications and show Outlook-style notifications + */ +export function useRocketChatMessageNotifications() { + const [messageNotification, setMessageNotification] = useState(null); + const lastMessageIdsRef = useRef>(new Set()); + const notificationQueueRef = useRef([]); + const isShowingRef = useRef(false); + const isInitializedRef = useRef(false); + + useEffect(() => { + // Listen for new messages via custom event + const handleNewMessages = (event: CustomEvent) => { + const messages = event.detail?.messages || []; + const previousCount = event.detail?.previousCount ?? -1; + const currentCount = event.detail?.currentCount ?? 0; + + if (!messages || messages.length === 0) return; + + // On first load, just store all message IDs without showing notifications + if (!isInitializedRef.current) { + console.log('[useRocketChatMessageNotifications] Initializing - storing existing message IDs without notifications'); + lastMessageIdsRef.current = new Set(messages.map((m: any) => m.id)); + isInitializedRef.current = true; + return; + } + + // Only show notifications if the count increased (new messages arrived) + // AND we have a previous count to compare with + if (previousCount >= 0 && currentCount > previousCount) { + // Find new messages (not in lastMessageIdsRef) - these are the ones that just arrived + const newMessages = messages + .filter((message: any) => { + const messageId = message.id; + return !lastMessageIdsRef.current.has(messageId); + }) + .slice(0, 5); // Limit to 5 most recent new messages + + // Update lastMessageIdsRef with all current messages + lastMessageIdsRef.current = new Set(messages.map((m: any) => m.id)); + + // If there are new messages, queue them for notification + if (newMessages.length > 0) { + console.log('[useRocketChatMessageNotifications] 💬 New messages detected:', newMessages.length); + + newMessages.forEach((message: any) => { + const senderName = message.sender?.name || message.sender?.username || 'Inconnu'; + const roomName = message.roomName || 'Chat'; + const messageText = message.text || message.message || ''; + + const notification: OutlookNotificationData = { + id: `rocketchat-${message.id}-${Date.now()}`, + source: 'rocketchat', + title: 'Parole', + subtitle: 'Nouveau message', + message: `${senderName} dans ${roomName}: ${messageText.length > 50 ? messageText.substring(0, 50) + '...' : messageText}`, + icon: MessageSquare, + iconColor: 'text-purple-600', + iconBgColor: 'bg-purple-100', + borderColor: 'border-purple-500', + link: '/parole', + timestamp: new Date(message.rawTimestamp || message.timestamp), + autoDismiss: 10000, // 10 seconds for messages + actions: [ + { + label: 'Ouvrir', + onClick: () => { + window.location.href = '/parole'; + }, + variant: 'default', + className: 'bg-purple-600 hover:bg-purple-700 text-white', + }, + ], + }; + + notificationQueueRef.current.push(notification); + }); + + // Show the first notification if none is currently showing + if (!isShowingRef.current && notificationQueueRef.current.length > 0) { + showNextNotification(); + } + } + } else { + // Just update the message IDs without showing notifications + lastMessageIdsRef.current = new Set(messages.map((m: any) => m.id)); + } + }; + + window.addEventListener('new-messages-detected', handleNewMessages as EventListener); + + return () => { + window.removeEventListener('new-messages-detected', handleNewMessages as EventListener); + }; + }, []); + + const showNextNotification = () => { + if (notificationQueueRef.current.length === 0) { + isShowingRef.current = false; + return; + } + + const nextNotification = notificationQueueRef.current.shift(); + if (nextNotification) { + isShowingRef.current = true; + setMessageNotification(nextNotification); + } + }; + + const handleDismiss = () => { + setMessageNotification(null); + isShowingRef.current = false; + + // Show next notification after a short delay + setTimeout(() => { + showNextNotification(); + }, 500); + }; + + return { + messageNotification, + setMessageNotification: handleDismiss, + }; +}