/** * Query authors by hash ID or pubkey (for backward compatibility) */ import type { Event } from 'nostr-tools' import type { SimplePoolWithSub } from '@/types/nostr-tools-extended' import { buildTagFilter, extractTagsFromEvent } from './nostrTagSystem' import { getPrimaryRelaySync } from './config' import { PLATFORM_SERVICE, MIN_EVENT_DATE } from './platformConfig' import { parsePresentationEvent, fetchAuthorPresentationFromPool } from './articlePublisherHelpersPresentation' import { getLatestVersion } from './versionManager' import { objectCache } from './objectCache' /** * Fetch author presentation by hash ID or pubkey * If the parameter looks like a pubkey (64 hex chars), it uses pubkey lookup * Otherwise, it uses hash ID lookup */ export async function fetchAuthorByHashId( pool: SimplePoolWithSub, hashIdOrPubkey: string ): Promise { // Check if it's a pubkey (64 hex characters) for backward compatibility if (/^[a-f0-9]{64}$/i.test(hashIdOrPubkey)) { return fetchAuthorPresentationFromPool(pool, hashIdOrPubkey) } // Otherwise, treat as hash ID const hashId = hashIdOrPubkey // Check cache first - this is the primary source const cached = await objectCache.get('author', hashId) if (cached) { const presentation = cached as import('@/types/nostr').AuthorPresentationArticle // Calculate totalSponsoring from cache const { getAuthorSponsoring } = await import('./sponsoring') presentation.totalSponsoring = await getAuthorSponsoring(presentation.pubkey) return presentation } const filters = [ { ...buildTagFilter({ type: 'author', id: hashId, service: PLATFORM_SERVICE, }), since: MIN_EVENT_DATE, limit: 100, // Get all versions to find the latest }, ] return new Promise((resolve) => { let resolved = false const relayUrl = getPrimaryRelaySync() const { createSubscription } = require('@/types/nostr-tools-extended') const sub = createSubscription(pool, [relayUrl], filters) const events: Event[] = [] const finalize = async (value: import('@/types/nostr').AuthorPresentationArticle | null): Promise => { if (resolved) { return } resolved = true sub.unsub() // Cache the result if found if (value && events.length > 0) { const event = events.find(e => e.id === value.id) || events[0] if (event) { const tags = extractTagsFromEvent(event) if (value.hash) { // Calculate totalSponsoring from cache before storing const { getAuthorSponsoring } = await import('./sponsoring') value.totalSponsoring = await getAuthorSponsoring(value.pubkey) await objectCache.set('author', value.hash, event, value, tags.version ?? 0, tags.hidden, value.index) } } } resolve(value) } sub.on('event', (event: Event): void => { // Collect all events first const tags = extractTagsFromEvent(event) if (tags.type === 'author' && !tags.hidden && tags.id === hashId) { events.push(event) } }) sub.on('eose', (): void => { void (async (): Promise => { // Get the latest version from all collected events const latestEvent = getLatestVersion(events) if (latestEvent) { const parsed = await parsePresentationEvent(latestEvent) if (parsed) { await finalize(parsed) return } } await finalize(null) })() }) setTimeout((): void => { void (async (): Promise => { // Get the latest version from all collected events const timeoutLatestEvent = getLatestVersion(events) if (timeoutLatestEvent) { const timeoutParsed = await parsePresentationEvent(timeoutLatestEvent) if (timeoutParsed) { await finalize(timeoutParsed) return } } await finalize(null) })() }, 5000).unref?.() }) }