mirror of
https://github.com/dergigi/boris.git
synced 2025-12-27 11:34:50 +01:00
perf(local-first): apply local-first then remote pattern across services (titles, bookmarks, highlights)
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import { RelayPool, completeOnEose } from 'applesauce-relay'
|
||||
import { lastValueFrom, 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 { Helpers } from 'applesauce-core'
|
||||
import { RELAYS } from '../config/relays'
|
||||
import { prioritizeLocalRelays, partitionRelays } from '../utils/helpers'
|
||||
|
||||
const { getArticleTitle } = Helpers
|
||||
|
||||
@@ -25,9 +26,11 @@ export async function fetchArticleTitle(
|
||||
const pointer = decoded.data as AddressPointer
|
||||
|
||||
// Define relays to query
|
||||
const relays = pointer.relays && pointer.relays.length > 0
|
||||
const baseRelays = pointer.relays && pointer.relays.length > 0
|
||||
? pointer.relays
|
||||
: RELAYS
|
||||
const orderedRelays = prioritizeLocalRelays(baseRelays)
|
||||
const { local: localRelays } = partitionRelays(orderedRelays)
|
||||
|
||||
// Fetch the article event
|
||||
const filter = {
|
||||
@@ -36,11 +39,27 @@ export async function fetchArticleTitle(
|
||||
'#d': [pointer.identifier]
|
||||
}
|
||||
|
||||
const events = await lastValueFrom(
|
||||
relayPool
|
||||
.req(relays, filter)
|
||||
.pipe(completeOnEose(), takeUntil(timer(5000)), toArray())
|
||||
)
|
||||
// Try to get the first event quickly from local relays
|
||||
let events = [] as any[]
|
||||
if (localRelays.length > 0) {
|
||||
try {
|
||||
events = await lastValueFrom(
|
||||
relayPool
|
||||
.req(localRelays, filter)
|
||||
.pipe(onlyEvents(), take(1), takeUntil(timer(1200)), toArray())
|
||||
)
|
||||
} catch {
|
||||
events = []
|
||||
}
|
||||
}
|
||||
// Fallback to all relays if nothing from local quickly
|
||||
if (events.length === 0) {
|
||||
events = await lastValueFrom(
|
||||
relayPool
|
||||
.req(orderedRelays, filter)
|
||||
.pipe(onlyEvents(), take(1), takeUntil(timer(5000)), toArray())
|
||||
)
|
||||
}
|
||||
|
||||
if (events.length === 0) {
|
||||
return null
|
||||
|
||||
@@ -16,7 +16,7 @@ import { Bookmark } from '../types/bookmarks'
|
||||
import { collectBookmarksFromEvents } from './bookmarkProcessing.ts'
|
||||
import { UserSettings } from './settingsService'
|
||||
import { rebroadcastEvents } from './rebroadcastService'
|
||||
import { prioritizeLocalRelays } from '../utils/helpers'
|
||||
import { prioritizeLocalRelays, partitionRelays } from '../utils/helpers'
|
||||
|
||||
|
||||
|
||||
@@ -33,11 +33,11 @@ export const fetchBookmarks = async (
|
||||
}
|
||||
// Get relay URLs from the pool
|
||||
const relayUrls = prioritizeLocalRelays(Array.from(relayPool.relays.values()).map(relay => relay.url))
|
||||
const { local: localRelays, remote: remoteRelays } = partitionRelays(relayUrls)
|
||||
// Fetch bookmark events - NIP-51 standards, legacy formats, and web bookmarks (NIP-B0)
|
||||
console.log('🔍 Fetching bookmark events from relays:', relayUrls)
|
||||
// Try local-first quickly, then full set fallback
|
||||
let rawEvents = [] as NostrEvent[]
|
||||
const localRelays = relayUrls.filter(url => url.includes('localhost') || url.includes('127.0.0.1'))
|
||||
if (localRelays.length > 0) {
|
||||
try {
|
||||
rawEvents = await lastValueFrom(
|
||||
@@ -49,12 +49,17 @@ export const fetchBookmarks = async (
|
||||
rawEvents = []
|
||||
}
|
||||
}
|
||||
if (rawEvents.length === 0) {
|
||||
rawEvents = await lastValueFrom(
|
||||
relayPool
|
||||
.req(relayUrls, { kinds: [10003, 30003, 30001, 39701], authors: [activeAccount.pubkey] })
|
||||
.pipe(completeOnEose(), takeUntil(timer(6000)), toArray())
|
||||
)
|
||||
if (remoteRelays.length > 0) {
|
||||
try {
|
||||
const remoteEvents = await lastValueFrom(
|
||||
relayPool
|
||||
.req(remoteRelays, { kinds: [10003, 30003, 30001, 39701], authors: [activeAccount.pubkey] })
|
||||
.pipe(completeOnEose(), takeUntil(timer(6000)), toArray())
|
||||
)
|
||||
rawEvents = rawEvents.concat(remoteEvents)
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
console.log('📊 Raw events fetched:', rawEvents.length, 'events')
|
||||
|
||||
@@ -119,11 +124,31 @@ export const fetchBookmarks = async (
|
||||
let idToEvent: Map<string, NostrEvent> = new Map()
|
||||
if (noteIds.length > 0) {
|
||||
try {
|
||||
const events = await lastValueFrom(
|
||||
relayPool
|
||||
.req(relayUrls, { ids: noteIds })
|
||||
.pipe(completeOnEose(), takeUntil(timer(4000)), toArray())
|
||||
)
|
||||
const { local: localHydrate, remote: remoteHydrate } = partitionRelays(relayUrls)
|
||||
let events: NostrEvent[] = []
|
||||
if (localHydrate.length > 0) {
|
||||
try {
|
||||
events = await lastValueFrom(
|
||||
relayPool
|
||||
.req(localHydrate, { ids: noteIds })
|
||||
.pipe(completeOnEose(), takeUntil(timer(800)), toArray())
|
||||
)
|
||||
} catch {
|
||||
events = []
|
||||
}
|
||||
}
|
||||
if (remoteHydrate.length > 0) {
|
||||
try {
|
||||
const remote = await lastValueFrom(
|
||||
relayPool
|
||||
.req(remoteHydrate, { ids: noteIds })
|
||||
.pipe(completeOnEose(), takeUntil(timer(2500)), toArray())
|
||||
)
|
||||
events = events.concat(remote)
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
idToEvent = new Map(events.map((e: NostrEvent) => [e.id, e]))
|
||||
} catch (error) {
|
||||
console.warn('Failed to fetch events for hydration:', error)
|
||||
|
||||
@@ -251,29 +251,64 @@ export const fetchHighlights = async (
|
||||
): Promise<Highlight[]> => {
|
||||
try {
|
||||
const relayUrls = Array.from(relayPool.relays.values()).map(relay => relay.url)
|
||||
const ordered = prioritizeLocalRelays(relayUrls)
|
||||
const { local: localRelays, remote: remoteRelays } = partitionRelays(ordered)
|
||||
|
||||
console.log('🔍 Fetching highlights (kind 9802) by author:', pubkey)
|
||||
|
||||
const seenIds = new Set<string>()
|
||||
const rawEvents = await lastValueFrom(
|
||||
relayPool
|
||||
.req(relayUrls, { kinds: [9802], authors: [pubkey] })
|
||||
.pipe(
|
||||
onlyEvents(),
|
||||
tap((event: NostrEvent) => {
|
||||
if (!seenIds.has(event.id)) {
|
||||
seenIds.add(event.id)
|
||||
const highlight = eventToHighlight(event)
|
||||
if (onHighlight) {
|
||||
onHighlight(highlight)
|
||||
}
|
||||
}
|
||||
}),
|
||||
completeOnEose(),
|
||||
takeUntil(timer(10000)),
|
||||
toArray()
|
||||
let rawEvents: NostrEvent[] = []
|
||||
if (localRelays.length > 0) {
|
||||
try {
|
||||
rawEvents = await lastValueFrom(
|
||||
relayPool
|
||||
.req(localRelays, { kinds: [9802], authors: [pubkey] })
|
||||
.pipe(
|
||||
onlyEvents(),
|
||||
tap((event: NostrEvent) => {
|
||||
if (!seenIds.has(event.id)) {
|
||||
seenIds.add(event.id)
|
||||
const highlight = eventToHighlight(event)
|
||||
if (onHighlight) {
|
||||
onHighlight(highlight)
|
||||
}
|
||||
}
|
||||
}),
|
||||
completeOnEose(),
|
||||
takeUntil(timer(1200)),
|
||||
toArray()
|
||||
)
|
||||
)
|
||||
)
|
||||
} catch {
|
||||
rawEvents = []
|
||||
}
|
||||
}
|
||||
if (remoteRelays.length > 0) {
|
||||
try {
|
||||
const remoteEvents = await lastValueFrom(
|
||||
relayPool
|
||||
.req(remoteRelays, { kinds: [9802], authors: [pubkey] })
|
||||
.pipe(
|
||||
onlyEvents(),
|
||||
tap((event: NostrEvent) => {
|
||||
if (!seenIds.has(event.id)) {
|
||||
seenIds.add(event.id)
|
||||
const highlight = eventToHighlight(event)
|
||||
if (onHighlight) {
|
||||
onHighlight(highlight)
|
||||
}
|
||||
}
|
||||
}),
|
||||
completeOnEose(),
|
||||
takeUntil(timer(6000)),
|
||||
toArray()
|
||||
)
|
||||
)
|
||||
rawEvents = rawEvents.concat(remoteEvents)
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
console.log('📊 Raw highlight events fetched:', rawEvents.length)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user