/** * In-App Notification Channel * Handles in-application notifications with persistence */ import { InAppNotification, NotificationType } from '../types'; export class InAppChannel { private notifications: Map = new Map(); private maxNotificationsPerUser = 100; /** * Send an in-app notification */ async send(notification: InAppNotification): Promise { const userNotifications = this.notifications.get(notification.userId) || []; // Add new notification at the beginning userNotifications.unshift(notification); // Trim to max if (userNotifications.length > this.maxNotificationsPerUser) { userNotifications.splice(this.maxNotificationsPerUser); } this.notifications.set(notification.userId, userNotifications); console.log(`[InAppChannel] Notification added for user ${notification.userId}`); } /** * Get notifications for a user */ getNotifications(userId: string, options?: { unreadOnly?: boolean; type?: NotificationType; limit?: number; offset?: number; }): InAppNotification[] { let userNotifications = this.notifications.get(userId) || []; // Filter by unread if (options?.unreadOnly) { userNotifications = userNotifications.filter(n => !n.read); } // Filter by type if (options?.type) { userNotifications = userNotifications.filter(n => n.type === options.type); } // Filter expired const now = new Date().toISOString(); userNotifications = userNotifications.filter(n => !n.expiresAt || n.expiresAt > now ); // Apply pagination const offset = options?.offset || 0; const limit = options?.limit || 50; return userNotifications.slice(offset, offset + limit); } /** * Get notification by ID */ getNotification(notificationId: string): InAppNotification | undefined { for (const userNotifications of this.notifications.values()) { const notification = userNotifications.find(n => n.id === notificationId); if (notification) return notification; } return undefined; } /** * Mark notification as read */ markAsRead(notificationId: string): boolean { for (const [userId, userNotifications] of this.notifications.entries()) { const notification = userNotifications.find(n => n.id === notificationId); if (notification) { notification.read = true; return true; } } return false; } /** * Mark all notifications as read for a user */ markAllAsRead(userId: string): number { const userNotifications = this.notifications.get(userId); if (!userNotifications) return 0; let count = 0; userNotifications.forEach(n => { if (!n.read) { n.read = true; count++; } }); return count; } /** * Get unread count for a user */ getUnreadCount(userId: string): number { const userNotifications = this.notifications.get(userId) || []; return userNotifications.filter(n => !n.read).length; } /** * Delete a notification */ delete(notificationId: string): boolean { for (const [userId, userNotifications] of this.notifications.entries()) { const index = userNotifications.findIndex(n => n.id === notificationId); if (index !== -1) { userNotifications.splice(index, 1); return true; } } return false; } /** * Delete all notifications for a user */ deleteAll(userId: string): number { const userNotifications = this.notifications.get(userId); if (!userNotifications) return 0; const count = userNotifications.length; this.notifications.delete(userId); return count; } /** * Clean up expired notifications */ cleanupExpired(): number { const now = new Date().toISOString(); let removed = 0; for (const [userId, userNotifications] of this.notifications.entries()) { const originalLength = userNotifications.length; const filtered = userNotifications.filter(n => !n.expiresAt || n.expiresAt > now ); removed += originalLength - filtered.length; this.notifications.set(userId, filtered); } return removed; } /** * Get stats for a user */ getStats(userId: string): { total: number; unread: number; byType: Record; } { const userNotifications = this.notifications.get(userId) || []; const byType: Record = {}; userNotifications.forEach(n => { byType[n.type] = (byType[n.type] || 0) + 1; }); return { total: userNotifications.length, unread: userNotifications.filter(n => !n.read).length, byType: byType as Record }; } /** * Group notifications by date */ getGroupedByDate(userId: string): { today: InAppNotification[]; yesterday: InAppNotification[]; thisWeek: InAppNotification[]; older: InAppNotification[]; } { const userNotifications = this.notifications.get(userId) || []; const now = new Date(); const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000); const weekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000); const result = { today: [] as InAppNotification[], yesterday: [] as InAppNotification[], thisWeek: [] as InAppNotification[], older: [] as InAppNotification[] }; userNotifications.forEach(n => { const createdAt = new Date(n.createdAt); if (createdAt >= today) { result.today.push(n); } else if (createdAt >= yesterday) { result.yesterday.push(n); } else if (createdAt >= weekAgo) { result.thisWeek.push(n); } else { result.older.push(n); } }); return result; } }