Browse Source

bug-fixes

imwald
Silberengel 1 month ago
parent
commit
6e06656d86
  1. 15
      src/components/NormalFeed/index.tsx
  2. 65
      src/constants.ts
  3. 10
      src/lib/event-metadata.ts
  4. 15
      src/pages/primary/NoteListPage/RelaysFeed.tsx
  5. 171
      src/providers/FeedProvider.tsx
  6. 3
      src/providers/feed-context.tsx
  7. 2
      src/services/client.service.ts
  8. 4
      src/services/lightning.service.ts

15
src/components/NormalFeed/index.tsx

@ -59,6 +59,10 @@ const NormalFeed = forwardRef<TNoteListRef, { @@ -59,6 +59,10 @@ const NormalFeed = forwardRef<TNoteListRef, {
*/
preserveTimelineOnSubRequestsChange?: boolean
mergeTimelineWhenSubRequestFiltersMatch?: boolean
/** Home Replies can widen relays without changing Notes/Gallery. */
repliesSubRequests?: TFeedSubRequest[]
/** Main Gallery historically widened with fast read relays; home can opt out to stay favorites+trending only. */
widenMainGalleryRelays?: boolean
/** Home following: second subscribe wave (delta relays / new authors); see {@link NoteList}. */
followingFeedDeltaSubRequests?: TFeedSubRequest[]
/** Stable subscription identity; see {@link NoteList} `feedSubscriptionKey`. */
@ -111,6 +115,8 @@ const NormalFeed = forwardRef<TNoteListRef, { @@ -111,6 +115,8 @@ const NormalFeed = forwardRef<TNoteListRef, {
onSubHeaderRefresh,
preserveTimelineOnSubRequestsChange = false,
mergeTimelineWhenSubRequestFiltersMatch = false,
repliesSubRequests,
widenMainGalleryRelays = true,
followingFeedDeltaSubRequests,
feedSubscriptionKey,
feedTimelineScopeKey,
@ -187,15 +193,18 @@ const NormalFeed = forwardRef<TNoteListRef, { @@ -187,15 +193,18 @@ const NormalFeed = forwardRef<TNoteListRef, {
return base
}, [isMainFeed, isWispTrendingOnlyFeed])
/** When in media mode, replace each shard's kinds with the media set; on the main home feed, widen relay set. */
/** Replies may widen relays; Gallery only swaps kinds and widens relays when the caller opts in. */
const effectiveSubRequests = useMemo(() => {
if (listMode === 'postsAndReplies' && repliesSubRequests) {
return repliesSubRequests
}
if (listMode !== 'media') return subRequests
return subRequests.map((req) => ({
...req,
urls: isMainFeed ? galleryRelayUrlsMergedWithReadLayer(req.urls) : req.urls,
urls: isMainFeed && widenMainGalleryRelays ? galleryRelayUrlsMergedWithReadLayer(req.urls) : req.urls,
filter: { ...req.filter, kinds: MEDIA_KINDS }
}))
}, [listMode, subRequests, MEDIA_KINDS, isMainFeed])
}, [listMode, subRequests, repliesSubRequests, MEDIA_KINDS, isMainFeed, widenMainGalleryRelays])
const handleListModeChange = useCallback(
(mode: TNoteListMode | string) => {

65
src/constants.ts

@ -1,13 +1,5 @@ @@ -1,13 +1,5 @@
import { kinds, type Filter } from 'nostr-tools'
/**
* API base URL. Prefer `VITE_IMWALD_API_BASE_URL`; `VITE_JUMBLE_API_BASE_URL` is still read for existing deploys.
*/
export const IMWALD_API_BASE_URL =
(import.meta.env.VITE_IMWALD_API_BASE_URL as string | undefined)?.trim() ||
(import.meta.env.VITE_JUMBLE_API_BASE_URL as string | undefined)?.trim() ||
'https://api.jumble.imwald.eu'
/** Git Republic web UI for repository links; override with VITE_GITREPUBLIC_WEB_BASE_URL for self-hosted. */
export const GITREPUBLIC_WEB_BASE_URL = (
(import.meta.env.VITE_GITREPUBLIC_WEB_BASE_URL as string | undefined) ?? 'https://gitrepublic.imwald.eu'
@ -128,12 +120,6 @@ export const OUTBOX_PUBLISH_RETRY_DELAY_MS = 5000 @@ -128,12 +120,6 @@ export const OUTBOX_PUBLISH_RETRY_DELAY_MS = 5000
*/
export const EARLY_PUBLISH_SUCCESS_GRACE_MS = 1200
/**
* Cap how long we wait on NIP-65 / inbox relay-list resolution (including `fetchRelayLists` network phase
* and kind-10432 fetch) before publishing or falling back to IndexedDB-only merge.
* Without this, a stuck `fetchReplaceableEventsFromProfileFetchRelays` can block the UI even when kind
* 10002 is already in IndexedDB (the 30s publish timeout only runs after targets are resolved).
*/
/**
* Budget for `fetchRelayLists` / NIP-65 resolution on the publish path. Longer waits block the reply button
* while relays stall; shorter values fall back to IndexedDB + deduped picker order sooner (still correct).
@ -254,8 +240,6 @@ export const METADATA_BATCH_AUTHORS_CHUNK = 22 @@ -254,8 +240,6 @@ export const METADATA_BATCH_AUTHORS_CHUNK = 22
*/
export const PROFILE_FETCH_PROMISE_TIMEOUT_MS = 20000
export const RECOMMENDED_RELAYS = DEFAULT_FAVORITE_RELAYS.concat([])
export const RECOMMENDED_BLOSSOM_SERVERS = [
'https://blossom.band',
'https://blossom.primal.net',
@ -366,12 +350,6 @@ export const DOCUMENT_RELAY_URLS = [ @@ -366,12 +350,6 @@ export const DOCUMENT_RELAY_URLS = [
'wss://relay.wikifreedia.xyz'
] as const
/**
* Block-list order (applied in sequence when building relay lists):
* 1. READ_ONLY never publish (search mirrors, index relays, NIP-42 read-only aggregators)
* 2. SOCIAL_KIND_BLOCKED skip for REQ/publish that touch {@link SOCIAL_KIND_BLOCKED_KINDS} (see list below)
* 3. E_TAG_FILTER_BLOCKED skip for reply/quote/stats fetches (#e, #a, #q filters)
*/
/**
* Relays that must never receive publishes: search engines, index mirrors, and similar endpoints that only ingest
* or aggregate for read. Distinct from {@link SOCIAL_KIND_BLOCKED_RELAY_URLS} (kind-coverage limits, not write policy).
@ -454,7 +432,7 @@ export const GIF_RELAY_URLS = [ @@ -454,7 +432,7 @@ export const GIF_RELAY_URLS = [
export const SEARCHABLE_RELAY_URLS = [
'wss://search.nos.today',
'wss://nostr.wine',
'wss://nostr.wine',
'wss://orly-relay.imwald.eu',
'wss://aggr.nostr.land',
'wss://thecitadel.nostr1.com',
@ -468,24 +446,22 @@ export const SEARCHABLE_RELAY_URLS = [ @@ -468,24 +446,22 @@ export const SEARCHABLE_RELAY_URLS = [
'wss://nostr-pub.wellorder.net',
'wss://pyramid.fiatjaf.com/',
'wss://nostrelites.org'
]
]
export const PROFILE_RELAY_URLS = [
'wss://nos.lol',
'wss://relay.damus.io',
'wss://profiles.nostr1.com',
'wss://purplepag.es',
'wss://thecitadel.nostr1.com'
]
export const FOLLOWS_HISTORY_RELAY_URLS = [
'wss://hist.nostr.land'
]
// Combined relay URLs for profile fetching - includes both FAST_READ_RELAY_URLS and SEARCHABLE_RELAY_URLS
export const PROFILE_FETCH_RELAY_URLS = [...SEARCHABLE_RELAY_URLS, ...FAST_READ_RELAY_URLS, ...PROFILE_RELAY_URLS]
'wss://nos.lol',
'wss://relay.damus.io',
'wss://profiles.nostr1.com',
'wss://purplepag.es',
'wss://thecitadel.nostr1.com'
]
export const GROUP_METADATA_EVENT_KIND = 39000
export const FOLLOWS_HISTORY_RELAY_URLS = [
'wss://hist.nostr.land'
]
// Combined relay URLs for profile fetching: search/index relays, fallback inboxes, and profile-specific relays.
export const PROFILE_FETCH_RELAY_URLS = [...SEARCHABLE_RELAY_URLS, ...FAST_READ_RELAY_URLS, ...PROFILE_RELAY_URLS]
export const ExtendedKind = {
PICTURE: 20,
@ -577,11 +553,6 @@ export const ExtendedKind = { @@ -577,11 +553,6 @@ export const ExtendedKind = {
export const UNSIGNED_EXPERIMENTAL_KIND_MIN = 69999
export const UNSIGNED_EXPERIMENTAL_KIND_MAX = 130000
export const UNSIGNED_EXPERIMENTAL_RELAY_URLS = [
'wss://nostr.land',
'wss://theforest.gitcitadel.eu',
]
export function isUnsignedExperimentalKind(kind: number): boolean {
return kind >= UNSIGNED_EXPERIMENTAL_KIND_MIN && kind <= UNSIGNED_EXPERIMENTAL_KIND_MAX
}
@ -670,7 +641,7 @@ export const NOTE_STATS_OP_REFERENCE_KINDS_WITHOUT_HIGHLIGHT: readonly number[] @@ -670,7 +641,7 @@ export const NOTE_STATS_OP_REFERENCE_KINDS_WITHOUT_HIGHLIGHT: readonly number[]
* stack those relays do not carry this note/comment surface (kinds **1** / **1111** / **11** per relay policy).
* @see {@link relayFilterIncludesSocialKindBlockedKind}
*/
export const SOCIAL_KIND_BLOCKED_KINDS: readonly number[] = [
const SOCIAL_KIND_BLOCKED_KINDS: readonly number[] = [
kinds.ShortTextNote,
ExtendedKind.DISCUSSION,
ExtendedKind.COMMENT
@ -738,7 +709,7 @@ export function relayFilterIncludesSocialKindBlockedKind(filter: Filter): boolea @@ -738,7 +709,7 @@ export function relayFilterIncludesSocialKindBlockedKind(filter: Filter): boolea
/**
* Document/event kinds that should always include {@link DOCUMENT_RELAY_URLS} in read/publish relay candidates.
*/
export const DOCUMENT_RELAY_KINDS: readonly number[] = [
const DOCUMENT_RELAY_KINDS: readonly number[] = [
kinds.LongFormArticle, // 30023
ExtendedKind.WIKI_ARTICLE, // 30818
ExtendedKind.WIKI_ARTICLE_MARKDOWN, // 30817
@ -936,18 +907,14 @@ export const URL_REGEX = @@ -936,18 +907,14 @@ export const URL_REGEX =
/https?:\/\/[\w\p{L}\p{N}\p{M}&.\-/?=#@%+_:!~*]+(?:,[^\s.][\w\p{L}\p{N}\p{M}&.\-/?=#@%+_:!~*,]*)*[^\s.,;:'")\]}!?"'](?=\.(?:\s|$)|,\s|,(?=\/|\s|$)|$|[^\w\p{L}\p{N}\p{M}&.\-/?=#@%+_:!~*,])/giu
export const WS_URL_REGEX =
/wss?:\/\/[\w\p{L}\p{N}\p{M}&.\-/?=#@%+_:!~*]+[^\s.,;:'")\]}!?"'](?=\.(?:\s|$)|,\s|,(?=\/|\s|$)|$|[^\w\p{L}\p{N}\p{M}&.\-/?=#@%+_:!~*,])/giu
export const EMAIL_REGEX = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
/** @see {@link '@/lib/content-patterns'} — single source for emoji + nostr regexes */
export {
EMOJI_SHORT_CODE_MAX_INNER_LENGTH,
EMOJI_SHORT_CODE_REGEX,
EMBEDDED_EVENT_REGEX,
EMBEDDED_MENTION_REGEX
} from '@/lib/content-patterns'
export const HASHTAG_REGEX = /#[a-zA-Z0-9_\-\u00C0-\u017F\u0100-\u017F\u0180-\u024F\u1E00-\u1EFF]+/g
export const LN_INVOICE_REGEX = /(ln(?:bc|tb|bcrt))([0-9]+[munp]?)?1([02-9ac-hj-np-z]+)/g
export const EMOJI_REGEX =
/[\u{1F600}-\u{1F64F}]|[\u{1F300}-\u{1F5FF}]|[\u{1F680}-\u{1F6FF}]|[\u{1F1E0}-\u{1F1FF}]|[\u{2600}-\u{26FF}]|[\u{2700}-\u{27BF}]|[\u{1F900}-\u{1F9FF}]|[\u{1FA70}-\u{1FAFF}]|[\u{1F004}]|[\u{1F0CF}]|[\u{1F18E}]|[\u{3030}]|[\u{2B50}]|[\u{2B55}]|[\u{2934}-\u{2935}]|[\u{2B05}-\u{2B07}]|[\u{2B1B}-\u{2B1C}]|[\u{3297}]|[\u{3299}]|[\u{303D}]|[\u{00A9}]|[\u{00AE}]|[\u{2122}]|[\u{23E9}-\u{23EF}]|[\u{23F0}]|[\u{23F3}]|[\u{FE00}-\u{FE0F}]|[\u{200D}]/gu
export const YOUTUBE_URL_REGEX =
/https?:\/\/(?:(?:(?:www|m|music)\.)?youtube\.com\/(?:watch\?[^#\s]*|embed\/[\w-]+|shorts\/[\w-]+|live\/[\w-]+)|(?:www\.)?youtube-nocookie\.com\/(?:watch\?[^#\s]*|embed\/[\w-]+|shorts\/[\w-]+|live\/[\w-]+)|youtu\.be\/[\w-]+)(?:\?[^#\s]*)?(?:#[^\s]*)?/gi

10
src/lib/event-metadata.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { ExtendedKind, FAST_READ_RELAY_URLS, POLL_TYPE } from '@/constants'
import { ExtendedKind, FAST_READ_RELAY_URLS, FAST_WRITE_RELAY_URLS, POLL_TYPE } from '@/constants'
import { TEmoji, TMailboxRelay, TPollType, TRelayList, TRelaySet, TPaymentInfo, TProfile } from '@/types'
import { Event, kinds } from 'nostr-tools'
import { buildATag } from './draft-event'
@ -19,7 +19,7 @@ const emptyHttpRelayListFields = { @@ -19,7 +19,7 @@ const emptyHttpRelayListFields = {
export function getRelayListFromEvent(event?: Event | null, blockedRelays?: string[]) {
if (!event) {
return {
write: FAST_READ_RELAY_URLS,
write: FAST_WRITE_RELAY_URLS,
read: FAST_READ_RELAY_URLS,
originalRelays: [],
...emptyHttpRelayListFields
@ -59,11 +59,11 @@ export function getRelayListFromEvent(event?: Event | null, blockedRelays?: stri @@ -59,11 +59,11 @@ export function getRelayListFromEvent(event?: Event | null, blockedRelays?: stri
}
})
// If there are too many relays, use the default FAST_READ_RELAY_URLS
// If there are too many relays, use the default inbox/outbox relays.
// Because they don't know anything about relays, their settings cannot be trusted
return {
write: relayList.write.length && relayList.write.length <= 8 ? relayList.write : FAST_READ_RELAY_URLS,
read: relayList.read.length && relayList.write.length <= 8 ? relayList.read : FAST_READ_RELAY_URLS,
write: relayList.write.length && relayList.write.length <= 8 ? relayList.write : FAST_WRITE_RELAY_URLS,
read: relayList.read.length && relayList.read.length <= 8 ? relayList.read : FAST_READ_RELAY_URLS,
originalRelays: relayList.originalRelays,
...emptyHttpRelayListFields
}

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

@ -17,7 +17,7 @@ const RelaysFeed = forwardRef< @@ -17,7 +17,7 @@ const RelaysFeed = forwardRef<
kindsOverride?: number[]
}
>(function RelaysFeed({ setSubHeader, onSubHeaderRefresh, kindsOverride }, ref) {
const { relayUrls } = useFeed()
const { relayUrls, replyRelayUrls } = useFeed()
const { showKinds } = useKindFilterOrDefaults()
const [areAlgoRelays, setAreAlgoRelays] = useState(false)
@ -86,6 +86,17 @@ const RelaysFeed = forwardRef< @@ -86,6 +86,17 @@ const RelaysFeed = forwardRef<
}
]
}, [canRenderFeed, relayUrls, defaultKinds])
const repliesSubRequests = useMemo(() => {
if (!canRenderFeed) return []
return [
{
urls: replyRelayUrls.length > 0 ? replyRelayUrls : relayUrls,
filter: {
kinds: defaultKinds
}
}
]
}, [canRenderFeed, replyRelayUrls, relayUrls, defaultKinds])
if (!canRenderFeed) {
return null
@ -101,6 +112,8 @@ const RelaysFeed = forwardRef< @@ -101,6 +112,8 @@ const RelaysFeed = forwardRef<
setSubHeader={setSubHeader}
onSubHeaderRefresh={onSubHeaderRefresh}
preserveTimelineOnSubRequestsChange
repliesSubRequests={repliesSubRequests}
widenMainGalleryRelays={false}
feedTimelineScopeKey="all-favorites"
showFeedClientFilter
hostPrimaryPageName="feed"

171
src/providers/FeedProvider.tsx

@ -1,10 +1,13 @@ @@ -1,10 +1,13 @@
import { FAST_READ_RELAY_URLS, FAST_WRITE_RELAY_URLS } from '@/constants'
import { feedRelayPolicyUrls } from '@/features/feed/relay-policy'
import { getFavoritesFeedRelayUrls } from '@/lib/favorites-feed-relays'
import { getRelayListFromEvent, getHttpRelayListFromEvent } from '@/lib/event-metadata'
import logger from '@/lib/logger'
import { AGGR_NOSTR_LAND_WSS } from '@/lib/nostr-land-aggr'
import { normalizeAnyRelayUrl } from '@/lib/url'
import { buildWispTrendingNotesRelayUrl } from '@/lib/wisp-trending-relay'
import { useEffect, useMemo, useState, useCallback } from 'react'
import type { Dispatch, ReactNode, SetStateAction } from 'react'
import { FeedContext } from './feed-context'
import { useFavoriteRelays } from './FavoriteRelaysProvider'
import { useNostr } from './NostrProvider'
@ -37,54 +40,133 @@ function buildAllFavoritesFeedRelayUrls( @@ -37,54 +40,133 @@ function buildAllFavoritesFeedRelayUrls(
})
}
export function FeedProvider({ children }: { children: React.ReactNode }) {
const { isInitialized, cacheRelayListEvent, httpRelayListEvent } = useNostr()
const { favoriteRelays, blockedRelays } = useFavoriteRelays()
/**
* Extra relay URLs always merged into the all-favorites feed:
* - Cache relays (kind 10432) if the user has configured any
* - HTTP index relays (kind 10243) if the user has configured any
* - The Wisp trending relay (always included)
*/
const extraFeedRelayUrls = useMemo(() => {
const extra: string[] = [buildWispTrendingNotesRelayUrl()]
function relayListMentionsNostrLand(urls: readonly string[]): boolean {
return urls.some((url) => {
const normalized = normalizeAnyRelayUrl(url) || url.trim()
if (!normalized) return false
try {
const parsed = new URL(normalized.replace(/^ws:\/\//i, 'http://').replace(/^wss:\/\//i, 'https://'))
return parsed.hostname.toLowerCase() === 'nostr.land'
} catch {
return false
}
})
}
function buildHomeReplyFeedRelayUrls(
primaryRelayUrls: string[],
inboxRelayUrls: string[],
cacheRelayUrls: string[],
httpRelayUrls: string[],
includeNostrLandAggr: boolean,
blockedRelays: string[]
): string[] {
return feedRelayPolicyUrls([
{ source: 'favorites', urls: primaryRelayUrls },
{ source: 'viewer-read', urls: inboxRelayUrls },
{ source: 'cache', urls: cacheRelayUrls },
{ source: 'http-index', urls: httpRelayUrls },
...(includeNostrLandAggr ? [{ source: 'read-only', urls: [AGGR_NOSTR_LAND_WSS] }] : [])
], {
operation: 'read',
blockedRelays,
nostrLandAggr: 'never',
applySocialKindBlockedFilter: false,
allowThirdPartyLocalRelays: true
})
}
export function FeedProvider({ children }: { children: ReactNode }) {
const { isInitialized, relayList, cacheRelayListEvent, httpRelayListEvent } = useNostr()
const { favoriteRelays, blockedRelays, relaySets } = useFavoriteRelays()
const favoriteFeedRelayUrls = useMemo(
() => [...favoriteRelays, ...relaySets.flatMap((relaySet) => relaySet.relayUrls)],
[favoriteRelays, relaySets]
)
/** Home Notes/Gallery stay focused: favorites/defaults plus the mixed trending relay. */
const primaryExtraRelayUrls = useMemo(() => [buildWispTrendingNotesRelayUrl()], [])
/** Home Replies widen to relays that can surface inbox/reply context. */
const replyExtraRelayLayers = useMemo(() => {
const cacheRelayUrls: string[] = []
if (cacheRelayListEvent) {
const list = getRelayListFromEvent(cacheRelayListEvent)
extra.push(...list.read, ...list.write)
const list = getRelayListFromEvent(cacheRelayListEvent, blockedRelays)
cacheRelayUrls.push(...list.read, ...list.write)
}
const httpRelayUrls: string[] = [...(relayList?.httpRead ?? []), ...(relayList?.httpWrite ?? [])]
if (httpRelayListEvent) {
const list = getHttpRelayListFromEvent(httpRelayListEvent)
extra.push(...list.httpRead, ...list.httpWrite)
const list = getHttpRelayListFromEvent(httpRelayListEvent, blockedRelays)
httpRelayUrls.push(...list.httpRead, ...list.httpWrite)
}
return {
inboxRelayUrls: relayList?.read?.length ? relayList.read : FAST_READ_RELAY_URLS,
outboxRelayUrls: relayList?.write?.length ? relayList.write : FAST_WRITE_RELAY_URLS,
cacheRelayUrls,
httpRelayUrls
}
return extra
}, [cacheRelayListEvent, httpRelayListEvent])
}, [relayList, cacheRelayListEvent, httpRelayListEvent, blockedRelays])
/** Default relays immediately so feeds / sidebar REQ never wait on Nostr session restore. */
const [relayUrls, setRelayUrls] = useState<string[]>(() =>
buildAllFavoritesFeedRelayUrls([], [], [buildWispTrendingNotesRelayUrl()])
)
const [replyRelayUrls, setReplyRelayUrls] = useState<string[]>(() =>
buildHomeReplyFeedRelayUrls(
buildAllFavoritesFeedRelayUrls([], [], [buildWispTrendingNotesRelayUrl()]),
[],
[],
[],
false,
[]
)
)
/** Same logical relay policy result — reuse array ref so NoteList does not re-subscribe. */
const setRelayUrlsIfChanged = useCallback((next: string[]) => {
setRelayUrls((prev) => {
if (relayUrlListIdentity(prev) === relayUrlListIdentity(next)) return prev
return next
})
}, [])
const setUrlStateIfChanged = useCallback(
(setter: Dispatch<SetStateAction<string[]>>, next: string[]) => {
setter((prev) => {
if (relayUrlListIdentity(prev) === relayUrlListIdentity(next)) return prev
return next
})
},
[]
)
const updateFeedRelayUrls = useCallback(() => {
const finalRelays = buildAllFavoritesFeedRelayUrls(favoriteRelays, blockedRelays, extraFeedRelayUrls)
logger.debug('Updating all-favorites relay URLs:', finalRelays)
setRelayUrlsIfChanged(finalRelays)
}, [favoriteRelays, blockedRelays, extraFeedRelayUrls, setRelayUrlsIfChanged])
const primaryRelays = buildAllFavoritesFeedRelayUrls(favoriteFeedRelayUrls, blockedRelays, primaryExtraRelayUrls)
const aggrEligibleRelayUrls = [
...favoriteFeedRelayUrls,
...replyExtraRelayLayers.inboxRelayUrls,
...replyExtraRelayLayers.outboxRelayUrls,
...replyExtraRelayLayers.cacheRelayUrls
]
const replyRelays = buildHomeReplyFeedRelayUrls(
primaryRelays,
replyExtraRelayLayers.inboxRelayUrls,
replyExtraRelayLayers.cacheRelayUrls,
replyExtraRelayLayers.httpRelayUrls,
relayListMentionsNostrLand(aggrEligibleRelayUrls),
blockedRelays
)
logger.debug('Updating home feed relay URLs:', {
primaryRelays,
replyRelays
})
setUrlStateIfChanged(setRelayUrls, primaryRelays)
setUrlStateIfChanged(setReplyRelayUrls, replyRelays)
}, [favoriteFeedRelayUrls, blockedRelays, primaryExtraRelayUrls, replyExtraRelayLayers, setUrlStateIfChanged])
const favoriteRelaysIdentity = useMemo(
() =>
[...favoriteRelays]
[...favoriteFeedRelayUrls]
.map((u) => normalizeAnyRelayUrl(u) || u.trim())
.filter(Boolean)
.sort()
.join('|'),
[favoriteRelays]
[favoriteFeedRelayUrls]
)
const blockedRelaysIdentity = useMemo(
() =>
@ -95,24 +177,45 @@ export function FeedProvider({ children }: { children: React.ReactNode }) { @@ -95,24 +177,45 @@ export function FeedProvider({ children }: { children: React.ReactNode }) {
.join('|'),
[blockedRelays]
)
const replyExtraRelaysIdentity = useMemo(
() =>
[
...replyExtraRelayLayers.inboxRelayUrls,
...replyExtraRelayLayers.outboxRelayUrls,
...replyExtraRelayLayers.cacheRelayUrls,
...replyExtraRelayLayers.httpRelayUrls
]
.map((u) => normalizeAnyRelayUrl(u) || u.trim())
.filter(Boolean)
.sort()
.join('|'),
[replyExtraRelayLayers]
)
useEffect(() => {
logger.debug('FeedProvider relay init:', {
isInitialized,
favoriteRelays: favoriteRelays.length,
relaySets: relaySets.length,
relaySetRelays: favoriteFeedRelayUrls.length - favoriteRelays.length,
inboxRelays: replyExtraRelayLayers.inboxRelayUrls.length,
outboxRelays: replyExtraRelayLayers.outboxRelayUrls.length,
cacheRelays: replyExtraRelayLayers.cacheRelayUrls.length,
httpRelays: replyExtraRelayLayers.httpRelayUrls.length,
blockedRelays: blockedRelays.length
})
if (favoriteRelays.length === 0) {
logger.debug('FeedProvider: favoriteRelays is empty, using defaults')
if (favoriteFeedRelayUrls.length === 0) {
logger.debug('FeedProvider: no favorite or relay-set relays, using defaults')
}
updateFeedRelayUrls()
}, [isInitialized, favoriteRelaysIdentity, blockedRelaysIdentity, updateFeedRelayUrls])
}, [isInitialized, favoriteRelaysIdentity, blockedRelaysIdentity, replyExtraRelaysIdentity, updateFeedRelayUrls])
return (
<FeedContext.Provider
value={{
relayUrls
relayUrls,
replyRelayUrls
}}
>
{children}

3
src/providers/feed-context.tsx

@ -5,7 +5,10 @@ @@ -5,7 +5,10 @@
import { createContext, useContext } from 'react'
export type TFeedContext = {
/** Home Notes/Gallery: favorites plus mixed trending discovery. */
relayUrls: string[]
/** Home Replies: primary feed relays plus viewer inbox, HTTP, cache, and eligible aggregator relays. */
replyRelayUrls: string[]
}
export const FeedContext = createContext<TFeedContext | undefined>(undefined)

2
src/services/client.service.ts

@ -3771,7 +3771,7 @@ class ClientService extends EventTarget { @@ -3771,7 +3771,7 @@ class ClientService extends EventTarget {
stripped.write.length > 0 ? stripped.write : write.filter(urlIsNonLocalForRemoteViewer)
if (read.length === 0 && write.length === 0) {
read = [...FAST_READ_RELAY_URLS]
write = [...FAST_READ_RELAY_URLS]
write = [...FAST_WRITE_RELAY_URLS]
}
}
return mergeKind10243({

4
src/services/lightning.service.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { FAST_READ_RELAY_URLS, CODY_PUBKEY, IMWALD_MAINTAINER_PUBKEY } from '@/constants'
import { FAST_READ_RELAY_URLS, FAST_WRITE_RELAY_URLS, CODY_PUBKEY, IMWALD_MAINTAINER_PUBKEY } from '@/constants'
import { getZapInfoFromEvent } from '@/lib/event-metadata'
import {
buildZapPollVoteRequestTemplate,
@ -65,7 +65,7 @@ class LightningService { @@ -65,7 +65,7 @@ class LightningService {
})(),
sender
? client.fetchRelayList(sender) // Keep using client for relay list merging
: Promise.resolve({ read: FAST_READ_RELAY_URLS, write: FAST_READ_RELAY_URLS })
: Promise.resolve({ read: FAST_READ_RELAY_URLS, write: FAST_WRITE_RELAY_URLS })
])
if (!profile) {
throw new Error('Recipient not found')

Loading…
Cancel
Save