From f13fba50bf756fb06e7f8fe7eda9a808199494fb Mon Sep 17 00:00:00 2001 From: Silberengel Date: Mon, 23 Mar 2026 06:46:07 +0100 Subject: [PATCH] fix empty message while relay reviews are still loading --- package-lock.json | 4 +- package.json | 2 +- .../Explore/ExploreRelayReviews.tsx | 65 ++++++++++++------- 3 files changed, 46 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index a890b32c..04c8a71e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "jumble-imwald", - "version": "19.1.1", + "version": "19.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "jumble-imwald", - "version": "19.1.1", + "version": "19.2.0", "license": "MIT", "dependencies": { "@asciidoctor/core": "^3.0.4", diff --git a/package.json b/package.json index d1e0f2e1..55972e9e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jumble-imwald", - "version": "19.1.1", + "version": "19.2.0", "description": "A user-friendly Nostr client focused on relay feed browsing and relay discovery, forked from Jumble", "private": true, "type": "module", diff --git a/src/components/Explore/ExploreRelayReviews.tsx b/src/components/Explore/ExploreRelayReviews.tsx index 7d648643..fd280b7e 100644 --- a/src/components/Explore/ExploreRelayReviews.tsx +++ b/src/components/Explore/ExploreRelayReviews.tsx @@ -8,12 +8,17 @@ import { appendCuratedReadOnlyRelays } from '@/pages/primary/SpellsPage/fauxSpel import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider' import { useNostr } from '@/providers/NostrProvider' import client from '@/services/client.service' +import { Loader2 } from 'lucide-react' import type { Event } from 'nostr-tools' import { useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' const REVIEW_QUERY_LIMIT = 100 const SHOW_COUNT = 20 +/** Fewer sockets + faster aggregate EOSE than full inbox stack; read-only mirrors still appended then capped. */ +const EXPLORE_REVIEWS_MAX_RELAYS = 12 +/** After all relays EOSE, wait longer than default so slow mirrors can flush events (default query eose is 500ms). */ +const EXPLORE_REVIEWS_EOSE_TAIL_MS = 4500 function dedupeRelayReviewsNewestFirst(events: Event[]): Event[] { const sorted = [...events].sort((a, b) => b.created_at - a.created_at) @@ -33,19 +38,22 @@ export default function ExploreRelayReviews() { const { favoriteRelays, blockedRelays } = useFavoriteRelays() const { relayList } = useNostr() - const relayUrls = useMemo( - () => - appendCuratedReadOnlyRelays( - getRelayUrlsWithFavoritesFastReadAndInbox( - favoriteRelays, - blockedRelays, - relayList?.read ?? [], - { userWriteRelays: relayList?.write ?? [] } - ), - blockedRelays + const relayUrls = useMemo(() => { + const stacked = appendCuratedReadOnlyRelays( + getRelayUrlsWithFavoritesFastReadAndInbox( + favoriteRelays, + blockedRelays, + relayList?.read ?? [], + { + userWriteRelays: relayList?.write ?? [], + maxRelays: EXPLORE_REVIEWS_MAX_RELAYS, + applyKind1BlockedFilter: false + } ), - [favoriteRelays, blockedRelays, relayList] - ) + blockedRelays + ) + return stacked.slice(0, EXPLORE_REVIEWS_MAX_RELAYS) + }, [favoriteRelays, blockedRelays, relayList]) const relayUrlsKey = useMemo(() => relayUrls.join('|'), [relayUrls]) @@ -53,8 +61,10 @@ export default function ExploreRelayReviews() { const [events, setEvents] = useState([]) const [showCount, setShowCount] = useState(SHOW_COUNT) const bottomRef = useRef(null) + const fetchGenRef = useRef(0) useEffect(() => { + const gen = ++fetchGenRef.current let cancelled = false setLoading(true) setEvents([]) @@ -67,27 +77,26 @@ export default function ExploreRelayReviews() { { kinds: [ExtendedKind.RELAY_REVIEW], limit: REVIEW_QUERY_LIMIT }, { onevent: (e) => { - if (cancelled) return + if (cancelled || fetchGenRef.current !== gen) return if (e.kind === ExtendedKind.RELAY_REVIEW && getRelayUrlFromRelayReviewEvent(e)) { - setLoading(false) setEvents((prev) => dedupeRelayReviewsNewestFirst([...prev, e])) } }, firstRelayResultGraceMs: FIRST_RELAY_RESULT_GRACE_MS, globalTimeout: 12_000, - eoseTimeout: 800, + eoseTimeout: EXPLORE_REVIEWS_EOSE_TAIL_MS, cache: true } ) - if (cancelled) return + if (cancelled || fetchGenRef.current !== gen) return const withRelay = raw.filter( (e) => e.kind === ExtendedKind.RELAY_REVIEW && getRelayUrlFromRelayReviewEvent(e) ) - setEvents(dedupeRelayReviewsNewestFirst(withRelay)) + setEvents((prev) => dedupeRelayReviewsNewestFirst([...prev, ...withRelay])) } catch { - if (!cancelled) setEvents([]) + if (!cancelled && fetchGenRef.current === gen) setEvents([]) } finally { - if (!cancelled) setLoading(false) + if (!cancelled && fetchGenRef.current === gen) setLoading(false) } })() @@ -111,16 +120,18 @@ export default function ExploreRelayReviews() { }, [showCount, events.length]) const visible = events.slice(0, showCount) + const showInitialSkeleton = loading && events.length === 0 + const showEmptyAfterLoad = !loading && events.length === 0 return (
- {loading ? ( + {showInitialSkeleton ? (
{Array.from({ length: 6 }).map((_, i) => ( ))}
- ) : events.length === 0 ? ( + ) : showEmptyAfterLoad ? (

{t('no relays found')}

) : ( <> @@ -129,8 +140,18 @@ export default function ExploreRelayReviews() { ))}
+ {loading ? ( +
+ + {t('Loading...')} +
+ ) : null} {showCount < events.length ?
: null} - {showCount >= events.length ? ( + {!loading && showCount >= events.length ? (

{t('no more relays')}

) : null}