Browse Source

bug-fixes

imwald
Silberengel 1 month ago
parent
commit
b95994be5f
  1. 68
      src/components/Profile/ProfileMediaFeed.tsx
  2. 28
      src/hooks/useProfilePins.tsx
  3. 21
      src/lib/favorites-feed-relays.ts
  4. 15
      src/pages/primary/SpellsPage/fauxSpellFeeds.ts

68
src/components/Profile/ProfileMediaFeed.tsx

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
import NoteList, { type TNoteListRef } from '@/components/NoteList'
import { buildProfilePageReadRelayUrls } from '@/lib/favorites-feed-relays'
import { buildAuthorInboxOutboxRelayUrls } from '@/lib/favorites-feed-relays'
import logger from '@/lib/logger'
import { normalizeHexPubkey } from '@/lib/pubkey'
import { computeSpellSubRequestsIdentityKey } from '@/lib/spell-feed-request-identity'
@ -10,82 +10,67 @@ import client from '@/services/client.service' @@ -10,82 +10,67 @@ import client from '@/services/client.service'
import { forwardRef, useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
function relayListsContentKey(favoriteRelays: string[], blockedRelays: string[]): string {
const fav = [...favoriteRelays].map((u) => normalizeUrl(u) || u).filter(Boolean).sort().join('\u0001')
const blk = [...blockedRelays].map((u) => normalizeUrl(u) || u).filter(Boolean).sort().join('\u0001')
return `${fav}\u0000${blk}`
function blockedRelaysContentKey(blockedRelays: string[]): string {
return [...blockedRelays].map((u) => normalizeUrl(u) || u).filter(Boolean).sort().join('\u0001')
}
const MEDIA_LOG = '[ProfileMedia]'
const ProfileMediaFeed = forwardRef<TNoteListRef, { pubkey: string }>(({ pubkey }, ref) => {
const { t } = useTranslation()
const { favoriteRelays, blockedRelays } = useFavoriteRelays()
const relayListsKey = useMemo(
() => relayListsContentKey(favoriteRelays, blockedRelays),
[favoriteRelays, blockedRelays]
)
const { blockedRelays } = useFavoriteRelays()
const blockedKey = useMemo(() => blockedRelaysContentKey(blockedRelays), [blockedRelays])
/**
* Start REQ immediately with the same stack as no NIP-65 yet (favorites + fast-read), then refine when
* {@link client.fetchRelayList} returns avoids an empty/skeleton Medien tab while Posts already shows cache.
* Before NIP-65: empty author tier so REQ still uses read-only + fast-read; refine when
* {@link client.fetchRelayList} returns.
*/
const provisionalProfileRelayUrls = useMemo(() => {
const provisionalAuthorRelayUrls = useMemo(() => {
if (!pubkey?.trim()) return [] as string[]
return buildProfilePageReadRelayUrls(
favoriteRelays,
blockedRelays,
{ read: [] as string[], write: [] as string[] },
false
)
}, [pubkey, relayListsKey, favoriteRelays, blockedRelays])
return buildAuthorInboxOutboxRelayUrls({ read: [], write: [] }, blockedRelays)
}, [pubkey, blockedKey, blockedRelays])
const [refinedProfileRelayUrls, setRefinedProfileRelayUrls] = useState<string[] | null>(null)
const [refinedAuthorRelayUrls, setRefinedAuthorRelayUrls] = useState<string[] | null>(null)
useEffect(() => {
const pk = pubkey?.trim()
if (!pk) {
logger.debug(`${MEDIA_LOG} empty pubkey — no relay resolution`)
setRefinedProfileRelayUrls([])
setRefinedAuthorRelayUrls([])
return
}
let cancelled = false
setRefinedProfileRelayUrls(null)
setRefinedAuthorRelayUrls(null)
void (async () => {
const authorRl = await client.fetchRelayList(pk).catch(() => ({
read: [] as string[],
write: [] as string[]
}))
if (cancelled) return
const profileStack = buildProfilePageReadRelayUrls(
favoriteRelays,
blockedRelays,
authorRl,
false
)
const authorStack = buildAuthorInboxOutboxRelayUrls(authorRl, blockedRelays)
const hexPk = normalizeHexPubkey(pk)
logger.debug(`${MEDIA_LOG} NIP-65 stack resolved for media tab`, {
logger.debug(`${MEDIA_LOG} NIP-65 author relays resolved for media tab`, {
pubkey: hexPk.slice(0, 8),
authorReadCount: authorRl.read?.length ?? 0,
authorWriteCount: authorRl.write?.length ?? 0,
profileRelayCount: profileStack.length,
profileRelaysSample: profileStack.slice(0, 4)
authorRelayCount: authorStack.length,
authorRelaysSample: authorStack.slice(0, 4)
})
logger.debug(`${MEDIA_LOG} full profile relay stack`, { profileRelays: profileStack })
setRefinedProfileRelayUrls(profileStack)
logger.debug(`${MEDIA_LOG} author inbox/outbox relay list`, { authorRelays: authorStack })
setRefinedAuthorRelayUrls(authorStack)
})()
return () => {
cancelled = true
}
}, [pubkey, relayListsKey, favoriteRelays, blockedRelays])
}, [pubkey, blockedKey, blockedRelays])
const profileRelayUrls = refinedProfileRelayUrls ?? provisionalProfileRelayUrls
const authorRelayUrls = refinedAuthorRelayUrls ?? provisionalAuthorRelayUrls
const subRequests = useMemo(() => {
const pk = pubkey?.trim()
if (!pk) return []
return buildProfileMediaSubRequests(profileRelayUrls, blockedRelays, pk)
}, [pubkey, profileRelayUrls, blockedRelays])
return buildProfileMediaSubRequests(authorRelayUrls, blockedRelays, pk)
}, [pubkey, authorRelayUrls, blockedRelays])
const feedSubscriptionKey = useMemo(
() => computeSpellSubRequestsIdentityKey(subRequests),
@ -98,7 +83,7 @@ const ProfileMediaFeed = forwardRef<TNoteListRef, { pubkey: string }>(({ pubkey @@ -98,7 +83,7 @@ const ProfileMediaFeed = forwardRef<TNoteListRef, { pubkey: string }>(({ pubkey
if (!subRequests.length) {
logger.debug(`${MEDIA_LOG} buildProfileMediaSubRequests returned no URLs (blocked or empty stacks)`, {
pubkey: normalizeHexPubkey(pk).slice(0, 8),
profileRelayCount: profileRelayUrls.length
authorRelayCount: authorRelayUrls.length
})
return
}
@ -112,7 +97,7 @@ const ProfileMediaFeed = forwardRef<TNoteListRef, { pubkey: string }>(({ pubkey @@ -112,7 +97,7 @@ const ProfileMediaFeed = forwardRef<TNoteListRef, { pubkey: string }>(({ pubkey
filterLimit: sr.filter.limit
})
logger.debug(`${MEDIA_LOG} augmented relay URLs`, { urls: sr.urls })
}, [pubkey, profileRelayUrls, subRequests, feedSubscriptionKey, refinedProfileRelayUrls])
}, [pubkey, authorRelayUrls, subRequests, feedSubscriptionKey, refinedAuthorRelayUrls])
const showKinds = useMemo(() => [...PROFILE_MEDIA_TAB_KINDS], [])
@ -141,8 +126,7 @@ const ProfileMediaFeed = forwardRef<TNoteListRef, { pubkey: string }>(({ pubkey @@ -141,8 +126,7 @@ const ProfileMediaFeed = forwardRef<TNoteListRef, { pubkey: string }>(({ pubkey
showKinds={showKinds}
useFilterAsIs
/**
* Provisional relay stack (favorites + fast read) then NIP-65 refinement changes URLs without changing the
* REQ filter merge so we do not wipe rows or re-enter a long loading state.
* Provisional author tier (empty) then NIP-65 inbox/outbox refinement; REQ filter unchanged merge rows.
*/
preserveTimelineOnSubRequestsChange
mergeTimelineWhenSubRequestFiltersMatch

28
src/hooks/useProfilePins.tsx

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
import { Event } from 'nostr-tools'
import {
buildAuthorInboxOutboxRelayUrls,
buildProfileAugmentedReadRelayUrls,
buildProfilePageReadRelayUrls,
PROFILE_PAGE_PINS_RESOLVE_LIMIT
} from '@/lib/favorites-feed-relays'
import {
@ -70,18 +70,13 @@ function orderPinEvents(pinList: Event, eventsById: Map<string, Event>): Event[] @@ -70,18 +70,13 @@ function orderPinEvents(pinList: Event, eventsById: Map<string, Event>): Event[]
return ordered
}
function relayListsContentKey(favoriteRelays: string[], blockedRelays: string[]): string {
const fav = [...favoriteRelays].map((u) => normalizeUrl(u) || u).filter(Boolean).sort().join('\u0001')
const blk = [...blockedRelays].map((u) => normalizeUrl(u) || u).filter(Boolean).sort().join('\u0001')
return `${fav}\u0000${blk}`
function blockedRelaysContentKey(blockedRelays: string[]): string {
return [...blockedRelays].map((u) => normalizeUrl(u) || u).filter(Boolean).sort().join('\u0001')
}
export function useProfilePins(pubkey: string | undefined) {
const { favoriteRelays, blockedRelays } = useFavoriteRelays()
const relayListsKey = useMemo(
() => relayListsContentKey(favoriteRelays, blockedRelays),
[favoriteRelays, blockedRelays]
)
const { blockedRelays } = useFavoriteRelays()
const blockedKey = useMemo(() => blockedRelaysContentKey(blockedRelays), [blockedRelays])
const [pinEvents, setPinEvents] = useState<Event[]>([])
const [loadingPins, setLoadingPins] = useState(false)
@ -137,15 +132,8 @@ export function useProfilePins(pubkey: string | undefined) { @@ -137,15 +132,8 @@ export function useProfilePins(pubkey: string | undefined) {
})),
client.fetchPinListEvent(pk).catch(() => undefined)
])
// Same stack as profile feed: viewed npub NIP-65 read+write → your favorites → FAST_READ_RELAY_URLS,
// deduped, blocked stripped, max PROFILE_PAGE_FEED_MAX_RELAYS (6). Relays here accept `#d` on REQ.
const profileRelays = buildProfilePageReadRelayUrls(
favoriteRelays,
blockedRelays,
authorRl,
false
)
const pinsResolveRelays = buildProfileAugmentedReadRelayUrls(profileRelays, blockedRelays)
const authorRelays = buildAuthorInboxOutboxRelayUrls(authorRl, blockedRelays)
const pinsResolveRelays = buildProfileAugmentedReadRelayUrls(authorRelays, blockedRelays)
if (!pinsResolveRelays.length) {
setPinEvents([])
return
@ -249,7 +237,7 @@ export function useProfilePins(pubkey: string | undefined) { @@ -249,7 +237,7 @@ export function useProfilePins(pubkey: string | undefined) {
setLoadingPins(false)
}
},
[pubkey, relayListsKey, favoriteRelays, blockedRelays]
[pubkey, blockedKey, blockedRelays]
)
useEffect(() => {

21
src/lib/favorites-feed-relays.ts

@ -68,19 +68,32 @@ export function mergeRelayUrlLayers(layers: string[][], blockedRelays: string[]) @@ -68,19 +68,32 @@ export function mergeRelayUrlLayers(layers: string[][], blockedRelays: string[])
}
/**
* Profile pins + media: prepend {@link READ_ONLY_RELAY_URLS} and {@link FAST_READ_RELAY_URLS} to the
* capped NIP-65 stack so REQ still hits aggregators when the authors six relays fill the profile cap alone.
* Viewed authors NIP-65 read list (inboxes), then write list (outboxes), each with LAN/local URLs first; blocked
* stripped. Used for profile pins + Medien before {@link buildProfileAugmentedReadRelayUrls}.
*/
export function buildAuthorInboxOutboxRelayUrls(
authorRelayList: { read: string[]; write: string[] },
blockedRelays: string[]
): string[] {
const inboxLayer = relayUrlsLocalsFirst(authorRelayList.read ?? [])
const outboxLayer = relayUrlsLocalsFirst(authorRelayList.write ?? [])
return mergeRelayUrlLayers([inboxLayer, outboxLayer], blockedRelays)
}
/**
* Profile pins + Medien: author NIP-65 tier (pass from {@link buildAuthorInboxOutboxRelayUrls}), then
* {@link READ_ONLY_RELAY_URLS}, then {@link FAST_READ_RELAY_URLS}; dedupe, blocked-stripped, capped.
*/
export const PROFILE_AUGMENTED_READ_MAX_RELAYS = 16
export function buildProfileAugmentedReadRelayUrls(
profileRelayUrls: string[],
authorRelayUrls: string[],
blockedRelays: string[],
maxRelays: number = PROFILE_AUGMENTED_READ_MAX_RELAYS
): string[] {
const readOnlyLayer = READ_ONLY_RELAY_URLS.map((u) => normalizeUrl(u) || u).filter(Boolean)
const fastReadLayer = FAST_READ_RELAY_URLS.map((u) => normalizeUrl(u) || u).filter(Boolean)
const merged = mergeRelayUrlLayers([readOnlyLayer, fastReadLayer, profileRelayUrls], blockedRelays)
const merged = mergeRelayUrlLayers([authorRelayUrls, readOnlyLayer, fastReadLayer], blockedRelays)
return merged.slice(0, maxRelays)
}

15
src/pages/primary/SpellsPage/fauxSpellFeeds.ts

@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
* topics go in a single `#t` filter (NIP-01 OR semantics). The notifications spell uses a narrow
* kind list vs full profile kinds.
*/
import { ExtendedKind, FAST_READ_RELAY_URLS, PROFILE_FEED_KINDS, READ_ONLY_RELAY_URLS } from '@/constants'
import { ExtendedKind, PROFILE_FEED_KINDS, READ_ONLY_RELAY_URLS } from '@/constants'
import { buildProfileAugmentedReadRelayUrls } from '@/lib/favorites-feed-relays'
import { normalizeTopic } from '@/lib/discussion-topics'
import { userIdToPubkey } from '@/lib/pubkey'
@ -23,8 +23,8 @@ export const FAUX_SPELL_EVENT_LIMIT = 200 @@ -23,8 +23,8 @@ export const FAUX_SPELL_EVENT_LIMIT = 200
/** Profile Media tab: single REQ `limit` (matches merged cap in NoteList one-shot). */
export const PROFILE_MEDIA_REQ_LIMIT = 200
/** Max relay URLs per Medien REQ after read-only + fast-read layers (see {@link buildProfileMediaSubRequests}). */
export const PROFILE_MEDIA_MAX_RELAYS = 10
/** Max relay URLs per Medien REQ (author stack + aggregators; see {@link buildProfileMediaSubRequests}). */
export const PROFILE_MEDIA_MAX_RELAYS = 16
/**
* Trim relay lists and filter limits (and bookmark `ids`) so faux feeds stay cheap to open.
@ -134,13 +134,16 @@ export function buildProfileMediaSpellFilter(pubkey: string): Filter { @@ -134,13 +134,16 @@ export function buildProfileMediaSpellFilter(pubkey: string): Filter {
}
}
/** Read-only + {@link FAST_READ_RELAY_URLS} before the author-only base stack; capped at {@link PROFILE_MEDIA_MAX_RELAYS}. */
/**
* Author inboxes/outboxes + read-only + fast read (see {@link buildProfileAugmentedReadRelayUrls}), capped at
* {@link PROFILE_MEDIA_MAX_RELAYS}.
*/
export function buildProfileMediaSubRequests(
profileRelayUrls: string[],
authorRelayUrls: string[],
blockedRelays: string[],
pubkey: string
): TFeedSubRequest[] {
const urls = buildProfileAugmentedReadRelayUrls(profileRelayUrls, blockedRelays, PROFILE_MEDIA_MAX_RELAYS)
const urls = buildProfileAugmentedReadRelayUrls(authorRelayUrls, blockedRelays, PROFILE_MEDIA_MAX_RELAYS)
if (!urls.length) return []
return [{ urls, filter: buildProfileMediaSpellFilter(pubkey) }]
}

Loading…
Cancel
Save