diff --git a/src/components/Explore.tsx b/src/components/Explore.tsx index 44262a4d..87df8ca5 100644 --- a/src/components/Explore.tsx +++ b/src/components/Explore.tsx @@ -112,15 +112,50 @@ const Explore: React.FC = ({ relayPool, eventStore, settings, acti // show spinner but keep existing data setLoading(true) - // If not logged in, only fetch nostrverse content + // If not logged in, only fetch nostrverse content with streaming posts if (!activeAccount) { const relayUrls = Array.from(relayPool.relays.values()).map(relay => relay.url) - const [nostrversePosts, nostriverseHighlights] = await Promise.all([ - fetchNostrverseBlogPosts(relayPool, relayUrls, 50, eventStore || undefined), - fetchNostrverseHighlights(relayPool, 100, eventStore || undefined) - ]) + const highlightPromise = fetchNostrverseHighlights(relayPool, 100, eventStore || undefined) - setBlogPosts(nostrversePosts) + // Stream posts as they arrive + const postsPromise = fetchNostrverseBlogPosts( + relayPool, + relayUrls, + 50, + eventStore || undefined, + (post) => { + setBlogPosts(prev => { + const dTag = post.event.tags.find(t => t[0] === 'd')?.[1] || '' + const key = `${post.author}:${dTag}` + const existingIndex = prev.findIndex(p => { + const pDTag = p.event.tags.find(t => t[0] === 'd')?.[1] || '' + return `${p.author}:${pDTag}` === key + }) + if (existingIndex >= 0) { + const existing = prev[existingIndex] + if (post.event.created_at <= existing.event.created_at) return prev + const next = [...prev] + next[existingIndex] = post + return next.sort((a, b) => (b.published || b.event.created_at) - (a.published || a.event.created_at)) + } + const next = [...prev, post] + return next.sort((a, b) => (b.published || b.event.created_at) - (a.published || a.event.created_at)) + }) + } + ) + + const [finalPosts, nostriverseHighlights] = await Promise.all([postsPromise, highlightPromise]) + // Ensure final sorted list set (in case stream missed an update) + setBlogPosts(prev => { + const byKey = new Map() + for (const p of [...prev, ...finalPosts]) { + const dTag = p.event.tags.find(t => t[0] === 'd')?.[1] || '' + const key = `${p.author}:${dTag}` + const existing = byKey.get(key) + if (!existing || p.event.created_at > existing.event.created_at) byKey.set(key, p) + } + return Array.from(byKey.values()).sort((a, b) => (b.published || b.event.created_at) - (a.published || a.event.created_at)) + }) setHighlights(nostriverseHighlights) setLoading(false) return @@ -280,7 +315,26 @@ const Explore: React.FC = ({ relayPool, eventStore, settings, acti const [friendsPosts, friendsHighlights, nostrversePosts, nostriverseHighlights] = await Promise.all([ fetchBlogPostsFromAuthors(relayPool, contactsArray, relayUrls), fetchHighlightsFromAuthors(relayPool, contactsArray), - fetchNostrverseBlogPosts(relayPool, relayUrls, 50, eventStore || undefined), + fetchNostrverseBlogPosts(relayPool, relayUrls, 50, eventStore || undefined, (post) => { + // Stream nostrverse posts too when logged in + setBlogPosts(prev => { + const dTag = post.event.tags.find(t => t[0] === 'd')?.[1] || '' + const key = `${post.author}:${dTag}` + const existingIndex = prev.findIndex(p => { + const pDTag = p.event.tags.find(t => t[0] === 'd')?.[1] || '' + return `${p.author}:${pDTag}` === key + }) + if (existingIndex >= 0) { + const existing = prev[existingIndex] + if (post.event.created_at <= existing.event.created_at) return prev + const next = [...prev] + next[existingIndex] = post + return next.sort((a, b) => (b.published || b.event.created_at) - (a.published || a.event.created_at)) + } + const next = [...prev, post] + return next.sort((a, b) => (b.published || b.event.created_at) - (a.published || a.event.created_at)) + }) + }), fetchNostrverseHighlights(relayPool, 100, eventStore || undefined) ]) diff --git a/src/services/nostrverseService.ts b/src/services/nostrverseService.ts index c1e98841..2f96942d 100644 --- a/src/services/nostrverseService.ts +++ b/src/services/nostrverseService.ts @@ -20,7 +20,8 @@ export const fetchNostrverseBlogPosts = async ( relayPool: RelayPool, relayUrls: string[], limit = 50, - eventStore?: IEventStore + eventStore?: IEventStore, + onPost?: (post: BlogPostPreview) => void ): Promise => { try { console.log('[NOSTRVERSE] 📚 Fetching blog posts (kind 30023), limit:', limit) @@ -44,6 +45,19 @@ export const fetchNostrverseBlogPosts = async ( const existing = uniqueEvents.get(key) if (!existing || event.created_at > existing.created_at) { uniqueEvents.set(key, event) + + // Stream post immediately if callback provided + if (onPost) { + const post: BlogPostPreview = { + event, + title: getArticleTitle(event) || 'Untitled', + summary: getArticleSummary(event), + image: getArticleImage(event), + published: getArticlePublished(event), + author: event.pubkey + } + onPost(post) + } } } }