Browse Source

bug-fixes

imwald
Silberengel 1 month ago
parent
commit
0a1b0a4468
  1. 2
      src/components/Embedded/EmbeddedNote.tsx
  2. 57
      src/components/NoteOptions/useMenuActions.tsx
  3. 9
      src/components/SearchResult/index.tsx
  4. 11
      src/lib/relay-list-builder.ts
  5. 4
      src/pages/secondary/RssFeedSettingsPage/index.tsx
  6. 4
      src/providers/FeedProvider.tsx
  7. 9
      src/providers/NostrProvider/index.tsx
  8. 56
      src/services/client-replaceable-events.service.ts
  9. 20
      src/services/media-upload.service.ts
  10. 18
      src/services/spell.service.ts

2
src/components/Embedded/EmbeddedNote.tsx

@ -2,7 +2,6 @@ import { Skeleton } from '@/components/ui/skeleton' @@ -2,7 +2,6 @@ import { Skeleton } from '@/components/ui/skeleton'
import ExternalLink from '@/components/ExternalLink'
import {
FAST_READ_RELAY_URLS,
FAST_WRITE_RELAY_URLS,
PROFILE_RELAY_URLS,
SEARCHABLE_RELAY_URLS,
ExtendedKind
@ -537,7 +536,6 @@ function buildEmbedWideRelayUrlsStatic( @@ -537,7 +536,6 @@ function buildEmbedWideRelayUrlsStatic(
...nip66Service.getSearchableRelayUrls(),
...SEARCHABLE_RELAY_URLS,
...FAST_READ_RELAY_URLS,
...FAST_WRITE_RELAY_URLS,
...PROFILE_RELAY_URLS,
...menuRelayUrls
])

57
src/components/NoteOptions/useMenuActions.tsx

@ -220,8 +220,7 @@ export function useMenuActions({ @@ -220,8 +220,7 @@ export function useMenuActions({
const allRelays = [
...(currentBrowsingRelayUrlsRef.current || []),
...(favoriteRelaysRef.current || []),
...FAST_READ_RELAY_URLS,
...FAST_WRITE_RELAY_URLS
...FAST_READ_RELAY_URLS
]
const comprehensiveRelays = Array.from(
new Set(allRelays.map(url => normalizeAnyRelayUrl(url)).filter((url): url is string => !!url))
@ -247,22 +246,15 @@ export function useMenuActions({ @@ -247,22 +246,15 @@ export function useMenuActions({
if (!pubkey) return
try {
// Build comprehensive relay list for pin list fetching
const allRelays = [
...(currentBrowsingRelayUrls || []),
...(favoriteRelays || []),
...FAST_READ_RELAY_URLS,
...FAST_READ_RELAY_URLS,
...FAST_WRITE_RELAY_URLS
]
const normalizedRelays = allRelays
.map(url => normalizeAnyRelayUrl(url))
.filter((url): url is string => !!url)
const comprehensiveRelays = Array.from(new Set(normalizedRelays))
const pinListReadRelays = Array.from(
new Set(
[...currentBrowsingRelayUrls, ...favoriteRelays, ...FAST_READ_RELAY_URLS]
.map((url) => normalizeAnyRelayUrl(url))
.filter((url): url is string => !!url)
)
)
const latestPinList = await fetchNewestPinListForPubkey(pubkey, comprehensiveRelays)
const latestPinList = await fetchNewestPinListForPubkey(pubkey, pinListReadRelays)
logger.component('PinNote', 'Current pin list event', { hasEvent: !!latestPinList })
@ -270,16 +262,31 @@ export function useMenuActions({ @@ -270,16 +262,31 @@ export function useMenuActions({
const successMessage = isPinned ? t('Note unpinned') : t('Note pinned')
logger.component('PinNote', 'Pin list tag count after merge', { count: newTags.length })
const publishRelays = Array.from(
new Set([
...pinListReadRelays,
...FAST_WRITE_RELAY_URLS.map((url) => normalizeAnyRelayUrl(url) || url).filter(
(url): url is string => !!url
)
])
)
// Create and publish the new pin list event
logger.component('PinNote', 'Publishing new pin list event', { tagCount: newTags.length, relayCount: comprehensiveRelays.length })
const publishedEvent = await publish({
kind: 10001,
tags: newTags,
content: '',
created_at: Math.floor(Date.now() / 1000)
}, {
specifiedRelayUrls: comprehensiveRelays
logger.component('PinNote', 'Publishing new pin list event', {
tagCount: newTags.length,
relayCount: publishRelays.length
})
const publishedEvent = await publish(
{
kind: 10001,
tags: newTags,
content: '',
created_at: Math.floor(Date.now() / 1000)
},
{
specifiedRelayUrls: publishRelays
}
)
// Show publishing feedback with relay messages
if ((publishedEvent as any)?.relayStatuses) {

9
src/components/SearchResult/index.tsx

@ -1,9 +1,4 @@ @@ -1,9 +1,4 @@
import {
FAST_READ_RELAY_URLS,
FAST_WRITE_RELAY_URLS,
NIP_SEARCH_PAGE_KINDS,
SEARCHABLE_RELAY_URLS
} from '@/constants'
import { FAST_READ_RELAY_URLS, NIP_SEARCH_PAGE_KINDS, SEARCHABLE_RELAY_URLS } from '@/constants'
import { compareEventsForDTagQuery } from '@/lib/dtag-search'
import { TSearchParams } from '@/types'
import NormalFeed from '../NormalFeed'
@ -47,7 +42,7 @@ export default function SearchResult({ searchParams }: { searchParams: TSearchPa @@ -47,7 +42,7 @@ export default function SearchResult({ searchParams }: { searchParams: TSearchPa
relays.push(...(favoriteRelays || []))
relays.push(...FAST_READ_RELAY_URLS, ...FAST_WRITE_RELAY_URLS, ...SEARCHABLE_RELAY_URLS)
relays.push(...FAST_READ_RELAY_URLS, ...SEARCHABLE_RELAY_URLS)
const normalized = Array.from(
new Set(relays.map((url) => normalizeUrl(url) || url).filter((url): url is string => !!url))

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

@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
* - Includes seen relays
*/
import { FAST_READ_RELAY_URLS, FAST_WRITE_RELAY_URLS, PROFILE_FETCH_RELAY_URLS, SEARCHABLE_RELAY_URLS } from '@/constants'
import { FAST_READ_RELAY_URLS, PROFILE_FETCH_RELAY_URLS, SEARCHABLE_RELAY_URLS } from '@/constants'
import { feedRelayPolicyUrls } from '@/features/feed/relay-policy'
import { userReadRelaysWithHttp } from '@/lib/favorites-feed-relays'
import { urlIsNonLocalForRemoteViewer } from '@/lib/relay-list-sanitize'
@ -57,7 +57,10 @@ export interface RelayListBuilderOptions { @@ -57,7 +57,10 @@ export interface RelayListBuilderOptions {
includeProfileFetchRelays?: boolean
/** Whether to include FAST_READ_RELAY_URLS as fallback */
includeFastReadRelays?: boolean
/** Whether to include FAST_WRITE_RELAY_URLS as fallback */
/**
* Legacy name: adds {@link FAST_READ_RELAY_URLS} as extra bootstrap mirrors for REQ/read lists
* (historically mis-tagged as fast write).
*/
includeFastWriteRelays?: boolean
/** Whether to include SEARCHABLE_RELAY_URLS - for search */
includeSearchableRelays?: boolean
@ -236,9 +239,9 @@ export async function buildComprehensiveRelayList(options: RelayListBuilderOptio @@ -236,9 +239,9 @@ export async function buildComprehensiveRelayList(options: RelayListBuilderOptio
FAST_READ_RELAY_URLS.forEach(addRelay)
}
// 8. Fast write relays (for writing)
// 8. Extra fast-read bootstrap mirrors (call sites use legacy `includeFastWriteRelays`)
if (includeFastWriteRelays) {
FAST_WRITE_RELAY_URLS.forEach(addRelay)
FAST_READ_RELAY_URLS.forEach(addRelay)
}
// 9. Searchable relays (for search)

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

@ -2,7 +2,7 @@ import storage from '@/services/local-storage.service' @@ -2,7 +2,7 @@ import storage from '@/services/local-storage.service'
import { RefreshButton } from '@/components/RefreshButton'
import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
import { usePrimaryNoteView } from '@/contexts/primary-note-view-context'
import { ExtendedKind, FAST_WRITE_RELAY_URLS, PROFILE_RELAY_URLS } from '@/constants'
import { ExtendedKind, FAST_READ_RELAY_URLS, PROFILE_RELAY_URLS } from '@/constants'
import { getLatestEvent } from '@/lib/event'
import { forwardRef, useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -121,7 +121,7 @@ const RssFeedSettingsPage = forwardRef(({ index, hideTitlebar = false }: { index @@ -121,7 +121,7 @@ const RssFeedSettingsPage = forwardRef(({ index, hideTitlebar = false }: { index
}
setLoading(true)
try {
const events = await queryService.fetchEvents(FAST_WRITE_RELAY_URLS.concat(PROFILE_RELAY_URLS), {
const events = await queryService.fetchEvents(FAST_READ_RELAY_URLS.concat(PROFILE_RELAY_URLS), {
kinds: [ExtendedKind.RSS_FEED_LIST],
authors: [pubkey],
limit: 1

4
src/providers/FeedProvider.tsx

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { FAST_READ_RELAY_URLS, FAST_WRITE_RELAY_URLS } from '@/constants'
import { FAST_READ_RELAY_URLS } from '@/constants'
import { feedRelayPolicyUrls } from '@/features/feed/relay-policy'
import { getRelayListFromEvent, getHttpRelayListFromEvent } from '@/lib/event-metadata'
import { buildAllFavoritesFeedRelayUrls } from '@/lib/home-feed-relays'
@ -86,7 +86,7 @@ export function FeedProvider({ children }: { children: ReactNode }) { @@ -86,7 +86,7 @@ export function FeedProvider({ children }: { children: ReactNode }) {
return {
inboxRelayUrls: relayList?.read?.length ? relayList.read : FAST_READ_RELAY_URLS,
outboxRelayUrls: relayList?.write?.length ? relayList.write : FAST_WRITE_RELAY_URLS,
outboxRelayUrls: relayList?.write?.length ? relayList.write : FAST_READ_RELAY_URLS,
cacheRelayUrls,
httpRelayUrls
}

9
src/providers/NostrProvider/index.tsx

@ -5,7 +5,6 @@ import { @@ -5,7 +5,6 @@ import {
ACCOUNT_SESSION_NETWORK_HYDRATE_MIN_INTERVAL_MS,
DEFAULT_FAVORITE_RELAYS,
FAST_READ_RELAY_URLS,
FAST_WRITE_RELAY_URLS,
ExtendedKind,
PROFILE_FETCH_RELAY_URLS,
PROFILE_RELAY_URLS,
@ -412,7 +411,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { @@ -412,7 +411,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
})
queryService
.fetchEvents(FAST_WRITE_RELAY_URLS.concat(PROFILE_RELAY_URLS), {
.fetchEvents(FAST_READ_RELAY_URLS.concat(PROFILE_RELAY_URLS), {
kinds: [ExtendedKind.RSS_FEED_LIST],
authors: [account.pubkey],
limit: 1
@ -505,7 +504,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { @@ -505,7 +504,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
const normalizedRelays = [
...mergedRelayList.write.map((url: string) => normalizeUrl(url) || url),
...mergedRelayList.read.map((url: string) => normalizeUrl(url) || url),
...FAST_WRITE_RELAY_URLS.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)
]
const fetchRelays = Array.from(new Set(normalizedRelays)).slice(0, 16)
@ -670,7 +669,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { @@ -670,7 +669,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
...mergedRelayList.read.map((u) => normalizeUrl(u) || u),
...SEARCHABLE_RELAY_URLS.map((u) => normalizeUrl(u) || u),
...PROFILE_FETCH_RELAY_URLS.map((u) => normalizeUrl(u) || u),
...FAST_WRITE_RELAY_URLS.map((u) => normalizeUrl(u) || u)
...FAST_READ_RELAY_URLS.map((u) => normalizeUrl(u) || u)
])
).filter(Boolean)
queryService
@ -866,7 +865,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { @@ -866,7 +865,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
...rl.read.map((u) => normalizeUrl(u) || u),
...SEARCHABLE_RELAY_URLS.map((u) => normalizeUrl(u) || u),
...PROFILE_FETCH_RELAY_URLS.map((u) => normalizeUrl(u) || u),
...FAST_WRITE_RELAY_URLS.map((u) => normalizeUrl(u) || u)
...FAST_READ_RELAY_URLS.map((u) => normalizeUrl(u) || u)
])
).filter(Boolean)
return queryService.fetchEvents(relays, {

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

@ -1,7 +1,6 @@ @@ -1,7 +1,6 @@
import {
ExtendedKind,
FAST_READ_RELAY_URLS,
FAST_WRITE_RELAY_URLS,
MAX_CONCURRENT_RELAY_CONNECTIONS,
METADATA_BATCH_AUTHORS_CHUNK,
METADATA_BATCH_QUERY_EOSE_TIMEOUT_MS,
@ -653,8 +652,8 @@ export class ReplaceableEventService { @@ -653,8 +652,8 @@ export class ReplaceableEventService {
includeFastReadRelays: true,
includeFavoriteRelays: true,
includeLocalRelays: true,
/** Many users publish kind 0 to NIP-65 write relays; batch path skipped these before. */
includeFastWriteRelays: true,
/** Many users publish kind 0 to NIP-65 write relays; batch path includes public read mirrors via {@link buildComprehensiveRelayList}. */
includeFastWriteRelays: false,
includeSearchableRelays: false,
preferPublicReadRelaysEarly: true
})
@ -677,52 +676,39 @@ export class ReplaceableEventService { @@ -677,52 +676,39 @@ export class ReplaceableEventService {
)
).filter(Boolean)
} else if (kind === kinds.Contacts) {
// Contacts (kind 3): often on write relays; aggregators/profile mirrors also carry copies.
// Contacts (kind 3): aggregators + profile mirrors + fast read.
relayUrls = Array.from(
new Set(
[
...FAST_WRITE_RELAY_URLS,
...READ_ONLY_RELAY_URLS,
...PROFILE_FETCH_RELAY_URLS,
...FAST_READ_RELAY_URLS
].map((u) => normalizeUrl(u) || u)
[...READ_ONLY_RELAY_URLS, ...PROFILE_FETCH_RELAY_URLS, ...FAST_READ_RELAY_URLS].map(
(u) => normalizeUrl(u) || u
)
)
).filter(Boolean)
} else if (kind === kinds.RelayList) {
// NIP-65 (10002): almost always on the author's write/outbox relays; FAST_READ-only misses most users.
// NIP-65 (10002): aggregators + profile mirrors + fast read.
relayUrls = Array.from(
new Set(
[
...FAST_WRITE_RELAY_URLS,
...READ_ONLY_RELAY_URLS,
...PROFILE_FETCH_RELAY_URLS,
...FAST_READ_RELAY_URLS
].map((u) => normalizeUrl(u) || u)
[...READ_ONLY_RELAY_URLS, ...PROFILE_FETCH_RELAY_URLS, ...FAST_READ_RELAY_URLS].map(
(u) => normalizeUrl(u) || u
)
)
).filter(Boolean)
} else if (kind === kinds.Mutelist || kind === kinds.BookmarkList) {
// Mute / bookmark lists: same distribution as contacts (writes + mirrors); FAST_READ-only misses many copies.
// Mute / bookmark lists: same distribution as contacts; FAST_READ + mirrors.
relayUrls = Array.from(
new Set(
[
...FAST_WRITE_RELAY_URLS,
...READ_ONLY_RELAY_URLS,
...PROFILE_FETCH_RELAY_URLS,
...FAST_READ_RELAY_URLS
].map((u) => normalizeUrl(u) || u)
[...READ_ONLY_RELAY_URLS, ...PROFILE_FETCH_RELAY_URLS, ...FAST_READ_RELAY_URLS].map(
(u) => normalizeUrl(u) || u
)
)
).filter(Boolean)
} else if (kind === ExtendedKind.PAYMENT_INFO) {
// NIP-A3 kind 10133: often published to the user's write relays only; FAST_READ alone misses many copies.
// Mirror contacts + pin-list coverage (writes + profile mirrors + aggregators + fast read).
// NIP-A3 kind 10133: aggregators + profile mirrors + fast read.
relayUrls = Array.from(
new Set(
[
...FAST_WRITE_RELAY_URLS,
...READ_ONLY_RELAY_URLS,
...PROFILE_FETCH_RELAY_URLS,
...FAST_READ_RELAY_URLS
].map((u) => normalizeUrl(u) || u)
[...READ_ONLY_RELAY_URLS, ...PROFILE_FETCH_RELAY_URLS, ...FAST_READ_RELAY_URLS].map(
(u) => normalizeUrl(u) || u
)
)
).filter(Boolean)
} else {
@ -1174,7 +1160,7 @@ export class ReplaceableEventService { @@ -1174,7 +1160,7 @@ export class ReplaceableEventService {
includeFavoriteRelays: true,
includeProfileFetchRelays: true,
includeFastReadRelays: true,
includeFastWriteRelays: true,
includeFastWriteRelays: false,
includeSearchableRelays: true,
includeLocalRelays: true
})
@ -1400,7 +1386,7 @@ export class ReplaceableEventService { @@ -1400,7 +1386,7 @@ export class ReplaceableEventService {
/**
* Fetch follow list event.
* When relayUrls are provided (e.g. user write + search relays), queries those directly.
* Otherwise uses the default relay set (FAST_WRITE + PROFILE_FETCH + FAST_READ).
* Otherwise uses the default relay set (READ_ONLY + PROFILE_FETCH + FAST_READ).
*/
/** Hard cap: {@link fetchReplaceableEvent} can otherwise wedge the DataLoader chain when relays never answer. */
private static readonly FETCH_FOLLOW_LIST_REPLACEABLE_TIMEOUT_MS = 14_000
@ -1557,7 +1543,7 @@ export class ReplaceableEventService { @@ -1557,7 +1543,7 @@ export class ReplaceableEventService {
includeFavoriteRelays: true,
includeProfileFetchRelays: true,
includeFastReadRelays: true,
includeFastWriteRelays: true,
includeFastWriteRelays: false,
includeSearchableRelays: true,
includeLocalRelays: true
})

20
src/services/media-upload.service.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
/** Compression runs entirely in-app before upload (`compress-upload-media`). Load `local-storage` before `./client.service` (that graph can re-enter here; constructor reads storage). */
/** Compression runs entirely in-app before upload (`compress-upload-media`). Load `local-storage` before `./client.service`; the default export is lazily constructed so `client`↔`draft-event`↔this module cycles cannot run the constructor before `storage` is initialized. */
import storage from './local-storage.service'
import { compressMediaForUpload } from '@/lib/compress-upload-media'
import { fetchWithTimeout } from '@/lib/fetch-with-timeout'
@ -312,5 +312,21 @@ class MediaUploadService { @@ -312,5 +312,21 @@ class MediaUploadService {
}
}
const instance = new MediaUploadService()
/**
* Eager `new MediaUploadService()` at module load can run while `storage` is still in the TDZ:
* `client.service` (and its graph) may synchronously pull `draft-event` this module again
* before static imports have finished binding. Lazily construct on first property access.
*/
function createMediaUploadServiceLazy(): MediaUploadService {
let inner: MediaUploadService | undefined
return new Proxy({} as MediaUploadService, {
get(_target, prop, receiver) {
if (!inner) inner = new MediaUploadService()
const v = Reflect.get(inner, prop, receiver) as unknown
return typeof v === 'function' ? (v as (...args: unknown[]) => unknown).bind(inner) : v
}
})
}
const instance = createMediaUploadServiceLazy()
export default instance

18
src/services/spell.service.ts

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
* NIP-A7 Spells: parse and execute kind 777 events as portable relay query filters.
*/
import { ExtendedKind, FAST_WRITE_RELAY_URLS } from '@/constants'
import { ExtendedKind, FAST_READ_RELAY_URLS } from '@/constants'
import { getRelayUrlsWithFavoritesFastReadAndInbox } from '@/lib/favorites-feed-relays'
import { tagNameEquals } from '@/lib/tag'
import logger from '@/lib/logger'
@ -48,9 +48,9 @@ export type SpellExecutionContext = { @@ -48,9 +48,9 @@ export type SpellExecutionContext = {
contacts: string[]
}
/** When the spell has no `relays` tag and NIP-65 write list is empty: known-good write relays. */
function defaultSpellWriteFallbackRelays(): string[] {
return dedupeRelayUrls([...FAST_WRITE_RELAY_URLS])
/** When the spell has no `relays` tag and NIP-65 write list is empty: known-good read mirrors for REQ. */
function defaultSpellRelayFallbackRelays(): string[] {
return dedupeRelayUrls([...FAST_READ_RELAY_URLS])
}
/** Max kind-777 events to pull when syncing spell definitions from relays (you only). */
@ -109,15 +109,15 @@ function dedupeRelayUrls(urls: string[]): string[] { @@ -109,15 +109,15 @@ function dedupeRelayUrls(urls: string[]): string[] {
export type GetRelaysForSpellOptions = {
/**
* When true (default): merge FAST_WRITE after the primary list (REQ feeds) for resilience.
* When false: use only spell `relays` tag, NIP-65 write relays, or write fallback no extra padding (COUNT).
* When true (default): merge {@link FAST_READ_RELAY_URLS} after the primary list (REQ) for resilience.
* When false: use only spell `relays` tag, NIP-65 write relays, or read fallback no extra padding (COUNT).
*/
mergeDefaultReadRelays?: boolean
}
/**
* Get relay URLs for executing a spell: spell `relays` tag, else the user's NIP-65 **write** (outbox) relays.
* Publishing and running spells use outboxes only (plus optional FAST_WRITE padding when mergeDefaults is true).
* Running a spell issues REQ queries; optional {@link FAST_READ_RELAY_URLS} padding helps when primaries are slow.
*/
export function getRelaysForSpell(
spell: Event,
@ -137,10 +137,10 @@ export function getRelaysForSpell( @@ -137,10 +137,10 @@ export function getRelaysForSpell(
primary = [...context.relayListWrite]
}
if (!primary.length) {
return defaultSpellWriteFallbackRelays()
return defaultSpellRelayFallbackRelays()
}
if (mergeDefaults) {
return dedupeRelayUrls([...primary, ...FAST_WRITE_RELAY_URLS])
return dedupeRelayUrls([...primary, ...FAST_READ_RELAY_URLS])
}
return dedupeRelayUrls(primary)
}

Loading…
Cancel
Save