Browse Source

bug-fixes

imwald
Silberengel 1 month ago
parent
commit
eaace9e275
  1. 40
      src/components/NormalFeed/index.tsx
  2. 9
      src/hooks/useNotificationReactionDisplay.ts
  3. 16
      src/lib/home-feed-relays.ts
  4. 3
      src/lib/nostr-land-relay-eligibility.ts
  5. 11
      src/pages/primary/NoteListPage/RelaysFeed.tsx
  6. 24
      src/providers/FeedProvider.test.ts
  7. 33
      src/providers/FeedProvider.tsx
  8. 7
      src/providers/feed-context.tsx

40
src/components/NormalFeed/index.tsx

@ -61,6 +61,11 @@ const NormalFeed = forwardRef<TNoteListRef, {
mergeTimelineWhenSubRequestFiltersMatch?: boolean mergeTimelineWhenSubRequestFiltersMatch?: boolean
/** Home Replies can widen relays without changing Notes/Gallery. */ /** Home Replies can widen relays without changing Notes/Gallery. */
repliesSubRequests?: TFeedSubRequest[] repliesSubRequests?: TFeedSubRequest[]
/**
* When set on the home main feed, Gallery tab REQ uses this relay stack (same as {@link repliesSubRequests})
* instead of OP-only {@link subRequests} URLs.
*/
mainFeedGalleryRelayUrls?: string[]
/** Main Gallery historically widened with fast read relays; home can opt out to stay favorites+trending only. */ /** Main Gallery historically widened with fast read relays; home can opt out to stay favorites+trending only. */
widenMainGalleryRelays?: boolean widenMainGalleryRelays?: boolean
/** Home following: second subscribe wave (delta relays / new authors); see {@link NoteList}. */ /** Home following: second subscribe wave (delta relays / new authors); see {@link NoteList}. */
@ -102,6 +107,8 @@ const NormalFeed = forwardRef<TNoteListRef, {
oneShotAfterMergeComparator?: (a: Event, b: Event) => number oneShotAfterMergeComparator?: (a: Event, b: Event) => number
extraShouldHideEvent?: (ev: Event) => boolean extraShouldHideEvent?: (ev: Event) => boolean
extraShouldHideRepliesEvent?: (ev: Event) => boolean extraShouldHideRepliesEvent?: (ev: Event) => boolean
/** When set with home Gallery, filters rows (e.g. aggr-only) using the widened relay stack. */
extraShouldHideGalleryEvent?: (ev: Event) => boolean
/** Override default cap for merged one-shot batches (wide d-tag / search merges). */ /** Override default cap for merged one-shot batches (wide d-tag / search merges). */
oneShotMergedCap?: number oneShotMergedCap?: number
/** When every relay in the subscribe wave fails before EOSE, merge a one-shot fetch from default read relays (home multi-relay feeds). */ /** When every relay in the subscribe wave fails before EOSE, merge a one-shot fetch from default read relays (home multi-relay feeds). */
@ -119,6 +126,7 @@ const NormalFeed = forwardRef<TNoteListRef, {
preserveTimelineOnSubRequestsChange = false, preserveTimelineOnSubRequestsChange = false,
mergeTimelineWhenSubRequestFiltersMatch = false, mergeTimelineWhenSubRequestFiltersMatch = false,
repliesSubRequests, repliesSubRequests,
mainFeedGalleryRelayUrls,
widenMainGalleryRelays = true, widenMainGalleryRelays = true,
followingFeedDeltaSubRequests, followingFeedDeltaSubRequests,
feedSubscriptionKey, feedSubscriptionKey,
@ -139,6 +147,7 @@ const NormalFeed = forwardRef<TNoteListRef, {
oneShotAfterMergeComparator, oneShotAfterMergeComparator,
extraShouldHideEvent, extraShouldHideEvent,
extraShouldHideRepliesEvent, extraShouldHideRepliesEvent,
extraShouldHideGalleryEvent,
oneShotMergedCap, oneShotMergedCap,
timelinePublicReadFallback = false, timelinePublicReadFallback = false,
alexandriaEmptyUrl = null alexandriaEmptyUrl = null
@ -198,7 +207,7 @@ const NormalFeed = forwardRef<TNoteListRef, {
return base return base
}, [isMainFeed, isWispTrendingOnlyFeed]) }, [isMainFeed, isWispTrendingOnlyFeed])
/** Replies may widen relays; Gallery only swaps kinds and widens relays when the caller opts in. */ /** Replies may widen relays; Gallery swaps kinds and may use {@link mainFeedGalleryRelayUrls} on home. */
const effectiveSubRequests = useMemo(() => { const effectiveSubRequests = useMemo(() => {
if (listMode === 'postsAndReplies' && repliesSubRequests) { if (listMode === 'postsAndReplies' && repliesSubRequests) {
return repliesSubRequests return repliesSubRequests
@ -206,10 +215,29 @@ const NormalFeed = forwardRef<TNoteListRef, {
if (listMode !== 'media') return subRequests if (listMode !== 'media') return subRequests
return subRequests.map((req) => ({ return subRequests.map((req) => ({
...req, ...req,
urls: isMainFeed && widenMainGalleryRelays ? galleryRelayUrlsMergedWithReadLayer(req.urls) : req.urls, urls:
isMainFeed && mainFeedGalleryRelayUrls && mainFeedGalleryRelayUrls.length > 0
? mainFeedGalleryRelayUrls
: isMainFeed && widenMainGalleryRelays
? galleryRelayUrlsMergedWithReadLayer(req.urls)
: req.urls,
filter: { ...req.filter, kinds: MEDIA_KINDS } filter: { ...req.filter, kinds: MEDIA_KINDS }
})) }))
}, [listMode, subRequests, repliesSubRequests, MEDIA_KINDS, isMainFeed, widenMainGalleryRelays]) }, [
listMode,
subRequests,
repliesSubRequests,
MEDIA_KINDS,
isMainFeed,
widenMainGalleryRelays,
mainFeedGalleryRelayUrls
])
const noteListExtraShouldHide = useMemo(() => {
if (listMode === 'postsAndReplies') return extraShouldHideRepliesEvent
if (listMode === 'media' && extraShouldHideGalleryEvent) return extraShouldHideGalleryEvent
return extraShouldHideEvent
}, [listMode, extraShouldHideRepliesEvent, extraShouldHideGalleryEvent, extraShouldHideEvent])
const handleListModeChange = useCallback( const handleListModeChange = useCallback(
(mode: TNoteListMode | string) => { (mode: TNoteListMode | string) => {
@ -374,11 +402,7 @@ const NormalFeed = forwardRef<TNoteListRef, {
progressiveWarmupMatch={progressiveWarmupMatch} progressiveWarmupMatch={progressiveWarmupMatch}
progressiveDocumentKinds={progressiveDocumentKinds} progressiveDocumentKinds={progressiveDocumentKinds}
oneShotAfterMergeComparator={oneShotAfterMergeComparator} oneShotAfterMergeComparator={oneShotAfterMergeComparator}
extraShouldHideEvent={ extraShouldHideEvent={noteListExtraShouldHide}
listMode === 'postsAndReplies'
? extraShouldHideRepliesEvent
: extraShouldHideEvent
}
oneShotMergedCap={oneShotMergedCap} oneShotMergedCap={oneShotMergedCap}
timelinePublicReadFallback={timelinePublicReadFallback && listMode === 'postsAndReplies'} timelinePublicReadFallback={timelinePublicReadFallback && listMode === 'postsAndReplies'}
alexandriaEmptyUrl={alexandriaEmptyUrl} alexandriaEmptyUrl={alexandriaEmptyUrl}

9
src/hooks/useNotificationReactionDisplay.ts

@ -7,7 +7,6 @@ import { getRootEventHexId } from '@/lib/event'
import { relayHintsFromEventTags } from '@/lib/relay-list-builder' import { relayHintsFromEventTags } from '@/lib/relay-list-builder'
import { getFirstHexEventIdFromETags } from '@/lib/tag' import { getFirstHexEventIdFromETags } from '@/lib/tag'
import { eventService } from '@/services/client.service' import { eventService } from '@/services/client.service'
import type { NEvent } from '@/types'
import { Event, kinds } from 'nostr-tools' import { Event, kinds } from 'nostr-tools'
import { useEffect, useLayoutEffect, useMemo, useState } from 'react' import { useEffect, useLayoutEffect, useMemo, useState } from 'react'
@ -19,8 +18,8 @@ export type NotificationReactionDisplay =
function classifyDiscussionReactionFromTargets( function classifyDiscussionReactionFromTargets(
reaction: Event, reaction: Event,
target: NEvent, target: Event,
root: NEvent | undefined root: Event | undefined
): NotificationReactionDisplay { ): NotificationReactionDisplay {
let inDiscussion = target.kind === ExtendedKind.DISCUSSION let inDiscussion = target.kind === ExtendedKind.DISCUSSION
if (!inDiscussion && target.kind === ExtendedKind.COMMENT) { if (!inDiscussion && target.kind === ExtendedKind.COMMENT) {
@ -39,7 +38,7 @@ function peekReactionDisplayFromSessionCaches(event: Event): NotificationReactio
if (!targetId) return { status: 'default' } if (!targetId) return { status: 'default' }
const target = eventService.peekHexIdNoteFromSessionCache(targetId) const target = eventService.peekHexIdNoteFromSessionCache(targetId)
if (!target) return { status: 'default' } if (!target) return { status: 'default' }
let root: NEvent | undefined let root: Event | undefined
if (target.kind === ExtendedKind.COMMENT) { if (target.kind === ExtendedKind.COMMENT) {
const rootId = getRootEventHexId(target) const rootId = getRootEventHexId(target)
if (rootId) root = eventService.peekHexIdNoteFromSessionCache(rootId) if (rootId) root = eventService.peekHexIdNoteFromSessionCache(rootId)
@ -93,7 +92,7 @@ export function useNotificationReactionDisplay(event: Event): NotificationReacti
return return
} }
let root: NEvent | undefined let root: Event | undefined
let inDiscussion = target.kind === ExtendedKind.DISCUSSION let inDiscussion = target.kind === ExtendedKind.DISCUSSION
if (!inDiscussion && target.kind === ExtendedKind.COMMENT) { if (!inDiscussion && target.kind === ExtendedKind.COMMENT) {
const rootId = getRootEventHexId(target) const rootId = getRootEventHexId(target)

16
src/lib/home-feed-relays.ts

@ -9,19 +9,29 @@ function relayUrlIsNostrLandAggr(url: string): boolean {
return normalized === aggr return normalized === aggr
} }
/** Drop nostr.land aggregate from REQ stacks where it must not appear (e.g. home feeds). */
export function stripNostrLandAggrFromRelayUrls(urls: readonly string[]): string[] {
return urls.filter((url) => !relayUrlIsNostrLandAggr(url))
}
export function buildAllFavoritesFeedRelayUrls( export function buildAllFavoritesFeedRelayUrls(
favoriteRelays: string[], favoriteRelays: string[],
blockedRelays: string[], blockedRelays: string[],
extraFeedRelayUrls: string[] extraFeedRelayUrls: string[]
): string[] { ): string[] {
return feedRelayPolicyUrls([ return stripNostrLandAggrFromRelayUrls(
feedRelayPolicyUrls(
[
{ source: 'favorites', urls: getFavoritesFeedRelayUrls(favoriteRelays, blockedRelays) }, { source: 'favorites', urls: getFavoritesFeedRelayUrls(favoriteRelays, blockedRelays) },
{ source: 'fallback', urls: extraFeedRelayUrls } { source: 'fallback', urls: extraFeedRelayUrls }
], { ],
{
operation: 'favorites-feed', operation: 'favorites-feed',
blockedRelays, blockedRelays,
nostrLandAggr: 'never', nostrLandAggr: 'never',
applySocialKindBlockedFilter: false, applySocialKindBlockedFilter: false,
allowThirdPartyLocalRelays: true allowThirdPartyLocalRelays: true
}).filter((url) => !relayUrlIsNostrLandAggr(url)) }
)
)
} }

3
src/lib/nostr-land-relay-eligibility.ts

@ -3,7 +3,8 @@ import { normalizeAnyRelayUrl } from '@/lib/url'
/** /**
* True when any URLs host is `nostr.land` (e.g. `wss://nostr.land`, `wss://aggr.nostr.land`). * True when any URLs host is `nostr.land` (e.g. `wss://nostr.land`, `wss://aggr.nostr.land`).
* Used to decide whether read fetches should prepend {@link AGGR_NOSTR_LAND_WSS} (except the primary home OP feed). * Used to decide whether read fetches should prepend {@link AGGR_NOSTR_LAND_WSS} (home OP / Replies / Gallery
* never prepend aggr via {@link FeedProvider}; side-panel threads, profiles, spells, and other reads still use it).
*/ */
export function relayUrlsMentionNostrLandDomain(urls: readonly string[]): boolean { export function relayUrlsMentionNostrLandDomain(urls: readonly string[]): boolean {
return urls.some((url) => { return urls.some((url) => {

11
src/pages/primary/NoteListPage/RelaysFeed.tsx

@ -115,6 +115,15 @@ const RelaysFeed = forwardRef<
}, },
[relayUrls] [relayUrls]
) )
const hideAggrOnlyReplyGalleryStackEvent = useCallback(
(event: Event) => {
const seenRelays = client.getSeenEventRelayUrls(event.id).map(relaySeenKey)
if (!seenRelays.includes(AGGR_RELAY_KEY)) return false
const allowedRelays = new Set(replyRelayUrls.map(relaySeenKey))
return !seenRelays.some((relay) => relay !== AGGR_RELAY_KEY && allowedRelays.has(relay))
},
[replyRelayUrls]
)
const hideAggrOnlyNonReplyEvent = useCallback( const hideAggrOnlyNonReplyEvent = useCallback(
(event: Event) => hideAggrOnlyMainFeedEvent(event) && !isReplyNoteEvent(event), (event: Event) => hideAggrOnlyMainFeedEvent(event) && !isReplyNoteEvent(event),
[hideAggrOnlyMainFeedEvent] [hideAggrOnlyMainFeedEvent]
@ -135,12 +144,14 @@ const RelaysFeed = forwardRef<
onSubHeaderRefresh={onSubHeaderRefresh} onSubHeaderRefresh={onSubHeaderRefresh}
preserveTimelineOnSubRequestsChange preserveTimelineOnSubRequestsChange
repliesSubRequests={repliesSubRequests} repliesSubRequests={repliesSubRequests}
mainFeedGalleryRelayUrls={replyRelayUrls}
widenMainGalleryRelays={false} widenMainGalleryRelays={false}
feedSubscriptionKey="home-all-favorites" feedSubscriptionKey="home-all-favorites"
feedTimelineScopeKey="all-favorites" feedTimelineScopeKey="all-favorites"
showFeedClientFilter showFeedClientFilter
hostPrimaryPageName="feed" hostPrimaryPageName="feed"
extraShouldHideEvent={hideAggrOnlyMainFeedEvent} extraShouldHideEvent={hideAggrOnlyMainFeedEvent}
extraShouldHideGalleryEvent={hideAggrOnlyReplyGalleryStackEvent}
extraShouldHideRepliesEvent={hideAggrOnlyNonReplyEvent} extraShouldHideRepliesEvent={hideAggrOnlyNonReplyEvent}
timelinePublicReadFallback timelinePublicReadFallback
/> />

24
src/providers/FeedProvider.test.ts

@ -1,7 +1,8 @@
import { describe, expect, it } from 'vitest' import { describe, expect, it } from 'vitest'
import { feedRelayPolicyUrls } from '@/features/feed/relay-policy'
import { AGGR_NOSTR_LAND_WSS } from '@/lib/nostr-land-aggr' import { AGGR_NOSTR_LAND_WSS } from '@/lib/nostr-land-aggr'
import { buildWispTrendingNotesRelayUrl } from '@/lib/wisp-trending-relay' import { buildWispTrendingNotesRelayUrl } from '@/lib/wisp-trending-relay'
import { buildAllFavoritesFeedRelayUrls } from '@/lib/home-feed-relays' import { buildAllFavoritesFeedRelayUrls, stripNostrLandAggrFromRelayUrls } from '@/lib/home-feed-relays'
describe('home feed relay policy', () => { describe('home feed relay policy', () => {
it('keeps aggr.nostr.land out of the main home feed', () => { it('keeps aggr.nostr.land out of the main home feed', () => {
@ -16,4 +17,25 @@ describe('home feed relay policy', () => {
expect(urls).not.toContain('wss://aggr.nostr.land/') expect(urls).not.toContain('wss://aggr.nostr.land/')
expect(urls).not.toContain(AGGR_NOSTR_LAND_WSS) expect(urls).not.toContain(AGGR_NOSTR_LAND_WSS)
}) })
it('home reply merge omits aggr even when listed on viewer read relays', () => {
const merged = stripNostrLandAggrFromRelayUrls(
feedRelayPolicyUrls(
[
{ source: 'favorites', urls: ['wss://relay.example/'] },
{ source: 'viewer-read', urls: [AGGR_NOSTR_LAND_WSS, 'wss://inbox.example/'] }
],
{
operation: 'read',
blockedRelays: [],
nostrLandAggr: 'never',
applySocialKindBlockedFilter: false,
allowThirdPartyLocalRelays: true
}
)
)
expect(merged).not.toContain(AGGR_NOSTR_LAND_WSS)
expect(merged).toContain('wss://relay.example/')
expect(merged).toContain('wss://inbox.example/')
})
}) })

33
src/providers/FeedProvider.tsx

@ -1,7 +1,7 @@
import { FAST_READ_RELAY_URLS } from '@/constants' import { FAST_READ_RELAY_URLS } from '@/constants'
import { feedRelayPolicyUrls } from '@/features/feed/relay-policy' import { feedRelayPolicyUrls } from '@/features/feed/relay-policy'
import { getRelayListFromEvent, getHttpRelayListFromEvent } from '@/lib/event-metadata' import { getRelayListFromEvent, getHttpRelayListFromEvent } from '@/lib/event-metadata'
import { buildAllFavoritesFeedRelayUrls } from '@/lib/home-feed-relays' import { buildAllFavoritesFeedRelayUrls, stripNostrLandAggrFromRelayUrls } from '@/lib/home-feed-relays'
import logger from '@/lib/logger' import logger from '@/lib/logger'
import { syncViewerRelayStackNostrLandAggrEligible } from '@/lib/nostr-land-relay-eligibility' import { syncViewerRelayStackNostrLandAggrEligible } from '@/lib/nostr-land-relay-eligibility'
import { normalizeAnyRelayUrl } from '@/lib/url' import { normalizeAnyRelayUrl } from '@/lib/url'
@ -29,7 +29,9 @@ function buildHomeReplyFeedRelayUrls(
httpRelayUrls: string[], httpRelayUrls: string[],
blockedRelays: string[] blockedRelays: string[]
): string[] { ): string[] {
return feedRelayPolicyUrls( /** Home Replies/Gallery: never prepend aggr (reserved for side-panel threads, profiles, spells). */
return stripNostrLandAggrFromRelayUrls(
feedRelayPolicyUrls(
[ [
{ source: 'favorites', urls: primaryRelayUrls }, { source: 'favorites', urls: primaryRelayUrls },
{ source: 'viewer-read', urls: inboxRelayUrls }, { source: 'viewer-read', urls: inboxRelayUrls },
@ -39,10 +41,12 @@ function buildHomeReplyFeedRelayUrls(
{ {
operation: 'read', operation: 'read',
blockedRelays, blockedRelays,
nostrLandAggr: 'never',
applySocialKindBlockedFilter: false, applySocialKindBlockedFilter: false,
allowThirdPartyLocalRelays: true allowThirdPartyLocalRelays: true
} }
) )
)
} }
export function FeedProvider({ children }: { children: ReactNode }) { export function FeedProvider({ children }: { children: ReactNode }) {
@ -60,18 +64,18 @@ export function FeedProvider({ children }: { children: ReactNode }) {
*/ */
const primaryExtraRelayUrls = useMemo(() => [buildWispTrendingNotesRelayUrl()], []) const primaryExtraRelayUrls = useMemo(() => [buildWispTrendingNotesRelayUrl()], [])
/** Home Replies widen to relays that can surface inbox/reply context. */ /** Read-side layers merged into {@link replyRelayUrls}; {@link outboxRelayUrls} is only for aggr eligibility sync. */
const replyExtraRelayLayers = useMemo(() => { const replyExtraRelayLayers = useMemo(() => {
const cacheRelayUrls: string[] = [] const cacheRelayUrls: string[] = []
if (cacheRelayListEvent) { if (cacheRelayListEvent) {
const list = getRelayListFromEvent(cacheRelayListEvent, blockedRelays) const list = getRelayListFromEvent(cacheRelayListEvent, blockedRelays)
cacheRelayUrls.push(...list.read, ...list.write) cacheRelayUrls.push(...list.read)
} }
const httpRelayUrls: string[] = [...(relayList?.httpRead ?? []), ...(relayList?.httpWrite ?? [])] const httpRelayUrls: string[] = [...(relayList?.httpRead ?? [])]
if (httpRelayListEvent) { if (httpRelayListEvent) {
const list = getHttpRelayListFromEvent(httpRelayListEvent, blockedRelays) const list = getHttpRelayListFromEvent(httpRelayListEvent, blockedRelays)
httpRelayUrls.push(...list.httpRead, ...list.httpWrite) httpRelayUrls.push(...list.httpRead)
} }
return { return {
@ -106,7 +110,8 @@ export function FeedProvider({ children }: { children: ReactNode }) {
[] []
) )
const viewerNostrLandAggrEligible = useMemo(() => { /** Keeps {@link getViewerRelayStackNostrLandAggrEligible} in sync for non-home reads (threads, profiles, etc.). */
useEffect(() => {
const urls = [ const urls = [
...favoriteFeedRelayUrls, ...favoriteFeedRelayUrls,
...replyExtraRelayLayers.inboxRelayUrls, ...replyExtraRelayLayers.inboxRelayUrls,
@ -114,7 +119,7 @@ export function FeedProvider({ children }: { children: ReactNode }) {
...replyExtraRelayLayers.cacheRelayUrls, ...replyExtraRelayLayers.cacheRelayUrls,
...replyExtraRelayLayers.httpRelayUrls ...replyExtraRelayLayers.httpRelayUrls
] ]
return syncViewerRelayStackNostrLandAggrEligible(urls) syncViewerRelayStackNostrLandAggrEligible(urls)
}, [favoriteFeedRelayUrls, replyExtraRelayLayers]) }, [favoriteFeedRelayUrls, replyExtraRelayLayers])
const lastHomeFeedUrlLogRef = useRef({ primary: '', reply: '' }) const lastHomeFeedUrlLogRef = useRef({ primary: '', reply: '' })
@ -139,14 +144,7 @@ export function FeedProvider({ children }: { children: ReactNode }) {
} }
setUrlStateIfChanged(setRelayUrls, primaryRelays) setUrlStateIfChanged(setRelayUrls, primaryRelays)
setUrlStateIfChanged(setReplyRelayUrls, replyRelays) setUrlStateIfChanged(setReplyRelayUrls, replyRelays)
}, [ }, [favoriteFeedRelayUrls, blockedRelays, primaryExtraRelayUrls, replyExtraRelayLayers, setUrlStateIfChanged])
favoriteFeedRelayUrls,
blockedRelays,
primaryExtraRelayUrls,
replyExtraRelayLayers,
setUrlStateIfChanged,
viewerNostrLandAggrEligible
])
const favoriteRelaysIdentity = useMemo( const favoriteRelaysIdentity = useMemo(
() => () =>
@ -170,7 +168,6 @@ export function FeedProvider({ children }: { children: ReactNode }) {
() => () =>
[ [
...replyExtraRelayLayers.inboxRelayUrls, ...replyExtraRelayLayers.inboxRelayUrls,
...replyExtraRelayLayers.outboxRelayUrls,
...replyExtraRelayLayers.cacheRelayUrls, ...replyExtraRelayLayers.cacheRelayUrls,
...replyExtraRelayLayers.httpRelayUrls ...replyExtraRelayLayers.httpRelayUrls
] ]
@ -191,7 +188,6 @@ export function FeedProvider({ children }: { children: ReactNode }) {
relaySets.length, relaySets.length,
favoriteFeedRelayUrls.length - favoriteRelays.length, favoriteFeedRelayUrls.length - favoriteRelays.length,
replyExtraRelayLayers.inboxRelayUrls.length, replyExtraRelayLayers.inboxRelayUrls.length,
replyExtraRelayLayers.outboxRelayUrls.length,
replyExtraRelayLayers.cacheRelayUrls.length, replyExtraRelayLayers.cacheRelayUrls.length,
replyExtraRelayLayers.httpRelayUrls.length, replyExtraRelayLayers.httpRelayUrls.length,
blockedRelays.length blockedRelays.length
@ -206,7 +202,6 @@ export function FeedProvider({ children }: { children: ReactNode }) {
relaySets: relaySets.length, relaySets: relaySets.length,
relaySetRelays: favoriteFeedRelayUrls.length - favoriteRelays.length, relaySetRelays: favoriteFeedRelayUrls.length - favoriteRelays.length,
inboxRelays: replyExtraRelayLayers.inboxRelayUrls.length, inboxRelays: replyExtraRelayLayers.inboxRelayUrls.length,
outboxRelays: replyExtraRelayLayers.outboxRelayUrls.length,
cacheRelays: replyExtraRelayLayers.cacheRelayUrls.length, cacheRelays: replyExtraRelayLayers.cacheRelayUrls.length,
httpRelays: replyExtraRelayLayers.httpRelayUrls.length, httpRelays: replyExtraRelayLayers.httpRelayUrls.length,
blockedRelays: blockedRelays.length blockedRelays: blockedRelays.length

7
src/providers/feed-context.tsx

@ -5,9 +5,12 @@
import { createContext, useContext } from 'react' import { createContext, useContext } from 'react'
export type TFeedContext = { export type TFeedContext = {
/** Home Notes/Gallery: favorites plus mixed trending discovery. */ /** Home Notes (OP): favorites plus Wisp trending only — no aggr, no FAST_READ padding. */
relayUrls: string[] relayUrls: string[]
/** Home Replies: primary feed relays plus viewer inbox, HTTP, cache, and eligible aggregator relays. */ /**
* Home Replies + Gallery: same OP base, then NIP-65 read inboxes (FAST_READ when empty), kind 10432 cache
* read relays, HTTP read index never aggr (aggr is for side-panel threads, profiles, and spells only).
*/
replyRelayUrls: string[] replyRelayUrls: string[]
} }

Loading…
Cancel
Save