mirror of
https://github.com/dergigi/boris.git
synced 2025-12-17 14:44:26 +01:00
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:
@@ -72,15 +72,26 @@ export function useArticleLoader({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
mountedRef.current = true
|
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
|
// Synchronously check cache sources BEFORE starting async loading
|
||||||
// This prevents showing loading skeletons when content is immediately available
|
// This prevents showing loading skeletons when content is immediately available
|
||||||
// Do this outside the async function for immediate execution
|
// Do this outside the async function for immediate execution
|
||||||
try {
|
try {
|
||||||
|
console.log('[article-loader] Checking localStorage cache...')
|
||||||
// Check localStorage cache first (synchronous)
|
// Check localStorage cache first (synchronous)
|
||||||
const cachedArticle = getFromCache(naddr)
|
const cachedArticle = getFromCache(naddr)
|
||||||
if (cachedArticle) {
|
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'
|
const title = cachedArticle.title || 'Untitled Article'
|
||||||
setCurrentTitle(title)
|
setCurrentTitle(title)
|
||||||
setReaderContent({
|
setReaderContent({
|
||||||
@@ -146,16 +157,24 @@ export function useArticleLoader({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return early - we have cached content, no need to query relays
|
// Return early - we have cached content, no need to query relays
|
||||||
|
console.log('[article-loader] Returning early with cached content')
|
||||||
return
|
return
|
||||||
|
} else {
|
||||||
|
console.log('[article-loader] ❌ Cache MISS - not found in localStorage')
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// If cache check fails, fall through to async loading
|
// 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 loadArticle = async () => {
|
||||||
const requestId = ++currentRequestIdRef.current
|
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}`)
|
setSelectedUrl(`nostr:${naddr}`)
|
||||||
setIsCollapsed(true)
|
setIsCollapsed(true)
|
||||||
@@ -167,14 +186,22 @@ export function useArticleLoader({
|
|||||||
// Check eventStore for instant load (from bookmark cards, explore, etc.)
|
// Check eventStore for instant load (from bookmark cards, explore, etc.)
|
||||||
// Cache was already checked synchronously above, so this only handles EventStore
|
// Cache was already checked synchronously above, so this only handles EventStore
|
||||||
if (eventStore) {
|
if (eventStore) {
|
||||||
|
console.log('[article-loader] Checking EventStore...')
|
||||||
try {
|
try {
|
||||||
// Decode naddr to get the coordinate
|
// Decode naddr to get the coordinate
|
||||||
const decoded = nip19.decode(naddr)
|
const decoded = nip19.decode(naddr)
|
||||||
if (decoded.type === 'naddr') {
|
if (decoded.type === 'naddr') {
|
||||||
const pointer = decoded.data as AddressPointer
|
const pointer = decoded.data as AddressPointer
|
||||||
const coordinate = `${pointer.kind}:${pointer.pubkey}:${pointer.identifier}`
|
const coordinate = `${pointer.kind}:${pointer.pubkey}:${pointer.identifier}`
|
||||||
|
console.log('[article-loader] Looking for event with coordinate:', coordinate)
|
||||||
const storedEvent = eventStore.getEvent?.(coordinate)
|
const storedEvent = eventStore.getEvent?.(coordinate)
|
||||||
if (storedEvent) {
|
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'
|
const title = Helpers.getArticleTitle(storedEvent) || 'Untitled Article'
|
||||||
setCurrentTitle(title)
|
setCurrentTitle(title)
|
||||||
const image = Helpers.getArticleImage(storedEvent)
|
const image = Helpers.getArticleImage(storedEvent)
|
||||||
@@ -197,17 +224,24 @@ export function useArticleLoader({
|
|||||||
|
|
||||||
// If we found the content in EventStore, we can return early
|
// If we found the content in EventStore, we can return early
|
||||||
// This prevents unnecessary relay queries when offline
|
// This prevents unnecessary relay queries when offline
|
||||||
|
console.log('[article-loader] Returning early with EventStore content')
|
||||||
return
|
return
|
||||||
|
} else {
|
||||||
|
console.log('[article-loader] ❌ EventStore MISS - no event found for coordinate:', coordinate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Ignore store errors, fall through to relay query
|
// 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
|
// At this point, we've checked EventStore and cache - neither had content
|
||||||
// Only show loading skeleton if we also don't have preview data
|
// Only show loading skeleton if we also don't have preview data
|
||||||
if (previewData) {
|
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!)
|
// If we have preview data from navigation, show it immediately (no skeleton!)
|
||||||
setCurrentTitle(previewData.title)
|
setCurrentTitle(previewData.title)
|
||||||
setReaderContent({
|
setReaderContent({
|
||||||
@@ -221,11 +255,13 @@ export function useArticleLoader({
|
|||||||
setReaderLoading(false) // Turn off loading immediately - we have the preview!
|
setReaderLoading(false) // Turn off loading immediately - we have the preview!
|
||||||
} else {
|
} else {
|
||||||
// No cache, no EventStore, no preview data - need to load from relays
|
// 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)
|
setReaderLoading(true)
|
||||||
setReaderContent(undefined)
|
setReaderContent(undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.log('[article-loader] Querying relays for article...')
|
||||||
// Decode naddr to filter
|
// Decode naddr to filter
|
||||||
const decoded = nip19.decode(naddr)
|
const decoded = nip19.decode(naddr)
|
||||||
if (decoded.type !== 'naddr') {
|
if (decoded.type !== 'naddr') {
|
||||||
@@ -237,15 +273,34 @@ export function useArticleLoader({
|
|||||||
authors: [pointer.pubkey],
|
authors: [pointer.pubkey],
|
||||||
'#d': [pointer.identifier]
|
'#d': [pointer.identifier]
|
||||||
}
|
}
|
||||||
|
console.log('[article-loader] Relay query filter:', filter)
|
||||||
|
|
||||||
let firstEmitted = false
|
let firstEmitted = false
|
||||||
let latestEvent: NostrEvent | null = null
|
let latestEvent: NostrEvent | null = null
|
||||||
|
|
||||||
// Stream local-first via queryEvents; rely on EOSE (no timeouts)
|
// Stream local-first via queryEvents; rely on EOSE (no timeouts)
|
||||||
|
console.log('[article-loader] Starting queryEvents...')
|
||||||
const events = await queryEvents(relayPool, filter, {
|
const events = await queryEvents(relayPool, filter, {
|
||||||
onEvent: (evt) => {
|
onEvent: (evt) => {
|
||||||
if (!mountedRef.current) return
|
if (!mountedRef.current) {
|
||||||
if (currentRequestIdRef.current !== requestId) return
|
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
|
// Store in event store for future local reads
|
||||||
try {
|
try {
|
||||||
@@ -262,6 +317,7 @@ export function useArticleLoader({
|
|||||||
|
|
||||||
// Emit immediately on first event
|
// Emit immediately on first event
|
||||||
if (!firstEmitted) {
|
if (!firstEmitted) {
|
||||||
|
console.log('[article-loader] ✅ First event received - updating UI immediately')
|
||||||
firstEmitted = true
|
firstEmitted = true
|
||||||
const title = Helpers.getArticleTitle(evt) || 'Untitled Article'
|
const title = Helpers.getArticleTitle(evt) || 'Untitled Article'
|
||||||
setCurrentTitle(title)
|
setCurrentTitle(title)
|
||||||
@@ -282,15 +338,31 @@ export function useArticleLoader({
|
|||||||
setCurrentArticleEventId(evt.id)
|
setCurrentArticleEventId(evt.id)
|
||||||
setCurrentArticle?.(evt)
|
setCurrentArticle?.(evt)
|
||||||
setReaderLoading(false)
|
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
|
// 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
|
const finalEvent = (events.sort((a, b) => b.created_at - a.created_at)[0]) || latestEvent
|
||||||
if (finalEvent) {
|
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'
|
const title = Helpers.getArticleTitle(finalEvent) || 'Untitled Article'
|
||||||
setCurrentTitle(title)
|
setCurrentTitle(title)
|
||||||
const image = Helpers.getArticleImage(finalEvent)
|
const image = Helpers.getArticleImage(finalEvent)
|
||||||
@@ -310,9 +382,16 @@ export function useArticleLoader({
|
|||||||
setCurrentArticleCoordinate(articleCoordinate)
|
setCurrentArticleCoordinate(articleCoordinate)
|
||||||
setCurrentArticleEventId(finalEvent.id)
|
setCurrentArticleEventId(finalEvent.id)
|
||||||
setCurrentArticle?.(finalEvent)
|
setCurrentArticle?.(finalEvent)
|
||||||
|
console.log('[article-loader] ✅ Finalized with event from relays')
|
||||||
} else {
|
} else {
|
||||||
// As a last resort, fall back to the legacy helper (which includes cache)
|
// 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)
|
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
|
if (!mountedRef.current || currentRequestIdRef.current !== requestId) return
|
||||||
setCurrentTitle(article.title)
|
setCurrentTitle(article.title)
|
||||||
setReaderContent({
|
setReaderContent({
|
||||||
|
|||||||
@@ -37,19 +37,36 @@ function getCacheKey(naddr: string): string {
|
|||||||
export function getFromCache(naddr: string): ArticleContent | null {
|
export function getFromCache(naddr: string): ArticleContent | null {
|
||||||
try {
|
try {
|
||||||
const cacheKey = getCacheKey(naddr)
|
const cacheKey = getCacheKey(naddr)
|
||||||
|
console.log('[article-cache] Checking cache with key:', cacheKey)
|
||||||
const cached = localStorage.getItem(cacheKey)
|
const cached = localStorage.getItem(cacheKey)
|
||||||
if (!cached) return null
|
if (!cached) {
|
||||||
|
console.log('[article-cache] ❌ No cached entry found')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const { content, timestamp }: CachedArticle = JSON.parse(cached)
|
const { content, timestamp }: CachedArticle = JSON.parse(cached)
|
||||||
const age = Date.now() - timestamp
|
const age = Date.now() - timestamp
|
||||||
|
console.log('[article-cache] Found cached entry', {
|
||||||
|
age: age,
|
||||||
|
ageDays: Math.floor(age / (24 * 60 * 60 * 1000)),
|
||||||
|
ttlDays: Math.floor(CACHE_TTL / (24 * 60 * 60 * 1000)),
|
||||||
|
isExpired: age > CACHE_TTL
|
||||||
|
})
|
||||||
|
|
||||||
if (age > CACHE_TTL) {
|
if (age > CACHE_TTL) {
|
||||||
|
console.log('[article-cache] ⚠️ Cache expired, removing')
|
||||||
localStorage.removeItem(cacheKey)
|
localStorage.removeItem(cacheKey)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('[article-cache] ✅ Cache valid, returning content', {
|
||||||
|
title: content.title,
|
||||||
|
hasMarkdown: !!content.markdown,
|
||||||
|
markdownLength: content.markdown?.length
|
||||||
|
})
|
||||||
return content
|
return content
|
||||||
} catch {
|
} catch (err) {
|
||||||
|
console.warn('[article-cache] Error reading cache:', err)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user