Browse Source

bug-fixes

imwald
Silberengel 1 month ago
parent
commit
7c1ecb673e
  1. 2
      package.json
  2. 4
      src/components/ProfileListBySearch/index.tsx
  3. 71
      src/components/SearchResult/FullTextSearchByRelay.tsx
  4. 35
      src/constants.ts
  5. 4
      src/hooks/useSearchProfiles.tsx
  6. 4
      src/lib/favorites-feed-relays.ts
  7. 12
      src/lib/relay-list-builder.ts
  8. 6
      src/lib/tombstone-events.ts
  9. 4
      src/lib/viewer-relay-defaults.ts
  10. 6
      src/pages/primary/SpellsPage/index.tsx
  11. 4
      src/pages/secondary/BookmarkListPage/index.tsx
  12. 7
      src/providers/NostrProvider/index.tsx
  13. 28
      src/services/client-replaceable-events.service.ts
  14. 38
      src/services/client.service.ts

2
package.json

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
{
"name": "imwald",
"version": "23.10.3",
"version": "23.11.1",
"description": "Imwald — a user-friendly Nostr client focused on relay feed browsing, publications, and relay discovery",
"private": true,
"type": "module",

4
src/components/ProfileListBySearch/index.tsx

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
import { useSecondaryPage } from '@/PageManager'
import { PROFILE_FETCH_RELAY_URLS } from '@/constants'
import { PROFILE_RELAY_URLS } from '@/constants'
import { decodeProfileSearchQueryToPubkeyHex } from '@/lib/profile-search-query'
import { buildAlexandriaEventsSearchUrlForTSearchParams } from '@/lib/alexandria-events-search-url'
import { toProfile } from '@/lib/link'
@ -15,7 +15,7 @@ import { AlexandriaEventsSearchEmptyCta } from '@/components/AlexandriaEventsSea @@ -15,7 +15,7 @@ import { AlexandriaEventsSearchEmptyCta } from '@/components/AlexandriaEventsSea
const LIMIT = 50
const PROFILE_SEARCH_RELAY_URLS = Array.from(
new Set(PROFILE_FETCH_RELAY_URLS.map((u) => normalizeUrl(u) || u).filter(Boolean))
new Set(PROFILE_RELAY_URLS.map((u) => normalizeUrl(u) || u).filter(Boolean))
)
export function ProfileListBySearch({ search }: { search: string }) {

71
src/components/SearchResult/FullTextSearchByRelay.tsx

@ -266,8 +266,10 @@ export default function FullTextSearchByRelay({ @@ -266,8 +266,10 @@ export default function FullTextSearchByRelay({
relayRows.length > 0 && relayRows.every((r) => r.phase === 'done' || r.phase === 'error')
useEffect(() => {
const abort = new AbortController()
/** Unmount / total wall only — must not abort in-flight NIP-50 when the “first hits + …ms” scheduling cutoff runs. */
const runAbort = new AbortController()
let masterTimer: ReturnType<typeof setTimeout> | null = null
let stopSchedulingTimer: ReturnType<typeof setTimeout> | null = null
const myRun = ++runGeneration.current
const cleanupInvalidatePreviousRun = () => {
runGeneration.current += 1
@ -277,7 +279,11 @@ export default function FullTextSearchByRelay({ @@ -277,7 +279,11 @@ export default function FullTextSearchByRelay({
clearTimeout(masterTimer)
masterTimer = null
}
abort.abort()
if (stopSchedulingTimer != null) {
clearTimeout(stopSchedulingTimer)
stopSchedulingTimer = null
}
runAbort.abort()
cleanupInvalidatePreviousRun()
}
@ -307,38 +313,44 @@ export default function FullTextSearchByRelay({ @@ -307,38 +313,44 @@ export default function FullTextSearchByRelay({
/** Set when the first {@link runOneRelay} begins (first real NIP-50 query); master wall clock starts then. */
let waveT0: number | null = null
let waveEndAt = 0
/** Only after ≥1 event from a relay: apply "first results + …ms" (empty EOSE must not shorten the wave). */
let appliedRelativeWaveCutoff = false
/** After first preview-visible relay hits: stop dequeuing new relays; in-flight REQs keep their per-relay budget. */
let stopSchedulingNewRelays = false
/** Only after ≥1 preview-visible event from a relay: stop starting new relays after …ms (empty EOSE must not shorten). */
let appliedRelativeSchedulingCutoff = false
const scheduleMasterAbort = () => {
const scheduleMasterWallAbort = () => {
if (masterTimer != null) {
clearTimeout(masterTimer)
masterTimer = null
}
const ms = Math.max(0, waveEndAt - Date.now())
if (waveT0 === null) return
const ms = Math.max(0, waveT0 + SEARCH_TOTAL_WALL_MS - Date.now())
masterTimer = setTimeout(() => {
masterTimer = null
abort.abort()
stopSchedulingNewRelays = true
runAbort.abort()
}, ms)
}
const beginWaveIfNeeded = () => {
if (waveT0 !== null) return
waveT0 = Date.now()
waveEndAt = waveT0 + SEARCH_TOTAL_WALL_MS
scheduleMasterAbort()
scheduleMasterWallAbort()
}
const onFirstSearchHits = () => {
if (appliedRelativeWaveCutoff || waveT0 === null) return
appliedRelativeWaveCutoff = true
const now = Date.now()
waveEndAt = Math.min(waveT0 + SEARCH_TOTAL_WALL_MS, now + SEARCH_AFTER_FIRST_RELAY_MS)
scheduleMasterAbort()
const onFirstPreviewVisibleRelayHits = () => {
if (appliedRelativeSchedulingCutoff || waveT0 === null) return
appliedRelativeSchedulingCutoff = true
if (stopSchedulingTimer != null) {
clearTimeout(stopSchedulingTimer)
}
stopSchedulingTimer = setTimeout(() => {
stopSchedulingTimer = null
stopSchedulingNewRelays = true
}, SEARCH_AFTER_FIRST_RELAY_MS)
}
abort.signal.addEventListener(
runAbort.signal.addEventListener(
'abort',
() => {
setRelayRows((prev) =>
@ -415,7 +427,7 @@ export default function FullTextSearchByRelay({ @@ -415,7 +427,7 @@ export default function FullTextSearchByRelay({
includeOtherStoresFullText: true,
fullTextStoreHitCap: 260
})
if (myRun !== runGeneration.current || abort.signal.aborted) return
if (myRun !== runGeneration.current || runAbort.signal.aborted) return
const mergedLocalMatching = mergedLocal.filter((e) => mergedSearchNoteHasPreviewBody(e))
if (mergedLocalMatching.length === 0) return
applyMergedUpdate((map) => {
@ -432,25 +444,24 @@ export default function FullTextSearchByRelay({ @@ -432,25 +444,24 @@ export default function FullTextSearchByRelay({
})()
const runOneRelay = async (relayUrl: string) => {
if (myRun !== runGeneration.current || abort.signal.aborted) return
if (myRun !== runGeneration.current || runAbort.signal.aborted) return
beginWaveIfNeeded()
const t0 = performance.now()
const remainingWaveMs = Math.max(500, waveEndAt - Date.now())
const perRelayBudget = Math.min(SEARCH_PER_RELAY_QUERY_MS, remainingWaveMs)
try {
const { events: raw, connectionError } = await client.fetchEventsFromSingleRelay(
relayUrl,
filter,
{ globalTimeout: perRelayBudget, signal: abort.signal }
{ globalTimeout: SEARCH_PER_RELAY_QUERY_MS, signal: runAbort.signal }
)
if (myRun !== runGeneration.current) return
const sorted = [...raw]
.sort((a, b) => compareEventsForDTagQuery(q, a, b))
.slice(0, FULL_TEXT_SEARCH_MAX_NOTES_PER_RELAY)
const previewVisible = sorted.filter((e) => mergedSearchNoteHasPreviewBody(e))
const ms = Math.round(performance.now() - t0)
if (sorted.length === 0 && connectionError) {
if (previewVisible.length === 0 && connectionError) {
setRelayRows((prev) =>
prev.map((r) =>
r.relayUrl === relayUrl
@ -462,10 +473,10 @@ export default function FullTextSearchByRelay({ @@ -462,10 +473,10 @@ export default function FullTextSearchByRelay({
}
mergeIntoHits(relayUrl, sorted)
void addSearchEventsToSessionCacheBatched(sorted, runGeneration, myRun)
void addSearchEventsToSessionCacheBatched(previewVisible, runGeneration, myRun)
if (sorted.length > 0) {
onFirstSearchHits()
if (previewVisible.length > 0) {
onFirstPreviewVisibleRelayHits()
}
setRelayRows((prev) =>
prev.map((r) =>
@ -473,16 +484,16 @@ export default function FullTextSearchByRelay({ @@ -473,16 +484,16 @@ export default function FullTextSearchByRelay({
? {
...r,
phase: 'done',
eventCount: sorted.length,
eventCount: previewVisible.length,
ms,
errorMessage: sorted.length > 0 ? undefined : connectionError
errorMessage: previewVisible.length > 0 ? undefined : connectionError
}
: r
)
)
} catch (err) {
if (myRun !== runGeneration.current) return
if (abort.signal.aborted) return
if (runAbort.signal.aborted) return
const msg = err instanceof Error ? err.message : String(err)
const ms = Math.round(performance.now() - t0)
setRelayRows((prev) =>
@ -494,7 +505,7 @@ export default function FullTextSearchByRelay({ @@ -494,7 +505,7 @@ export default function FullTextSearchByRelay({
}
const worker = async () => {
while (myRun === runGeneration.current && !abort.signal.aborted) {
while (myRun === runGeneration.current && !runAbort.signal.aborted && !stopSchedulingNewRelays) {
const relayUrl = nextRelayUrl()
if (!relayUrl) break
await runOneRelay(relayUrl)

35
src/constants.ts

@ -73,9 +73,7 @@ export const DESKTOP_APP_DOWNLOAD_URL_DEFAULT = @@ -73,9 +73,7 @@ export const DESKTOP_APP_DOWNLOAD_URL_DEFAULT =
export const DEFAULT_FAVORITE_RELAYS = [
'wss://theforest.nostr1.com',
'wss://christpill.nostr1.com',
'wss://nostr.land',
'wss://nostr21.com'
'wss://nostr.land'
]
/**
@ -284,7 +282,6 @@ export const PROFILE_FETCH_PROMISE_TIMEOUT_MS = 22_000 @@ -284,7 +282,6 @@ export const PROFILE_FETCH_PROMISE_TIMEOUT_MS = 22_000
* @see https://blossom.happytavern.co/ — Lotus-style ephemeral Blossom (0x0 backend).
*/
export const STANDARD_BLOSSOM_UPLOAD_HOSTS = [
{ url: 'https://blossom.happytavern.co', labelKey: 'BlossomUploadOptionHappyTavern' },
{ url: 'https://0x0.happytavern.co', labelKey: 'BlossomUploadOptionHappyTavern' },
{ url: 'https://blossom.band', labelKey: 'BlossomUploadOptionBand' },
{ url: 'https://blossom.primal.net', labelKey: 'BlossomUploadOptionPrimal' },
@ -415,7 +412,8 @@ export const READ_ONLY_RELAY_URLS = [ @@ -415,7 +412,8 @@ export const READ_ONLY_RELAY_URLS = [
'wss://search.nos.today',
'wss://trending.nostr.wine',
'wss://relay.nip46.com',
'wss://filter.nostr.wine'
'wss://filter.nostr.wine',
'wss://primus.nostr1.com'
]
/**
@ -455,14 +453,10 @@ export const E_TAG_FILTER_BLOCKED_RELAY_URLS = [ @@ -455,14 +453,10 @@ export const E_TAG_FILTER_BLOCKED_RELAY_URLS = [
// Optimized relay list for read operations (includes aggregator)
export const FAST_READ_RELAY_URLS = [
'wss://theforest.nostr1.com',
'wss://orly-relay.imwald.eu',
'wss://nostr.wine',
'wss://nostr.land',
'wss://nostr21.com',
'wss://thecitadel.nostr1.com',
'wss://aggr.nostr.land',
'wss://primus.nostr1.com',
'wss://wheat.happytavern.co'
'wss://nostr.wine',
'wss://orly-relay.imwald.eu',
'wss://nostr21.com'
]
// Optimized relay list for write operations (no aggregator since it's read-only)
@ -477,11 +471,11 @@ export const FAST_WRITE_RELAY_URLS = [ @@ -477,11 +471,11 @@ export const FAST_WRITE_RELAY_URLS = [
/** Relays used for NIP-94 file metadata (kind 1063) / GIF discovery and publish.
* Publish to all of these so GIFs are discoverable across clients; some may be temporarily down. */
export const GIF_RELAY_URLS = [
'wss://relay.gifbuddy.lol',
'wss://relay.damus.io',
'wss://relay.primal.net',
'wss://thecitadel.nostr1.com',
'wss://nos.lol',
'wss://nostr.mom'
]
export const SEARCHABLE_RELAY_URLS = [
@ -489,29 +483,20 @@ export const SEARCHABLE_RELAY_URLS = [ @@ -489,29 +483,20 @@ export const SEARCHABLE_RELAY_URLS = [
'wss://nostr.wine',
'wss://orly-relay.imwald.eu',
'wss://relay.noswhere.com',
'wss://relay.wikifreedia.xyz',
'wss://nostr.einundzwanzig.space',
'wss://nostr-pub.wellorder.net',
'wss://pyramid.fiatjaf.com/',
'wss://nostrelites.org',
'wss://wheat.happytavern.co'
'wss://nostr-pub.wellorder.net'
]
export const PROFILE_RELAY_URLS = [
'wss://profiles.nostr1.com',
'wss://purplepag.es',
'wss://relay.primal.net',
'wss://relay.damus.io',
'wss://nos.lol'
'wss://profiles.nostrver.se/',
'wss://indexer.coracle.social/'
]
export const FOLLOWS_HISTORY_RELAY_URLS = [
'wss://hist.nostr.land'
]
// Profile reads + NIP-50 profile search: search/index relays first, then fast read + profile mirrors (order preserved; dedupe at use sites).
export const PROFILE_FETCH_RELAY_URLS = [...SEARCHABLE_RELAY_URLS, ...FAST_READ_RELAY_URLS, ...PROFILE_RELAY_URLS]
export const ExtendedKind = {
PICTURE: 20,
VIDEO: 21,

4
src/hooks/useSearchProfiles.tsx

@ -1,11 +1,11 @@ @@ -1,11 +1,11 @@
import { PROFILE_FETCH_RELAY_URLS } from '@/constants'
import { PROFILE_RELAY_URLS } from '@/constants'
import { normalizeUrl } from '@/lib/url'
import client from '@/services/client.service'
import { TProfile } from '@/types'
import { useEffect, useState } from 'react'
const PROFILE_SEARCH_RELAY_URLS = Array.from(
new Set(PROFILE_FETCH_RELAY_URLS.map((u) => normalizeUrl(u) || u).filter(Boolean))
new Set(PROFILE_RELAY_URLS.map((u) => normalizeUrl(u) || u).filter(Boolean))
)
export function useSearchProfiles(search: string, limit: number) {

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

@ -2,7 +2,7 @@ import { @@ -2,7 +2,7 @@ import {
DEFAULT_FAVORITE_RELAYS,
DOCUMENT_RELAY_URLS,
FAST_READ_RELAY_URLS,
PROFILE_FETCH_RELAY_URLS,
PROFILE_RELAY_URLS,
READ_ONLY_RELAY_URLS,
isDocumentRelayKind,
relayFilterIncludesSocialKindBlockedKind
@ -222,7 +222,7 @@ export function buildProfilePageReadRelayUrls( @@ -222,7 +222,7 @@ export function buildProfilePageReadRelayUrls(
)
/** Authors without kind 10002: widen REQ targets so notes/metadata are still discoverable on index relays. */
if (authorHasNoNip65) {
const profileSource = useGlobal ? PROFILE_FETCH_RELAY_URLS : profileFetchRelayUrlsWithoutFastReadLayer()
const profileSource = useGlobal ? PROFILE_RELAY_URLS : profileFetchRelayUrlsWithoutFastReadLayer()
const profileFetchLayer = profileSource.map((u) => normalizeUrl(u) || u).filter(Boolean) as string[]
return mergeRelayUrlLayers([urls, profileFetchLayer], blockedRelays).slice(0, maxRelays + 8)
}

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

@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
* - Includes seen relays
*/
import { FAST_READ_RELAY_URLS, PROFILE_FETCH_RELAY_URLS, SEARCHABLE_RELAY_URLS } from '@/constants'
import { FAST_READ_RELAY_URLS, PROFILE_RELAY_URLS, SEARCHABLE_RELAY_URLS } from '@/constants'
import { feedRelayPolicyUrls } from '@/features/feed/relay-policy'
import { mergeRelayUrlLayers, userReadRelaysWithHttp } from '@/lib/favorites-feed-relays'
import { urlIsNonLocalForRemoteViewer } from '@/lib/relay-list-sanitize'
@ -38,7 +38,7 @@ function dedupeNormalizedRelayUrls(urls: string[]): string[] { @@ -38,7 +38,7 @@ function dedupeNormalizedRelayUrls(urls: string[]): string[] {
* PROFILE_FETCH + FAST_READ.
*/
function exploreDiscoveryBootstrapRelayUrls(): string[] {
return dedupeNormalizedRelayUrls([...PROFILE_FETCH_RELAY_URLS, ...FAST_READ_RELAY_URLS])
return dedupeNormalizedRelayUrls([...PROFILE_RELAY_URLS, ...FAST_READ_RELAY_URLS])
}
export interface RelayListBuilderOptions {
@ -54,7 +54,7 @@ export interface RelayListBuilderOptions { @@ -54,7 +54,7 @@ export interface RelayListBuilderOptions {
containingEventRelays?: string[]
/** Whether to include user's own relays (read/write/local) - for profiles/metadata */
includeUserOwnRelays?: boolean
/** Whether to include PROFILE_FETCH_RELAY_URLS - for profiles/metadata */
/** Whether to include PROFILE_RELAY_URLS - for profiles/metadata */
includeProfileFetchRelays?: boolean
/** Whether to include FAST_READ_RELAY_URLS as fallback */
includeFastReadRelays?: boolean
@ -72,7 +72,7 @@ export interface RelayListBuilderOptions { @@ -72,7 +72,7 @@ export interface RelayListBuilderOptions {
/** Whether to include user's favorite relays (kind 10012) */
includeFavoriteRelays?: boolean
/**
* When true with fast-read / searchable / profile-fetch includes: insert `PROFILE_FETCH_RELAY_URLS`,
* When true with fast-read / searchable / profile-fetch includes: insert `PROFILE_RELAY_URLS`,
* `FAST_READ_RELAY_URLS`, and `SEARCHABLE_RELAY_URLS` immediately after hints/seen/containing and **before**
* author + user NIP-65 lists. Used for batched metadata and embed fetches so public mirrors are not queued
* behind broken personal relays under the global connection cap.
@ -133,7 +133,7 @@ export async function buildComprehensiveRelayList(options: RelayListBuilderOptio @@ -133,7 +133,7 @@ export async function buildComprehensiveRelayList(options: RelayListBuilderOptio
// connection slots on broken personal relays before PROFILE_FETCH + FAST_READ answer).
if (preferPublicReadRelaysEarly) {
if (includeProfileFetchRelays) {
PROFILE_FETCH_RELAY_URLS.forEach(addRelay)
PROFILE_RELAY_URLS.forEach(addRelay)
}
if (includeFastReadRelays) {
FAST_READ_RELAY_URLS.forEach(addRelay)
@ -212,7 +212,7 @@ export async function buildComprehensiveRelayList(options: RelayListBuilderOptio @@ -212,7 +212,7 @@ export async function buildComprehensiveRelayList(options: RelayListBuilderOptio
// 6. Profile fetch relays (for profiles/metadata)
if (includeProfileFetchRelays) {
PROFILE_FETCH_RELAY_URLS.forEach(addRelay)
PROFILE_RELAY_URLS.forEach(addRelay)
}
// 7. Fast read relays (fallback)

6
src/lib/tombstone-events.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { PROFILE_FETCH_RELAY_URLS } from '@/constants'
import { PROFILE_RELAY_URLS } from '@/constants'
import { normalizeAnyRelayUrl, normalizeHttpRelayUrl, normalizeUrl } from '@/lib/url'
import type { TRelayList } from '@/types'
@ -16,7 +16,7 @@ export function buildDeletionRelayUrls(relayList: TRelayList | null | undefined) @@ -16,7 +16,7 @@ export function buildDeletionRelayUrls(relayList: TRelayList | null | undefined)
const httpW = relayList?.httpWrite ?? []
if (!relayList?.read?.length && !relayList?.write?.length && !httpR.length && !httpW.length) {
return Array.from(
new Set(PROFILE_FETCH_RELAY_URLS.map((url) => normalizeUrl(url) || url).filter(Boolean))
new Set(PROFILE_RELAY_URLS.map((url) => normalizeUrl(url) || url).filter(Boolean))
).slice(0, 20)
}
const ws = relayList?.write ?? []
@ -27,7 +27,7 @@ export function buildDeletionRelayUrls(relayList: TRelayList | null | undefined) @@ -27,7 +27,7 @@ export function buildDeletionRelayUrls(relayList: TRelayList | null | undefined)
...rs.slice(0, 8).map((url: string) => normalizeUrl(url) || url),
...httpW.map((url: string) => normalizeHttpRelayUrl(url) || url),
...httpR.slice(0, 8).map((url: string) => normalizeHttpRelayUrl(url) || url),
...PROFILE_FETCH_RELAY_URLS.map((url: string) => normalizeAnyRelayUrl(url) || url)
...PROFILE_RELAY_URLS.map((url: string) => normalizeAnyRelayUrl(url) || url)
])
).slice(0, 20)
}

4
src/lib/viewer-relay-defaults.ts

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
import {
DEFAULT_FAVORITE_RELAYS,
FAST_READ_RELAY_URLS,
PROFILE_FETCH_RELAY_URLS
PROFILE_RELAY_URLS
} from '@/constants'
import { normalizeUrl } from '@/lib/url'
@ -43,7 +43,7 @@ const fastReadKeySet = (): Set<string> => { @@ -43,7 +43,7 @@ const fastReadKeySet = (): Set<string> => {
/** PROFILE_FETCH stack with {@link FAST_READ_RELAY_URLS} entries removed (order preserved). */
export function profileFetchRelayUrlsWithoutFastReadLayer(): string[] {
const drop = fastReadKeySet()
return PROFILE_FETCH_RELAY_URLS.filter((u) => {
return PROFILE_RELAY_URLS.filter((u) => {
const n = (normalizeUrl(u) || u).toLowerCase()
return n && !drop.has(n)
})

6
src/pages/primary/SpellsPage/index.tsx

@ -27,7 +27,7 @@ import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider' @@ -27,7 +27,7 @@ import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider'
import { useKindFilterOrDefaults } from '@/providers/KindFilterProvider'
import { useBookmarks } from '@/providers/bookmarks-context'
import { useNostr } from '@/providers/NostrProvider'
import { useNotificationThreadWatch } from '@/providers/NotificationThreadWatchProvider'
import { useNotificationThreadWatchOptional } from '@/providers/NotificationThreadWatchProvider'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { useUserTrust } from '@/contexts/user-trust-context'
import { dedupeFollowSetEventsByD } from '@/lib/follow-set-spell'
@ -89,7 +89,9 @@ const SpellsPage = forwardRef<TPageRef>(function SpellsPage( @@ -89,7 +89,9 @@ const SpellsPage = forwardRef<TPageRef>(function SpellsPage(
} = useNostr()
const { addBookmark, removeBookmark } = useBookmarks()
const { hideUntrustedNotifications } = useUserTrust()
const { eventsIFollowListEvent, eventsIMutedListEvent } = useNotificationThreadWatch()
const notificationThreadWatch = useNotificationThreadWatchOptional()
const eventsIFollowListEvent = notificationThreadWatch?.eventsIFollowListEvent ?? null
const eventsIMutedListEvent = notificationThreadWatch?.eventsIMutedListEvent ?? null
const { isSmallScreen } = useScreenSize()
const { favoriteRelays, blockedRelays } = useFavoriteRelays()
const useGlobalRelayBootstrap = useGlobalRelayBootstrapDefaults()

4
src/pages/secondary/BookmarkListPage/index.tsx

@ -27,7 +27,7 @@ import { getLatestEvent } from '@/lib/event' @@ -27,7 +27,7 @@ import { getLatestEvent } from '@/lib/event'
import { buildAccountListRelayUrlsForMerge } from '@/lib/account-list-relay-urls'
import { fetchLatestReplaceableListEvent } from '@/lib/replaceable-list-latest'
import { normalizeUrl } from '@/lib/url'
import { PROFILE_FETCH_RELAY_URLS } from '@/constants'
import { PROFILE_RELAY_URLS } from '@/constants'
import { queryService } from '@/services/client.service'
import dayjs from 'dayjs'
import { Code, Eraser, MoreVertical } from 'lucide-react'
@ -64,7 +64,7 @@ const BookmarkListPage = forwardRef( @@ -64,7 +64,7 @@ const BookmarkListPage = forwardRef(
const urls = Array.from(
new Set(
[
...PROFILE_FETCH_RELAY_URLS.map((u) => normalizeUrl(u) || u),
...PROFILE_RELAY_URLS.map((u) => normalizeUrl(u) || u),
...(relayList?.write ?? []).map((u) => normalizeUrl(u) || u)
].filter(Boolean)
)

7
src/providers/NostrProvider/index.tsx

@ -7,7 +7,6 @@ import { @@ -7,7 +7,6 @@ import {
DEFAULT_FAVORITE_RELAYS,
FAST_READ_RELAY_URLS,
ExtendedKind,
PROFILE_FETCH_RELAY_URLS,
PROFILE_RELAY_URLS,
SEARCHABLE_RELAY_URLS,
UNSIGNED_EXPERIMENTAL_KIND_MAX,
@ -543,7 +542,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { @@ -543,7 +542,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
...mergedRelayList.write.map((url: string) => normalizeUrl(url) || url),
...mergedRelayList.read.map((url: string) => normalizeUrl(url) || url),
...FAST_READ_RELAY_URLS.map((url: string) => normalizeUrl(url) || url),
...PROFILE_FETCH_RELAY_URLS.map((url: string) => normalizeUrl(url) || url)
...PROFILE_RELAY_URLS.map((url: string) => normalizeUrl(url) || url)
]
const fetchRelays = Array.from(new Set(normalizedRelays)).slice(0, 16)
const events = await queryService.fetchEvents(fetchRelays, [
@ -706,7 +705,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { @@ -706,7 +705,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
...mergedRelayList.write.map((u) => normalizeUrl(u) || u),
...mergedRelayList.read.map((u) => normalizeUrl(u) || u),
...SEARCHABLE_RELAY_URLS.map((u) => normalizeUrl(u) || u),
...PROFILE_FETCH_RELAY_URLS.map((u) => normalizeUrl(u) || u),
...PROFILE_RELAY_URLS.map((u) => normalizeUrl(u) || u),
...FAST_READ_RELAY_URLS.map((u) => normalizeUrl(u) || u)
])
).filter(Boolean)
@ -914,7 +913,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { @@ -914,7 +913,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
...rl.write.map((u) => normalizeUrl(u) || u),
...rl.read.map((u) => normalizeUrl(u) || u),
...SEARCHABLE_RELAY_URLS.map((u) => normalizeUrl(u) || u),
...PROFILE_FETCH_RELAY_URLS.map((u) => normalizeUrl(u) || u),
...PROFILE_RELAY_URLS.map((u) => normalizeUrl(u) || u),
...FAST_READ_RELAY_URLS.map((u) => normalizeUrl(u) || u)
])
).filter(Boolean)

28
src/services/client-replaceable-events.service.ts

@ -7,7 +7,7 @@ import { @@ -7,7 +7,7 @@ import {
METADATA_BATCH_QUERY_EOSE_TIMEOUT_MS,
METADATA_BATCH_QUERY_GLOBAL_TIMEOUT_MS,
PROFILE_BATCH_NETWORK_LOAD_TIMEOUT_MS,
PROFILE_FETCH_RELAY_URLS,
PROFILE_RELAY_URLS,
READ_ONLY_RELAY_URLS,
RECOMMENDED_BLOSSOM_SERVERS
} from '@/constants'
@ -120,7 +120,7 @@ export class ReplaceableEventService { @@ -120,7 +120,7 @@ export class ReplaceableEventService {
/**
* Build comprehensive relay list: author's outboxes + user's inboxes + relay hints + defaults
* For profiles/metadata: includes user's own relays (read/write/local) + PROFILE_FETCH_RELAY_URLS
* For profiles/metadata: includes user's own relays (read/write/local) + PROFILE_RELAY_URLS
*/
private async buildComprehensiveRelayListForAuthor(
authorPubkey: string,
@ -138,7 +138,7 @@ export class ReplaceableEventService { @@ -138,7 +138,7 @@ export class ReplaceableEventService {
relayHints,
containingEventRelays,
includeUserOwnRelays: isProfileOrMetadata, // For profiles/metadata, include user's own relays
includeProfileFetchRelays: isProfileOrMetadata, // For profiles/metadata, include PROFILE_FETCH_RELAY_URLS
includeProfileFetchRelays: isProfileOrMetadata, // For profiles/metadata, include PROFILE_RELAY_URLS
includeFastReadRelays: true,
includeLocalRelays: true
})
@ -148,7 +148,7 @@ export class ReplaceableEventService { @@ -148,7 +148,7 @@ export class ReplaceableEventService {
* Fetch replaceable event (profile, relay list, etc.)
* Uses DataLoader to batch IndexedDB checks and network fetches
* ALWAYS uses: author's outboxes + user's inboxes + relay hints + defaults
* For profiles/metadata: includes user's own relays (read/write/local) + PROFILE_FETCH_RELAY_URLS
* For profiles/metadata: includes user's own relays (read/write/local) + PROFILE_RELAY_URLS
*
* @param pubkey - Author's pubkey
* @param kind - Event kind
@ -518,11 +518,11 @@ export class ReplaceableEventService { @@ -518,11 +518,11 @@ export class ReplaceableEventService {
Array.from(missingGroups.entries()).map(async ([kind, missingItems]) => {
const pubkeys = missingItems.map(item => item.pubkey)
// ALWAYS use comprehensive relay list: author's outboxes + user's inboxes + defaults
// For profiles/metadata: includes user's own relays (read/write/local) + PROFILE_FETCH_RELAY_URLS
// For profiles/metadata: includes user's own relays (read/write/local) + PROFILE_RELAY_URLS
// For each pubkey, build comprehensive relay list
// CRITICAL FIX: For batch fetches, use default relays instead of fetching relay lists for each author
// Fetching relay lists for hundreds of authors causes infinite loops and browser crashes
// Use PROFILE_FETCH_RELAY_URLS + FAST_READ_RELAY_URLS for profiles, or FAST_READ_RELAY_URLS for other kinds.
// Use PROFILE_RELAY_URLS + FAST_READ_RELAY_URLS for profiles, or FAST_READ_RELAY_URLS for other kinds.
// For metadata with a logged-in user, merge defaults with {@link buildComprehensiveRelayList}: inboxes (read),
// local/cache relays (10432), favorite relays (10012), plus profile + fast read — same idea as favorites feed
// / inbox-scoped discovery without per-author relay list fetches.
@ -546,10 +546,10 @@ export class ReplaceableEventService { @@ -546,10 +546,10 @@ export class ReplaceableEventService {
preferPublicReadRelaysEarly: true
})
} catch {
relayUrls = Array.from(new Set([...PROFILE_FETCH_RELAY_URLS, ...FAST_READ_RELAY_URLS]))
relayUrls = Array.from(new Set([...PROFILE_RELAY_URLS, ...FAST_READ_RELAY_URLS]))
}
} else {
relayUrls = Array.from(new Set([...PROFILE_FETCH_RELAY_URLS, ...FAST_READ_RELAY_URLS]))
relayUrls = Array.from(new Set([...PROFILE_RELAY_URLS, ...FAST_READ_RELAY_URLS]))
}
} else if (kind === ExtendedKind.FAVORITE_RELAYS) {
relayUrls = await buildExploreProfileAndUserRelayList(client.pubkey)
@ -558,7 +558,7 @@ export class ReplaceableEventService { @@ -558,7 +558,7 @@ export class ReplaceableEventService {
// and 100ms EOSE loses the race when several relays are down.
relayUrls = Array.from(
new Set(
[...READ_ONLY_RELAY_URLS, ...PROFILE_FETCH_RELAY_URLS, ...FAST_READ_RELAY_URLS].map(
[...READ_ONLY_RELAY_URLS, ...PROFILE_RELAY_URLS, ...FAST_READ_RELAY_URLS].map(
(u) => normalizeUrl(u) || u
)
)
@ -567,7 +567,7 @@ export class ReplaceableEventService { @@ -567,7 +567,7 @@ export class ReplaceableEventService {
// Contacts (kind 3): aggregators + profile mirrors + fast read.
relayUrls = Array.from(
new Set(
[...READ_ONLY_RELAY_URLS, ...PROFILE_FETCH_RELAY_URLS, ...FAST_READ_RELAY_URLS].map(
[...READ_ONLY_RELAY_URLS, ...PROFILE_RELAY_URLS, ...FAST_READ_RELAY_URLS].map(
(u) => normalizeUrl(u) || u
)
)
@ -576,7 +576,7 @@ export class ReplaceableEventService { @@ -576,7 +576,7 @@ export class ReplaceableEventService {
// NIP-65 (10002): aggregators + profile mirrors + fast read.
relayUrls = Array.from(
new Set(
[...READ_ONLY_RELAY_URLS, ...PROFILE_FETCH_RELAY_URLS, ...FAST_READ_RELAY_URLS].map(
[...READ_ONLY_RELAY_URLS, ...PROFILE_RELAY_URLS, ...FAST_READ_RELAY_URLS].map(
(u) => normalizeUrl(u) || u
)
)
@ -585,7 +585,7 @@ export class ReplaceableEventService { @@ -585,7 +585,7 @@ export class ReplaceableEventService {
// Mute / bookmark lists: same distribution as contacts; FAST_READ + mirrors.
relayUrls = Array.from(
new Set(
[...READ_ONLY_RELAY_URLS, ...PROFILE_FETCH_RELAY_URLS, ...FAST_READ_RELAY_URLS].map(
[...READ_ONLY_RELAY_URLS, ...PROFILE_RELAY_URLS, ...FAST_READ_RELAY_URLS].map(
(u) => normalizeUrl(u) || u
)
)
@ -594,7 +594,7 @@ export class ReplaceableEventService { @@ -594,7 +594,7 @@ export class ReplaceableEventService {
// NIP-A3 kind 10133: aggregators + profile mirrors + fast read.
relayUrls = Array.from(
new Set(
[...READ_ONLY_RELAY_URLS, ...PROFILE_FETCH_RELAY_URLS, ...FAST_READ_RELAY_URLS].map(
[...READ_ONLY_RELAY_URLS, ...PROFILE_RELAY_URLS, ...FAST_READ_RELAY_URLS].map(
(u) => normalizeUrl(u) || u
)
)
@ -937,7 +937,7 @@ export class ReplaceableEventService { @@ -937,7 +937,7 @@ export class ReplaceableEventService {
new Set([
...relayHints,
...authorRelays,
...PROFILE_FETCH_RELAY_URLS,
...PROFILE_RELAY_URLS,
...FAST_READ_RELAY_URLS
])
)

38
src/services/client.service.ts

@ -29,7 +29,7 @@ import { @@ -29,7 +29,7 @@ import {
EARLY_PUBLISH_SUCCESS_GRACE_MS,
DEFAULT_FAVORITE_RELAYS,
NIP66_DISCOVERY_RELAY_URLS,
PROFILE_FETCH_RELAY_URLS,
PROFILE_RELAY_URLS,
PROFILE_RELAY_URLS,
READ_ONLY_RELAY_URLS,
NIP42_POOL_AUTOMATIC_AUTH_RELAY_URLS,
@ -1242,12 +1242,12 @@ class ClientService extends EventTarget { @@ -1242,12 +1242,12 @@ class ClientService extends EventTarget {
].includes(event.kind)
) {
bootstrapExtras.push(
...(useGlobalRelayDefaults ? PROFILE_FETCH_RELAY_URLS : profileFetchRelayUrlsWithoutFastReadLayer())
...(useGlobalRelayDefaults ? PROFILE_RELAY_URLS : profileFetchRelayUrlsWithoutFastReadLayer())
)
logger.debug('[DetermineTargetRelays] Relay list event detected, adding PROFILE_FETCH_RELAY_URLS', {
logger.debug('[DetermineTargetRelays] Relay list event detected, adding PROFILE_RELAY_URLS', {
kind: event.kind,
profileFetchRelays: useGlobalRelayDefaults
? PROFILE_FETCH_RELAY_URLS
? PROFILE_RELAY_URLS
: profileFetchRelayUrlsWithoutFastReadLayer(),
additionalRelayCount: bootstrapExtras.length
})
@ -1264,7 +1264,7 @@ class ClientService extends EventTarget { @@ -1264,7 +1264,7 @@ class ClientService extends EventTarget {
})
} else if (event.kind === ExtendedKind.RSS_FEED_LIST) {
if (useGlobalRelayDefaults) {
bootstrapExtras.push(...FAST_WRITE_RELAY_URLS, ...PROFILE_FETCH_RELAY_URLS)
bootstrapExtras.push(...FAST_WRITE_RELAY_URLS, ...PROFILE_RELAY_URLS)
} else {
bootstrapExtras.push(...profileFetchRelayUrlsWithoutFastReadLayer())
}
@ -3733,7 +3733,7 @@ class ClientService extends EventTarget { @@ -3733,7 +3733,7 @@ class ClientService extends EventTarget {
/**
* Npubs for @-mention dropdown: (1) follow-list profiles matching the query,
* (2) local index, (3) kind-0 NIP-50 search on {@link PROFILE_FETCH_RELAY_URLS} (includes search relays + profile mirrors; deduped).
* (2) local index, (3) kind-0 NIP-50 search on {@link PROFILE_RELAY_URLS} (includes search relays + profile mirrors; deduped).
* Returns cached results immediately, then streams relay results via callback.
*/
/**
@ -3775,7 +3775,7 @@ class ClientService extends EventTarget { @@ -3775,7 +3775,7 @@ class ClientService extends EventTarget {
const pk = authorPubkey?.trim().toLowerCase()
if (!pk || !/^[0-9a-f]{64}$/.test(pk)) return
const urls = (relayUrls.length > 0 ? relayUrls : [...PROFILE_FETCH_RELAY_URLS])
const urls = (relayUrls.length > 0 ? relayUrls : [...PROFILE_RELAY_URLS])
.map((u) => normalizeUrl(u) || u)
.filter(Boolean)
const capped = Array.from(new Set(urls)).slice(0, 16)
@ -3867,7 +3867,7 @@ class ClientService extends EventTarget { @@ -3867,7 +3867,7 @@ class ClientService extends EventTarget {
// Relay query starts immediately so it can run in parallel with local + follow work (slow relays).
const profileSearchRelayUrls = dedupeNormalizeRelayUrlsOrdered(
PROFILE_FETCH_RELAY_URLS.map((u) => normalizeUrl(u) || u).filter(Boolean)
PROFILE_RELAY_URLS.map((u) => normalizeUrl(u) || u).filter(Boolean)
)
const relayTask =
q.length >= 1
@ -4152,8 +4152,8 @@ class ClientService extends EventTarget { @@ -4152,8 +4152,8 @@ class ClientService extends EventTarget {
const [fallback] = await this.mergeRelayListsFromStoredOnly([pubkey])
return fallback!
} catch {
const read = PROFILE_FETCH_RELAY_URLS
const write = PROFILE_FETCH_RELAY_URLS
const read = PROFILE_RELAY_URLS
const write = PROFILE_RELAY_URLS
return {
write,
read,
@ -4174,7 +4174,7 @@ class ClientService extends EventTarget { @@ -4174,7 +4174,7 @@ class ClientService extends EventTarget {
/**
* Merge relay list from IndexedDB only (no network). Same rules as a timed-out {@link fetchRelayLists}:
* defaults to {@link PROFILE_FETCH_RELAY_URLS} when kind 10002 is missing.
* defaults to {@link PROFILE_RELAY_URLS} when kind 10002 is missing.
*/
async peekRelayListFromStorage(pubkey: string): Promise<TRelayList> {
const [rl] = await this.mergeRelayListsFromStoredOnly([pubkey])
@ -4308,14 +4308,14 @@ class ClientService extends EventTarget { @@ -4308,14 +4308,14 @@ class ClientService extends EventTarget {
if (isOwnRelayList && storedCacheEvent) {
const cacheRelayList = getRelayListFromEvent(storedCacheEvent)
return mergeKind10243({
write: cacheRelayList.write.length > 0 ? cacheRelayList.write : PROFILE_FETCH_RELAY_URLS,
read: cacheRelayList.read.length > 0 ? cacheRelayList.read : PROFILE_FETCH_RELAY_URLS,
write: cacheRelayList.write.length > 0 ? cacheRelayList.write : PROFILE_RELAY_URLS,
read: cacheRelayList.read.length > 0 ? cacheRelayList.read : PROFILE_RELAY_URLS,
originalRelays: cacheRelayList.originalRelays,
...emptyHttp
})
}
let read = PROFILE_FETCH_RELAY_URLS
let write = PROFILE_FETCH_RELAY_URLS
let read = PROFILE_RELAY_URLS
let write = PROFILE_RELAY_URLS
if (!isOwnRelayList) {
const stripped = stripMailboxLocalUrlsForRemoteViewers({ read, write })
read =
@ -4551,8 +4551,8 @@ class ClientService extends EventTarget { @@ -4551,8 +4551,8 @@ class ClientService extends EventTarget {
try {
return await this.mergeRelayListsFromStoredOnly(pubkeys)
} catch {
const read = PROFILE_FETCH_RELAY_URLS
const write = PROFILE_FETCH_RELAY_URLS
const read = PROFILE_RELAY_URLS
const write = PROFILE_RELAY_URLS
return pubkeys.map(() => ({
write,
read,
@ -4571,7 +4571,7 @@ class ClientService extends EventTarget { @@ -4571,7 +4571,7 @@ class ClientService extends EventTarget {
/**
* Fetch cache relay events (kind 10432) from multiple sources:
* - PROFILE_FETCH_RELAY_URLS
* - PROFILE_RELAY_URLS
* - User's inboxes (read relays from kind 10002)
* - User's outboxes (write relays from kind 10002)
*/
@ -4708,7 +4708,7 @@ class ClientService extends EventTarget { @@ -4708,7 +4708,7 @@ class ClientService extends EventTarget {
...relayList.write.map((u) => normalizeUrl(u) || u),
...relayList.read.map((u) => normalizeUrl(u) || u),
...FAST_READ_RELAY_URLS.map((u) => normalizeUrl(u) || u),
...PROFILE_FETCH_RELAY_URLS.map((u) => normalizeUrl(u) || u)
...PROFILE_RELAY_URLS.map((u) => normalizeUrl(u) || u)
]).filter(Boolean)
const capped = urls.slice(0, 20)
if (capped.length === 0) return []

Loading…
Cancel
Save