diff --git a/src/components/Explore/ExploreRelayReviews.tsx b/src/components/Explore/ExploreRelayReviews.tsx
index b4ed2427..871837ea 100644
--- a/src/components/Explore/ExploreRelayReviews.tsx
+++ b/src/components/Explore/ExploreRelayReviews.tsx
@@ -10,6 +10,7 @@ import {
userReadRelaysWithHttp
} from '@/lib/favorites-feed-relays'
import { toRelay } from '@/lib/link'
+import { normalizeAnyRelayUrl } from '@/lib/url'
import { appendCuratedReadOnlyRelays } from '@/pages/primary/SpellsPage/fauxSpellFeeds'
import { useSmartRelayNavigation } from '@/PageManager'
import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider'
@@ -29,7 +30,7 @@ function RelayGroupHeader({ url, reviewCount }: { url: string; reviewCount: numb
className="flex w-full min-w-0 items-center gap-2 px-4 md:px-4 pt-4 pb-2 border-b text-left hover:opacity-75 transition-opacity"
onClick={() => navigateToRelay(toRelay(url))}
>
-
+
{relayInfo?.name && (
{relayInfo.name}
@@ -87,11 +88,35 @@ async function loadCachedRelayReviews(limit: number): Promise
{
}
}
+function stableRelayInputsKey(
+ favoriteRelays: string[],
+ blockedRelays: string[],
+ relayList: { read?: string[]; write?: string[]; httpRead?: string[] } | null | undefined
+): string {
+ const normSortJoin = (urls: string[]) =>
+ [...urls]
+ .map((u) => normalizeAnyRelayUrl(u) || u.trim())
+ .filter(Boolean)
+ .sort((a, b) => a.localeCompare(b))
+ .join('|')
+ return [
+ normSortJoin(favoriteRelays),
+ normSortJoin(blockedRelays),
+ normSortJoin([...(relayList?.httpRead ?? []), ...(relayList?.read ?? [])]),
+ normSortJoin(relayList?.write ?? [])
+ ].join('::')
+}
+
export default function ExploreRelayReviews() {
const { t } = useTranslation()
const { favoriteRelays, blockedRelays } = useFavoriteRelays()
const { relayList } = useNostr()
+ const relayInputsKey = useMemo(
+ () => stableRelayInputsKey(favoriteRelays, blockedRelays, relayList),
+ [favoriteRelays, blockedRelays, relayList]
+ )
+
const relayUrls = useMemo(() => {
const stacked = appendCuratedReadOnlyRelays(
getRelayUrlsWithFavoritesFastReadAndInbox(
@@ -106,10 +131,14 @@ export default function ExploreRelayReviews() {
),
blockedRelays
)
- return stacked.slice(0, EXPLORE_REVIEWS_MAX_RELAYS)
- }, [favoriteRelays, blockedRelays, relayList])
+ const sliced = stacked.slice(0, EXPLORE_REVIEWS_MAX_RELAYS)
+ const normalized = sliced.map((u) => normalizeAnyRelayUrl(u) || u.trim()).filter(Boolean)
+ normalized.sort((a, b) => a.localeCompare(b))
+ return normalized
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- relayInputsKey is a content hash of favorites/blocked/NIP-65; relayList identity churn must not re-open REQ sockets.
+ }, [relayInputsKey])
- const relayUrlsKey = useMemo(() => relayUrls.join('|'), [relayUrls])
+ const relayUrlsKey = relayInputsKey
const [loading, setLoading] = useState(true)
const [events, setEvents] = useState([])
diff --git a/src/components/RelayIcon/index.tsx b/src/components/RelayIcon/index.tsx
index ee683c46..7047e064 100644
--- a/src/components/RelayIcon/index.tsx
+++ b/src/components/RelayIcon/index.tsx
@@ -32,13 +32,16 @@ function resolveRelayImageUrl(raw: string, relayUrl: string): string | undefined
export default function RelayIcon({
url,
className,
- iconSize = 14
+ iconSize = 14,
+ /** When true, do not hit NIP-11 (parent already fetches relay info, or icon-only row). */
+ skipRelayInfoFetch = false
}: {
url?: string
className?: string
iconSize?: number
+ skipRelayInfoFetch?: boolean
}) {
- const { relayInfo } = useFetchRelayInfo(url)
+ const { relayInfo } = useFetchRelayInfo(skipRelayInfoFetch ? undefined : url)
const iconUrl = useMemo(() => {
if (!url) return undefined
diff --git a/src/components/RelayInfo/RelayReviewCard.tsx b/src/components/RelayInfo/RelayReviewCard.tsx
index 9241cd7b..43432cc4 100644
--- a/src/components/RelayInfo/RelayReviewCard.tsx
+++ b/src/components/RelayInfo/RelayReviewCard.tsx
@@ -28,7 +28,7 @@ export default function RelayReviewCard({
const { navigateToRelay } = useSmartRelayNavigation()
const stars = useMemo(() => getStarsFromRelayReviewEvent(event), [event])
const relayUrl = useMemo(() => getRelayUrlFromRelayReviewEvent(event), [event])
- const { relayInfo } = useFetchRelayInfo(relayUrl)
+ const { relayInfo } = useFetchRelayInfo(showRelayInfo ? relayUrl : undefined)
return (
(undefined)
useEffect(() => {
- if (!url) return
+ if (!url) {
+ setRelayInfo(undefined)
+ setIsFetching(false)
+ return
+ }
const fetchRelayInfos = async () => {
setIsFetching(true)
const timer = setTimeout(() => {