From ecc5aab6b2a809e1a7731267f8240fcd48028272 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Wed, 13 May 2026 08:57:52 +0200 Subject: [PATCH] fix race condition --- .../Explore/ExploreRelayReviews.tsx | 37 +++++++++++++++++-- src/components/RelayIcon/index.tsx | 7 +++- src/components/RelayInfo/RelayReviewCard.tsx | 2 +- src/hooks/useFetchRelayInfo.tsx | 6 ++- 4 files changed, 44 insertions(+), 8 deletions(-) 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(() => {