debug: add comprehensive logging to article loader

Add detailed debug logs prefixed with [article-loader] and [article-cache]
to track:
- Cache checks (hit/miss/expired)
- EventStore checks
- Relay queries and event streaming
- UI state updates
- Request lifecycle and abort conditions

This will help debug why articles are still loading from relays on refresh.
This commit is contained in:
Gigi
2025-10-31 00:39:29 +01:00
parent 907ef82efb
commit d0f942c495
2 changed files with 104 additions and 8 deletions

View File

@@ -72,15 +72,26 @@ export function useArticleLoader({
useEffect(() => {
mountedRef.current = true
if (!relayPool || !naddr) return
if (!relayPool || !naddr) {
console.log('[article-loader] Skipping load - missing relayPool or naddr', { hasRelayPool: !!relayPool, hasNaddr: !!naddr })
return
}
console.log('[article-loader] Starting load for naddr:', naddr)
// Synchronously check cache sources BEFORE starting async loading
// This prevents showing loading skeletons when content is immediately available
// Do this outside the async function for immediate execution
try {
console.log('[article-loader] Checking localStorage cache...')
// Check localStorage cache first (synchronous)
const cachedArticle = getFromCache(naddr)
if (cachedArticle) {
console.log('[article-loader] ✅ Cache HIT - loading from localStorage', {
title: cachedArticle.title,
hasMarkdown: !!cachedArticle.markdown,
markdownLength: cachedArticle.markdown?.length
})
const title = cachedArticle.title || 'Untitled Article'
setCurrentTitle(title)
setReaderContent({
@@ -146,16 +157,24 @@ export function useArticleLoader({
}
// Return early - we have cached content, no need to query relays
console.log('[article-loader] Returning early with cached content')
return
} else {
console.log('[article-loader] ❌ Cache MISS - not found in localStorage')
}
} catch (err) {
// If cache check fails, fall through to async loading
console.warn('Cache check failed:', err)
console.warn('[article-loader] Cache check failed:', err)
}
const loadArticle = async () => {
const requestId = ++currentRequestIdRef.current
if (!mountedRef.current) return
console.log('[article-loader] Starting async loadArticle function', { requestId })
if (!mountedRef.current) {
console.log('[article-loader] Component unmounted, aborting')
return
}
setSelectedUrl(`nostr:${naddr}`)
setIsCollapsed(true)
@@ -167,14 +186,22 @@ export function useArticleLoader({
// Check eventStore for instant load (from bookmark cards, explore, etc.)
// Cache was already checked synchronously above, so this only handles EventStore
if (eventStore) {
console.log('[article-loader] Checking EventStore...')
try {
// Decode naddr to get the coordinate
const decoded = nip19.decode(naddr)
if (decoded.type === 'naddr') {
const pointer = decoded.data as AddressPointer
const coordinate = `${pointer.kind}:${pointer.pubkey}:${pointer.identifier}`
console.log('[article-loader] Looking for event with coordinate:', coordinate)
const storedEvent = eventStore.getEvent?.(coordinate)
if (storedEvent) {
console.log('[article-loader] ✅ EventStore HIT - found event', {
id: storedEvent.id,
kind: storedEvent.kind,
hasContent: !!storedEvent.content,
contentLength: storedEvent.content?.length
})
const title = Helpers.getArticleTitle(storedEvent) || 'Untitled Article'
setCurrentTitle(title)
const image = Helpers.getArticleImage(storedEvent)
@@ -197,17 +224,24 @@ export function useArticleLoader({
// If we found the content in EventStore, we can return early
// This prevents unnecessary relay queries when offline
console.log('[article-loader] Returning early with EventStore content')
return
} else {
console.log('[article-loader] ❌ EventStore MISS - no event found for coordinate:', coordinate)
}
}
} catch (err) {
// Ignore store errors, fall through to relay query
console.warn('[article-loader] EventStore check failed:', err)
}
} else {
console.log('[article-loader] No EventStore available, skipping check')
}
// At this point, we've checked EventStore and cache - neither had content
// Only show loading skeleton if we also don't have preview data
if (previewData) {
console.log('[article-loader] Using preview data (no skeleton)', { title: previewData.title })
// If we have preview data from navigation, show it immediately (no skeleton!)
setCurrentTitle(previewData.title)
setReaderContent({
@@ -221,11 +255,13 @@ export function useArticleLoader({
setReaderLoading(false) // Turn off loading immediately - we have the preview!
} else {
// No cache, no EventStore, no preview data - need to load from relays
console.log('[article-loader] ⚠️ No cache, EventStore, or preview - showing loading skeleton and querying relays')
setReaderLoading(true)
setReaderContent(undefined)
}
try {
console.log('[article-loader] Querying relays for article...')
// Decode naddr to filter
const decoded = nip19.decode(naddr)
if (decoded.type !== 'naddr') {
@@ -237,15 +273,34 @@ export function useArticleLoader({
authors: [pointer.pubkey],
'#d': [pointer.identifier]
}
console.log('[article-loader] Relay query filter:', filter)
let firstEmitted = false
let latestEvent: NostrEvent | null = null
// Stream local-first via queryEvents; rely on EOSE (no timeouts)
console.log('[article-loader] Starting queryEvents...')
const events = await queryEvents(relayPool, filter, {
onEvent: (evt) => {
if (!mountedRef.current) return
if (currentRequestIdRef.current !== requestId) return
if (!mountedRef.current) {
console.log('[article-loader] Component unmounted during event stream, ignoring')
return
}
if (currentRequestIdRef.current !== requestId) {
console.log('[article-loader] Request ID mismatch, ignoring event', {
currentRequestId: currentRequestIdRef.current,
eventRequestId: requestId
})
return
}
console.log('[article-loader] 📨 Received event from relay', {
id: evt.id,
kind: evt.kind,
created_at: evt.created_at,
contentLength: evt.content?.length,
isFirst: !firstEmitted
})
// Store in event store for future local reads
try {
@@ -262,6 +317,7 @@ export function useArticleLoader({
// Emit immediately on first event
if (!firstEmitted) {
console.log('[article-loader] ✅ First event received - updating UI immediately')
firstEmitted = true
const title = Helpers.getArticleTitle(evt) || 'Untitled Article'
setCurrentTitle(title)
@@ -282,15 +338,31 @@ export function useArticleLoader({
setCurrentArticleEventId(evt.id)
setCurrentArticle?.(evt)
setReaderLoading(false)
console.log('[article-loader] UI updated with first event')
}
}
})
if (!mountedRef.current || currentRequestIdRef.current !== requestId) return
console.log('[article-loader] QueryEvents completed', {
eventCount: events.length,
hasLatestEvent: !!latestEvent,
mounted: mountedRef.current,
requestIdMatch: currentRequestIdRef.current === requestId
})
if (!mountedRef.current || currentRequestIdRef.current !== requestId) {
console.log('[article-loader] Component unmounted or request ID changed, aborting')
return
}
// Finalize with newest version if it's newer than what we first rendered
const finalEvent = (events.sort((a, b) => b.created_at - a.created_at)[0]) || latestEvent
if (finalEvent) {
console.log('[article-loader] ✅ Finalizing with event', {
id: finalEvent.id,
created_at: finalEvent.created_at,
wasFirstEmitted: firstEmitted
})
const title = Helpers.getArticleTitle(finalEvent) || 'Untitled Article'
setCurrentTitle(title)
const image = Helpers.getArticleImage(finalEvent)
@@ -310,9 +382,16 @@ export function useArticleLoader({
setCurrentArticleCoordinate(articleCoordinate)
setCurrentArticleEventId(finalEvent.id)
setCurrentArticle?.(finalEvent)
console.log('[article-loader] ✅ Finalized with event from relays')
} else {
// As a last resort, fall back to the legacy helper (which includes cache)
console.log('[article-loader] ⚠️ No events from relays, falling back to fetchArticleByNaddr')
const article = await fetchArticleByNaddr(relayPool, naddr, false, settingsRef.current)
console.log('[article-loader] fetchArticleByNaddr result:', {
hasArticle: !!article,
title: article?.title,
hasMarkdown: !!article?.markdown
})
if (!mountedRef.current || currentRequestIdRef.current !== requestId) return
setCurrentTitle(article.title)
setReaderContent({