mirror of
https://github.com/dergigi/boris.git
synced 2025-12-17 14:44:26 +01:00
fix: simplify eventManager and restore working event fetching
- Revert eventManager to simpler role: initialization and service coordination - Restore original working fetching logic in useEventLoader - eventManager now provides: getCachedEvent, getEventLoader, setServices - Fixes broken bookmark hydration and direct event loading - Uses eventManager for cache checking but direct subscription for fetching
This commit is contained in:
@@ -55,18 +55,40 @@ export function useEventLoader({
|
||||
useEffect(() => {
|
||||
if (!eventId) return
|
||||
|
||||
// Try to get from event store first (check cache synchronously)
|
||||
const cachedEvent = eventManager.getCachedEvent(eventId)
|
||||
if (cachedEvent) {
|
||||
displayEvent(cachedEvent)
|
||||
setReaderLoading(false)
|
||||
setIsCollapsed(false)
|
||||
setSelectedUrl('')
|
||||
return
|
||||
}
|
||||
|
||||
// Event not in cache, set loading state and fetch from relays
|
||||
setReaderLoading(true)
|
||||
setReaderContent(undefined)
|
||||
setSelectedUrl('') // Don't set nostr: URL to avoid showing highlights
|
||||
setIsCollapsed(false)
|
||||
|
||||
// Fetch event using the event manager
|
||||
eventManager.fetchEvent(eventId).then(
|
||||
(event) => {
|
||||
// If no relay pool yet, wait for it (will re-run when relayPool changes)
|
||||
if (!relayPool) {
|
||||
return
|
||||
}
|
||||
|
||||
// Fetch from relays using event manager's loader
|
||||
const eventLoader = eventManager.getEventLoader()
|
||||
if (!eventLoader) {
|
||||
setReaderLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
const subscription = eventLoader({ id: eventId }).subscribe({
|
||||
next: (event) => {
|
||||
displayEvent(event)
|
||||
setReaderLoading(false)
|
||||
},
|
||||
(err) => {
|
||||
error: (err) => {
|
||||
const errorContent: ReadableContent = {
|
||||
url: '',
|
||||
html: `<div style="padding: 1rem; color: var(--color-error, red);">Failed to load event: ${err instanceof Error ? err.message : 'Unknown error'}</div>`,
|
||||
@@ -75,6 +97,8 @@ export function useEventLoader({
|
||||
setReaderContent(errorContent)
|
||||
setReaderLoading(false)
|
||||
}
|
||||
)
|
||||
}, [eventId, displayEvent, setReaderLoading, setSelectedUrl, setIsCollapsed, setReaderContent])
|
||||
})
|
||||
|
||||
return () => subscription.unsubscribe()
|
||||
}, [eventId, relayPool, displayEvent, setReaderLoading, setSelectedUrl, setIsCollapsed, setReaderContent])
|
||||
}
|
||||
|
||||
@@ -2,26 +2,17 @@ import { RelayPool } from 'applesauce-relay'
|
||||
import { IEventStore } from 'applesauce-core'
|
||||
import { createEventLoader } from 'applesauce-loaders/loaders'
|
||||
import { NostrEvent } from 'nostr-tools'
|
||||
import { BehaviorSubject, Observable } from 'rxjs'
|
||||
|
||||
type EventCallback = (event: NostrEvent) => void
|
||||
type ErrorCallback = (error: Error) => void
|
||||
import { Observable } from 'rxjs'
|
||||
|
||||
/**
|
||||
* Centralized event manager for fetching and caching events
|
||||
* Handles deduplication of requests and provides a single source of truth
|
||||
* Centralized event manager for event fetching coordination
|
||||
* Manages initialization and provides utilities for event loading
|
||||
*/
|
||||
class EventManager {
|
||||
private eventStore: IEventStore | null = null
|
||||
private relayPool: RelayPool | null = null
|
||||
private eventLoader: ReturnType<typeof createEventLoader> | null = null
|
||||
|
||||
// Track pending requests to avoid duplicates
|
||||
private pendingRequests = new Map<string, Array<{ onSuccess: EventCallback; onError: ErrorCallback }>>()
|
||||
|
||||
// Event stream for real-time updates
|
||||
private eventSubject = new BehaviorSubject<NostrEvent | null>(null)
|
||||
|
||||
/**
|
||||
* Initialize the event manager with event store and relay pool
|
||||
*/
|
||||
@@ -29,7 +20,8 @@ class EventManager {
|
||||
this.eventStore = eventStore
|
||||
this.relayPool = relayPool
|
||||
|
||||
if (relayPool && this.eventLoader === null) {
|
||||
// Recreate loader when services change
|
||||
if (relayPool) {
|
||||
this.eventLoader = createEventLoader(relayPool, {
|
||||
eventStore: eventStore || undefined
|
||||
})
|
||||
@@ -37,98 +29,40 @@ class EventManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch an event by ID, with automatic deduplication and caching
|
||||
* Get the event loader for fetching events
|
||||
*/
|
||||
async fetchEvent(eventId: string): Promise<NostrEvent> {
|
||||
// Check cache first
|
||||
if (this.eventStore) {
|
||||
const cached = this.eventStore.getEvent(eventId)
|
||||
if (cached) {
|
||||
return cached
|
||||
}
|
||||
}
|
||||
|
||||
// Return a promise that will be resolved when the event is fetched
|
||||
return new Promise((resolve, reject) => {
|
||||
this.fetchEventAsync(eventId, resolve, reject)
|
||||
})
|
||||
getEventLoader(): ReturnType<typeof createEventLoader> | null {
|
||||
return this.eventLoader
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to event fetching with callbacks
|
||||
* Get the event store
|
||||
*/
|
||||
private fetchEventAsync(
|
||||
eventId: string,
|
||||
onSuccess: EventCallback,
|
||||
onError: ErrorCallback
|
||||
): void {
|
||||
// Check if we're already fetching this event
|
||||
if (this.pendingRequests.has(eventId)) {
|
||||
// Add to existing request queue
|
||||
this.pendingRequests.get(eventId)!.push({ onSuccess, onError })
|
||||
return
|
||||
}
|
||||
|
||||
// Start a new fetch request
|
||||
this.pendingRequests.set(eventId, [{ onSuccess, onError }])
|
||||
|
||||
// If no relay pool yet, wait for it
|
||||
if (!this.relayPool || !this.eventLoader) {
|
||||
// Will retry when services are set
|
||||
setTimeout(() => {
|
||||
// Retry if still no pool
|
||||
if (!this.relayPool) {
|
||||
this.retryPendingRequest(eventId)
|
||||
}
|
||||
}, 1000)
|
||||
return
|
||||
}
|
||||
|
||||
const subscription = this.eventLoader({ id: eventId }).subscribe({
|
||||
next: (event: NostrEvent) => {
|
||||
// Call all pending callbacks
|
||||
const callbacks = this.pendingRequests.get(eventId) || []
|
||||
this.pendingRequests.delete(eventId)
|
||||
|
||||
callbacks.forEach(cb => cb.onSuccess(event))
|
||||
|
||||
// Emit to stream
|
||||
this.eventSubject.next(event)
|
||||
|
||||
subscription.unsubscribe()
|
||||
},
|
||||
error: (err: unknown) => {
|
||||
// Call all pending callbacks with error
|
||||
const callbacks = this.pendingRequests.get(eventId) || []
|
||||
this.pendingRequests.delete(eventId)
|
||||
|
||||
const error = err instanceof Error ? err : new Error(String(err))
|
||||
callbacks.forEach(cb => cb.onError(error))
|
||||
|
||||
subscription.unsubscribe()
|
||||
}
|
||||
})
|
||||
getEventStore(): IEventStore | null {
|
||||
return this.eventStore
|
||||
}
|
||||
|
||||
/**
|
||||
* Retry pending requests after delay (useful when relay pool becomes available)
|
||||
* Get the relay pool
|
||||
*/
|
||||
private retryPendingRequest(eventId: string): void {
|
||||
const callbacks = this.pendingRequests.get(eventId)
|
||||
if (!callbacks) return
|
||||
|
||||
// Re-trigger the fetch
|
||||
this.pendingRequests.delete(eventId)
|
||||
if (callbacks.length > 0) {
|
||||
this.fetchEventAsync(eventId, callbacks[0].onSuccess, callbacks[0].onError)
|
||||
}
|
||||
getRelayPool(): RelayPool | null {
|
||||
return this.relayPool
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the event stream for reactive updates
|
||||
* Check if event exists in store and return it if available
|
||||
*/
|
||||
getEventStream(): Observable<NostrEvent | null> {
|
||||
return this.eventSubject.asObservable()
|
||||
getCachedEvent(eventId: string): NostrEvent | null {
|
||||
if (!this.eventStore) return null
|
||||
return this.eventStore.getEvent(eventId) || null
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch event by ID, returning an observable
|
||||
*/
|
||||
fetchEvent(eventId: string): Observable<NostrEvent> | null {
|
||||
if (!this.eventLoader) return null
|
||||
return this.eventLoader({ id: eventId })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user