perf(ui): stream results to UI; show cached/local immediately (articles, highlights, explore)

This commit is contained in:
Gigi
2025-10-12 22:33:46 +02:00
parent 5513fc9850
commit ca95d6c7f4
5 changed files with 72 additions and 16 deletions

View File

@@ -46,7 +46,21 @@ const Explore: React.FC<ExploreProps> = ({ relayPool }) => {
const posts = await fetchBlogPostsFromAuthors(
relayPool,
Array.from(contacts),
relayUrls
relayUrls,
(post) => {
// Stream posts as we get them
setBlogPosts((prev) => {
const exists = prev.some(p => p.event.id === post.event.id)
if (exists) return prev
const next = [...prev, post]
// Keep sorted by published or created_at
return next.sort((a, b) => {
const timeA = a.published || a.event.created_at
const timeB = b.published || b.event.created_at
return timeB - timeA
})
})
}
)
if (posts.length === 0) {

View File

@@ -57,7 +57,21 @@ export function useExternalUrlLoader({
// Check if fetchHighlightsForUrl exists, otherwise skip
if (typeof fetchHighlightsForUrl === 'function') {
const highlightsList = await fetchHighlightsForUrl(relayPool, url)
const seen = new Set<string>()
const highlightsList = await fetchHighlightsForUrl(
relayPool,
url,
(highlight) => {
if (seen.has(highlight.id)) return
seen.add(highlight.id)
setHighlights((prev) => {
if (prev.some(h => h.id === highlight.id)) return prev
const next = [...prev, highlight]
return next.sort((a, b) => b.created_at - a.created_at)
})
}
)
// Ensure final list is sorted and contains all items
setHighlights(highlightsList.sort((a, b) => b.created_at - a.created_at))
console.log(`📌 Found ${highlightsList.length} highlights for URL`)
} else {

View File

@@ -1,5 +1,5 @@
import { RelayPool, completeOnEose } from 'applesauce-relay'
import { lastValueFrom, race, takeUntil, timer, toArray } from 'rxjs'
import { RelayPool, completeOnEose, onlyEvents } from 'applesauce-relay'
import { lastValueFrom, take, takeUntil, timer, toArray } from 'rxjs'
import { nip19 } from 'nostr-tools'
import { AddressPointer } from 'nostr-tools/nip19'
import { NostrEvent } from 'nostr-tools'
@@ -119,7 +119,12 @@ export async function fetchArticleByNaddr(
events = await lastValueFrom(
relayPool
.req(localRelays, filter)
.pipe(completeOnEose(), takeUntil(timer(1200)), toArray())
.pipe(
onlyEvents(),
take(1),
takeUntil(timer(1200)),
toArray()
)
)
} catch {
events = []
@@ -131,7 +136,12 @@ export async function fetchArticleByNaddr(
events = await lastValueFrom(
relayPool
.req(orderedRelays, filter)
.pipe(completeOnEose(), takeUntil(timer(6000)), toArray())
.pipe(
onlyEvents(),
take(1),
takeUntil(timer(6000)),
toArray()
)
)
}

View File

@@ -25,7 +25,8 @@ export interface BlogPostPreview {
export const fetchBlogPostsFromAuthors = async (
relayPool: RelayPool,
pubkeys: string[],
relayUrls: string[]
relayUrls: string[],
onPost?: (post: BlogPostPreview) => void
): Promise<BlogPostPreview[]> => {
try {
if (pubkeys.length === 0) {
@@ -48,7 +49,11 @@ export const fetchBlogPostsFromAuthors = async (
authors: pubkeys,
limit: 100
})
.pipe(completeOnEose(), takeUntil(timer(1200)), toArray())
.pipe(
completeOnEose(),
takeUntil(timer(1200)),
toArray()
)
)
} catch {
events = []
@@ -84,14 +89,18 @@ export const fetchBlogPostsFromAuthors = async (
// Convert to blog post previews and sort by published date (most recent first)
const blogPosts: BlogPostPreview[] = Array.from(uniqueEvents.values())
.map(event => ({
event,
title: getArticleTitle(event) || 'Untitled',
summary: getArticleSummary(event),
image: getArticleImage(event),
published: getArticlePublished(event),
author: event.pubkey
}))
.map(event => {
const post: BlogPostPreview = {
event,
title: getArticleTitle(event) || 'Untitled',
summary: getArticleSummary(event),
image: getArticleImage(event),
published: getArticlePublished(event),
author: event.pubkey
}
if (onPost) onPost(post)
return post
})
.sort((a, b) => {
const timeA = a.published || a.event.created_at
const timeB = b.published || b.event.created_at

View File

@@ -169,6 +169,7 @@ export const fetchHighlightsForArticle = async (
export const fetchHighlightsForUrl = async (
relayPool: RelayPool,
url: string,
onHighlight?: (highlight: Highlight) => void,
settings?: UserSettings
): Promise<Highlight[]> => {
try {
@@ -187,6 +188,10 @@ export const fetchHighlightsForUrl = async (
onlyEvents(),
tap((event: NostrEvent) => {
seenIds.add(event.id)
if (onHighlight) {
const h = eventToHighlight(event)
onHighlight(h)
}
}),
completeOnEose(),
takeUntil(timer(1200)),
@@ -205,6 +210,10 @@ export const fetchHighlightsForUrl = async (
onlyEvents(),
tap((event: NostrEvent) => {
seenIds.add(event.id)
if (onHighlight) {
const h = eventToHighlight(event)
onHighlight(h)
}
}),
completeOnEose(),
takeUntil(timer(6000)),