@ -58,7 +58,7 @@ async function prioritizeSearchEvents(
events : NDKEvent [ ] ,
events : NDKEvent [ ] ,
targetPubkey? : string ,
targetPubkey? : string ,
maxResults : number = SEARCH_LIMITS . GENERAL_CONTENT ,
maxResults : number = SEARCH_LIMITS . GENERAL_CONTENT ,
ndk? : NDK
ndk? : NDK ,
) : Promise < NDKEvent [ ] > {
) : Promise < NDKEvent [ ] > {
if ( events . length === 0 ) {
if ( events . length === 0 ) {
return [ ] ;
return [ ] ;
@ -72,7 +72,9 @@ async function prioritizeSearchEvents(
if ( ndk ) {
if ( ndk ) {
try {
try {
// Import user list functions dynamically to avoid circular dependencies
// Import user list functions dynamically to avoid circular dependencies
const { fetchCurrentUserLists , getPubkeysFromListKind } = await import ( "./user_lists.ts" ) ;
const { fetchCurrentUserLists , getPubkeysFromListKind } = await import (
"./user_lists.ts"
) ;
const { checkCommunity } = await import ( "./community_checker.ts" ) ;
const { checkCommunity } = await import ( "./community_checker.ts" ) ;
// Get current user's follow lists (if logged in)
// Get current user's follow lists (if logged in)
@ -80,10 +82,14 @@ async function prioritizeSearchEvents(
userFollowPubkeys = getPubkeysFromListKind ( userLists , 3 ) ; // Kind 3 = follow list
userFollowPubkeys = getPubkeysFromListKind ( userLists , 3 ) ; // Kind 3 = follow list
// Check community status for unique pubkeys in events (limit to prevent hanging)
// Check community status for unique pubkeys in events (limit to prevent hanging)
const uniquePubkeys = new Set ( events . map ( e = > e . pubkey ) . filter ( Boolean ) ) ;
const uniquePubkeys = new Set (
events . map ( ( e ) = > e . pubkey ) . filter ( Boolean ) ,
) ;
const pubkeysToCheck = Array . from ( uniquePubkeys ) . slice ( 0 , 20 ) ; // Limit to first 20 pubkeys
const pubkeysToCheck = Array . from ( uniquePubkeys ) . slice ( 0 , 20 ) ; // Limit to first 20 pubkeys
console . log ( ` subscription_search: Checking community status for ${ pubkeysToCheck . length } pubkeys out of ${ uniquePubkeys . size } total ` ) ;
console . log (
` subscription_search: Checking community status for ${ pubkeysToCheck . length } pubkeys out of ${ uniquePubkeys . size } total ` ,
) ;
const communityChecks = await Promise . allSettled (
const communityChecks = await Promise . allSettled (
pubkeysToCheck . map ( async ( pubkey ) = > {
pubkeysToCheck . map ( async ( pubkey ) = > {
@ -91,19 +97,25 @@ async function prioritizeSearchEvents(
const isCommunityMember = await Promise . race ( [
const isCommunityMember = await Promise . race ( [
checkCommunity ( pubkey ) ,
checkCommunity ( pubkey ) ,
new Promise ( ( _ , reject ) = >
new Promise ( ( _ , reject ) = >
setTimeout ( ( ) = > reject ( new Error ( 'Community check timeout' ) ) , 2000 )
setTimeout (
( ) = > reject ( new Error ( "Community check timeout" ) ) ,
2000 ,
)
)
) ,
] ) ;
] ) ;
return { pubkey , isCommunityMember } ;
return { pubkey , isCommunityMember } ;
} catch ( error ) {
} catch ( error ) {
console . warn ( ` subscription_search: Community check failed for ${ pubkey } : ` , error ) ;
console . warn (
` subscription_search: Community check failed for ${ pubkey } : ` ,
error ,
) ;
return { pubkey , isCommunityMember : false } ;
return { pubkey , isCommunityMember : false } ;
}
}
} )
} ) ,
) ;
) ;
// Build set of community member pubkeys
// Build set of community member pubkeys
communityChecks . forEach ( result = > {
communityChecks . forEach ( ( result ) = > {
if ( result . status === "fulfilled" && result . value . isCommunityMember ) {
if ( result . status === "fulfilled" && result . value . isCommunityMember ) {
communityMemberPubkeys . add ( result . value . pubkey ) ;
communityMemberPubkeys . add ( result . value . pubkey ) ;
}
}
@ -112,13 +124,18 @@ async function prioritizeSearchEvents(
console . log ( "subscription_search: Prioritization data loaded:" , {
console . log ( "subscription_search: Prioritization data loaded:" , {
userFollows : userFollowPubkeys.size ,
userFollows : userFollowPubkeys.size ,
communityMembers : communityMemberPubkeys.size ,
communityMembers : communityMemberPubkeys.size ,
totalEvents : events.length
totalEvents : events.length ,
} ) ;
} ) ;
} catch ( error ) {
} catch ( error ) {
console . warn ( "subscription_search: Failed to load prioritization data:" , error ) ;
console . warn (
"subscription_search: Failed to load prioritization data:" ,
error ,
) ;
}
}
} else {
} else {
console . log ( "subscription_search: No NDK provided, skipping user list and community checks" ) ;
console . log (
"subscription_search: No NDK provided, skipping user list and community checks" ,
) ;
}
}
// Separate events into priority tiers
// Separate events into priority tiers
@ -131,7 +148,9 @@ async function prioritizeSearchEvents(
const isFromTarget = targetPubkey && event . pubkey === targetPubkey ;
const isFromTarget = targetPubkey && event . pubkey === targetPubkey ;
const isPrioritizedKind = PRIORITIZED_EVENT_KINDS . has ( event . kind || 0 ) ;
const isPrioritizedKind = PRIORITIZED_EVENT_KINDS . has ( event . kind || 0 ) ;
const isFromFollow = userFollowPubkeys . has ( event . pubkey || "" ) ;
const isFromFollow = userFollowPubkeys . has ( event . pubkey || "" ) ;
const isFromCommunityMember = communityMemberPubkeys . has ( event . pubkey || "" ) ;
const isFromCommunityMember = communityMemberPubkeys . has (
event . pubkey || "" ,
) ;
// AI-NOTE: Prioritized kinds are always in tier 1
// AI-NOTE: Prioritized kinds are always in tier 1
// Target pubkey priority only applies to n: searches (when targetPubkey is provided)
// Target pubkey priority only applies to n: searches (when targetPubkey is provided)
@ -181,7 +200,7 @@ async function prioritizeSearchEvents(
tier2 : tier2.length , // User follows
tier2 : tier2.length , // User follows
tier3 : tier3.length , // Community members
tier3 : tier3.length , // Community members
tier4 : tier4.length , // Others
tier4 : tier4.length , // Others
total : result.length
total : result.length ,
} ) ;
} ) ;
return result ;
return result ;
@ -224,25 +243,34 @@ export async function searchBySubscription(
// AI-NOTE: Ensure cached events have created_at property preserved
// AI-NOTE: Ensure cached events have created_at property preserved
// This fixes the "Unknown date" issue when events are retrieved from cache
// This fixes the "Unknown date" issue when events are retrieved from cache
const eventsWithCreatedAt = cachedResult . events . map ( event = > {
const eventsWithCreatedAt = cachedResult . events . map ( ( event ) = > {
if ( event && typeof event === 'object' && ! event . created_at ) {
if ( event && typeof event === "object" && ! event . created_at ) {
console . warn ( "subscription_search: Event missing created_at, setting to 0:" , event . id ) ;
console . warn (
"subscription_search: Event missing created_at, setting to 0:" ,
event . id ,
) ;
( event as any ) . created_at = 0 ;
( event as any ) . created_at = 0 ;
}
}
return event ;
return event ;
} ) ;
} ) ;
const secondOrderWithCreatedAt = cachedResult . secondOrder . map ( event = > {
const secondOrderWithCreatedAt = cachedResult . secondOrder . map ( ( event ) = > {
if ( event && typeof event === 'object' && ! event . created_at ) {
if ( event && typeof event === "object" && ! event . created_at ) {
console . warn ( "subscription_search: Second order event missing created_at, setting to 0:" , event . id ) ;
console . warn (
"subscription_search: Second order event missing created_at, setting to 0:" ,
event . id ,
) ;
( event as any ) . created_at = 0 ;
( event as any ) . created_at = 0 ;
}
}
return event ;
return event ;
} ) ;
} ) ;
const tTagEventsWithCreatedAt = cachedResult . tTagEvents . map ( event = > {
const tTagEventsWithCreatedAt = cachedResult . tTagEvents . map ( ( event ) = > {
if ( event && typeof event === 'object' && ! event . created_at ) {
if ( event && typeof event === "object" && ! event . created_at ) {
console . warn ( "subscription_search: T-tag event missing created_at, setting to 0:" , event . id ) ;
console . warn (
"subscription_search: T-tag event missing created_at, setting to 0:" ,
event . id ,
) ;
( event as any ) . created_at = 0 ;
( event as any ) . created_at = 0 ;
}
}
return event ;
return event ;
@ -252,18 +280,22 @@ export async function searchBySubscription(
. . . cachedResult ,
. . . cachedResult ,
events : eventsWithCreatedAt ,
events : eventsWithCreatedAt ,
secondOrder : secondOrderWithCreatedAt ,
secondOrder : secondOrderWithCreatedAt ,
tTagEvents : tTagEventsWithCreatedAt
tTagEvents : tTagEventsWithCreatedAt ,
} ;
} ;
// AI-NOTE: Return cached results immediately but trigger second-order search in background
// AI-NOTE: Return cached results immediately but trigger second-order search in background
// This ensures we get fast results while still updating second-order data
// This ensures we get fast results while still updating second-order data
console . log ( "subscription_search: Returning cached result immediately, triggering background second-order search" ) ;
console . log (
"subscription_search: Returning cached result immediately, triggering background second-order search" ,
) ;
// Trigger second-order search in background for all search types
// Trigger second-order search in background for all search types
if ( ndk ) {
if ( ndk ) {
// Start second-order search in background for n and d searches only
// Start second-order search in background for n and d searches only
if ( searchType === "n" || searchType === "d" ) {
if ( searchType === "n" || searchType === "d" ) {
console . log ( "subscription_search: Triggering background second-order search for cached result" ) ;
console . log (
"subscription_search: Triggering background second-order search for cached result" ,
) ;
performSecondOrderSearchInBackground (
performSecondOrderSearchInBackground (
searchType as "n" | "d" ,
searchType as "n" | "d" ,
eventsWithCreatedAt ,
eventsWithCreatedAt ,
@ -271,7 +303,7 @@ export async function searchBySubscription(
cachedResult . addresses || new Set ( ) ,
cachedResult . addresses || new Set ( ) ,
ndk ,
ndk ,
searchType === "n" ? eventsWithCreatedAt [ 0 ] ? . pubkey : undefined ,
searchType === "n" ? eventsWithCreatedAt [ 0 ] ? . pubkey : undefined ,
callbacks
callbacks ,
) ;
) ;
}
}
}
}
@ -316,7 +348,10 @@ export async function searchBySubscription(
// AI-NOTE: Check for preloaded events first (for profile searches)
// AI-NOTE: Check for preloaded events first (for profile searches)
if ( searchFilter . preloadedEvents && searchFilter . preloadedEvents . length > 0 ) {
if ( searchFilter . preloadedEvents && searchFilter . preloadedEvents . length > 0 ) {
console . log ( "subscription_search: Using preloaded events:" , searchFilter . preloadedEvents . length ) ;
console . log (
"subscription_search: Using preloaded events:" ,
searchFilter . preloadedEvents . length ,
) ;
processPrimaryRelayResults (
processPrimaryRelayResults (
new Set ( searchFilter . preloadedEvents ) ,
new Set ( searchFilter . preloadedEvents ) ,
searchType ,
searchType ,
@ -328,7 +363,9 @@ export async function searchBySubscription(
) ;
) ;
if ( hasResults ( searchState , searchType ) ) {
if ( hasResults ( searchState , searchType ) ) {
console . log ( "subscription_search: Found results from preloaded events, returning immediately" ) ;
console . log (
"subscription_search: Found results from preloaded events, returning immediately" ,
) ;
const immediateResult = createSearchResult (
const immediateResult = createSearchResult (
searchState ,
searchState ,
searchType ,
searchType ,
@ -376,10 +413,16 @@ export async function searchBySubscription(
) ;
) ;
const timeoutPromise = new Promise ( ( _ , reject ) = > {
const timeoutPromise = new Promise ( ( _ , reject ) = > {
setTimeout ( ( ) = > reject ( new Error ( "Primary relay search timeout" ) ) , TIMEOUTS . SUBSCRIPTION_SEARCH ) ;
setTimeout (
( ) = > reject ( new Error ( "Primary relay search timeout" ) ) ,
TIMEOUTS . SUBSCRIPTION_SEARCH ,
) ;
} ) ;
} ) ;
const primaryEvents = await Promise . race ( [ primaryEventsPromise , timeoutPromise ] ) as any ;
const primaryEvents = await Promise . race ( [
primaryEventsPromise ,
timeoutPromise ,
] ) as any ;
console . log (
console . log (
"subscription_search: Primary relay returned" ,
"subscription_search: Primary relay returned" ,
@ -473,10 +516,16 @@ export async function searchBySubscription(
) ;
) ;
const fallbackTimeoutPromise = new Promise ( ( _ , reject ) = > {
const fallbackTimeoutPromise = new Promise ( ( _ , reject ) = > {
setTimeout ( ( ) = > reject ( new Error ( "Fallback search timeout" ) ) , TIMEOUTS . SUBSCRIPTION_SEARCH ) ;
setTimeout (
( ) = > reject ( new Error ( "Fallback search timeout" ) ) ,
TIMEOUTS . SUBSCRIPTION_SEARCH ,
) ;
} ) ;
} ) ;
const fallbackEvents = await Promise . race ( [ fallbackEventsPromise , fallbackTimeoutPromise ] ) as any ;
const fallbackEvents = await Promise . race ( [
fallbackEventsPromise ,
fallbackTimeoutPromise ,
] ) as any ;
console . log (
console . log (
"subscription_search: Fallback search returned" ,
"subscription_search: Fallback search returned" ,
@ -520,8 +569,13 @@ export async function searchBySubscription(
) ;
) ;
// If it's a timeout error, continue to return empty result
// If it's a timeout error, continue to return empty result
if ( fallbackError instanceof Error && fallbackError . message . includes ( "timeout" ) ) {
if (
console . log ( "subscription_search: Fallback search timed out, returning empty result" ) ;
fallbackError instanceof Error &&
fallbackError . message . includes ( "timeout" )
) {
console . log (
"subscription_search: Fallback search timed out, returning empty result" ,
) ;
}
}
}
}
@ -556,7 +610,9 @@ export async function searchBySubscription(
// If it's a timeout error, continue to Phase 2 instead of failing
// If it's a timeout error, continue to Phase 2 instead of failing
if ( error instanceof Error && error . message . includes ( "timeout" ) ) {
if ( error instanceof Error && error . message . includes ( "timeout" ) ) {
console . log ( "subscription_search: Primary relay search timed out, continuing to Phase 2" ) ;
console . log (
"subscription_search: Primary relay search timed out, continuing to Phase 2" ,
) ;
} else {
} else {
// For other errors, we might want to fail the search
// For other errors, we might want to fail the search
throw error ;
throw error ;
@ -685,7 +741,11 @@ async function createSearchFilter(
hexPubkey = decoded . data as string ;
hexPubkey = decoded . data as string ;
}
}
} catch ( e ) {
} catch ( e ) {
console . warn ( "subscription_search: Failed to decode npub:" , profile . pubkey , e ) ;
console . warn (
"subscription_search: Failed to decode npub:" ,
profile . pubkey ,
e ,
) ;
}
}
}
}
event . pubkey = hexPubkey ;
event . pubkey = hexPubkey ;
@ -695,11 +755,17 @@ async function createSearchFilter(
// This ensures the profile cards show the actual creation date instead of "Unknown date"
// This ensures the profile cards show the actual creation date instead of "Unknown date"
if ( ( profile as any ) . created_at ) {
if ( ( profile as any ) . created_at ) {
event . created_at = ( profile as any ) . created_at ;
event . created_at = ( profile as any ) . created_at ;
console . log ( "subscription_search: Using preserved timestamp:" , event . created_at ) ;
console . log (
"subscription_search: Using preserved timestamp:" ,
event . created_at ,
) ;
} else {
} else {
// Fallback to current timestamp if no preserved timestamp
// Fallback to current timestamp if no preserved timestamp
event . created_at = Math . floor ( Date . now ( ) / 1000 ) ;
event . created_at = Math . floor ( Date . now ( ) / 1000 ) ;
console . log ( "subscription_search: Using fallback timestamp:" , event . created_at ) ;
console . log (
"subscription_search: Using fallback timestamp:" ,
event . created_at ,
) ;
}
}
return event ;
return event ;
@ -712,7 +778,10 @@ async function createSearchFilter(
searchTerm : normalizedSearchTerm ,
searchTerm : normalizedSearchTerm ,
preloadedEvents : events , // AI-NOTE: Pass preloaded events
preloadedEvents : events , // AI-NOTE: Pass preloaded events
} ;
} ;
console . log ( "subscription_search: Created profile filter with preloaded events:" , nFilter ) ;
console . log (
"subscription_search: Created profile filter with preloaded events:" ,
nFilter ,
) ;
return nFilter ;
return nFilter ;
}
}
default : {
default : {
@ -721,8 +790,6 @@ async function createSearchFilter(
}
}
}
}
/ * *
/ * *
* Create primary relay set for search operations
* Create primary relay set for search operations
* AI - NOTE : Updated to use all available relays to prevent search failures
* AI - NOTE : Updated to use all available relays to prevent search failures
@ -816,7 +883,9 @@ function processPrimaryRelayResults(
for ( const event of events ) {
for ( const event of events ) {
// Check if we've reached the event limit
// Check if we've reached the event limit
if ( processedCount >= maxEvents ) {
if ( processedCount >= maxEvents ) {
console . log ( ` subscription_search: Reached event limit of ${ maxEvents } in primary relay processing ` ) ;
console . log (
` subscription_search: Reached event limit of ${ maxEvents } in primary relay processing ` ,
) ;
break ;
break ;
}
}
@ -1029,7 +1098,9 @@ function searchOtherRelaysInBackground(
sub . on ( "event" , ( event : NDKEvent ) = > {
sub . on ( "event" , ( event : NDKEvent ) = > {
// Check if we've reached the event limit
// Check if we've reached the event limit
if ( eventCount >= maxEvents ) {
if ( eventCount >= maxEvents ) {
console . log ( ` subscription_search: Reached event limit of ${ maxEvents } , stopping event processing ` ) ;
console . log (
` subscription_search: Reached event limit of ${ maxEvents } , stopping event processing ` ,
) ;
sub . stop ( ) ;
sub . stop ( ) ;
return ;
return ;
}
}
@ -1058,7 +1129,9 @@ function searchOtherRelaysInBackground(
// Add timeout to prevent hanging
// Add timeout to prevent hanging
const timeoutId = setTimeout ( async ( ) = > {
const timeoutId = setTimeout ( async ( ) = > {
if ( ! resolved ) {
if ( ! resolved ) {
console . log ( "subscription_search: Background search timeout, resolving with current results" ) ;
console . log (
"subscription_search: Background search timeout, resolving with current results" ,
) ;
resolved = true ;
resolved = true ;
sub . stop ( ) ;
sub . stop ( ) ;
const result = await processEoseResults (
const result = await processEoseResults (
@ -1106,7 +1179,12 @@ async function processEoseResults(
if ( searchType === "n" ) {
if ( searchType === "n" ) {
return processProfileEoseResults ( searchState , searchFilter , ndk , callbacks ) ;
return processProfileEoseResults ( searchState , searchFilter , ndk , callbacks ) ;
} else if ( searchType === "d" ) {
} else if ( searchType === "d" ) {
return await processContentEoseResults ( searchState , searchType , ndk , callbacks ) ;
return await processContentEoseResults (
searchState ,
searchType ,
ndk ,
callbacks ,
) ;
} else if ( searchType === "t" ) {
} else if ( searchType === "t" ) {
return await processTTagEoseResults ( searchState , ndk ) ;
return await processTTagEoseResults ( searchState , ndk ) ;
}
}
@ -1242,7 +1320,7 @@ async function processContentEoseResults(
dedupedEvents ,
dedupedEvents ,
undefined , // No specific target pubkey for d-tag searches
undefined , // No specific target pubkey for d-tag searches
SEARCH_LIMITS . GENERAL_CONTENT ,
SEARCH_LIMITS . GENERAL_CONTENT ,
ndk
ndk ,
) ;
) ;
// AI-NOTE: Attach profile data to first-order events for display
// AI-NOTE: Attach profile data to first-order events for display
@ -1276,7 +1354,10 @@ async function processContentEoseResults(
/ * *
/ * *
* Process t - tag EOSE results
* Process t - tag EOSE results
* /
* /
async function processTTagEoseResults ( searchState : any , ndk? : NDK ) : Promise < SearchResult > {
async function processTTagEoseResults (
searchState : any ,
ndk? : NDK ,
) : Promise < SearchResult > {
if ( searchState . tTagEvents . length === 0 ) {
if ( searchState . tTagEvents . length === 0 ) {
return createEmptySearchResult ( "t" , searchState . normalizedSearchTerm ) ;
return createEmptySearchResult ( "t" , searchState . normalizedSearchTerm ) ;
}
}
@ -1287,7 +1368,7 @@ async function processTTagEoseResults(searchState: any, ndk?: NDK): Promise<Sear
searchState . tTagEvents ,
searchState . tTagEvents ,
undefined , // No specific target pubkey for t-tag searches
undefined , // No specific target pubkey for t-tag searches
SEARCH_LIMITS . GENERAL_CONTENT ,
SEARCH_LIMITS . GENERAL_CONTENT ,
ndk
ndk ,
) ;
) ;
// AI-NOTE: Attach profile data to t-tag events for display
// AI-NOTE: Attach profile data to t-tag events for display
@ -1460,7 +1541,9 @@ async function performSecondOrderSearchInBackground(
await Promise . race ( [ fetchPromise , fetchTimeoutPromise ] ) ;
await Promise . race ( [ fetchPromise , fetchTimeoutPromise ] ) ;
// Now do the prioritization without timeout
// Now do the prioritization without timeout
console . log ( "subscription_search: Event fetching completed, starting prioritization..." ) ;
console . log (
"subscription_search: Event fetching completed, starting prioritization..." ,
) ;
// Deduplicate by event ID
// Deduplicate by event ID
const uniqueSecondOrder = new Map < string , NDKEvent > ( ) ;
const uniqueSecondOrder = new Map < string , NDKEvent > ( ) ;
@ -1484,18 +1567,18 @@ async function performSecondOrderSearchInBackground(
deduplicatedSecondOrder ,
deduplicatedSecondOrder ,
targetPubkey ,
targetPubkey ,
SEARCH_LIMITS . SECOND_ORDER_RESULTS ,
SEARCH_LIMITS . SECOND_ORDER_RESULTS ,
ndk
ndk ,
) ;
) ;
const prioritizationTimeoutPromise = new Promise ( ( _ , reject ) = > {
const prioritizationTimeoutPromise = new Promise ( ( _ , reject ) = > {
setTimeout ( ( ) = > reject ( new Error ( 'Prioritization timeout' ) ) , 15000 ) ; // 15 second timeout
setTimeout ( ( ) = > reject ( new Error ( "Prioritization timeout" ) ) , 15000 ) ; // 15 second timeout
} ) ;
} ) ;
let prioritizedSecondOrder : NDKEvent [ ] ;
let prioritizedSecondOrder : NDKEvent [ ] ;
try {
try {
prioritizedSecondOrder = await Promise . race ( [
prioritizedSecondOrder = await Promise . race ( [
prioritizationPromise ,
prioritizationPromise ,
prioritizationTimeoutPromise
prioritizationTimeoutPromise ,
] ) as NDKEvent [ ] ;
] ) as NDKEvent [ ] ;
console . log (
console . log (
@ -1504,7 +1587,10 @@ async function performSecondOrderSearchInBackground(
"prioritized results" ,
"prioritized results" ,
) ;
) ;
} catch ( error ) {
} catch ( error ) {
console . warn ( "subscription_search: Prioritization failed, using simple sorting:" , error ) ;
console . warn (
"subscription_search: Prioritization failed, using simple sorting:" ,
error ,
) ;
// Fallback to simple sorting if prioritization fails
// Fallback to simple sorting if prioritization fails
prioritizedSecondOrder = deduplicatedSecondOrder . sort ( ( a , b ) = > {
prioritizedSecondOrder = deduplicatedSecondOrder . sort ( ( a , b ) = > {
// Prioritize events from target pubkey first (for n: searches)
// Prioritize events from target pubkey first (for n: searches)
@ -1577,16 +1663,23 @@ async function performSecondOrderSearchInBackground(
* @param ndk NDK instance for fetching profile data
* @param ndk NDK instance for fetching profile data
* @returns Promise that resolves when profile data is attached
* @returns Promise that resolves when profile data is attached
* /
* /
async function attachProfileDataToEvents ( events : NDKEvent [ ] , ndk : NDK ) : Promise < void > {
async function attachProfileDataToEvents (
events : NDKEvent [ ] ,
ndk : NDK ,
) : Promise < void > {
if ( events . length === 0 ) {
if ( events . length === 0 ) {
return ;
return ;
}
}
console . log ( ` subscription_search: Attaching profile data to ${ events . length } events ` ) ;
console . log (
` subscription_search: Attaching profile data to ${ events . length } events ` ,
) ;
try {
try {
// Import user list functions dynamically to avoid circular dependencies
// Import user list functions dynamically to avoid circular dependencies
const { fetchCurrentUserLists , isPubkeyInUserLists } = await import ( "./user_lists.ts" ) ;
const { fetchCurrentUserLists , isPubkeyInUserLists } = await import (
"./user_lists.ts"
) ;
// Get current user's lists for user list status
// Get current user's lists for user list status
const userLists = await fetchCurrentUserLists ( undefined , ndk ) ;
const userLists = await fetchCurrentUserLists ( undefined , ndk ) ;
@ -1599,14 +1692,18 @@ async function attachProfileDataToEvents(events: NDKEvent[], ndk: NDK): Promise<
}
}
} ) ;
} ) ;
console . log ( ` subscription_search: Found ${ uniquePubkeys . size } unique pubkeys to fetch profiles for ` ) ;
console . log (
` subscription_search: Found ${ uniquePubkeys . size } unique pubkeys to fetch profiles for ` ,
) ;
// Fetch profile data for each unique pubkey
// Fetch profile data for each unique pubkey
const profilePromises = Array . from ( uniquePubkeys ) . map ( async ( pubkey ) = > {
const profilePromises = Array . from ( uniquePubkeys ) . map ( async ( pubkey ) = > {
try {
try {
// Import getUserMetadata dynamically to avoid circular dependencies
// Import getUserMetadata dynamically to avoid circular dependencies
const { getUserMetadata } = await import ( "./nostrUtils.ts" ) ;
const { getUserMetadata } = await import ( "./nostrUtils.ts" ) ;
const npub = await import ( "./nostrUtils.ts" ) . then ( m = > m . toNpub ( pubkey ) ) ;
const npub = await import ( "./nostrUtils.ts" ) . then ( ( m ) = >
m . toNpub ( pubkey )
) ;
if ( npub ) {
if ( npub ) {
const profileData = await getUserMetadata ( npub , ndk , true ) ;
const profileData = await getUserMetadata ( npub , ndk , true ) ;
@ -1619,13 +1716,16 @@ async function attachProfileDataToEvents(events: NDKEvent[], ndk: NDK): Promise<
pubkey ,
pubkey ,
profileData : {
profileData : {
. . . profileData ,
. . . profileData ,
isInUserLists : isInLists
isInUserLists : isInLists ,
}
} ,
} ;
} ;
}
}
}
}
} catch ( error ) {
} catch ( error ) {
console . warn ( ` subscription_search: Failed to fetch profile for ${ pubkey } : ` , error ) ;
console . warn (
` subscription_search: Failed to fetch profile for ${ pubkey } : ` ,
error ,
) ;
}
}
return null ;
return null ;
} ) ;
} ) ;
@ -1640,7 +1740,9 @@ async function attachProfileDataToEvents(events: NDKEvent[], ndk: NDK): Promise<
}
}
} ) ;
} ) ;
console . log ( ` subscription_search: Successfully fetched ${ profileMap . size } profiles ` ) ;
console . log (
` subscription_search: Successfully fetched ${ profileMap . size } profiles ` ,
) ;
// Attach profile data to each event
// Attach profile data to each event
events . forEach ( ( event ) = > {
events . forEach ( ( event ) = > {