Browse Source

fix relay feeds

imwald
Silberengel 1 month ago
parent
commit
0e987713c5
  1. 14
      src/components/NormalFeed/index.tsx
  2. 96
      src/components/NoteList/index.tsx
  3. 5
      src/components/ui/sonner.tsx
  4. 23
      src/constants.ts
  5. 2
      src/i18n/locales/de.ts
  6. 2
      src/i18n/locales/en.ts
  7. 39
      src/pages/primary/NoteListPage/RelaysFeed.tsx
  8. 3
      src/providers/ThemeProvider.tsx
  9. 13
      src/services/client-query.service.ts
  10. 25
      src/services/client.service.ts

14
src/components/NormalFeed/index.tsx

@ -7,7 +7,7 @@ import storage from '@/services/local-storage.service' @@ -7,7 +7,7 @@ import storage from '@/services/local-storage.service'
import type { TPrimaryPageName } from '@/PageManager'
import { TFeedSubRequest, TNoteListMode } from '@/types'
import { cn } from '@/lib/utils'
import { forwardRef, useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react'
import { forwardRef, useCallback, useLayoutEffect, useMemo, useRef, useState, type ReactNode } from 'react'
import KindFilter from '../KindFilter'
const NormalFeed = forwardRef<TNoteListRef, {
@ -28,7 +28,7 @@ const NormalFeed = forwardRef<TNoteListRef, { @@ -28,7 +28,7 @@ const NormalFeed = forwardRef<TNoteListRef, {
mergeTimelineWhenSubRequestFiltersMatch?: boolean
/** Home favorite-relays chip scope; see {@link NoteList} `feedTimelineScopeKey`. */
feedTimelineScopeKey?: string
/** Single-relay Explore / chip: kindless REQ (limit 200), no feed kind filter. */
/** Single-relay Explore / chip: kindless REQ (see `SINGLE_RELAY_KINDLESS_REQ_LIMIT` in constants), no feed kind filter. */
useFilterAsIs?: boolean
clientSideKindFilter?: boolean
allowKindlessRelayExplore?: boolean
@ -38,6 +38,10 @@ const NormalFeed = forwardRef<TNoteListRef, { @@ -38,6 +38,10 @@ const NormalFeed = forwardRef<TNoteListRef, {
showFeedClientFilter?: boolean
/** When set, {@link NoteList} clears 🔍 filters when another primary tab is shown (mounted-but-hidden pages). */
hostPrimaryPageName?: TPrimaryPageName
/** Single-relay kindless wave EOSEd with no events: parent re-subscribes with explicit kinds. */
onSingleRelayKindlessEmpty?: () => void
/** Shown above the feed list (e.g. after kindless→kinds fallback on a single-relay chip). */
feedTopNotice?: ReactNode
}>(function NormalFeed(
{
subRequests,
@ -53,7 +57,9 @@ const NormalFeed = forwardRef<TNoteListRef, { @@ -53,7 +57,9 @@ const NormalFeed = forwardRef<TNoteListRef, {
clientSideKindFilter = false,
allowKindlessRelayExplore = false,
showFeedClientFilter: showFeedClientFilterProp,
hostPrimaryPageName
hostPrimaryPageName,
onSingleRelayKindlessEmpty,
feedTopNotice
},
ref
) {
@ -217,6 +223,8 @@ const NormalFeed = forwardRef<TNoteListRef, { @@ -217,6 +223,8 @@ const NormalFeed = forwardRef<TNoteListRef, {
showFeedClientFilter={showFeedClientFilter}
hostPrimaryPageName={hostPrimaryPageName}
feedClientFilterTabRowHost={mergeFilterWithTabsRow ? feedFilterTabRowHost : undefined}
onSingleRelayKindlessEmpty={onSingleRelayKindlessEmpty}
feedTopNotice={feedTopNotice}
/>
</div>
</>

96
src/components/NoteList/index.tsx

@ -47,7 +47,8 @@ import { @@ -47,7 +47,8 @@ import {
useLayoutEffect,
useMemo,
useRef,
useState
useState,
type ReactNode
} from 'react'
import { CircleAlert } from 'lucide-react'
import { useLongPressAction } from '@/hooks/use-long-press-action'
@ -254,8 +255,9 @@ const NoteList = forwardRef( @@ -254,8 +255,9 @@ const NoteList = forwardRef(
*/
timelineLoadingSafetyTimeoutMs,
/**
* With {@link useFilterAsIs}: omit relay `kinds` when the subrequest filter has none, and narrow
* incoming events to {@link showKinds} before merging (so caps are not filled by unrelated kinds).
* With {@link useFilterAsIs}: omit relay `kinds` when the subrequest filter has none. Kindless relay feeds
* merge the full batch; the kind picker still applies in the list via {@link applyKindPickerInUi}. Other
* `useFilterAsIs` paths may still narrow merged batches to {@link showKinds}.
*/
clientSideKindFilter = false,
/**
@ -293,7 +295,9 @@ const NoteList = forwardRef( @@ -293,7 +295,9 @@ const NoteList = forwardRef(
* When {@link NormalFeed} renders Notes/Replies + kind row, it passes the slot element so the 🔍 control
* sits on that row instead of an extra bar above the list. Omitted on spells / standalone NoteList.
*/
feedClientFilterTabRowHost
feedClientFilterTabRowHost,
onSingleRelayKindlessEmpty,
feedTopNotice
}: {
subRequests: TFeedSubRequest[]
showKinds: number[]
@ -335,6 +339,10 @@ const NoteList = forwardRef( @@ -335,6 +339,10 @@ const NoteList = forwardRef(
showFeedClientFilter?: boolean
hostPrimaryPageName?: TPrimaryPageName
feedClientFilterTabRowHost?: HTMLElement | null
/** Single-relay kindless: if EOSE with no events, parent switches to explicit kinds in `subRequests`. */
onSingleRelayKindlessEmpty?: () => void
/** Optional banner above the feed (e.g. kindless→kinds fallback). */
feedTopNotice?: ReactNode
},
ref
) => {
@ -400,6 +408,10 @@ const NoteList = forwardRef( @@ -400,6 +408,10 @@ const NoteList = forwardRef(
const feedPaintLiveRelayDoneRef = useRef(false)
/** True if any timeline `onEvents` batch had `batch.length > 0`, or one-shot fetches returned any raw events (before UI filters). */
const feedRelayReturnedAnyEventRef = useRef(false)
/** One-shot per timeline init: avoid double-calling parent fallback (Strict Mode / duplicate EOSE). */
const singleRelayKindlessFallbackAttemptedRef = useRef(false)
const onSingleRelayKindlessEmptyRef = useRef(onSingleRelayKindlessEmpty)
onSingleRelayKindlessEmptyRef.current = onSingleRelayKindlessEmpty
/** Dedupe {@link toast.error} when relays return nothing for a feed load. */
const emptyRelayNoHitsToastKeyRef = useRef('')
/** Per-relay outcomes for the current subscribe wave (merged shards); drives empty-feed toast detail. */
@ -605,8 +617,9 @@ const NoteList = forwardRef( @@ -605,8 +617,9 @@ const NoteList = forwardRef(
clientSideKindFilterRef.current = clientSideKindFilter
/**
* When to apply kind picker + kind-1/1111/GitRelease visibility to rows. Kindless home relay chips use a
* kindless REQ and narrow here via {@link clientSideKindFilter}; standalone relay explore keeps firehose.
* When to apply kind picker + kind-1/1111/GitRelease visibility to visible rows. Kindless relay REQs merge
* the full relay batch; this still filters what the list shows (unlike standalone relay explore, which sets
* {@link allowKindlessRelayExplore} without {@link clientSideKindFilter} and shows the firehose).
*/
const applyKindPickerInUi = useMemo(
() =>
@ -1184,6 +1197,7 @@ const NoteList = forwardRef( @@ -1184,6 +1197,7 @@ const NoteList = forwardRef(
feedPaintRelayMetaRef.current = null
feedPaintLiveRelayDoneRef.current = false
feedRelayReturnedAnyEventRef.current = false
singleRelayKindlessFallbackAttemptedRef.current = false
// Re-subscribe with rows visible (e.g. relay URL expansion): don't flash global loading / skeleton.
const keepRowsVisible =
@ -1289,18 +1303,16 @@ const NoteList = forwardRef( @@ -1289,18 +1303,16 @@ const NoteList = forwardRef(
return undefined
}
/**
* Kindless relay REQ (`allowKindlessRelayExplore`): never drop events here relays return many kinds;
* merging only rows in {@link showKinds} left almost nothing in the timeline (e.g. christpill 200 events 1
* visible) while relay explore showed the full firehose. {@link applyKindPickerInUi} / {@link filteredEvents}
* still apply the kind picker for what the user sees.
*/
const narrowLiveBatch = (evs: Event[]) => {
if (seeAllFeedEventsRef.current) return evs
if (
allowKindlessRelayExploreRef.current &&
!(useFilterAsIsRef.current && clientSideKindFilterRef.current)
) {
return evs
}
if (!useFilterAsIsRef.current || !clientSideKindFilterRef.current) {
if (!allowKindlessRelayExploreRef.current) return evs
return evs
}
if (allowKindlessRelayExploreRef.current) return evs
if (!useFilterAsIsRef.current || !clientSideKindFilterRef.current) return evs
return evs.filter((e) => showKinds.includes(e.kind))
}
@ -1536,15 +1548,38 @@ const NoteList = forwardRef( @@ -1536,15 +1548,38 @@ const NoteList = forwardRef(
setHasMore(true)
}
}
// Single-relay home chip: kindless REQ returned nothing — parent re-subscribes with explicit kinds.
if (
eosed &&
effectActive &&
onSingleRelayKindlessEmptyRef.current &&
!singleRelayKindlessFallbackAttemptedRef.current &&
!feedRelayReturnedAnyEventRef.current
) {
const reqs = subRequestsRef.current
const f0 = reqs[0]
if (
reqs.length === 1 &&
f0 &&
f0.urls.length === 1 &&
allowKindlessRelayExploreRef.current &&
useFilterAsIsRef.current &&
clientSideKindFilterRef.current
) {
const f = f0.filter as Filter
const noKinds = !f.kinds || f.kinds.length === 0
if (noKinds) {
singleRelayKindlessFallbackAttemptedRef.current = true
onSingleRelayKindlessEmptyRef.current()
}
}
}
},
onNew: (event: Event) => {
if (!effectActive) return
feedRelayReturnedAnyEventRef.current = true
if (
!seeAllFeedEventsRef.current &&
(!allowKindlessRelayExploreRef.current ||
(useFilterAsIsRef.current && clientSideKindFilterRef.current))
) {
if (!seeAllFeedEventsRef.current && !allowKindlessRelayExploreRef.current) {
if (!useFilterAsIsRef.current && !showKinds.includes(event.kind)) return
if (
clientSideKindFilterRef.current &&
@ -1657,7 +1692,8 @@ const NoteList = forwardRef( @@ -1657,7 +1692,8 @@ const NoteList = forwardRef(
oneShotEoseTimeoutMs,
oneShotFirstRelayGraceMs,
clientSideKindFilter,
allowKindlessRelayExplore
allowKindlessRelayExplore,
onSingleRelayKindlessEmpty
])
const oneShotDebugPrevLoadingRef = useRef(false)
@ -2461,12 +2497,28 @@ const NoteList = forwardRef( @@ -2461,12 +2497,28 @@ const NoteList = forwardRef(
pullingContent=""
>
<div>
{feedTopNotice ? (
<div
className="mb-2 rounded-md border border-border/80 bg-muted/35 px-3 py-2 text-sm text-muted-foreground"
role="note"
>
{feedTopNotice}
</div>
) : null}
{showFeedClientFilter ? feedClientFilterBar : null}
{list}
</div>
</PullToRefresh>
) : (
<div>
{feedTopNotice ? (
<div
className="mb-2 rounded-md border border-border/80 bg-muted/35 px-3 py-2 text-sm text-muted-foreground"
role="note"
>
{feedTopNotice}
</div>
) : null}
{showFeedClientFilter ? feedClientFilterBar : null}
{list}
</div>

5
src/components/ui/sonner.tsx

@ -1,10 +1,11 @@ @@ -1,10 +1,11 @@
import { useTheme } from '@/providers/ThemeProvider'
import { useThemeOptional } from '@/providers/ThemeProvider'
import { Toaster as Sonner } from 'sonner'
type ToasterProps = React.ComponentProps<typeof Sonner>
const Toaster = ({ ...props }: ToasterProps) => {
const { themeSetting } = useTheme()
const themeCtx = useThemeOptional()
const themeSetting = themeCtx?.themeSetting ?? 'system'
return (
<Sonner

23
src/constants.ts

@ -101,7 +101,7 @@ export const FEED_FIRST_RELAY_RESULT_GRACE_MIN_LIMIT = 200 @@ -101,7 +101,7 @@ export const FEED_FIRST_RELAY_RESULT_GRACE_MIN_LIMIT = 200
/**
* Kindless single-relay page REQ: explicit `limit`, no `kinds` (see NoteList `allowKindlessRelayExplore`).
*/
export const SINGLE_RELAY_KINDLESS_REQ_LIMIT = 200
export const SINGLE_RELAY_KINDLESS_REQ_LIMIT = 500
/**
* Minimum time between full account network hydrates (NostrProvider: relay + replaceable fetch from relays).
@ -249,6 +249,13 @@ export const READ_ONLY_RELAY_URLS = [ @@ -249,6 +249,13 @@ export const READ_ONLY_RELAY_URLS = [
'wss://relay.nip46.com'
]
/**
* Relays that need NIP-42 signed before the first REQ returns useful data. Same pool treatment as
* {@link READ_ONLY_RELAY_URLS} (longer connect timeout + proactive `automaticallyAuth`), but **not**
* necessarily read-only for publish keep those relays out of {@link READ_ONLY_RELAY_URLS}.
*/
export const NIP42_POOL_AUTOMATIC_AUTH_RELAY_URLS = ['wss://nostr.wine'] as const
/**
* Relays that reject or poorly serve social kinds (short notes, discussions, URL comments).
* Strip these from REQ/publish relay stacks when the filter or event uses {@link SOCIAL_KIND_BLOCKED_KINDS},
@ -469,6 +476,20 @@ export function relayFilterIncludesSocialKindBlockedKind(filter: Filter): boolea @@ -469,6 +476,20 @@ export function relayFilterIncludesSocialKindBlockedKind(filter: Filter): boolea
return arr.some((kind) => SOCIAL_KIND_BLOCKED_KIND_SET.has(kind))
}
/**
* After dropping {@link SOCIAL_KIND_BLOCKED_RELAY_URLS} from a relay stack: if every URL was removed but the caller
* passed exactly one relay (e.g. a favorite-relay chip), keep it. Blended stacks still omit these relays; a
* user-targeted single-relay feed should actually contact that relay (e.g. thecitadel for kinds the relay does carry).
*/
export function relaysAfterSocialKindBlockedStrip(
originalDedupedUrls: string[],
afterStrip: string[]
): string[] {
if (afterStrip.length > 0) return afterStrip
if (originalDedupedUrls.length === 1) return [...originalDedupedUrls]
return afterStrip
}
/** Event kinds that show “Read this note aloud” in note options (Web Speech API). */
export const READ_ALOUD_KINDS: readonly number[] = [
kinds.ShortTextNote,

2
src/i18n/locales/de.ts

@ -649,6 +649,8 @@ export default { @@ -649,6 +649,8 @@ export default {
None: 'Keine',
'Cache & offline storage': 'Cache & Offline-Speicher',
feedStarting: 'Starting feeds and relays… This can take a few seconds after login.',
singleRelayKindFallbackNotice:
'Dieses Relay hat auf eine offene Anfrage (ohne kinds im Filter) keine Events geliefert. Der Feed unten nutzt stattdessen deinen gewohnten Kind-Filter.',
refreshCacheButtonExplainer:
'Refresh Cache runs an IndexedDB upgrade check, re-fetches your relay lists and profile-related events from the network (same work as the automatic startup sync), syncs kind-5 deletions into tombstones and removes deleted items from the local cache, then refreshes the store counts below.',
'eventArchive.sectionTitle': 'Notes & feed archive',

2
src/i18n/locales/en.ts

@ -638,6 +638,8 @@ export default { @@ -638,6 +638,8 @@ export default {
None: 'None',
'Cache & offline storage': 'Cache & offline storage',
feedStarting: 'Starting feeds and relays… This can take a few seconds after login.',
singleRelayKindFallbackNotice:
'This relay returned no events for an open-ended request (no kinds in the filter). The feed below uses your usual kind filter instead.',
refreshCacheButtonExplainer:
'Refresh Cache runs an IndexedDB upgrade check, re-fetches your relay lists and profile-related events from the network (same work as the automatic startup sync), syncs kind-5 deletions into tombstones and removes deleted items from the local cache, then refreshes the store counts below.',
'eventArchive.sectionTitle': 'Notes & feed archive',

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

@ -7,7 +7,8 @@ import { useFeed } from '@/providers/FeedProvider' @@ -7,7 +7,8 @@ import { useFeed } from '@/providers/FeedProvider'
import { useKindFilterOrDefaults } from '@/providers/KindFilterProvider'
import relayInfoService from '@/services/relay-info.service'
import { kinds } from 'nostr-tools'
import React, { forwardRef, useEffect, useMemo, useState } from 'react'
import React, { forwardRef, useCallback, useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
const RelaysFeed = forwardRef<
TNoteListRef,
@ -18,10 +19,13 @@ const RelaysFeed = forwardRef< @@ -18,10 +19,13 @@ const RelaysFeed = forwardRef<
kindsOverride?: number[]
}
>(function RelaysFeed({ setSubHeader, onSubHeaderRefresh, kindsOverride }, ref) {
const { t } = useTranslation()
const { feedInfo, relayUrls } = useFeed()
const { showKinds } = useKindFilterOrDefaults()
const [areAlgoRelays, setAreAlgoRelays] = useState(false)
const [relayAlgoReady, setRelayAlgoReady] = useState(false)
/** After kindless single-relay REQ EOSEs with no events, re-subscribe with the normal kind list. */
const [singleRelayKindFallback, setSingleRelayKindFallback] = useState(false)
const relayUrlsKey = useMemo(
() =>
@ -76,10 +80,6 @@ const RelaysFeed = forwardRef< @@ -76,10 +80,6 @@ const RelaysFeed = forwardRef<
? showKinds
: [kinds.ShortTextNote]
/** One relay + user kind filter: avoid huge `kinds` REQ (many relays error with "too many kinds"). */
const singleRelayKindlessExplore =
feedInfo.feedType === 'relay' && relayUrls.length === 1 && !kindsOverride?.length
const canRenderFeed =
(feedInfo.feedType === 'relay' ||
feedInfo.feedType === 'relays' ||
@ -97,6 +97,29 @@ const RelaysFeed = forwardRef< @@ -97,6 +97,29 @@ const RelaysFeed = forwardRef<
return undefined
}, [feedInfo.feedType, feedInfo.id])
/** New relay chip / set: try kindless first again. */
useEffect(() => {
setSingleRelayKindFallback(false)
}, [feedTimelineScopeKey])
const onSingleRelayKindlessEmpty = useCallback(() => {
setSingleRelayKindFallback(true)
}, [])
/**
* One relay + user kind filter: kindless `{ limit }` REQ first (many relays error on huge `kinds` arrays).
* If that EOSEs with no events, `onSingleRelayKindlessEmpty` switches to explicit `kinds`.
*/
const singleRelayKindlessExplore =
feedInfo.feedType === 'relay' &&
relayUrls.length === 1 &&
!kindsOverride?.length &&
!singleRelayKindFallback
const feedTopNotice = singleRelayKindFallback ? (
<p className="leading-snug">{t('singleRelayKindFallbackNotice')}</p>
) : null
// Hooks must run every render — never place useMemo after conditional returns.
const subRequests = useMemo(() => {
if (!canRenderFeed) return []
@ -136,6 +159,12 @@ const RelaysFeed = forwardRef< @@ -136,6 +159,12 @@ const RelaysFeed = forwardRef<
clientSideKindFilter={singleRelayKindlessExplore}
showFeedClientFilter
hostPrimaryPageName="feed"
onSingleRelayKindlessEmpty={
feedInfo.feedType === 'relay' && relayUrls.length === 1 && !kindsOverride?.length
? onSingleRelayKindlessEmpty
: undefined
}
feedTopNotice={feedTopNotice}
/>
)
})

3
src/providers/ThemeProvider.tsx

@ -89,3 +89,6 @@ export const useTheme = () => { @@ -89,3 +89,6 @@ export const useTheme = () => {
return context
}
/** For leaf UI (e.g. Toaster) during Vite HMR when the tree can briefly mount outside ThemeProvider. */
export const useThemeOptional = (): ThemeProviderState | undefined => useContext(ThemeProviderContext)

13
src/services/client-query.service.ts

@ -2,6 +2,7 @@ import { @@ -2,6 +2,7 @@ import {
FEED_FIRST_RELAY_RESULT_GRACE_MIN_LIMIT,
FIRST_RELAY_RESULT_GRACE_MS,
relayFilterIncludesSocialKindBlockedKind,
relaysAfterSocialKindBlockedStrip,
SOCIAL_KIND_BLOCKED_RELAY_URLS,
MAX_CONCURRENT_RELAY_CONNECTIONS,
MAX_CONCURRENT_SUBS_PER_RELAY,
@ -447,7 +448,8 @@ export class QueryService { @@ -447,7 +448,8 @@ export class QueryService {
callbacks: SubscribeCallbacks,
relayOpMeta?: { source: string; logLevel?: 'info' | 'debug' }
): { close: () => void } {
let relays = Array.from(new Set(urls))
const originalDedupedRelays = Array.from(new Set(urls))
let relays = originalDedupedRelays
const filters = Array.isArray(filter) ? filter : [filter]
const stripSocialBlockedRelays =
@ -455,7 +457,8 @@ export class QueryService { @@ -455,7 +457,8 @@ export class QueryService {
filters.some((f) => relayFilterIncludesSocialKindBlockedKind(f))
if (stripSocialBlockedRelays) {
const socialKindBlockedSet = new Set(SOCIAL_KIND_BLOCKED_RELAY_URLS.map((u) => normalizeUrl(u) || u))
relays = relays.filter((url) => !socialKindBlockedSet.has(normalizeUrl(url) || url))
const stripped = relays.filter((url) => !socialKindBlockedSet.has(normalizeUrl(url) || url))
relays = relaysAfterSocialKindBlockedStrip(originalDedupedRelays, stripped)
}
if (this.shouldSkipRelayForSession) {
relays = relays.filter((url) => {
@ -686,7 +689,8 @@ export class QueryService { @@ -686,7 +689,8 @@ export class QueryService {
onevent?: (evt: NEvent) => void
} & QueryOptions
): Promise<NEvent[]> {
let relays = Array.from(new Set(urls))
const originalDedupedRelays = Array.from(new Set(urls))
let relays = originalDedupedRelays
if (relays.length === 0) {
const { FAST_READ_RELAY_URLS } = await import('@/constants')
relays = [...FAST_READ_RELAY_URLS]
@ -697,7 +701,8 @@ export class QueryService { @@ -697,7 +701,8 @@ export class QueryService {
filters.some((f) => relayFilterIncludesSocialKindBlockedKind(f))
if (stripSocialBlockedRelays) {
const socialKindBlockedSet = new Set(SOCIAL_KIND_BLOCKED_RELAY_URLS.map((u) => normalizeUrl(u) || u))
relays = relays.filter((url) => !socialKindBlockedSet.has(normalizeUrl(url) || url))
const stripped = relays.filter((url) => !socialKindBlockedSet.has(normalizeUrl(url) || url))
relays = relaysAfterSocialKindBlockedStrip(originalDedupedRelays, stripped)
}
const { onevent, ...queryOpts } = options ?? {}
return this.query(relays, filter, onevent, queryOpts)

25
src/services/client.service.ts

@ -5,6 +5,7 @@ import { @@ -5,6 +5,7 @@ import {
FIRST_RELAY_RESULT_GRACE_MS,
isSocialKindBlockedKind,
relayFilterIncludesSocialKindBlockedKind,
relaysAfterSocialKindBlockedStrip,
SOCIAL_KIND_BLOCKED_RELAY_URLS,
MAX_PUBLISH_RELAYS,
RELAY_POOL_CONNECTION_TIMEOUT_MS,
@ -14,6 +15,7 @@ import { @@ -14,6 +15,7 @@ import {
NIP66_DISCOVERY_RELAY_URLS,
PROFILE_FETCH_RELAY_URLS,
READ_ONLY_RELAY_URLS,
NIP42_POOL_AUTOMATIC_AUTH_RELAY_URLS,
SEARCHABLE_RELAY_URLS
} from '@/constants'
@ -128,7 +130,9 @@ function summarizeFiltersForRelayLog(filters: Filter[]): Record<string, unknown> @@ -128,7 +130,9 @@ function summarizeFiltersForRelayLog(filters: Filter[]): Record<string, unknown>
}
const READ_ONLY_RELAY_CONNECT_BOOST_URLS = new Set(
READ_ONLY_RELAY_URLS.map((u) => normalizeUrl(u) || u)
[...READ_ONLY_RELAY_URLS, ...NIP42_POOL_AUTOMATIC_AUTH_RELAY_URLS].map(
(u) => normalizeUrl(u) || u
)
)
/** Hostname (+ path when not "/") for readable publish / retry console lines. */
@ -324,9 +328,10 @@ class ClientService extends EventTarget { @@ -324,9 +328,10 @@ class ClientService extends EventTarget {
this.signerType = signerType
this.queryService.setSigner(signer, signerType)
/**
* NIP-42: answer `AUTH` on the wire only for read-only aggregators (`READ_ONLY_RELAY_URLS`, e.g. aggr).
* They often require AUTH before REQ; `master`-style auth only on `CLOSED` is too late. Other relays stay
* on reactive `relay.auth()` after `auth-required` to avoid double-sign races with the wider pool.
* NIP-42: proactive `AUTH` for relays that need it before the first REQ (read-only aggregators +
* {@link NIP42_POOL_AUTOMATIC_AUTH_RELAY_URLS}). Without this, a REQ can EOSE empty while the extension
* is still signing; the batch then finishes and never refetches. Other relays stay on reactive
* `relay.auth()` after `auth-required` to avoid double-sign races with the wider pool.
*/
if (signer && signerType !== 'npub') {
this.pool.automaticallyAuth = (relayURL: string) => {
@ -1812,7 +1817,8 @@ class ClientService extends EventTarget { @@ -1812,7 +1817,8 @@ class ClientService extends EventTarget {
},
relayReqLog?: { groupId?: string; onBatchEnd?: (rows: RelayOpTerminalRow[]) => void }
) {
let relays = Array.from(new Set(urls))
const originalDedupedRelays = Array.from(new Set(urls))
let relays = originalDedupedRelays
const filters = Array.isArray(filter) ? filter : [filter]
const stripSocialBlockedRelays =
@ -1820,7 +1826,8 @@ class ClientService extends EventTarget { @@ -1820,7 +1826,8 @@ class ClientService extends EventTarget {
filters.some((f) => relayFilterIncludesSocialKindBlockedKind(f))
if (stripSocialBlockedRelays) {
const socialKindBlockedSet = new Set(SOCIAL_KIND_BLOCKED_RELAY_URLS.map((u) => normalizeUrl(u) || u))
relays = relays.filter((url) => !socialKindBlockedSet.has(normalizeUrl(url) || url))
const stripped = relays.filter((url) => !socialKindBlockedSet.has(normalizeUrl(url) || url))
relays = relaysAfterSocialKindBlockedStrip(originalDedupedRelays, stripped)
}
relays = this.relayUrlsAfterStrikesOrRecover(relays)
@ -2481,7 +2488,8 @@ class ClientService extends EventTarget { @@ -2481,7 +2488,8 @@ class ClientService extends EventTarget {
immediateReturn?: boolean
} = {}
) {
let relays = Array.from(new Set(urls))
const originalDedupedRelays = Array.from(new Set(urls))
let relays = originalDedupedRelays
if (relays.length === 0) relays = [...FAST_READ_RELAY_URLS]
const filters = Array.isArray(filter) ? filter : [filter]
const stripSocialBlockedRelays =
@ -2489,7 +2497,8 @@ class ClientService extends EventTarget { @@ -2489,7 +2497,8 @@ class ClientService extends EventTarget {
filters.some((f) => relayFilterIncludesSocialKindBlockedKind(f))
if (stripSocialBlockedRelays) {
const socialKindBlockedSet = new Set(SOCIAL_KIND_BLOCKED_RELAY_URLS.map((u) => normalizeUrl(u) || u))
relays = relays.filter((url) => !socialKindBlockedSet.has(normalizeUrl(url) || url))
const stripped = relays.filter((url) => !socialKindBlockedSet.has(normalizeUrl(url) || url))
relays = relaysAfterSocialKindBlockedStrip(originalDedupedRelays, stripped)
}
relays = this.relayUrlsAfterStrikesOrRecover(relays)
const events = await this.queryService.query(relays, filter, onevent, {

Loading…
Cancel
Save