Browse Source

bug-fixes

imwald
Silberengel 3 weeks ago
parent
commit
5fb032a120
  1. 68
      src/hooks/useFetchThreadContextEvent.tsx
  2. 3
      src/lib/favorites-feed-relays.ts
  3. 8
      src/lib/nostr-land-relay-eligibility.test.ts
  4. 8
      src/lib/nostr-land-relay-eligibility.ts
  5. 11
      src/lib/relay-list-builder.ts
  6. 9
      src/lib/thread-context-relays.ts
  7. 3
      src/services/client-events.service.ts

68
src/hooks/useFetchThreadContextEvent.tsx

@ -10,10 +10,37 @@ import { buildThreadContextFetchRelayUrls } from '@/lib/thread-context-relays' @@ -10,10 +10,37 @@ import { buildThreadContextFetchRelayUrls } from '@/lib/thread-context-relays'
import client, { eventService } from '@/services/client.service'
import { navigationEventStore } from '@/services/navigation-event-store'
import { Event } from 'nostr-tools'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'
export type ThreadContextRole = 'parent' | 'root'
/** First usable event from parallel fetches, or undefined after all settle or `timeoutMs`. */
function raceThreadContextFetches(
tasks: Array<() => Promise<Event | undefined>>,
timeoutMs: number
): Promise<Event | undefined> {
if (tasks.length === 0) return Promise.resolve(undefined)
return new Promise((resolve) => {
const timer = window.setTimeout(() => resolve(undefined), timeoutMs)
let settled = 0
const finish = (ev: Event | undefined) => {
settled++
if (ev) {
window.clearTimeout(timer)
resolve(ev)
return
}
if (settled === tasks.length) {
window.clearTimeout(timer)
resolve(undefined)
}
}
for (const run of tasks) {
void run().then(finish).catch(() => finish(undefined))
}
})
}
export function useFetchThreadContextEvent(
eventId: string | undefined,
contextEvent: Event | undefined,
@ -28,10 +55,8 @@ export function useFetchThreadContextEvent( @@ -28,10 +55,8 @@ export function useFetchThreadContextEvent(
const [event, setEvent] = useState<Event | undefined>(initialEvent)
const [isFetching, setIsFetching] = useState(!initialEvent)
const [refetchToken, setRefetchToken] = useState(0)
const searchableAttemptedRef = useRef(false)
const refetch = useCallback(() => {
searchableAttemptedRef.current = false
setRefetchToken((n) => n + 1)
}, [])
@ -45,10 +70,6 @@ export function useFetchThreadContextEvent( @@ -45,10 +70,6 @@ export function useFetchThreadContextEvent(
[blockedRelays]
)
useEffect(() => {
searchableAttemptedRef.current = false
}, [eventId])
useEffect(() => {
let cancelled = false
@ -111,35 +132,28 @@ export function useFetchThreadContextEvent( @@ -111,35 +132,28 @@ export function useFetchThreadContextEvent(
? { relayHints: relayUrls, threadContext: true as const }
: { threadContext: true as const }
const fetchParentOrRoot = async () => {
const fetchParentOrRoot = () => {
if (skipShortcuts) {
return eventService.fetchEventForceRetry(eventId, threadOpts)
}
return eventService.fetchEvent(eventId, threadOpts)
}
let fetchedEvent = await Promise.race([
fetchParentOrRoot(),
new Promise<undefined>((resolve) => {
window.setTimeout(() => resolve(undefined), THREAD_CONTEXT_EVENT_FETCH_GLOBAL_TIMEOUT_MS)
const aggrAwareSearch = sanitizeRelayUrlsForFetch(getAggrAwareSearchRelayUrls())
const tasks: Array<() => Promise<Event | undefined>> = [fetchParentOrRoot]
if (aggrAwareSearch.length > 0) {
tasks.push(async () => {
const ev = await client.fetchEventWithExternalRelays(eventId, aggrAwareSearch)
if (ev) client.addEventToCache(ev)
return ev
})
])
const aggrAwareSearch = getAggrAwareSearchRelayUrls()
if (!fetchedEvent && !searchableAttemptedRef.current && aggrAwareSearch.length > 0) {
searchableAttemptedRef.current = true
const searchable = sanitizeRelayUrlsForFetch(aggrAwareSearch)
fetchedEvent = await Promise.race([
client.fetchEventWithExternalRelays(eventId, searchable),
new Promise<undefined>((resolve) => {
window.setTimeout(() => resolve(undefined), THREAD_CONTEXT_EVENT_FETCH_GLOBAL_TIMEOUT_MS)
})
])
if (fetchedEvent) {
client.addEventToCache(fetchedEvent)
}
}
const fetchedEvent = await raceThreadContextFetches(
tasks,
THREAD_CONTEXT_EVENT_FETCH_GLOBAL_TIMEOUT_MS
)
if (cancelled) return
if (fetchedEvent && !isEventDeleted(fetchedEvent)) {
setEvent(fetchedEvent)

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

@ -19,7 +19,6 @@ import { @@ -19,7 +19,6 @@ import {
import { feedRelayPolicyUrls, type FeedRelayLayer } from '@/features/feed/relay-policy'
import { stripMailboxLocalUrlsForRemoteViewers } from '@/lib/relay-list-sanitize'
import { relaySessionStrikes } from '@/lib/relay-strikes'
import { prependAggrNostrLandIfViewerEligible } from '@/lib/nostr-land-relay-eligibility'
import { profileFetchRelayUrlsWithoutFastReadLayer } from '@/lib/viewer-relay-defaults'
function isBlockedRelay(url: string, blockedRelays: string[]): boolean {
@ -40,7 +39,7 @@ export function userReadRelaysWithHttp( @@ -40,7 +39,7 @@ export function userReadRelaysWithHttp(
): string[] {
const http = relayList?.httpRead ?? []
const read = relayList?.read ?? []
return prependAggrNostrLandIfViewerEligible(dedupeNormalizeRelayUrlsOrdered([...http, ...read]))
return dedupeNormalizeRelayUrlsOrdered([...http, ...read])
}
export function getFavoritesFeedRelayUrls(

8
src/lib/nostr-land-relay-eligibility.test.ts

@ -4,6 +4,7 @@ import { @@ -4,6 +4,7 @@ import {
filterAggrNostrLandUnlessViewerEligible,
getAggrAwareSearchRelayUrls,
getViewerNostrLandAggrSearchRelayUrls,
prependAggrForEventLookupRelayUrls,
prependAggrNostrLandIfViewerEligible,
relayUrlsIncludeCanonicalNostrLandRelay,
syncViewerRelayStackNostrLandAggrEligible
@ -32,6 +33,13 @@ describe('nostr.land aggr eligibility', () => { @@ -32,6 +33,13 @@ describe('nostr.land aggr eligibility', () => {
])
})
it('prependAggrForEventLookupRelayUrls matches prependAggrNostrLandIfViewerEligible', () => {
syncViewerRelayStackNostrLandAggrEligible(['wss://nostr.land/'])
expect(prependAggrForEventLookupRelayUrls(['wss://inbox.example/'])[0]).toMatch(
/^wss:\/\/aggr\.nostr\.land\/?$/
)
})
it('getAggrAwareSearchRelayUrls prepends aggr when eligible', () => {
syncViewerRelayStackNostrLandAggrEligible(['wss://nostr.land/'])
const urls = getAggrAwareSearchRelayUrls()

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

@ -108,6 +108,14 @@ export function urlsForViewerNostrLandAggrEligibilitySync(options: { @@ -108,6 +108,14 @@ export function urlsForViewerNostrLandAggrEligibilitySync(options: {
]
}
/**
* Prepend aggr for event-by-id lookups (threads, embeds, parent previews, comprehensive fetch).
* Home OP timelines must not use this use {@link buildAllFavoritesFeedRelayUrls} / `nostrLandAggr: 'never'`.
*/
export function prependAggrForEventLookupRelayUrls(relayUrls: readonly string[]): string[] {
return prependAggrNostrLandIfViewerEligible(relayUrls)
}
/** Deduped prepend of aggr when the viewer opted into nostr.land relays (see sync…). */
export function prependAggrNostrLandIfViewerEligible(relayUrls: readonly string[]): string[] {
if (!viewerStackMentionsNostrLand) return [...relayUrls]

11
src/lib/relay-list-builder.ts

@ -13,6 +13,7 @@ import { FAST_READ_RELAY_URLS, PROFILE_RELAY_URLS, SEARCHABLE_RELAY_URLS } from @@ -13,6 +13,7 @@ import { FAST_READ_RELAY_URLS, PROFILE_RELAY_URLS, SEARCHABLE_RELAY_URLS } from
import { feedRelayPolicyUrls } from '@/features/feed/relay-policy'
import { mergeRelayUrlLayers, userReadRelaysWithHttp } from '@/lib/favorites-feed-relays'
import { isRelayBlockedByUser } from '@/lib/relay-blocked'
import { prependAggrForEventLookupRelayUrls } from '@/lib/nostr-land-relay-eligibility'
import { urlIsNonLocalForRemoteViewer } from '@/lib/relay-list-sanitize'
import {
canonicalRelaySessionKey,
@ -385,7 +386,7 @@ export async function buildComprehensiveRelayList(options: RelayListBuilderOptio @@ -385,7 +386,7 @@ export async function buildComprehensiveRelayList(options: RelayListBuilderOptio
}),
personalKeys
)
if (httpRelayUrls.length === 0) return ws
if (httpRelayUrls.length === 0) return prependAggrForEventLookupRelayUrls(ws)
const seen = new Set(ws.map(relayKey))
const out = [...ws]
for (const u of httpRelayUrls) {
@ -394,7 +395,7 @@ export async function buildComprehensiveRelayList(options: RelayListBuilderOptio @@ -394,7 +395,7 @@ export async function buildComprehensiveRelayList(options: RelayListBuilderOptio
seen.add(k)
out.push(u)
}
return out
return prependAggrForEventLookupRelayUrls(out)
}
/**
@ -649,7 +650,7 @@ export async function buildReplyReadRelayList( @@ -649,7 +650,7 @@ export async function buildReplyReadRelayList(
includeProfileFetchRelays: false,
blockedRelays
})
return scoped
return prependAggrForEventLookupRelayUrls(scoped)
}
let useGlobal = true
@ -682,5 +683,7 @@ export async function buildReplyReadRelayList( @@ -682,5 +683,7 @@ export async function buildReplyReadRelayList(
includeProfileFetchRelays: useGlobal,
blockedRelays
})
return mergeRelayUrlLayers([scoped, defaultFavoriteRelaysForViewer(useGlobal)], blockedRelays)
return prependAggrForEventLookupRelayUrls(
mergeRelayUrlLayers([scoped, defaultFavoriteRelaysForViewer(useGlobal)], blockedRelays)
)
}

9
src/lib/thread-context-relays.ts

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
import { prependAggrForEventLookupRelayUrls } from '@/lib/nostr-land-relay-eligibility'
import { sanitizeRelayUrlsForFetch } from '@/lib/read-only-relay-personal'
import { urlIsNonLocalForRemoteViewer } from '@/lib/relay-list-sanitize'
import { buildReplyReadRelayList, relayHintsFromEventTags } from '@/lib/relay-list-builder'
@ -41,5 +42,11 @@ export async function buildThreadContextFetchRelayUrls( @@ -41,5 +42,11 @@ export async function buildThreadContextFetchRelayUrls(
...new Set([...tagHints, ...relayHintsFromEventTags(contextEvent)])
])
const opAuthorPubkey = pubkeyFromThreadETag(targetTag)
return buildReplyReadRelayList(opAuthorPubkey, viewerPubkey, blockedRelays, threadRelayHints)
const relays = await buildReplyReadRelayList(
opAuthorPubkey,
viewerPubkey,
blockedRelays,
threadRelayHints
)
return prependAggrForEventLookupRelayUrls(relays)
}

3
src/services/client-events.service.ts

@ -11,6 +11,7 @@ import { @@ -11,6 +11,7 @@ import {
SINGLE_EVENT_BY_ID_QUERY_EOSE_TIMEOUT_MS,
SINGLE_EVENT_BY_ID_QUERY_GLOBAL_TIMEOUT_MS
} from '@/constants'
import { prependAggrForEventLookupRelayUrls } from '@/lib/nostr-land-relay-eligibility'
import { sanitizeRelayUrlsForFetch } from '@/lib/read-only-relay-personal'
import { urlIsNonLocalForRemoteViewer } from '@/lib/relay-list-sanitize'
import logger from '@/lib/logger'
@ -1195,7 +1196,7 @@ export class EventService { @@ -1195,7 +1196,7 @@ export class EventService {
[...new Set(urls.map((u) => normalizeUrl(u)).filter((u): u is string => Boolean(u)))]
)
if (extraRelayHints?.length) {
relays = normalizeRelayList(extraRelayHints)
relays = normalizeRelayList(prependAggrForEventLookupRelayUrls(extraRelayHints))
}
if (/^[0-9a-f]{64}$/i.test(id)) {

Loading…
Cancel
Save