import { NotificationConfig, NotificationOptions, Notification, NotificationUpdate, NotificationChannels, NotificationType, } from '../types'; class NotificationService { private config: NotificationConfig = { channels: {}, defaultDuration: 5000, position: 'top-right', maxStack: 5, }; private notifications: Map = new Map(); private listeners: Map void>> = new Map(); private idCounter = 0; // Configure the notification service configure(config: NotificationConfig): NotificationService { this.config = { ...this.config, ...config }; return this; } // Show a notification show(options: NotificationOptions): string { const id = options.id || this.generateId(); const notification: Notification = { id, title: options.title, message: options.message, type: options.type || 'info', createdAt: new Date(), actions: options.actions, data: options.data, link: options.link, avatar: options.avatar, progress: options.progress, status: options.status, badge: options.badge, }; // Store notification this.notifications.set(id, notification); // Send to channels const channels = options.channels || this.getDefaultChannels(options.type); this.sendToChannels(notification, channels, options); // Auto-dismiss if duration specified if (options.duration && options.duration !== 'persistent') { setTimeout(() => { this.dismiss(id); }, options.duration); } return id; } // Send notification through multiple channels async send(options: NotificationOptions): Promise { const notification: Notification = { id: options.id || this.generateId(), title: options.title, message: options.message, type: options.type || 'info', createdAt: new Date(), actions: options.actions, data: options.data, link: options.link, avatar: options.avatar, progress: options.progress, status: options.status, badge: options.badge, }; // Determine channels const channels = options.channels || { toast: true, bell: true, push: false, email: false, }; // Send to each channel const promises: Promise[] = []; if (channels.toast) { promises.push(this.sendToToast(notification, options)); } if (channels.bell) { promises.push(this.sendToBell(notification, options)); } if (channels.push) { promises.push(this.sendToPush(notification, options)); } if (channels.email && options.audience) { promises.push(this.sendToEmail(notification, options)); } await Promise.all(promises); } // Update an existing notification update(id: string, updates: NotificationUpdate): void { const notification = this.notifications.get(id); if (!notification) return; // Update notification Object.assign(notification, { title: updates.title ?? notification.title, message: updates.message ?? notification.message, type: updates.type ?? notification.type, loading: updates.loading, progress: updates.progress !== undefined ? updates.progress : notification.progress, actions: updates.actions ?? notification.actions, link: updates.link !== undefined ? updates.link : notification.link, avatar: updates.avatar !== undefined ? updates.avatar : notification.avatar, status: updates.status !== undefined ? updates.status : notification.status, badge: updates.badge !== undefined ? updates.badge : notification.badge, }); // Notify listeners this.notifyListeners('update', notification); } // Dismiss a notification dismiss(id: string): void { const notification = this.notifications.get(id); if (!notification) return; notification.dismissedAt = new Date(); // Notify listeners this.notifyListeners('dismiss', notification); // Remove from memory after a delay setTimeout(() => { this.notifications.delete(id); }, 1000); } // Mark notification as read markAsRead(id: string): void { const notification = this.notifications.get(id); if (!notification) return; notification.readAt = new Date(); // Notify listeners this.notifyListeners('read', notification); } // Get all notifications getAll(filter?: { unread?: boolean; type?: NotificationType }): Notification[] { let notifications = Array.from(this.notifications.values()); if (filter?.unread) { notifications = notifications.filter(n => !n.readAt); } if (filter?.type) { notifications = notifications.filter(n => n.type === filter.type); } return notifications.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime() ); } // Get unread count getUnreadCount(): number { return Array.from(this.notifications.values()) .filter(n => !n.readAt && !n.dismissedAt) .length; } // Clear all notifications clear(): void { this.notifications.clear(); this.notifyListeners('clear', null); } // Subscribe to notification events subscribe(event: string, callback: (notification: Notification | null) => void): () => void { if (!this.listeners.has(event)) { this.listeners.set(event, new Set()); } this.listeners.get(event)!.add(callback); // Return unsubscribe function return () => { this.listeners.get(event)?.delete(callback); }; } // Private methods private generateId(): string { return `notification_${Date.now()}_${++this.idCounter}`; } private getDefaultChannels(type?: NotificationType): NotificationChannels { // Default channels based on notification type switch (type) { case 'error': return { toast: true, bell: true, push: false, email: false }; case 'warning': return { toast: true, bell: true, push: false, email: false }; case 'success': return { toast: true, bell: false, push: false, email: false }; case 'loading': return { toast: true, bell: false, push: false, email: false }; default: return { toast: true, bell: true, push: false, email: false }; } } private sendToChannels( notification: Notification, channels: NotificationChannels, options: NotificationOptions ): void { if (channels.toast) { this.sendToToast(notification, options); } if (channels.bell) { this.sendToBell(notification, options); } if (channels.push) { this.sendToPush(notification, options); } if (channels.email && options.audience) { this.sendToEmail(notification, options); } } private async sendToToast(notification: Notification, options: NotificationOptions): Promise { // Toast implementation would go here this.notifyListeners('toast', notification); } private async sendToBell(notification: Notification, options: NotificationOptions): Promise { // Bell notification implementation would go here this.notifyListeners('bell', notification); } private async sendToPush(notification: Notification, options: NotificationOptions): Promise { // Push notification implementation would go here console.log('Push notifications not implemented yet'); } private async sendToEmail(notification: Notification, options: NotificationOptions): Promise { // Email notification implementation would go here console.log('Email notifications would integrate with email service'); } private notifyListeners(event: string, notification: Notification | null): void { const listeners = this.listeners.get(event); if (listeners) { listeners.forEach(callback => callback(notification)); } } } // Singleton instance export const notifications = new NotificationService();