2026-01-13 14:49:19 +01:00

123 lines
4.6 KiB
TypeScript

import type { Event, Filter } from 'nostr-tools'
import type { SimplePoolWithSub } from '@/types/nostr-tools-extended'
import { createSubscription } from '@/types/nostr-tools-extended'
import { buildTagFilter, extractTagsFromEvent } from '../nostrTagSystem'
import { getPrimaryRelaySync } from '../config'
import { MIN_EVENT_DATE, PLATFORM_SERVICE } from '../platformConfig'
import { getLatestVersion } from '../versionManager'
import { objectCache } from '../objectCache'
import { parsePresentationEvent } from './parsePresentationEvent'
export async function fetchAuthorPresentationFromPool(
pool: SimplePoolWithSub,
pubkey: string
): Promise<import('@/types/nostr').AuthorPresentationArticle | null> {
const cached = await objectCache.getAuthorByPubkey(pubkey)
if (cached) {
return enrichAuthorPresentationWithSponsoring({ author: cached, pubkey })
}
const filters: Filter[] = [
{
...buildTagFilter({ type: 'author', authorPubkey: pubkey, service: PLATFORM_SERVICE }),
since: MIN_EVENT_DATE,
limit: 100,
},
]
return fetchAuthorPresentationFromRelay({ pool, pubkey, filters })
}
async function enrichAuthorPresentationWithSponsoring(params: {
author: import('@/types/nostr').AuthorPresentationArticle
pubkey: string
}): Promise<import('@/types/nostr').AuthorPresentationArticle> {
const { getAuthorSponsoring } = await import('../sponsoring')
const totalSponsoring = await getAuthorSponsoring(params.pubkey)
return { ...params.author, totalSponsoring }
}
async function fetchAuthorPresentationFromRelay(params: {
pool: SimplePoolWithSub
pubkey: string
filters: Filter[]
}): Promise<import('@/types/nostr').AuthorPresentationArticle | null> {
const relayUrl = getPrimaryRelaySync()
const events: Event[] = []
const sub = createSubscription(params.pool, [relayUrl], params.filters)
const finalize = createRelayFetchFinalizer({ pubkey: params.pubkey, sub, events })
return new Promise<import('@/types/nostr').AuthorPresentationArticle | null>((resolve) => {
const finalizeAndResolve = async (value: import('@/types/nostr').AuthorPresentationArticle | null): Promise<void> => {
const finalValue = await finalize(value)
resolve(finalValue)
}
sub.on('event', (event: Event): void => {
const tags = extractTagsFromEvent(event)
if (tags.type === 'author' && !tags.hidden) {
events.push(event)
}
})
sub.on('eose', (): void => {
void tryFinalizeFromLatestEvent({ events, finalize: finalizeAndResolve })
})
setTimeout((): void => {
void tryFinalizeFromLatestEvent({ events, finalize: finalizeAndResolve })
}, 2000).unref?.()
})
}
async function tryFinalizeFromLatestEvent(params: {
events: Event[]
finalize: (value: import('@/types/nostr').AuthorPresentationArticle | null) => Promise<void>
}): Promise<void> {
const latestEvent = getLatestVersion(params.events)
if (!latestEvent) {
await params.finalize(null)
return
}
const parsed = await parsePresentationEvent(latestEvent)
await params.finalize(parsed)
}
function createRelayFetchFinalizer(params: {
pubkey: string
sub: import('@/types/nostr-tools-extended').Subscription
events: Event[]
}): (value: import('@/types/nostr').AuthorPresentationArticle | null) => Promise<import('@/types/nostr').AuthorPresentationArticle | null> {
let resolved = false
return async (value: import('@/types/nostr').AuthorPresentationArticle | null): Promise<import('@/types/nostr').AuthorPresentationArticle | null> => {
if (resolved) {
return value
}
resolved = true
params.sub.unsub()
return cacheAuthorPresentationFromEvents({ value, events: params.events, pubkey: params.pubkey })
}
}
async function cacheAuthorPresentationFromEvents(params: {
value: import('@/types/nostr').AuthorPresentationArticle | null
events: Event[]
pubkey: string
}): Promise<import('@/types/nostr').AuthorPresentationArticle | null> {
if (!params.value || params.events.length === 0 || !params.value.hash) {
return params.value
}
const event = params.events.find((e) => e.id === params.value?.id) ?? params.events[0]
if (!event) {
return params.value
}
const tags = extractTagsFromEvent(event)
const cachedValue = await enrichAuthorPresentationWithSponsoring({ author: params.value, pubkey: params.pubkey })
const { writeObjectToCache } = await import('../helpers/writeObjectHelper')
await writeObjectToCache({
objectType: 'author',
hash: params.value.hash,
event,
parsed: cachedValue,
version: tags.version,
hidden: tags.hidden,
index: params.value.index,
})
return cachedValue
}