|
|
|
|
@ -58,9 +58,13 @@ export async function cacheEvent(event: NostrEvent): Promise<void> {
@@ -58,9 +58,13 @@ export async function cacheEvent(event: NostrEvent): Promise<void> {
|
|
|
|
|
existingEvent = eventsByPubkey.find((e: CachedEvent) => e.kind === event.kind) as CachedEvent | undefined; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// If we found an existing event and it's different from the new one, save it to version history
|
|
|
|
|
if (existingEvent && existingEvent.id !== event.id) { |
|
|
|
|
// If we found an existing event and it's different from the new one,
|
|
|
|
|
// and the new event is newer, save the old one to version history
|
|
|
|
|
if (existingEvent && existingEvent.id !== event.id && event.created_at > existingEvent.created_at) { |
|
|
|
|
await saveEventVersion(existingEvent); |
|
|
|
|
} else if (existingEvent && existingEvent.id !== event.id && event.created_at <= existingEvent.created_at) { |
|
|
|
|
// New event is not newer - don't cache it, keep the existing one
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} catch (error) { |
|
|
|
|
// Version history save failed (non-critical) - continue with caching
|
|
|
|
|
@ -101,12 +105,76 @@ export async function cacheEvents(events: NostrEvent[]): Promise<void> {
@@ -101,12 +105,76 @@ export async function cacheEvents(events: NostrEvent[]): Promise<void> {
|
|
|
|
|
const deletedIds = await getDeletedEventIds(eventIds); |
|
|
|
|
|
|
|
|
|
// Filter out deleted events
|
|
|
|
|
const eventsToCache = events.filter(e => !deletedIds.has(e.id)); |
|
|
|
|
let eventsToCache = events.filter(e => !deletedIds.has(e.id)); |
|
|
|
|
|
|
|
|
|
if (eventsToCache.length === 0) return; |
|
|
|
|
|
|
|
|
|
// Create a new transaction for writing (after the read transaction is complete)
|
|
|
|
|
// For replaceable events, check for existing versions and save them to history
|
|
|
|
|
const db = await getDB(); |
|
|
|
|
const replaceableEvents = eventsToCache.filter(e =>
|
|
|
|
|
isReplaceableKind(e.kind) || isParameterizedReplaceableKind(e.kind) |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
const eventsToSkip = new Set<string>(); // Track events that shouldn't be cached
|
|
|
|
|
|
|
|
|
|
if (replaceableEvents.length > 0) { |
|
|
|
|
try { |
|
|
|
|
// Check each replaceable event for existing versions
|
|
|
|
|
for (const event of replaceableEvents) { |
|
|
|
|
const dTag = isParameterizedReplaceableKind(event.kind) |
|
|
|
|
? event.tags.find(t => t[0] === 'd')?.[1] || null |
|
|
|
|
: null; |
|
|
|
|
|
|
|
|
|
// Find existing event with same pubkey, kind, and d tag (if parameterized)
|
|
|
|
|
let existingEvent: CachedEvent | undefined; |
|
|
|
|
|
|
|
|
|
if (isParameterizedReplaceableKind(event.kind) && dTag) { |
|
|
|
|
// For parameterized replaceable, need to search by pubkey, kind, and d tag
|
|
|
|
|
const tx = db.transaction('events', 'readonly'); |
|
|
|
|
const pubkeyIndex = tx.store.index('pubkey'); |
|
|
|
|
const eventsByPubkey = await pubkeyIndex.getAll(event.pubkey); |
|
|
|
|
await tx.done; |
|
|
|
|
|
|
|
|
|
// Filter to same kind and d tag
|
|
|
|
|
existingEvent = eventsByPubkey.find((e: CachedEvent) => { |
|
|
|
|
if (e.kind !== event.kind) return false; |
|
|
|
|
const existingDTag = e.tags.find(t => t[0] === 'd')?.[1] || null; |
|
|
|
|
return existingDTag === dTag; |
|
|
|
|
}) as CachedEvent | undefined; |
|
|
|
|
} else { |
|
|
|
|
// For regular replaceable, search by pubkey and kind
|
|
|
|
|
const tx = db.transaction('events', 'readonly'); |
|
|
|
|
const pubkeyIndex = tx.store.index('pubkey'); |
|
|
|
|
const eventsByPubkey = await pubkeyIndex.getAll(event.pubkey); |
|
|
|
|
await tx.done; |
|
|
|
|
|
|
|
|
|
// Filter to same kind
|
|
|
|
|
existingEvent = eventsByPubkey.find((e: CachedEvent) => e.kind === event.kind) as CachedEvent | undefined; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// If we found an existing event and it's different from the new one,
|
|
|
|
|
// and the new event is newer, save the old one to version history
|
|
|
|
|
if (existingEvent && existingEvent.id !== event.id && event.created_at > existingEvent.created_at) { |
|
|
|
|
await saveEventVersion(existingEvent).catch((error) => { |
|
|
|
|
// Non-critical - version history failures shouldn't break caching
|
|
|
|
|
console.debug('Error saving event version to history in batch:', error); |
|
|
|
|
}); |
|
|
|
|
} else if (existingEvent && existingEvent.id !== event.id && event.created_at <= existingEvent.created_at) { |
|
|
|
|
// New event is not newer - mark it to skip caching
|
|
|
|
|
eventsToSkip.add(event.id); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} catch (error) { |
|
|
|
|
// Version history save failed (non-critical) - continue with caching
|
|
|
|
|
console.error('Error saving event versions to history in batch:', error); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Filter out events that are not newer than existing cached versions
|
|
|
|
|
eventsToCache = eventsToCache.filter(e => !eventsToSkip.has(e.id)); |
|
|
|
|
if (eventsToCache.length === 0) return; |
|
|
|
|
|
|
|
|
|
// Create a new transaction for writing (after the read transaction is complete)
|
|
|
|
|
const tx = db.transaction('events', 'readwrite'); |
|
|
|
|
|
|
|
|
|
// Prepare all cached events first
|
|
|
|
|
@ -120,6 +188,18 @@ export async function cacheEvents(events: NostrEvent[]): Promise<void> {
@@ -120,6 +188,18 @@ export async function cacheEvents(events: NostrEvent[]): Promise<void> {
|
|
|
|
|
|
|
|
|
|
// Wait for transaction to complete
|
|
|
|
|
await tx.done; |
|
|
|
|
|
|
|
|
|
// Also cache profile events (kind 0) in the profile cache
|
|
|
|
|
const profileEvents = eventsToCache.filter(e => e.kind === KIND.METADATA); |
|
|
|
|
if (profileEvents.length > 0) { |
|
|
|
|
const { cacheProfile } = await import('./profile-cache.js'); |
|
|
|
|
await Promise.all(profileEvents.map(event =>
|
|
|
|
|
cacheProfile(event).catch((error) => { |
|
|
|
|
// Non-critical - profile cache failures shouldn't break event caching
|
|
|
|
|
console.debug('Error caching profile in profile cache:', error); |
|
|
|
|
}) |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
} catch (error) { |
|
|
|
|
// Cache write failed (non-critical)
|
|
|
|
|
// Don't throw - caching failures shouldn't break the app
|
|
|
|
|
|