mirror of
https://github.com/dergigi/boris.git
synced 2025-12-19 07:34:28 +01:00
perf(ui): stream results to UI; show cached/local immediately (articles, highlights, explore)
This commit is contained in:
@@ -46,7 +46,21 @@ const Explore: React.FC<ExploreProps> = ({ relayPool }) => {
|
|||||||
const posts = await fetchBlogPostsFromAuthors(
|
const posts = await fetchBlogPostsFromAuthors(
|
||||||
relayPool,
|
relayPool,
|
||||||
Array.from(contacts),
|
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) {
|
if (posts.length === 0) {
|
||||||
|
|||||||
@@ -57,7 +57,21 @@ export function useExternalUrlLoader({
|
|||||||
|
|
||||||
// Check if fetchHighlightsForUrl exists, otherwise skip
|
// Check if fetchHighlightsForUrl exists, otherwise skip
|
||||||
if (typeof fetchHighlightsForUrl === 'function') {
|
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))
|
setHighlights(highlightsList.sort((a, b) => b.created_at - a.created_at))
|
||||||
console.log(`📌 Found ${highlightsList.length} highlights for URL`)
|
console.log(`📌 Found ${highlightsList.length} highlights for URL`)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { RelayPool, completeOnEose } from 'applesauce-relay'
|
import { RelayPool, completeOnEose, onlyEvents } from 'applesauce-relay'
|
||||||
import { lastValueFrom, race, takeUntil, timer, toArray } from 'rxjs'
|
import { lastValueFrom, take, takeUntil, timer, toArray } from 'rxjs'
|
||||||
import { nip19 } from 'nostr-tools'
|
import { nip19 } from 'nostr-tools'
|
||||||
import { AddressPointer } from 'nostr-tools/nip19'
|
import { AddressPointer } from 'nostr-tools/nip19'
|
||||||
import { NostrEvent } from 'nostr-tools'
|
import { NostrEvent } from 'nostr-tools'
|
||||||
@@ -119,7 +119,12 @@ export async function fetchArticleByNaddr(
|
|||||||
events = await lastValueFrom(
|
events = await lastValueFrom(
|
||||||
relayPool
|
relayPool
|
||||||
.req(localRelays, filter)
|
.req(localRelays, filter)
|
||||||
.pipe(completeOnEose(), takeUntil(timer(1200)), toArray())
|
.pipe(
|
||||||
|
onlyEvents(),
|
||||||
|
take(1),
|
||||||
|
takeUntil(timer(1200)),
|
||||||
|
toArray()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
} catch {
|
} catch {
|
||||||
events = []
|
events = []
|
||||||
@@ -131,7 +136,12 @@ export async function fetchArticleByNaddr(
|
|||||||
events = await lastValueFrom(
|
events = await lastValueFrom(
|
||||||
relayPool
|
relayPool
|
||||||
.req(orderedRelays, filter)
|
.req(orderedRelays, filter)
|
||||||
.pipe(completeOnEose(), takeUntil(timer(6000)), toArray())
|
.pipe(
|
||||||
|
onlyEvents(),
|
||||||
|
take(1),
|
||||||
|
takeUntil(timer(6000)),
|
||||||
|
toArray()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ export interface BlogPostPreview {
|
|||||||
export const fetchBlogPostsFromAuthors = async (
|
export const fetchBlogPostsFromAuthors = async (
|
||||||
relayPool: RelayPool,
|
relayPool: RelayPool,
|
||||||
pubkeys: string[],
|
pubkeys: string[],
|
||||||
relayUrls: string[]
|
relayUrls: string[],
|
||||||
|
onPost?: (post: BlogPostPreview) => void
|
||||||
): Promise<BlogPostPreview[]> => {
|
): Promise<BlogPostPreview[]> => {
|
||||||
try {
|
try {
|
||||||
if (pubkeys.length === 0) {
|
if (pubkeys.length === 0) {
|
||||||
@@ -48,7 +49,11 @@ export const fetchBlogPostsFromAuthors = async (
|
|||||||
authors: pubkeys,
|
authors: pubkeys,
|
||||||
limit: 100
|
limit: 100
|
||||||
})
|
})
|
||||||
.pipe(completeOnEose(), takeUntil(timer(1200)), toArray())
|
.pipe(
|
||||||
|
completeOnEose(),
|
||||||
|
takeUntil(timer(1200)),
|
||||||
|
toArray()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
} catch {
|
} catch {
|
||||||
events = []
|
events = []
|
||||||
@@ -84,14 +89,18 @@ export const fetchBlogPostsFromAuthors = async (
|
|||||||
|
|
||||||
// Convert to blog post previews and sort by published date (most recent first)
|
// Convert to blog post previews and sort by published date (most recent first)
|
||||||
const blogPosts: BlogPostPreview[] = Array.from(uniqueEvents.values())
|
const blogPosts: BlogPostPreview[] = Array.from(uniqueEvents.values())
|
||||||
.map(event => ({
|
.map(event => {
|
||||||
event,
|
const post: BlogPostPreview = {
|
||||||
title: getArticleTitle(event) || 'Untitled',
|
event,
|
||||||
summary: getArticleSummary(event),
|
title: getArticleTitle(event) || 'Untitled',
|
||||||
image: getArticleImage(event),
|
summary: getArticleSummary(event),
|
||||||
published: getArticlePublished(event),
|
image: getArticleImage(event),
|
||||||
author: event.pubkey
|
published: getArticlePublished(event),
|
||||||
}))
|
author: event.pubkey
|
||||||
|
}
|
||||||
|
if (onPost) onPost(post)
|
||||||
|
return post
|
||||||
|
})
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
const timeA = a.published || a.event.created_at
|
const timeA = a.published || a.event.created_at
|
||||||
const timeB = b.published || b.event.created_at
|
const timeB = b.published || b.event.created_at
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ export const fetchHighlightsForArticle = async (
|
|||||||
export const fetchHighlightsForUrl = async (
|
export const fetchHighlightsForUrl = async (
|
||||||
relayPool: RelayPool,
|
relayPool: RelayPool,
|
||||||
url: string,
|
url: string,
|
||||||
|
onHighlight?: (highlight: Highlight) => void,
|
||||||
settings?: UserSettings
|
settings?: UserSettings
|
||||||
): Promise<Highlight[]> => {
|
): Promise<Highlight[]> => {
|
||||||
try {
|
try {
|
||||||
@@ -187,6 +188,10 @@ export const fetchHighlightsForUrl = async (
|
|||||||
onlyEvents(),
|
onlyEvents(),
|
||||||
tap((event: NostrEvent) => {
|
tap((event: NostrEvent) => {
|
||||||
seenIds.add(event.id)
|
seenIds.add(event.id)
|
||||||
|
if (onHighlight) {
|
||||||
|
const h = eventToHighlight(event)
|
||||||
|
onHighlight(h)
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
completeOnEose(),
|
completeOnEose(),
|
||||||
takeUntil(timer(1200)),
|
takeUntil(timer(1200)),
|
||||||
@@ -205,6 +210,10 @@ export const fetchHighlightsForUrl = async (
|
|||||||
onlyEvents(),
|
onlyEvents(),
|
||||||
tap((event: NostrEvent) => {
|
tap((event: NostrEvent) => {
|
||||||
seenIds.add(event.id)
|
seenIds.add(event.id)
|
||||||
|
if (onHighlight) {
|
||||||
|
const h = eventToHighlight(event)
|
||||||
|
onHighlight(h)
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
completeOnEose(),
|
completeOnEose(),
|
||||||
takeUntil(timer(6000)),
|
takeUntil(timer(6000)),
|
||||||
|
|||||||
Reference in New Issue
Block a user