Browse Source

fix publications

imwald
Silberengel 1 month ago
parent
commit
5c2adbee4c
  1. 82
      src/hooks/usePublicationSectionLoader.ts
  2. 39
      src/lib/publication-section-fetch.ts
  3. 4
      src/pages/primary/NoteListPage/RelaysFeed.tsx
  4. 29
      src/providers/KindFilterProvider.tsx

82
src/hooks/usePublicationSectionLoader.ts

@ -9,10 +9,18 @@ import { generateBech32IdFromATag } from '@/lib/tag' @@ -9,10 +9,18 @@ import { generateBech32IdFromATag } from '@/lib/tag'
import { isReplaceableEvent } from '@/lib/event'
import client from '@/services/client.service'
import { eventService } from '@/services/client.service'
import logger from '@/lib/logger'
import indexedDb from '@/services/indexed-db.service'
import type { Event } from 'nostr-tools'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
const PUB_SEC_LOG = '[PublicationSection]'
function pubLog(message: string, data?: Record<string, unknown>) {
if (!import.meta.env.DEV) return
if (data) logger.info(`${PUB_SEC_LOG} ${message}`, data)
else logger.info(`${PUB_SEC_LOG} ${message}`)
}
export type SectionLoadStatus = 'idle' | 'loading' | 'loaded' | 'error'
export type PublicationSectionRow = {
@ -134,6 +142,11 @@ export function usePublicationSectionLoader(indexEvent: Event, referencesData: P @@ -134,6 +142,11 @@ export function usePublicationSectionLoader(indexEvent: Event, referencesData: P
if (refsToLoad.length === 0) return
pubLog('flush_start', {
keys: refsToLoad.map((r) => refKey(r)),
relayCount: relayUrlsRef.current.length
})
setRows((prev) => {
const next = new Map(prev)
for (const ref of refsToLoad) {
@ -147,25 +160,53 @@ export function usePublicationSectionLoader(indexEvent: Event, referencesData: P @@ -147,25 +160,53 @@ export function usePublicationSectionLoader(indexEvent: Event, referencesData: P
const urls = relayUrlsRef.current
const resolved = new Map<string, Event>()
if (urls.length > 0) {
const fromDb = await hydrateRefsFromIndexedDb(refsToLoad)
for (const [k, ev] of fromDb) {
resolved.set(k, ev)
client.addEventToCache(ev)
}
// Always hydrate from IDB — do not gate on relay URLs (they resolve async after first IO batch).
const fromDb = await hydrateRefsFromIndexedDb(refsToLoad)
for (const [k, ev] of fromDb) {
resolved.set(k, ev)
client.addEventToCache(ev)
}
const stillNeed = refsToLoad.filter((r) => !resolved.has(refKey(r)))
if (stillNeed.length > 0) {
const fromNet = await batchFetchPublicationSectionEvents(stillNeed, urls)
for (const [k, ev] of fromNet) {
resolved.set(k, ev)
client.addEventToCache(ev)
if (isReplaceableEvent(ev.kind)) void indexedDb.putReplaceableEvent(ev)
let stillNeed = refsToLoad.filter((r) => !resolved.has(refKey(r)))
pubLog('after_idb', {
fromDb: fromDb.size,
stillNeed: stillNeed.map((r) => ({ key: refKey(r), type: r.type }))
})
// No relay list yet: apply DB hits only, re-queue the rest (do not mark error).
if (urls.length === 0 && stillNeed.length > 0) {
for (const r of stillNeed) pendingRef.current.add(refKey(r))
pubLog('defer_net_until_relays', { reQueued: stillNeed.length })
setRows((prev) => {
const next = new Map(prev)
for (const ref of refsToLoad) {
const k = refKey(ref)
const row = next.get(k)
if (!row) continue
const ev = resolved.get(k)
if (ev) next.set(k, { ...row, event: ev, status: 'loaded' })
else next.set(k, { ...row, status: 'idle', event: undefined })
}
return next
})
return
}
if (urls.length > 0 && stillNeed.length > 0) {
const fromNet = await batchFetchPublicationSectionEvents(stillNeed, urls)
pubLog('after_batch_fetch', { fromNet: fromNet.size })
for (const [k, ev] of fromNet) {
resolved.set(k, ev)
client.addEventToCache(ev)
if (isReplaceableEvent(ev.kind)) void indexedDb.putReplaceableEvent(ev)
}
}
const missing = refsToLoad.filter((r) => !resolved.has(refKey(r)))
pubLog('before_fallback', {
missing: missing.map((r) => refKey(r)),
relayUrlsEmpty: urls.length === 0
})
await Promise.all(
missing.map(async (ref) => {
const k = refKey(ref)
@ -178,6 +219,17 @@ export function usePublicationSectionLoader(indexEvent: Event, referencesData: P @@ -178,6 +219,17 @@ export function usePublicationSectionLoader(indexEvent: Event, referencesData: P
})
)
const failed = refsToLoad.filter((r) => !resolved.has(refKey(r)))
pubLog('flush_done', {
loaded: refsToLoad.length - failed.length,
failed: failed.map((r) => ({
key: refKey(r),
type: r.type,
coordinate: r.coordinate,
eventId: r.eventId
}))
})
setRows((prev) => {
const next = new Map(prev)
for (const ref of refsToLoad) {
@ -223,8 +275,8 @@ export function usePublicationSectionLoader(indexEvent: Event, referencesData: P @@ -223,8 +275,8 @@ export function usePublicationSectionLoader(indexEvent: Event, referencesData: P
useEffect(() => {
if (!relayReady || orderedKeys.length === 0) return
const n = Math.min(3, orderedKeys.length)
requestKeys(orderedKeys.slice(0, n))
// Full list: scroll-IO may have fired before relays were ready; those keys were re-queued idle.
requestKeys(orderedKeys)
}, [relayReady, orderedKeys, requestKeys])
const failedKeys = useMemo(

39
src/lib/publication-section-fetch.ts

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
import logger from '@/lib/logger'
import { publicationCoordinateLookupKeys } from '@/lib/publication-coordinate'
import { buildComprehensiveRelayList } from '@/lib/relay-list-builder'
import { normalizeUrl } from '@/lib/url'
@ -165,7 +166,15 @@ export async function batchFetchPublicationSectionEvents( @@ -165,7 +166,15 @@ export async function batchFetchPublicationSectionEvents(
}
}
if (filters.length === 0) return out
if (filters.length === 0) {
if (import.meta.env.DEV) {
logger.info('[PublicationSection] batch_fetch_skip — no filters', {
aRefCount: aRefs.length,
idRefCount: idRefs.length
})
}
return out
}
let events: Event[] = []
try {
@ -175,7 +184,14 @@ export async function batchFetchPublicationSectionEvents( @@ -175,7 +184,14 @@ export async function batchFetchPublicationSectionEvents(
/** Do not early-resolve after the first event; this query must wait for the full batch. */
firstRelayResultGraceMs: false
})
} catch {
} catch (err) {
if (import.meta.env.DEV) {
logger.warn('[PublicationSection] batch_fetch_error', {
message: err instanceof Error ? err.message : String(err),
filterCount: filters.length,
relayCount: relayUrls.length
})
}
return out
}
@ -211,5 +227,24 @@ export async function batchFetchPublicationSectionEvents( @@ -211,5 +227,24 @@ export async function batchFetchPublicationSectionEvents(
if (ev) out.set(key, ev)
}
if (import.meta.env.DEV) {
const unmatchedA = aRefs.filter((r) => !out.has(publicationRefKey(r)))
const unmatchedE = idRefs.filter((r) => !out.has(publicationRefKey(r)))
logger.info('[PublicationSection] batch_fetch_result', {
relayCount: relayUrls.length,
filterCount: filters.length,
eventsReturned: events.length,
byCoordSize: byCoord.size,
resolved: out.size,
unmatchedACount: unmatchedA.length,
unmatchedECount: unmatchedE.length,
unmatchedAKeys: unmatchedA.map((r) => publicationRefKey(r)).slice(0, 12),
sampleEventCoords: events.slice(0, 3).map((ev) => {
const d = ev.tags.find((t) => t[0] === 'd')?.[1]
return d !== undefined && d !== '' ? coordinateFromEvent(ev) : `${ev.kind}:${ev.pubkey.slice(0, 8)}`
})
})
}
return out
}

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

@ -3,7 +3,7 @@ import type { TNoteListRef } from '@/components/NoteList' @@ -3,7 +3,7 @@ import type { TNoteListRef } from '@/components/NoteList'
import { checkAlgoRelay } from '@/lib/relay'
import { normalizeUrl } from '@/lib/url'
import { useFeed } from '@/providers/FeedProvider'
import { useKindFilter } from '@/providers/KindFilterProvider'
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'
@ -18,7 +18,7 @@ const RelaysFeed = forwardRef< @@ -18,7 +18,7 @@ const RelaysFeed = forwardRef<
}
>(function RelaysFeed({ setSubHeader, onSubHeaderRefresh, kindsOverride }, ref) {
const { feedInfo, relayUrls } = useFeed()
const { showKinds } = useKindFilter()
const { showKinds } = useKindFilterOrDefaults()
const [areAlgoRelays, setAreAlgoRelays] = useState(false)
const [relayAlgoReady, setRelayAlgoReady] = useState(false)

29
src/providers/KindFilterProvider.tsx

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
import { createContext, useContext, useState, useCallback, useMemo } from 'react'
import type { ReactNode } from 'react'
import storage from '@/services/local-storage.service'
import { DEFAULT_FEED_SHOW_KINDS, ExtendedKind } from '@/constants'
import { kinds } from 'nostr-tools'
@ -53,7 +54,33 @@ export const useKindFilter = () => { @@ -53,7 +54,33 @@ export const useKindFilter = () => {
return context
}
export function KindFilterProvider({ children }: { children: React.ReactNode }) {
/** When context is missing (e.g. Vite HMR / duplicate module instances), use storage-backed defaults. */
function createKindFilterFallback(): TKindFilterContext {
const defaultShowKinds = DEFAULT_FEED_SHOW_KINDS
const storedShowKinds = storage.getShowKinds()
const showKinds = storedShowKinds.length > 0 ? storedShowKinds : defaultShowKinds
const noop = () => {}
return {
showKinds,
showKind1OPs: storage.getShowKind1OPs(),
showKind1Replies: storage.getShowKind1Replies(),
showKind1111: storage.getShowKind1111(),
feedKindFilterBypass: storage.getFeedKindFilterBypass(),
updateShowKinds: noop,
updateShowKind1OPs: noop,
updateShowKind1Replies: noop,
updateShowKind1111: noop,
updateFeedKindFilterBypass: noop
}
}
export function useKindFilterOrDefaults(): TKindFilterContext {
const context = useContext(KindFilterContext)
const fallback = useMemo(() => createKindFilterFallback(), [])
return context ?? fallback
}
export function KindFilterProvider({ children }: { children: ReactNode }) {
const defaultShowKinds = DEFAULT_FEED_SHOW_KINDS
const storedShowKinds = storage.getShowKinds()
const storedShowKind1OPs = storage.getShowKind1OPs()

Loading…
Cancel
Save