From ec4539a664b7de1ff95deaf71994aa29588b1990 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Wed, 20 May 2026 16:28:22 +0200 Subject: [PATCH] fix bugs --- src/components/Nip05/index.tsx | 21 +++++--- src/components/Profile/FollowedBy.tsx | 54 ------------------- src/components/Profile/index.tsx | 36 +++++-------- src/constants.ts | 3 +- src/hooks/useFetchProfile.tsx | 2 + src/hooks/useProfileAuthorFeedSubRequests.ts | 34 ++++++++---- .../client-replaceable-events.service.ts | 7 +++ 7 files changed, 62 insertions(+), 95 deletions(-) delete mode 100644 src/components/Profile/FollowedBy.tsx diff --git a/src/components/Nip05/index.tsx b/src/components/Nip05/index.tsx index 2a9bc9a2..78044ea3 100644 --- a/src/components/Nip05/index.tsx +++ b/src/components/Nip05/index.tsx @@ -6,12 +6,19 @@ import { SecondaryPageLink } from '@/PageManager' import { BadgeAlert, BadgeCheck } from 'lucide-react' import { Favicon } from '../Favicon' -export default function Nip05({ pubkey, append }: { pubkey: string; append?: string }) { - const { profile } = useFetchProfile(pubkey) - const { nip05IsVerified, nip05Name, nip05Domain, isFetching } = useFetchNip05( - profile?.nip05, - pubkey - ) +export default function Nip05({ + pubkey, + nip05: nip05Prop, + append +}: { + pubkey: string + /** When set (e.g. profile page), skip a second {@link useFetchProfile} network pass. */ + nip05?: string + append?: string +}) { + const { profile } = useFetchProfile(nip05Prop === undefined ? pubkey : undefined) + const resolvedNip05 = nip05Prop ?? profile?.nip05 + const { nip05IsVerified, nip05Name, nip05Domain, isFetching } = useFetchNip05(resolvedNip05, pubkey) if (isFetching) { return ( @@ -21,7 +28,7 @@ export default function Nip05({ pubkey, append }: { pubkey: string; append?: str ) } - if (!profile?.nip05 || !nip05Name || !nip05Domain) return null + if (!resolvedNip05 || !nip05Name || !nip05Domain) return null return (
([]) - const { pubkey: accountPubkey } = useNostr() - - useEffect(() => { - if (!pubkey || !accountPubkey) return - - const init = async () => { - const followListEvent = await replaceableEventService.fetchReplaceableEvent(accountPubkey, kinds.Contacts) - const followings = followListEvent ? getPubkeysFromPTags(followListEvent.tags).reverse() : [] - const followingsOfFollowings = await Promise.all( - followings.map(async (following) => { - const followListEvent = await replaceableEventService.fetchReplaceableEvent(following, kinds.Contacts) - return followListEvent ? getPubkeysFromPTags(followListEvent.tags) : [] - }) - ) - const _followedBy: string[] = [] - const limit = isSmallScreen ? 3 : 5 - for (const [index, following] of followings.entries()) { - if (following === pubkey) continue - if (followingsOfFollowings[index].includes(pubkey)) { - _followedBy.push(following) - } - if (_followedBy.length >= limit) { - break - } - } - setFollowedBy(_followedBy) - } - init() - }, [pubkey, accountPubkey]) - - if (followedBy.length === 0) return null - - return ( -
-
{t('Followed by')}
- {followedBy.map((p) => ( - - ))} -
- ) -} diff --git a/src/components/Profile/index.tsx b/src/components/Profile/index.tsx index 1b6ca986..9a4e9898 100644 --- a/src/components/Profile/index.tsx +++ b/src/components/Profile/index.tsx @@ -62,7 +62,6 @@ import { useTranslation } from 'react-i18next' import logger from '@/lib/logger' import { AlexandriaEventsSearchEmptyCta } from '@/components/AlexandriaEventsSearchEmptyCta' import NotFound from '../NotFound' -import FollowedBy from './FollowedBy' import ProfileBadges from './ProfileBadges' import ProfileFeed from './ProfileFeed' import ProfileReportsDialog from './ProfileReportsDialog' @@ -195,9 +194,14 @@ export default function Profile({ }, [syncAuthorReplaceablesFromCache]) useEffect(() => { - if (!profile?.pubkey) return - void client.refreshAuthorPublishedReplaceablesOnProfileView(profile.pubkey) - }, [profile?.pubkey]) + if (!profile?.pubkey || profile.batchPlaceholder) return + const pk = profile.pubkey + // Defer wide replaceable refresh so initial kind-0 / feed relay setup can finish first. + const timer = window.setTimeout(() => { + void client.refreshAuthorPublishedReplaceablesOnProfileView(pk) + }, 2_000) + return () => clearTimeout(timer) + }, [profile?.pubkey, profile?.batchPlaceholder]) useEffect(() => { if (!isSelf || !profile?.pubkey || !accountProfileEvent) return @@ -213,7 +217,7 @@ export default function Profile({ const onAuthorReplaceablesRefreshed: EventListener = (domEvt) => { const detailPk = (domEvt as CustomEvent<{ pubkey?: string }>).detail?.pubkey?.toLowerCase() if (detailPk !== pk) return - void syncAuthorReplaceablesFromCache(profile.pubkey, { bustCache: true }) + void syncAuthorReplaceablesFromCache(profile.pubkey) } window.addEventListener( ReplaceableEventService.AUTHOR_REPLACEABLES_REFRESHED_EVENT, @@ -226,10 +230,6 @@ export default function Profile({ ) }, [profile?.pubkey, syncAuthorReplaceablesFromCache]) - const isFollowingYou = useMemo(() => { - // This will be handled by the FollowedBy component - return false - }, [profile, accountPubkey]) const defaultImage = useMemo( () => (profile?.pubkey ? generateImageByPubkey(profile?.pubkey) : ''), [profile] @@ -524,13 +524,8 @@ export default function Profile({
{username}
- {isFollowingYou && ( -
- {t('Follows you')} -
- )}
- + {/* Display multiple NIP-05 values if available, with verification */} {nip05List && nip05List.length > 1 && ( @@ -608,13 +603,10 @@ export default function Profile({ defaultLightningAddress={zapLightningDefault} prefetchedPayment={prefetchedZapPayment} /> -
-
- - - {isSelf && } -
- {!isSelf && } +
+ + + {isSelf && }
diff --git a/src/constants.ts b/src/constants.ts index e1095a6e..69c6ed60 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -406,7 +406,7 @@ export const NIP66_DISCOVERY_RELAY_URLS = [ // Relay with bookstr composite index support export const BOOKSTR_RELAY_URLS = [ - 'wss://orly-relay.imwald.eu' + 'wss://thecitadel.nostr1.com' ] /** @@ -469,7 +469,6 @@ export const FAST_READ_RELAY_URLS = [ 'wss://theforest.nostr1.com', 'wss://nostr.land', 'wss://nostr.wine', - 'wss://orly-relay.imwald.eu', 'wss://nostr21.com' ] diff --git a/src/hooks/useFetchProfile.tsx b/src/hooks/useFetchProfile.tsx index c01acfa0..9dbfa7dc 100644 --- a/src/hooks/useFetchProfile.tsx +++ b/src/hooks/useFetchProfile.tsx @@ -691,6 +691,8 @@ export function useFetchProfile(id?: string, skipCache = false) { const onAuthorReplaceablesRefreshed: EventListener = (domEvt) => { const detailPk = (domEvt as CustomEvent<{ pubkey?: string }>).detail?.pubkey?.toLowerCase() if (detailPk !== pkLowerResolved) return + // Background profile-view refresh already persisted kind 0 — avoid a second full fetchProfileEvent pass. + if (initializedPubkeysRef.current.has(pkLowerResolved)) return void checkProfile(pkLowerResolved, { current: profileRefreshCancelledRef.current }) } window.addEventListener( diff --git a/src/hooks/useProfileAuthorFeedSubRequests.ts b/src/hooks/useProfileAuthorFeedSubRequests.ts index 3c71408a..d8b75a1a 100644 --- a/src/hooks/useProfileAuthorFeedSubRequests.ts +++ b/src/hooks/useProfileAuthorFeedSubRequests.ts @@ -88,21 +88,35 @@ export function useProfileAuthorFeedSubRequests({ let cancelled = false const socialKinds = kinds.some(isSocialKindBlockedKind) + const applyRelayList = (authorRl: typeof emptyAuthor) => { + const urls = buildProfilePageReadRelayUrls( + favoriteRelays, + blockedRelays, + authorRl, + socialKinds, + includeAuthorLocalRelays, + kinds, + useGlobalRelayBootstrap + ) + if (urls.length > 0) { + setRelayUrls(urls) + } + } + + void client + .peekRelayListFromStorage(pubkey) + .then((cached) => { + if (cancelled) return + applyRelayList(cached) + }) + .catch(() => {}) + void client .fetchRelayList(pubkey) .catch(() => emptyAuthor) .then((authorRl) => { if (cancelled) return - const urls = buildProfilePageReadRelayUrls( - favoriteRelays, - blockedRelays, - authorRl, - socialKinds, - includeAuthorLocalRelays, - kinds, - useGlobalRelayBootstrap - ) - setRelayUrls(urls) + applyRelayList(authorRl) }) return () => { diff --git a/src/services/client-replaceable-events.service.ts b/src/services/client-replaceable-events.service.ts index dbc826e1..7f252a3f 100644 --- a/src/services/client-replaceable-events.service.ts +++ b/src/services/client-replaceable-events.service.ts @@ -87,6 +87,8 @@ export class ReplaceableEventService { }) /** One in-flight profile replaceables pull per author (avoids stacked REQs when profile UI remounts). */ private authorReplaceablesRefreshByPubkey = new Map>() + /** Per-author cooldown after a successful profile-view replaceable sweep (avoids reopen loops). */ + private authorProfileViewRefreshNotBeforeMs = new Map() private replaceableEventFromBigRelaysDataloader: DataLoader< { pubkey: string; kind: number }, NEvent | null, @@ -1409,6 +1411,9 @@ export class ReplaceableEventService { const pk = pubkey.trim().toLowerCase() if (!/^[0-9a-f]{64}$/.test(pk)) return + const notBefore = this.authorProfileViewRefreshNotBeforeMs.get(pk) ?? 0 + if (Date.now() < notBefore) return + const inFlight = this.authorReplaceablesRefreshByPubkey.get(pk) if (inFlight) return inFlight @@ -1502,6 +1507,8 @@ export class ReplaceableEventService { }) ) + this.authorProfileViewRefreshNotBeforeMs.set(pk, Date.now() + 90_000) + if (typeof window !== 'undefined') { window.dispatchEvent( new CustomEvent(ReplaceableEventService.AUTHOR_REPLACEABLES_REFRESHED_EVENT, { detail: { pubkey: pk } })