You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

143 lines
4.6 KiB

import NormalFeed from '@/components/NormalFeed'
import type { TNoteListRef } from '@/components/NoteList'
import { checkAlgoRelay } from '@/lib/relay'
import { normalizeUrl } from '@/lib/url'
import { useFeed } from '@/providers/FeedProvider'
import { useKindFilterOrDefaults } from '@/providers/KindFilterProvider'
import relayInfoService from '@/services/relay-info.service'
import { kinds } from 'nostr-tools'
import React, { forwardRef, useEffect, useMemo, useState } from 'react'
const RelaysFeed = forwardRef<
TNoteListRef,
{
setSubHeader?: (node: React.ReactNode) => void
onSubHeaderRefresh?: () => void
/** When set, subscription kinds (fixed list); otherwise uses KindFilterProvider. */
kindsOverride?: number[]
}
>(function RelaysFeed({ setSubHeader, onSubHeaderRefresh, kindsOverride }, ref) {
const { feedInfo, relayUrls } = useFeed()
const { showKinds } = useKindFilterOrDefaults()
const [areAlgoRelays, setAreAlgoRelays] = useState(false)
const [relayAlgoReady, setRelayAlgoReady] = useState(false)
const relayUrlsKey = useMemo(
() =>
[...relayUrls]
.map((u) => normalizeUrl(u) || u)
.filter(Boolean)
.sort()
.join('|'),
[relayUrls]
)
useEffect(() => {
if (relayUrls.length === 0) {
setRelayAlgoReady(false)
return
}
let cancelled = false
setRelayAlgoReady(false)
const init = async () => {
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => {
reject(new Error('getRelayInfos timeout after 8 seconds'))
}, 8000)
})
try {
const relayInfos = await Promise.race([
relayInfoService.getRelayInfos(relayUrls),
timeoutPromise
])
if (cancelled) return
const areAlgo = relayInfos.every((relayInfo) => checkAlgoRelay(relayInfo))
setAreAlgoRelays(areAlgo)
} catch (_error) {
if (!cancelled) setAreAlgoRelays(false)
} finally {
if (!cancelled) setRelayAlgoReady(true)
}
}
void init()
return () => {
cancelled = true
}
}, [relayUrlsKey, relayUrls.length])
const defaultKinds =
kindsOverride && kindsOverride.length > 0
? kindsOverride
: showKinds.length > 0
? showKinds
: [kinds.ShortTextNote]
/** One relay + user kind filter: avoid huge `kinds` REQ (many relays error with "too many kinds"). */
const singleRelayKindlessExplore =
feedInfo.feedType === 'relay' && relayUrls.length === 1 && !kindsOverride?.length
const canRenderFeed =
(feedInfo.feedType === 'relay' ||
feedInfo.feedType === 'relays' ||
feedInfo.feedType === 'all-favorites') &&
relayUrls.length > 0
/** Distinguishes home relay chips so we do not keep the previous timeline on single→all-favorites (strict superset). */
const feedTimelineScopeKey = useMemo(() => {
if (feedInfo.feedType === 'all-favorites') return 'all-favorites'
if (feedInfo.feedType === 'relays') return `relays:${feedInfo.id ?? ''}`
if (feedInfo.feedType === 'relay') {
const id = feedInfo.id ? normalizeUrl(feedInfo.id) || feedInfo.id : ''
return `relay:${id}`
}
return undefined
}, [feedInfo.feedType, feedInfo.id])
// Hooks must run every render — never place useMemo after conditional returns.
const subRequests = useMemo(() => {
if (!canRenderFeed) return []
if (singleRelayKindlessExplore) {
return [{ urls: relayUrls, filter: {} }]
}
return [
{
urls: relayUrls,
filter: {
kinds: defaultKinds
}
}
]
}, [canRenderFeed, relayUrls, defaultKinds, kindsOverride, singleRelayKindlessExplore])
if (!canRenderFeed) {
return null
}
// preserveTimeline: merge when relay list grows (e.g. all-favorites list fills in). Do not use
// mergeTimelineWhenSubRequestFiltersMatch here — same kinds + different URLs would keep the old
// timeline when switching home feed chips (all-favorites ↔ set ↔ single relay).
return (
<NormalFeed
ref={ref}
subRequests={subRequests}
areAlgoRelays={areAlgoRelays}
relayCapabilityReady={relayAlgoReady}
isMainFeed
setSubHeader={setSubHeader}
onSubHeaderRefresh={onSubHeaderRefresh}
preserveTimelineOnSubRequestsChange
feedTimelineScopeKey={feedTimelineScopeKey}
useFilterAsIs={singleRelayKindlessExplore}
allowKindlessRelayExplore={singleRelayKindlessExplore}
clientSideKindFilter={singleRelayKindlessExplore}
showFeedClientFilter
hostPrimaryPageName="feed"
/>
)
})
RelaysFeed.displayName = 'RelaysFeed'
export default RelaysFeed