import type { Event } from 'nostr-tools' import { nostrService } from './nostr' import { zapVerificationService } from './zapVerification' import type { Notification } from '@/types/notifications' import { createSubscription } from '@/types/nostr-tools-extended' import { getPrimaryRelaySync } from './config' function createZapReceiptFilters(userPubkey: string) { return [ { kinds: [9735], // Zap receipt '#p': [userPubkey], // Receipts targeting this user }, ] } async function buildPaymentNotification(event: Event, userPubkey: string): Promise { const paymentInfo = zapVerificationService.extractPaymentInfo(event) if (paymentInfo?.recipient !== userPubkey) { return null } let articleTitle: string | undefined if (paymentInfo.articleId) { try { const article = await nostrService.getArticleById(paymentInfo.articleId) articleTitle = article?.title } catch (e) { console.error('Error loading article for notification:', e) } } return { id: event.id, type: 'payment', title: 'New Payment Received', message: articleTitle ? `Vous avez reçu un zap de ${paymentInfo.amount} sats pour "${articleTitle}"` : `Vous avez reçu un zap de ${paymentInfo.amount} sats`, timestamp: event.created_at, read: false, ...(paymentInfo.articleId ? { articleId: paymentInfo.articleId } : {}), ...(articleTitle ? { articleTitle } : {}), amount: paymentInfo.amount, fromPubkey: paymentInfo.payer, } } function registerZapSubscription( sub: import('@/types/nostr-tools-extended').Subscription, userPubkey: string, onNotification: (notification: Notification) => void ) { sub.on('event', (event: Event) => { void buildPaymentNotification(event, userPubkey) .then((notification) => { if (notification) { onNotification(notification) } }) .catch((error) => { console.error('Error processing zap receipt notification:', error) }) }) } /** * Service for monitoring and managing notifications */ export class NotificationService { private subscriptions: Map void> = new Map() /** * Subscribe to zap receipts (payments) for a user's articles */ subscribeToPayments( userPubkey: string, onNotification: (notification: Notification) => void ): () => void { const pool = nostrService.getPool() if (!pool) { return () => {} } const filters = createZapReceiptFilters(userPubkey) const relayUrl = getPrimaryRelaySync() const sub = createSubscription(pool, [relayUrl], filters) registerZapSubscription(sub, userPubkey, onNotification) const unsubscribe = () => { sub.unsub() } return unsubscribe } /** * Stop all subscriptions */ stopAll(): void { this.subscriptions.forEach((unsubscribe) => unsubscribe()) this.subscriptions.clear() } } export const notificationService = new NotificationService() /** * Load stored notifications from IndexedDB */ export async function loadStoredNotifications(userPubkey: string): Promise { try { const { storageService } = await import('./storage/indexedDB') const key = `notifications_${userPubkey}` const stored = await storageService.get(key, 'notifications_storage') return stored ?? [] } catch (error) { console.error('Error loading stored notifications:', error) return [] } } /** * Save notifications to IndexedDB */ export async function saveNotifications(userPubkey: string, notifications: Notification[]): Promise { try { const { storageService } = await import('./storage/indexedDB') const key = `notifications_${userPubkey}` await storageService.set(key, notifications, 'notifications_storage') } catch (error) { console.error('Error saving notifications:', error) } } /** * Mark notification as read */ export function markNotificationAsRead( userPubkey: string, notificationId: string, notifications: Notification[] ): Notification[] { const updated = notifications.map((n) => n.id === notificationId ? { ...n, read: true } : n ) saveNotifications(userPubkey, updated) return updated } /** * Mark all notifications as read */ export function markAllAsRead(userPubkey: string, notifications: Notification[]): Notification[] { const updated = notifications.map((n) => ({ ...n, read: true })) saveNotifications(userPubkey, updated) return updated } /** * Delete a notification */ export function deleteNotification( userPubkey: string, notificationId: string, notifications: Notification[] ): Notification[] { const updated = notifications.filter((n) => n.id !== notificationId) saveNotifications(userPubkey, updated) return updated }