notifications big
This commit is contained in:
parent
477a0f324d
commit
cc1bab76c7
@ -1,6 +1,6 @@
|
||||
import React, { memo, useState, useEffect } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { Bell, Check, ExternalLink, AlertCircle, LogIn, Kanban, MessageSquare } from 'lucide-react';
|
||||
import { Bell, Check, ExternalLink, AlertCircle, LogIn, Kanban, MessageSquare, Mail } from 'lucide-react';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { useNotifications } from '@/hooks/use-notifications';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@ -215,6 +215,12 @@ export const NotificationBadge = memo(function NotificationBadge({ className }:
|
||||
Parole
|
||||
</Badge>
|
||||
)}
|
||||
{notification.source === 'email' && (
|
||||
<Badge variant="outline" className="ml-2 text-[10px] py-0 px-1.5 bg-green-50 text-green-700 border-green-200 flex items-center">
|
||||
<Mail className="mr-1 h-2.5 w-2.5" />
|
||||
Courrier
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{formatDistanceToNow(new Date(notification.timestamp), { addSuffix: true })}
|
||||
|
||||
@ -205,10 +205,143 @@ export class EmailAdapter implements NotificationAdapter {
|
||||
}
|
||||
|
||||
async getNotifications(userId: string, page = 1, limit = 20): Promise<Notification[]> {
|
||||
// For now, return empty array - we can implement this later if needed
|
||||
// The notification count is what matters for the badge
|
||||
// In the future, we could return unread emails as notifications
|
||||
return [];
|
||||
logger.debug('[EMAIL_ADAPTER] getNotifications called', { userId, page, limit });
|
||||
|
||||
try {
|
||||
// Get all accounts from the database
|
||||
const accounts = await prisma.mailCredentials.findMany({
|
||||
where: { userId },
|
||||
select: {
|
||||
id: true,
|
||||
email: true
|
||||
}
|
||||
});
|
||||
|
||||
if (accounts.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const notifications: Notification[] = [];
|
||||
|
||||
// For each account, get unread emails from INBOX
|
||||
// Use the same flow as getEmails() but filter for unread only
|
||||
for (const account of accounts) {
|
||||
try {
|
||||
const client = await getImapConnection(userId, account.id);
|
||||
|
||||
// Use the same approach as getEmails() - open mailbox first
|
||||
const mailboxInfo = await client.mailboxOpen('INBOX');
|
||||
|
||||
if (!mailboxInfo || typeof mailboxInfo === 'boolean') {
|
||||
logger.debug('[EMAIL_ADAPTER] Could not open INBOX', {
|
||||
userId,
|
||||
accountId: account.id,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const totalEmails = mailboxInfo.exists || 0;
|
||||
|
||||
if (totalEmails === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Search for unread emails (same as getNotificationCount logic)
|
||||
const searchResult = await client.search({ seen: false });
|
||||
|
||||
if (!searchResult || searchResult.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Limit the number of results for performance (get more than limit to have enough after filtering)
|
||||
const limitedResults = searchResult.slice(0, limit * 3);
|
||||
|
||||
// Fetch email metadata using the same structure as getEmails()
|
||||
const messages = await client.fetch(limitedResults, {
|
||||
envelope: true,
|
||||
flags: true,
|
||||
uid: true
|
||||
});
|
||||
|
||||
// Convert to notifications (same format as getEmails() processes emails)
|
||||
for await (const message of messages) {
|
||||
// Filter: only process if truly unread (double-check flags)
|
||||
if (message.flags && message.flags.has('\\Seen')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const envelope = message.envelope;
|
||||
const from = envelope.from?.[0];
|
||||
const subject = envelope.subject || '(Sans objet)';
|
||||
const fromName = from?.name || from?.address || 'Expéditeur inconnu';
|
||||
const fromAddress = from?.address || '';
|
||||
|
||||
const notification: Notification = {
|
||||
id: `email-${account.id}-${message.uid}`,
|
||||
source: 'email',
|
||||
sourceId: message.uid.toString(),
|
||||
type: 'email',
|
||||
title: 'Email',
|
||||
message: `${fromName}${fromAddress ? ` <${fromAddress}>` : ''}: ${subject}`,
|
||||
link: `/courrier?accountId=${account.id}&folder=INBOX&emailId=${message.uid}`,
|
||||
isRead: false,
|
||||
timestamp: envelope.date || new Date(),
|
||||
priority: 'normal',
|
||||
user: {
|
||||
id: fromAddress,
|
||||
name: fromName,
|
||||
},
|
||||
metadata: {
|
||||
accountId: account.id,
|
||||
accountEmail: account.email,
|
||||
folder: 'INBOX',
|
||||
emailId: message.uid.toString(),
|
||||
}
|
||||
};
|
||||
|
||||
notifications.push(notification);
|
||||
}
|
||||
|
||||
// Close mailbox (same as getEmails() does implicitly via connection pool)
|
||||
try {
|
||||
await client.mailboxClose();
|
||||
} catch (closeError) {
|
||||
// Non-fatal, connection pool will handle it
|
||||
logger.debug('[EMAIL_ADAPTER] Error closing mailbox (non-fatal)', {
|
||||
error: closeError instanceof Error ? closeError.message : String(closeError),
|
||||
});
|
||||
}
|
||||
} catch (accountError) {
|
||||
logger.error('[EMAIL_ADAPTER] Error processing account for notifications', {
|
||||
userId,
|
||||
accountId: account.id,
|
||||
error: accountError instanceof Error ? accountError.message : String(accountError),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by timestamp (newest first) and apply pagination
|
||||
notifications.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
|
||||
|
||||
const startIndex = (page - 1) * limit;
|
||||
const endIndex = startIndex + limit;
|
||||
const paginatedNotifications = notifications.slice(startIndex, endIndex);
|
||||
|
||||
logger.debug('[EMAIL_ADAPTER] getNotifications result', {
|
||||
total: notifications.length,
|
||||
returned: paginatedNotifications.length,
|
||||
page,
|
||||
limit,
|
||||
});
|
||||
|
||||
return paginatedNotifications;
|
||||
} catch (error) {
|
||||
logger.error('[EMAIL_ADAPTER] Error getting notifications', {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async markAsRead(userId: string, notificationId: string): Promise<boolean> {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user