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.
 
 
 
 

140 lines
4.3 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/feed-context'
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 { relayUrls, replyRelayUrls } = useFeed()
const { showKinds } = useKindFilterOrDefaults()
const [areAlgoRelays, setAreAlgoRelays] = useState(false)
const relayUrlsKey = useMemo(
() =>
[...relayUrls]
.map((u) => normalizeUrl(u) || u)
.filter(Boolean)
.sort()
.join('|'),
[relayUrls]
)
const replyRelayUrlsKey = useMemo(
() =>
[...replyRelayUrls]
.map((u) => normalizeUrl(u) || u)
.filter(Boolean)
.sort()
.join('|'),
[replyRelayUrls]
)
const homeFeedSeenOnAllowlistOp = useMemo(() => relayUrls, [relayUrlsKey])
const homeFeedSeenOnAllowlistReplies = useMemo(() => replyRelayUrls, [replyRelayUrlsKey])
useEffect(() => {
if (relayUrls.length === 0) {
setAreAlgoRelays(false)
return
}
let cancelled = 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 {
if (!cancelled) setAreAlgoRelays(false)
}
}
void init()
return () => {
cancelled = true
}
}, [relayUrlsKey, relayUrls.length])
/** Stable identity when kind filter is empty so `subRequests` does not invalidate every render. */
const fallbackNoteKinds = useMemo(() => [kinds.ShortTextNote], [])
const defaultKinds = useMemo(() => {
if (kindsOverride && kindsOverride.length > 0) return kindsOverride
if (showKinds.length > 0) return showKinds
return fallbackNoteKinds
}, [kindsOverride, showKinds, fallbackNoteKinds])
const canRenderFeed = relayUrls.length > 0
// Hooks must run every render — never place useMemo after conditional returns.
const subRequests = useMemo(() => {
if (!canRenderFeed) return []
return [
{
urls: relayUrls,
filter: {
kinds: defaultKinds
}
}
]
}, [canRenderFeed, relayUrlsKey, relayUrls, defaultKinds])
const repliesSubRequests = useMemo(() => {
if (!canRenderFeed) return []
return [
{
urls: replyRelayUrls.length > 0 ? replyRelayUrls : relayUrls,
filter: {
kinds: defaultKinds
}
}
]
}, [canRenderFeed, replyRelayUrlsKey, replyRelayUrls, relayUrlsKey, relayUrls, defaultKinds])
if (!canRenderFeed) {
return null
}
// preserveTimeline: merge when relay list grows (e.g. all-favorites list fills in).
return (
<NormalFeed
ref={ref}
subRequests={subRequests}
areAlgoRelays={areAlgoRelays}
isMainFeed
setSubHeader={setSubHeader}
onSubHeaderRefresh={onSubHeaderRefresh}
preserveTimelineOnSubRequestsChange
repliesSubRequests={repliesSubRequests}
mainFeedGalleryRelayUrls={replyRelayUrls}
widenMainGalleryRelays={false}
feedSubscriptionKey="home-all-favorites"
feedTimelineScopeKey="all-favorites"
homeFeedSeenOnAllowlistOp={homeFeedSeenOnAllowlistOp}
homeFeedSeenOnAllowlistReplies={homeFeedSeenOnAllowlistReplies}
showFeedClientFilter
hostPrimaryPageName="feed"
/>
)
})
RelaysFeed.displayName = 'RelaysFeed'
export default RelaysFeed