9 changed files with 332 additions and 118 deletions
@ -0,0 +1,100 @@ |
|||||||
|
import { |
||||||
|
getNoteBech32Id, |
||||||
|
getParentBech32Id, |
||||||
|
getParentEventHexId, |
||||||
|
getRootBech32Id, |
||||||
|
getRootEventHexId, |
||||||
|
resolveDeclaredThreadRootEventHex |
||||||
|
} from '@/lib/event' |
||||||
|
import { shouldDropEventOnIngest } from '@/lib/event-ingest-filter' |
||||||
|
import client, { eventService } from '@/services/client.service' |
||||||
|
import { loadArchivedEventForFetch } from '@/services/event-archive.service' |
||||||
|
import { candidateKeysForNoteUrlId } from '@/services/navigation-event-store' |
||||||
|
import { navigationEventStore } from '@/services/navigation-event-store' |
||||||
|
import type { Event } from 'nostr-tools' |
||||||
|
|
||||||
|
export function resolveEventPointerToHex(eventId: string): string | undefined { |
||||||
|
const trimmed = eventId.trim() |
||||||
|
if (/^[0-9a-f]{64}$/i.test(trimmed)) return trimmed.toLowerCase() |
||||||
|
for (const key of candidateKeysForNoteUrlId(trimmed)) { |
||||||
|
if (/^[0-9a-f]{64}$/i.test(key)) return key.toLowerCase() |
||||||
|
} |
||||||
|
return undefined |
||||||
|
} |
||||||
|
|
||||||
|
function eventMatchesPointer(ev: Event, eventId: string): boolean { |
||||||
|
if (ev.id === eventId) return true |
||||||
|
try { |
||||||
|
return getNoteBech32Id(ev) === eventId |
||||||
|
} catch { |
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** Session, navigation store, archive, publication store, and timeline disk — for parent/root strips. */ |
||||||
|
export async function resolveThreadContextEventFromLocalStores( |
||||||
|
eventId: string, |
||||||
|
initialEvent?: Event |
||||||
|
): Promise<Event | undefined> { |
||||||
|
if (initialEvent && eventMatchesPointer(initialEvent, eventId)) { |
||||||
|
return initialEvent |
||||||
|
} |
||||||
|
|
||||||
|
const fromNav = navigationEventStore.peekEvent(eventId) |
||||||
|
if (fromNav) return fromNav |
||||||
|
|
||||||
|
const fromSession = client.peekSessionCachedEvent(eventId) |
||||||
|
if (fromSession) return fromSession |
||||||
|
|
||||||
|
const hex = resolveEventPointerToHex(eventId) |
||||||
|
if (!hex) return undefined |
||||||
|
|
||||||
|
const fromArchive = await loadArchivedEventForFetch(hex) |
||||||
|
if (fromArchive && !shouldDropEventOnIngest(fromArchive, { explicitNoteLookupHexId: hex })) { |
||||||
|
client.addEventToCache(fromArchive, { explicitNoteLookupHexId: hex }) |
||||||
|
return fromArchive |
||||||
|
} |
||||||
|
|
||||||
|
const fromPublication = await eventService.peekPublicationStoreEvent(hex) |
||||||
|
if (fromPublication) return fromPublication |
||||||
|
|
||||||
|
const rows = await client.getLocalFeedEvents( |
||||||
|
[{ urls: [], filter: { ids: [hex], limit: 1 } }], |
||||||
|
{ maxMatches: 1, maxRowsScanned: 14_000 } |
||||||
|
) |
||||||
|
const hit = rows[0] |
||||||
|
if (hit && !shouldDropEventOnIngest(hit, { explicitNoteLookupHexId: hex })) { |
||||||
|
client.addEventToCache(hit, { explicitNoteLookupHexId: hex }) |
||||||
|
return hit |
||||||
|
} |
||||||
|
|
||||||
|
return undefined |
||||||
|
} |
||||||
|
|
||||||
|
/** Parent + root events from local stores before opening a note panel (avoids thread strip skeletons). */ |
||||||
|
export async function prefetchThreadContextForNavigation(forEvent: Event): Promise<Event[]> { |
||||||
|
const pointers = new Set<string>() |
||||||
|
const parentId = getParentBech32Id(forEvent) |
||||||
|
const rootId = getRootBech32Id(forEvent) |
||||||
|
if (parentId) pointers.add(parentId) |
||||||
|
if (rootId) pointers.add(rootId) |
||||||
|
|
||||||
|
const parentHex = getParentEventHexId(forEvent)?.toLowerCase() |
||||||
|
const rootHex = getRootEventHexId(forEvent)?.toLowerCase() |
||||||
|
if (parentHex && /^[0-9a-f]{64}$/i.test(parentHex)) pointers.add(parentHex) |
||||||
|
if (rootHex && /^[0-9a-f]{64}$/i.test(rootHex)) { |
||||||
|
pointers.add(resolveDeclaredThreadRootEventHex(rootHex)) |
||||||
|
} |
||||||
|
|
||||||
|
pointers.delete(forEvent.id.toLowerCase()) |
||||||
|
|
||||||
|
const out: Event[] = [] |
||||||
|
const seen = new Set<string>() |
||||||
|
for (const pointer of pointers) { |
||||||
|
const ev = await resolveThreadContextEventFromLocalStores(pointer) |
||||||
|
if (!ev || seen.has(ev.id.toLowerCase())) continue |
||||||
|
seen.add(ev.id.toLowerCase()) |
||||||
|
out.push(ev) |
||||||
|
} |
||||||
|
return out |
||||||
|
} |
||||||
Loading…
Reference in new issue