Browse Source

bug-fixes

imwald
Silberengel 1 month ago
parent
commit
e20b19fd80
  1. 78
      src/components/NoteList/index.tsx
  2. 83
      src/pages/primary/NoteListPage/RelaysFeed.tsx

78
src/components/NoteList/index.tsx

@ -219,7 +219,10 @@ const NoteList = forwardRef( @@ -219,7 +219,10 @@ const NoteList = forwardRef(
if (!subRequests.length) {
logger.warn('[NoteList] subRequests is empty, not initializing')
return
setLoading(false)
setEvents([])
// Return a no-op closer function to satisfy the cleanup function
return () => {}
}
async function init() {
@ -236,27 +239,62 @@ const NoteList = forwardRef( @@ -236,27 +239,62 @@ const NoteList = forwardRef(
setHasMore(true)
consecutiveEmptyRef.current = 0 // Reset counter on refresh
const mappedSubRequests = subRequests.map(({ urls, filter }) => ({
urls,
filter: useFilterAsIs
? { ...filter, limit: filter.limit ?? (areAlgoRelays ? ALGO_LIMIT : LIMIT) }
const mappedSubRequests = subRequests.map(({ urls, filter }) => {
// CRITICAL: Always ensure filter has kinds - relays require this to return events
const defaultKinds = showKinds.length > 0 ? showKinds : [kinds.ShortTextNote]
const finalFilter = useFilterAsIs
? {
...filter,
// If filter doesn't have kinds, add them (required for relay queries)
kinds: filter.kinds && filter.kinds.length > 0 ? filter.kinds : defaultKinds,
limit: filter.limit ?? (areAlgoRelays ? ALGO_LIMIT : LIMIT)
}
: {
...filter,
// If showKinds is empty, default to kind 1 (ShortTextNote) only
kinds: showKinds.length > 0 ? showKinds : [kinds.ShortTextNote],
kinds: defaultKinds,
limit: areAlgoRelays ? ALGO_LIMIT : LIMIT
}
}))
// CRITICAL: Validate filter has kinds before subscribing
if (!finalFilter.kinds || finalFilter.kinds.length === 0) {
logger.error('[NoteList] Filter missing kinds! Using default', {
originalFilter: filter,
showKinds,
useFilterAsIs
})
finalFilter.kinds = [kinds.ShortTextNote]
}
return { urls, filter: finalFilter }
})
logger.debug('[NoteList] Subscribing with filters', {
subRequestCount: mappedSubRequests.length,
filters: mappedSubRequests.map(({ urls, filter }) => ({
urls: urls.slice(0, 2), // Log first 2 URLs
kinds: filter.kinds,
limit: filter.limit
limit: filter.limit,
hasKinds: !!(filter.kinds && filter.kinds.length > 0)
}))
})
// CRITICAL: Validate all filters have kinds before subscribing
const invalidFilters = mappedSubRequests.filter(({ filter }) => !filter.kinds || filter.kinds.length === 0)
if (invalidFilters.length > 0) {
logger.error('[NoteList] CRITICAL: Some filters are missing kinds!', {
invalidCount: invalidFilters.length,
totalCount: mappedSubRequests.length,
showKinds,
useFilterAsIs
})
// Don't subscribe with invalid filters - this would return no events
setLoading(false)
setEvents([])
// Return a no-op closer function to satisfy the cleanup function
return () => {}
}
logger.info('[NoteList] About to call subscribeTimeline', {
mappedSubRequestsCount: mappedSubRequests.length
})
@ -265,7 +303,15 @@ const NoteList = forwardRef( @@ -265,7 +303,15 @@ const NoteList = forwardRef(
let timelineKey: string | undefined
try {
const result = await client.subscribeTimeline(
// Add timeout wrapper to prevent subscribeTimeline from hanging indefinitely
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => {
reject(new Error('subscribeTimeline timeout after 5 seconds'))
}, 5000) // 5 second timeout
})
const result = await Promise.race([
client.subscribeTimeline(
mappedSubRequests,
{
onEvents: (events: Event[], eosed: boolean) => {
@ -394,9 +440,11 @@ const NoteList = forwardRef( @@ -394,9 +440,11 @@ const NoteList = forwardRef(
needSort: !areAlgoRelays,
useCache: false // Main feeds should always fetch fresh from relays, not use cache
}
)
closer = result.closer
timelineKey = result.timelineKey
),
timeoutPromise
])
closer = result.closer
timelineKey = result.timelineKey
logger.info('[NoteList] subscribeTimeline completed', {
hasTimelineKey: !!timelineKey,
hasCloser: !!closer
@ -409,13 +457,15 @@ const NoteList = forwardRef( @@ -409,13 +457,15 @@ const NoteList = forwardRef(
stack: error instanceof Error ? error.stack : undefined
})
setLoading(false)
throw error
// Return a no-op closer function instead of throwing - allows cleanup to work
// The error is already logged, no need to crash the component
return () => {}
}
}
const promise = init()
return () => {
promise.then((closer) => closer())
promise.then((closer) => closer?.())
}
}, [subRequestsKey, refreshCount, showKinds, showKind1OPs, showKind1Replies, showKind1111, useFilterAsIs])

83
src/pages/primary/NoteListPage/RelaysFeed.tsx

@ -2,8 +2,10 @@ import NormalFeed from '@/components/NormalFeed' @@ -2,8 +2,10 @@ import NormalFeed from '@/components/NormalFeed'
import { checkAlgoRelay } from '@/lib/relay'
import logger from '@/lib/logger'
import { useFeed } from '@/providers/FeedProvider'
import { useKindFilter } from '@/providers/KindFilterProvider'
import relayInfoService from '@/services/relay-info.service'
import React, { useEffect, useMemo, useState } from 'react'
import { kinds } from 'nostr-tools'
import React, { useEffect, useMemo, useState, useRef } from 'react'
export default function RelaysFeed({
setSubHeader
@ -12,30 +14,32 @@ export default function RelaysFeed({ @@ -12,30 +14,32 @@ export default function RelaysFeed({
}) {
logger.debug('RelaysFeed component rendering')
const { feedInfo, relayUrls } = useFeed()
const [isReady, setIsReady] = useState(false)
const { showKinds } = useKindFilter()
const [areAlgoRelays, setAreAlgoRelays] = useState(false)
const relayInfoFetchedRef = useRef(false)
// Debug logging
logger.debug('RelaysFeed debug:', {
feedInfo,
relayUrls,
isReady
relayUrls: relayUrls.length,
showKinds: showKinds.length
})
// Fetch relay info in background (non-blocking) - don't wait for it to render
useEffect(() => {
// Only fetch once per relayUrls change
if (relayInfoFetchedRef.current || relayUrls.length === 0) {
return
}
const init = async () => {
// If relayUrls is empty, we can't initialize the feed
if (relayUrls.length === 0) {
logger.debug('RelaysFeed: relayUrls is empty, not initializing')
setIsReady(false)
return
}
relayInfoFetchedRef.current = true
// Add timeout to prevent hanging if getRelayInfos is slow
// Add aggressive timeout to prevent hanging (reduced from 5s to 2s)
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => {
reject(new Error('getRelayInfos timeout after 5 seconds'))
}, 5000)
reject(new Error('getRelayInfos timeout after 2 seconds'))
}, 2000)
})
try {
@ -43,39 +47,64 @@ export default function RelaysFeed({ @@ -43,39 +47,64 @@ export default function RelaysFeed({
relayInfoService.getRelayInfos(relayUrls),
timeoutPromise
])
setAreAlgoRelays(relayInfos.every((relayInfo) => checkAlgoRelay(relayInfo)))
setIsReady(true)
logger.debug('RelaysFeed: Initialized successfully', {
const areAlgo = relayInfos.every((relayInfo) => checkAlgoRelay(relayInfo))
setAreAlgoRelays(areAlgo)
logger.debug('RelaysFeed: Relay info fetched successfully', {
relayCount: relayUrls.length,
areAlgoRelays: relayInfos.every((relayInfo) => checkAlgoRelay(relayInfo))
areAlgoRelays: areAlgo
})
} catch (error) {
logger.warn('RelaysFeed: Failed to get relay infos, proceeding anyway', {
logger.debug('RelaysFeed: Failed to get relay infos (non-blocking)', {
error: error instanceof Error ? error.message : String(error),
relayUrls
relayUrls: relayUrls.length
})
// Proceed anyway - we can still show the feed even without relay info
// Default to false - feed will work without this info
setAreAlgoRelays(false)
setIsReady(true)
}
}
init()
// Don't await - let it run in background
init().catch((err) => {
logger.debug('RelaysFeed: Unhandled error in init', { error: err })
setAreAlgoRelays(false)
})
}, [relayUrls])
// Memoize subRequests before any early returns to avoid Rules of Hooks violation
const subRequests = useMemo(() => [{ urls: relayUrls, filter: {} }], [relayUrls])
// Reset fetch flag when relayUrls change
useEffect(() => {
relayInfoFetchedRef.current = false
}, [relayUrls])
if (!isReady) {
// Early returns for invalid feed types
if (feedInfo.feedType !== 'relay' && feedInfo.feedType !== 'relays' && feedInfo.feedType !== 'all-favorites') {
return null
}
if (feedInfo.feedType !== 'relay' && feedInfo.feedType !== 'relays' && feedInfo.feedType !== 'all-favorites') {
// CRITICAL: Don't render feed if relayUrls is empty - this would cause subscription to fail
if (relayUrls.length === 0) {
logger.debug('RelaysFeed: relayUrls is empty, not rendering feed')
return null
}
// CRITICAL: Provide proper filter with default kinds - NoteList requires kinds in filter
// Use showKinds from KindFilterProvider if available, otherwise default to kind 1
const defaultKinds = showKinds.length > 0 ? showKinds : [kinds.ShortTextNote]
// Memoize subRequests with proper filter - this ensures NoteList gets valid filter
const subRequests = useMemo(() => {
return [{
urls: relayUrls,
filter: {
kinds: defaultKinds
}
}]
}, [relayUrls, defaultKinds])
logger.component('RelaysFeed', 'Rendering NormalFeed', {
subRequests: subRequests.length,
relayUrls: relayUrls.length,
areAlgoRelays
areAlgoRelays,
filterKinds: subRequests[0]?.filter?.kinds?.length || 0
})
return (

Loading…
Cancel
Save