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(
if (!subRequests.length) { if (!subRequests.length) {
logger.warn('[NoteList] subRequests is empty, not initializing') 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() { async function init() {
@ -236,26 +239,61 @@ const NoteList = forwardRef(
setHasMore(true) setHasMore(true)
consecutiveEmptyRef.current = 0 // Reset counter on refresh consecutiveEmptyRef.current = 0 // Reset counter on refresh
const mappedSubRequests = subRequests.map(({ urls, filter }) => ({ const mappedSubRequests = subRequests.map(({ urls, filter }) => {
urls, // CRITICAL: Always ensure filter has kinds - relays require this to return events
filter: useFilterAsIs const defaultKinds = showKinds.length > 0 ? showKinds : [kinds.ShortTextNote]
? { ...filter, limit: filter.limit ?? (areAlgoRelays ? ALGO_LIMIT : LIMIT) } 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, ...filter,
// If showKinds is empty, default to kind 1 (ShortTextNote) only // If showKinds is empty, default to kind 1 (ShortTextNote) only
kinds: showKinds.length > 0 ? showKinds : [kinds.ShortTextNote], kinds: defaultKinds,
limit: areAlgoRelays ? ALGO_LIMIT : LIMIT 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', { logger.debug('[NoteList] Subscribing with filters', {
subRequestCount: mappedSubRequests.length, subRequestCount: mappedSubRequests.length,
filters: mappedSubRequests.map(({ urls, filter }) => ({ filters: mappedSubRequests.map(({ urls, filter }) => ({
urls: urls.slice(0, 2), // Log first 2 URLs urls: urls.slice(0, 2), // Log first 2 URLs
kinds: filter.kinds, 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', { logger.info('[NoteList] About to call subscribeTimeline', {
mappedSubRequestsCount: mappedSubRequests.length mappedSubRequestsCount: mappedSubRequests.length
@ -265,7 +303,15 @@ const NoteList = forwardRef(
let timelineKey: string | undefined let timelineKey: string | undefined
try { 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, mappedSubRequests,
{ {
onEvents: (events: Event[], eosed: boolean) => { onEvents: (events: Event[], eosed: boolean) => {
@ -394,9 +440,11 @@ const NoteList = forwardRef(
needSort: !areAlgoRelays, needSort: !areAlgoRelays,
useCache: false // Main feeds should always fetch fresh from relays, not use cache useCache: false // Main feeds should always fetch fresh from relays, not use cache
} }
) ),
closer = result.closer timeoutPromise
timelineKey = result.timelineKey ])
closer = result.closer
timelineKey = result.timelineKey
logger.info('[NoteList] subscribeTimeline completed', { logger.info('[NoteList] subscribeTimeline completed', {
hasTimelineKey: !!timelineKey, hasTimelineKey: !!timelineKey,
hasCloser: !!closer hasCloser: !!closer
@ -409,13 +457,15 @@ const NoteList = forwardRef(
stack: error instanceof Error ? error.stack : undefined stack: error instanceof Error ? error.stack : undefined
}) })
setLoading(false) 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() const promise = init()
return () => { return () => {
promise.then((closer) => closer()) promise.then((closer) => closer?.())
} }
}, [subRequestsKey, refreshCount, showKinds, showKind1OPs, showKind1Replies, showKind1111, useFilterAsIs]) }, [subRequestsKey, refreshCount, showKinds, showKind1OPs, showKind1Replies, showKind1111, useFilterAsIs])

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

@ -2,8 +2,10 @@ import NormalFeed from '@/components/NormalFeed'
import { checkAlgoRelay } from '@/lib/relay' import { checkAlgoRelay } from '@/lib/relay'
import logger from '@/lib/logger' import logger from '@/lib/logger'
import { useFeed } from '@/providers/FeedProvider' import { useFeed } from '@/providers/FeedProvider'
import { useKindFilter } from '@/providers/KindFilterProvider'
import relayInfoService from '@/services/relay-info.service' 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({ export default function RelaysFeed({
setSubHeader setSubHeader
@ -12,30 +14,32 @@ export default function RelaysFeed({
}) { }) {
logger.debug('RelaysFeed component rendering') logger.debug('RelaysFeed component rendering')
const { feedInfo, relayUrls } = useFeed() const { feedInfo, relayUrls } = useFeed()
const [isReady, setIsReady] = useState(false) const { showKinds } = useKindFilter()
const [areAlgoRelays, setAreAlgoRelays] = useState(false) const [areAlgoRelays, setAreAlgoRelays] = useState(false)
const relayInfoFetchedRef = useRef(false)
// Debug logging // Debug logging
logger.debug('RelaysFeed debug:', { logger.debug('RelaysFeed debug:', {
feedInfo, feedInfo,
relayUrls, relayUrls: relayUrls.length,
isReady showKinds: showKinds.length
}) })
// Fetch relay info in background (non-blocking) - don't wait for it to render
useEffect(() => { useEffect(() => {
// Only fetch once per relayUrls change
if (relayInfoFetchedRef.current || relayUrls.length === 0) {
return
}
const init = async () => { const init = async () => {
// If relayUrls is empty, we can't initialize the feed relayInfoFetchedRef.current = true
if (relayUrls.length === 0) {
logger.debug('RelaysFeed: relayUrls is empty, not initializing')
setIsReady(false)
return
}
// 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) => { const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => { setTimeout(() => {
reject(new Error('getRelayInfos timeout after 5 seconds')) reject(new Error('getRelayInfos timeout after 2 seconds'))
}, 5000) }, 2000)
}) })
try { try {
@ -43,39 +47,64 @@ export default function RelaysFeed({
relayInfoService.getRelayInfos(relayUrls), relayInfoService.getRelayInfos(relayUrls),
timeoutPromise timeoutPromise
]) ])
setAreAlgoRelays(relayInfos.every((relayInfo) => checkAlgoRelay(relayInfo))) const areAlgo = relayInfos.every((relayInfo) => checkAlgoRelay(relayInfo))
setIsReady(true) setAreAlgoRelays(areAlgo)
logger.debug('RelaysFeed: Initialized successfully', { logger.debug('RelaysFeed: Relay info fetched successfully', {
relayCount: relayUrls.length, relayCount: relayUrls.length,
areAlgoRelays: relayInfos.every((relayInfo) => checkAlgoRelay(relayInfo)) areAlgoRelays: areAlgo
}) })
} catch (error) { } 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), 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) 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]) }, [relayUrls])
// Memoize subRequests before any early returns to avoid Rules of Hooks violation // Reset fetch flag when relayUrls change
const subRequests = useMemo(() => [{ urls: relayUrls, filter: {} }], [relayUrls]) 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 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 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', { logger.component('RelaysFeed', 'Rendering NormalFeed', {
subRequests: subRequests.length, subRequests: subRequests.length,
relayUrls: relayUrls.length, relayUrls: relayUrls.length,
areAlgoRelays areAlgoRelays,
filterKinds: subRequests[0]?.filter?.kinds?.length || 0
}) })
return ( return (

Loading…
Cancel
Save