Browse Source

bug-fixes

imwald
Silberengel 1 month ago
parent
commit
8f4b50dff9
  1. 42
      src/components/ReplyNoteList/index.tsx
  2. 7
      src/constants.ts
  3. 18
      src/services/client-query.service.ts

42
src/components/ReplyNoteList/index.tsx

@ -78,6 +78,8 @@ type TRootInfo =
const LIMIT = 200 const LIMIT = 200
const SHOW_COUNT = 10 const SHOW_COUNT = 10
const MAX_KINDS_PER_THREAD_REQ_FILTER = 4 const MAX_KINDS_PER_THREAD_REQ_FILTER = 4
/** Some relays cap `#e` array length; chunk parent-id batches for nested-thread REQs. */
const MAX_PARENT_IDS_PER_NESTED_REQ = 64
function chunkKindsForThreadReq(list: readonly number[], size = MAX_KINDS_PER_THREAD_REQ_FILTER): number[][] { function chunkKindsForThreadReq(list: readonly number[], size = MAX_KINDS_PER_THREAD_REQ_FILTER): number[][] {
const out: number[][] = [] const out: number[][] = []
@ -1294,8 +1296,11 @@ function ReplyNoteList({
.filter((evt) => commentKinds.includes(evt.kind)) .filter((evt) => commentKinds.includes(evt.kind))
.map((evt) => evt.id) .map((evt) => evt.id)
if (parentIds.length > 0) { if (parentIds.length > 0) {
const nestedAccum: NEvent[] = []
for (let off = 0; off < parentIds.length; off += MAX_PARENT_IDS_PER_NESTED_REQ) {
const idChunk = parentIds.slice(off, off + MAX_PARENT_IDS_PER_NESTED_REQ)
const nestedFilters: Filter[] = [ const nestedFilters: Filter[] = [
{ '#e': parentIds, kinds: commentKinds, limit: LIMIT } { '#e': idChunk, kinds: commentKinds, limit: LIMIT }
] ]
const nestedReplies = await queryService.fetchEvents(finalRelayUrls, nestedFilters, { const nestedReplies = await queryService.fetchEvents(finalRelayUrls, nestedFilters, {
onevent: (evt: NEvent) => { onevent: (evt: NEvent) => {
@ -1306,7 +1311,9 @@ function ReplyNoteList({
} }
}) })
if (fetchGeneration !== replyFetchGenRef.current) return if (fetchGeneration !== replyFetchGenRef.current) return
const validNested = nestedReplies.filter( nestedAccum.push(...nestedReplies)
}
const validNested = nestedAccum.filter(
(evt) => (evt) =>
!shouldHideThreadResponseEvent(evt, mutePubkeySet, hideContentMentioningMutedUsers) !shouldHideThreadResponseEvent(evt, mutePubkeySet, hideContentMentioningMutedUsers)
) )
@ -1318,26 +1325,31 @@ function ReplyNoteList({
} }
} }
// Second pass for kind-11 discussions: nested 1111/1 chains are keyed under parent ids in // Second pass for discussions, plain kind-1 threads, and replaceable (longform/wiki) roots:
// ReplyProvider; fetching #e:[comment-id] fills gaps the root-scoped REQ can miss. // nested 1 / 1111 / 1244 often tag only the parent's #e; root-scoped REQ misses them (same
// idea as URL-thread #I follow-up above).
if ( if (
event.kind === ExtendedKind.DISCUSSION && regularReplies.length > 0 &&
rootInfo.type === 'E' && ((rootInfo.type === 'E' &&
regularReplies.length > 0 (event.kind === ExtendedKind.DISCUSSION || event.kind === kinds.ShortTextNote)) ||
rootInfo.type === 'A')
) { ) {
const commentKinds = [ const commentKindsNested = [
ExtendedKind.COMMENT, ExtendedKind.COMMENT,
ExtendedKind.VOICE_COMMENT, ExtendedKind.VOICE_COMMENT,
kinds.ShortTextNote kinds.ShortTextNote
] ]
const parentIds = regularReplies const parentIdsNested = regularReplies
.filter((evt) => commentKinds.includes(evt.kind)) .filter((evt) => commentKindsNested.includes(evt.kind))
.map((evt) => evt.id) .map((evt) => evt.id)
if (parentIds.length > 0) { if (parentIdsNested.length > 0) {
const nestedAccum: NEvent[] = []
for (let off = 0; off < parentIdsNested.length; off += MAX_PARENT_IDS_PER_NESTED_REQ) {
const idChunk = parentIdsNested.slice(off, off + MAX_PARENT_IDS_PER_NESTED_REQ)
const nestedFilters: Filter[] = [ const nestedFilters: Filter[] = [
{ '#e': parentIds, kinds: commentKinds, limit: LIMIT }, { '#e': idChunk, kinds: commentKindsNested, limit: LIMIT },
{ {
'#E': parentIds, '#E': idChunk,
kinds: [ExtendedKind.COMMENT, ExtendedKind.VOICE_COMMENT], kinds: [ExtendedKind.COMMENT, ExtendedKind.VOICE_COMMENT],
limit: LIMIT limit: LIMIT
} }
@ -1352,7 +1364,9 @@ function ReplyNoteList({
} }
}) })
if (fetchGeneration !== replyFetchGenRef.current) return if (fetchGeneration !== replyFetchGenRef.current) return
const validNested = nestedReplies.filter( nestedAccum.push(...nestedReplies)
}
const validNested = nestedAccum.filter(
(evt) => (evt) =>
!shouldHideThreadResponseEvent(evt, mutePubkeySet, hideContentMentioningMutedUsers) && !shouldHideThreadResponseEvent(evt, mutePubkeySet, hideContentMentioningMutedUsers) &&
replyMatchesThreadForList(evt, event, rootInfo, isDiscussionRoot) replyMatchesThreadForList(evt, event, rootInfo, isDiscussionRoot)

7
src/constants.ts

@ -145,6 +145,13 @@ export const MAX_REQ_RELAY_URLS = MAX_CONCURRENT_RELAY_CONNECTIONS
*/ */
export const RELAY_FILTER_MAX_KINDS_PER_OBJECT = 10 export const RELAY_FILTER_MAX_KINDS_PER_OBJECT = 10
/**
* Maximum NIP-01 filters per REQ (`["REQ", subId, …filters]`). Primal, damus.io, and others return
* NOTICE `bad req: arr too big` when the filter list is long (e.g. replaceable threads with #a + #e
* snapshot + many kind-chunked op-reference filters).
*/
export const RELAY_REQ_MAX_FILTERS_PER_MESSAGE = 10
/** `SimplePool.ensureRelay` WebSocket handshake timeout (parallel multi-relay + slow TLS). */ /** `SimplePool.ensureRelay` WebSocket handshake timeout (parallel multi-relay + slow TLS). */
export const RELAY_POOL_CONNECTION_TIMEOUT_MS = 20_000 export const RELAY_POOL_CONNECTION_TIMEOUT_MS = 20_000

18
src/services/client-query.service.ts

@ -8,6 +8,7 @@ import {
MAX_CONCURRENT_RELAY_CONNECTIONS, MAX_CONCURRENT_RELAY_CONNECTIONS,
MAX_CONCURRENT_SUBS_PER_RELAY, MAX_CONCURRENT_SUBS_PER_RELAY,
RELAY_FILTER_MAX_KINDS_PER_OBJECT, RELAY_FILTER_MAX_KINDS_PER_OBJECT,
RELAY_REQ_MAX_FILTERS_PER_MESSAGE,
RELAY_POOL_CONNECTION_TIMEOUT_MS, RELAY_POOL_CONNECTION_TIMEOUT_MS,
SEARCHABLE_RELAY_URLS SEARCHABLE_RELAY_URLS
} from '@/constants' } from '@/constants'
@ -293,6 +294,23 @@ export class QueryService {
): Promise<NEvent[]> { ): Promise<NEvent[]> {
const sanitizedFilters = sanitizeFiltersBeforeReq(filter) const sanitizedFilters = sanitizeFiltersBeforeReq(filter)
if (sanitizedFilters.length === 0) return [] if (sanitizedFilters.length === 0) return []
const maxFilters = RELAY_REQ_MAX_FILTERS_PER_MESSAGE
if (sanitizedFilters.length > maxFilters) {
const merged: NEvent[] = []
const seen = new Set<string>()
for (let i = 0; i < sanitizedFilters.length; i += maxFilters) {
const slice = sanitizedFilters.slice(i, i + maxFilters)
const part = await this.query(urls, slice, onevent, options)
for (const e of part) {
if (seen.has(e.id)) continue
seen.add(e.id)
merged.push(e)
}
}
return merged
}
/** One chunk → pass a single Filter (compat); several (e.g. kinds split) → full array for WS + HTTP. */ /** One chunk → pass a single Filter (compat); several (e.g. kinds split) → full array for WS + HTTP. */
const effectiveFilter: Filter | Filter[] = const effectiveFilter: Filter | Filter[] =
sanitizedFilters.length === 1 ? sanitizedFilters[0]! : sanitizedFilters sanitizedFilters.length === 1 ? sanitizedFilters[0]! : sanitizedFilters

Loading…
Cancel
Save