diff --git a/components/email.tsx b/components/email.tsx index 8b1ea63..1c956b6 100644 --- a/components/email.tsx +++ b/components/email.tsx @@ -40,6 +40,8 @@ export function Email() { const [accountErrors, setAccountErrors] = useState>({}); const { triggerNotification } = useWidgetNotification(); const lastUnreadCountRef = useRef(-1); + const lastEmailIdsRef = useRef>(new Set()); + const isInitializedRef = useRef(false); // Create a map for quick account lookup by ID (recalculated when accounts change) const accountMap = useMemo(() => { @@ -173,63 +175,86 @@ export function Email() { // Calculate unread count const currentUnreadCount = transformedEmails.filter(e => !e.read).length; + const currentEmailIds = new Set(transformedEmails.map(e => e.id)); - // Trigger notification if count changed - if (currentUnreadCount !== lastUnreadCountRef.current) { - const previousCount = lastUnreadCountRef.current; + // Detect new emails by comparing IDs (more reliable than count) + const newEmailIds = new Set( + Array.from(currentEmailIds).filter(id => !lastEmailIdsRef.current.has(id)) + ); + const hasNewEmails = newEmailIds.size > 0; + + // On first load, just store email IDs without triggering notifications + if (!isInitializedRef.current) { + console.log('[Email Widget] 📧 Initializing - storing existing email IDs without notifications', { + emailCount: transformedEmails.length, + unreadCount: currentUnreadCount, + }); + lastEmailIdsRef.current = currentEmailIds; lastUnreadCountRef.current = currentUnreadCount; - - // Prepare notification items (unread emails only, max 10) - const notificationItems = transformedEmails - .filter(e => !e.read) - .slice(0, 10) - .map(email => { - const account = accountMap.get((email as any).accountId); - return { - id: email.id, - title: email.subject || 'Sans objet', - message: `De ${email.fromName || email.from.split('@')[0]}`, - link: '/courrier', - timestamp: new Date(email.date), - metadata: { - accountId: (email as any).accountId, - accountEmail: account?.email, - }, - }; + isInitializedRef.current = true; + } else { + // Trigger notification if count changed or new emails detected + if (currentUnreadCount !== lastUnreadCountRef.current || hasNewEmails) { + const previousCount = lastUnreadCountRef.current; + lastUnreadCountRef.current = currentUnreadCount; + + // Prepare notification items (unread emails only, max 10) + const notificationItems = transformedEmails + .filter(e => !e.read) + .slice(0, 10) + .map(email => { + const account = accountMap.get((email as any).accountId); + return { + id: email.id, + title: email.subject || 'Sans objet', + message: `De ${email.fromName || email.from.split('@')[0]}`, + link: '/courrier', + timestamp: new Date(email.date), + metadata: { + accountId: (email as any).accountId, + accountEmail: account?.email, + }, + }; + }); + + // Trigger notification update (for badge) + await triggerNotification({ + source: 'email', + count: currentUnreadCount, + items: notificationItems, }); - // 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) { - // Get only the newly arrived emails (the difference) - const newEmails = transformedEmails - .filter(e => !e.read) - .slice(0, currentUnreadCount - previousCount); // Only the new ones + // Dispatch event for Outlook-style notifications (for new emails detected by ID) + if (hasNewEmails) { + // Get only the newly arrived emails (by ID comparison) + const newEmails = transformedEmails + .filter(e => newEmailIds.has(e.id) && !e.read) + .slice(0, 5); // Limit to 5 most recent new emails - 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, + if (newEmails.length > 0) { + console.log('[Email Widget] 📧 Dispatching new emails event', { + newEmailsCount: newEmails.length, + newEmailIds: Array.from(newEmailIds), previousCount, currentCount: currentUnreadCount, - } - })); + previousEmailIds: Array.from(lastEmailIdsRef.current), + }); + + window.dispatchEvent(new CustomEvent('new-emails-detected', { + detail: { + emails: transformedEmails, + accountMap: accountMap, + previousCount, + currentCount: currentUnreadCount, + } + })); + } } } } + + // Always update lastEmailIdsRef to track current state + lastEmailIdsRef.current = currentEmailIds; // Show error only if all accounts failed if (allEmails.length === 0 && accounts.length > 0 && Object.keys(accountErrors).length === accounts.length) { @@ -272,12 +297,14 @@ export function Email() { }; // Integrate unified refresh for automatic polling + // Use forceRefresh=true to ensure we get the latest emails immediately const { refresh } = useUnifiedRefresh({ resource: 'email', interval: REFRESH_INTERVALS.EMAIL, // 30 seconds (harmonized) enabled: status === 'authenticated', onRefresh: async () => { - await fetchEmails(false); // Use cache for auto-refresh + // Use forceRefresh to bypass cache and get latest emails immediately + await fetchEmails(true); // Force refresh to get new emails immediately await fetchUnreadCount(); }, priority: 'high', diff --git a/hooks/use-email-notifications.ts b/hooks/use-email-notifications.ts index b6a4e89..af1bcf6 100644 --- a/hooks/use-email-notifications.ts +++ b/hooks/use-email-notifications.ts @@ -30,62 +30,69 @@ export function useEmailNotifications() { return; } - // 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 + // Find new emails by comparing IDs (more reliable than count comparison) + // This works even if previousCount is -1 (first load after initialization) + 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 - // Update lastEmailIdsRef with all current emails - lastEmailIdsRef.current = new Set(emails.map((e: any) => e.id)); + // 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); - - 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', + // If there are new unread emails, queue them for notification + // This works regardless of previousCount value + if (newEmails.length > 0) { + console.log('[useEmailNotifications] 📧 New emails detected:', { + newEmailsCount: newEmails.length, + previousCount, + currentCount, + newEmailIds: newEmails.map((e: any) => e.id), + }); + + 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'; }, - ], - }; - - notificationQueueRef.current.push(notification); - }); + 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)); + console.log('[useEmailNotifications] ⏭️ No new emails detected', { + previousCount, + currentCount, + totalEmails: emails.length, + lastEmailIdsCount: lastEmailIdsRef.current.size, + }); } };