207 lines
6.1 KiB
TypeScript
207 lines
6.1 KiB
TypeScript
/**
|
|
* Notification service - stores and manages notifications in IndexedDB
|
|
*/
|
|
|
|
import { createIndexedDBHelper, type IndexedDBHelper } from './helpers/indexedDBHelper'
|
|
|
|
const DB_NAME = 'nostr_notifications'
|
|
const DB_VERSION = 1
|
|
const STORE_NAME = 'notifications'
|
|
|
|
export type NotificationType =
|
|
| 'purchase' // Achat
|
|
| 'review' // Avis
|
|
| 'sponsoring' // Sponsoring
|
|
| 'review_tip' // Remerciement (tip sur un avis)
|
|
| 'payment_note' // Note de paiement
|
|
| 'published' // Objet publié avec succès (passé de published: false à liste de relais)
|
|
|
|
export interface Notification {
|
|
id: string // Unique notification ID
|
|
type: NotificationType
|
|
objectType: string // Type d'objet (author, series, publication, etc.)
|
|
objectId: string // ID de l'objet dans objectCache
|
|
eventId: string // ID de l'événement Nostr
|
|
timestamp: number // Date de création de la notification (milliseconds)
|
|
read: boolean // Si la notification a été lue
|
|
data?: Record<string, unknown> // Données supplémentaires (relais publiés, montant, etc.)
|
|
// Compatibilité avec l'ancien format
|
|
title?: string
|
|
message?: string
|
|
articleId?: string
|
|
articleTitle?: string
|
|
amount?: number
|
|
fromPubkey?: string
|
|
}
|
|
|
|
class NotificationService {
|
|
private readonly dbHelper: IndexedDBHelper
|
|
|
|
constructor() {
|
|
this.dbHelper = createIndexedDBHelper({
|
|
dbName: DB_NAME,
|
|
version: DB_VERSION,
|
|
storeName: STORE_NAME,
|
|
keyPath: 'id',
|
|
indexes: [
|
|
{ name: 'type', keyPath: 'type', unique: false },
|
|
{ name: 'objectId', keyPath: 'objectId', unique: false },
|
|
{ name: 'eventId', keyPath: 'eventId', unique: false },
|
|
{ name: 'timestamp', keyPath: 'timestamp', unique: false },
|
|
{ name: 'read', keyPath: 'read', unique: false },
|
|
{ name: 'objectType', keyPath: 'objectType', unique: false },
|
|
],
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Create a new notification
|
|
* Utilise writeService pour écrire via Web Worker
|
|
*/
|
|
async createNotification(params: {
|
|
type: NotificationType
|
|
objectType: string
|
|
objectId: string
|
|
eventId: string
|
|
data?: Record<string, unknown>
|
|
}): Promise<void> {
|
|
try {
|
|
const { type, objectType, objectId, eventId, data } = params
|
|
// Check if notification already exists for this event
|
|
const existing = await this.getNotificationByEventId(eventId)
|
|
if (existing) {
|
|
return // Notification already exists
|
|
}
|
|
|
|
// Utiliser writeService pour créer la notification via Web Worker
|
|
const { writeService } = await import('./writeService')
|
|
await writeService.createNotification(type, objectType, objectId, eventId, data)
|
|
} catch (error) {
|
|
console.error('[NotificationService] Error creating notification:', error)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get notification by event ID
|
|
*/
|
|
async getNotificationByEventId(eventId: string): Promise<Notification | null> {
|
|
try {
|
|
return await this.dbHelper.getByIndex<Notification>('eventId', eventId)
|
|
} catch (error) {
|
|
console.error('[NotificationService] Error getting notification by event ID:', error)
|
|
return null
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get all notifications for a user
|
|
*/
|
|
async getAllNotifications(limit: number = 100): Promise<Notification[]> {
|
|
try {
|
|
const notifications: Notification[] = []
|
|
const store = await this.dbHelper.getStore('readonly')
|
|
const index = store.index('timestamp')
|
|
|
|
return new Promise<Notification[]>((resolve, reject) => {
|
|
const request = index.openCursor(null, 'prev') // Descending order (newest first)
|
|
|
|
request.onsuccess = (event: globalThis.Event): void => {
|
|
const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result
|
|
if (cursor) {
|
|
notifications.push(cursor.value as Notification)
|
|
if (notifications.length < limit) {
|
|
cursor.continue()
|
|
} else {
|
|
resolve(notifications)
|
|
}
|
|
} else {
|
|
resolve(notifications)
|
|
}
|
|
}
|
|
|
|
request.onerror = (): void => {
|
|
if (request.error) {
|
|
reject(request.error)
|
|
} else {
|
|
reject(new Error('Unknown error opening cursor'))
|
|
}
|
|
}
|
|
})
|
|
} catch (error) {
|
|
console.error('[NotificationService] Error getting all notifications:', error)
|
|
return []
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get unread notifications count
|
|
*/
|
|
async getUnreadCount(): Promise<number> {
|
|
try {
|
|
return await this.dbHelper.countByIndex('read', IDBKeyRange.only(false))
|
|
} catch (error) {
|
|
console.error('[NotificationService] Error getting unread count:', error)
|
|
return 0
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Mark notification as read
|
|
*/
|
|
async markAsRead(notificationId: string): Promise<void> {
|
|
try {
|
|
const notification = await this.dbHelper.get<Notification>(notificationId)
|
|
if (!notification) {
|
|
throw new Error('Notification not found')
|
|
}
|
|
|
|
const updatedNotification: Notification = {
|
|
...notification,
|
|
read: true,
|
|
}
|
|
|
|
await this.dbHelper.put(updatedNotification)
|
|
} catch (error) {
|
|
console.error('[NotificationService] Error marking notification as read:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Mark all notifications as read
|
|
*/
|
|
async markAllAsRead(): Promise<void> {
|
|
try {
|
|
const notifications = await this.getAllNotifications(10000)
|
|
const unreadNotifications = notifications.filter((n) => !n.read)
|
|
|
|
await Promise.all(
|
|
unreadNotifications.map((notification) => {
|
|
const updatedNotification: Notification = {
|
|
...notification,
|
|
read: true,
|
|
}
|
|
return this.dbHelper.put(updatedNotification)
|
|
})
|
|
)
|
|
} catch (error) {
|
|
console.error('[NotificationService] Error marking all notifications as read:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete notification
|
|
*/
|
|
async deleteNotification(notificationId: string): Promise<void> {
|
|
try {
|
|
await this.dbHelper.delete(notificationId)
|
|
} catch (error) {
|
|
console.error('[NotificationService] Error deleting notification:', error)
|
|
throw error
|
|
}
|
|
}
|
|
}
|
|
|
|
export const notificationService = new NotificationService()
|