fix: add admin relays to ndk explicit relays asyncronously

This commit is contained in:
daniyal 2024-10-21 13:22:52 +05:00
parent 610801a674
commit 59f4fd6b29
3 changed files with 58 additions and 160 deletions

View File

@ -33,12 +33,8 @@ type FetchModsOptions = {
interface NDKContextType { interface NDKContextType {
ndk: NDK ndk: NDK
fetchMods: (opts: FetchModsOptions) => Promise<ModDetails[]> fetchMods: (opts: FetchModsOptions) => Promise<ModDetails[]>
fetchEvents: (filter: NDKFilter, relayUrls?: string[]) => Promise<NDKEvent[]> fetchEvents: (filter: NDKFilter) => Promise<NDKEvent[]>
fetchEvent: ( fetchEvent: (filter: NDKFilter) => Promise<NDKEvent | null>
filter: NDKFilter,
relayUrls?: string[]
) => Promise<NDKEvent | null>
fetchEventsFromUserRelays: ( fetchEventsFromUserRelays: (
filter: NDKFilter, filter: NDKFilter,
hexKey: string, hexKey: string,
@ -72,6 +68,31 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
} }
}, []) }, [])
const addAdminRelays = async (ndk: NDK) => {
const adminNpubs = import.meta.env.VITE_ADMIN_NPUBS.split(',')
adminNpubs.forEach((npub) => {
const hexKey = npubToHex(npub)
if (hexKey) {
getRelayListForUser(hexKey, ndk)
.then((ndkRelayList) => {
if (ndkRelayList) {
ndkRelayList.bothRelayUrls.forEach((url) =>
ndk.addExplicitRelay(url)
)
}
})
.catch((err) => {
log(
true,
LogType.Error,
`❌ Error occurred in getting the instance of NDKRelayList for npub: ${npub}`,
err
)
})
}
})
}
const ndk = useMemo(() => { const ndk = useMemo(() => {
localStorage.setItem('debug', '*') localStorage.setItem('debug', '*')
const dexieAdapter = new NDKCacheAdapterDexie({ dbName: 'degmod-db' }) const dexieAdapter = new NDKCacheAdapterDexie({ dbName: 'degmod-db' })
@ -88,6 +109,7 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
], ],
cacheAdapter: dexieAdapter cacheAdapter: dexieAdapter
}) })
addAdminRelays(ndk)
ndk.connect() ndk.connect()
@ -110,33 +132,6 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
since, since,
limit limit
}: FetchModsOptions): Promise<ModDetails[]> => { }: FetchModsOptions): Promise<ModDetails[]> => {
const relays = new Set<string>()
relays.add(import.meta.env.VITE_APP_RELAY)
const adminNpubs = import.meta.env.VITE_ADMIN_NPUBS.split(',')
const promises = adminNpubs.map((npub) => {
const hexKey = npubToHex(npub)
if (!hexKey) return null
return getRelayListForUser(hexKey, ndk)
.then((ndkRelayList) => {
if (ndkRelayList) {
ndkRelayList.writeRelayUrls.forEach((url) => relays.add(url))
}
})
.catch((err) => {
log(
true,
LogType.Error,
`❌ Error occurred in getting the instance of NDKRelayList for npub: ${npub}`,
err
)
})
})
await Promise.allSettled(promises)
// Define the filter criteria for fetching mods // Define the filter criteria for fetching mods
const filter: NDKFilter = { const filter: NDKFilter = {
kinds: [NDKKind.Classified], // Specify the kind of events to fetch kinds: [NDKKind.Classified], // Specify the kind of events to fetch
@ -152,11 +147,10 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
} }
return ndk return ndk
.fetchEvents( .fetchEvents(filter, {
filter, closeOnEose: true,
{ closeOnEose: true, cacheUsage: NDKSubscriptionCacheUsage.PARALLEL }, cacheUsage: NDKSubscriptionCacheUsage.PARALLEL
NDKRelaySet.fromRelayUrls(Array.from(relays), ndk, true) })
)
.then((ndkEventSet) => { .then((ndkEventSet) => {
const ndkEvents = Array.from(ndkEventSet) const ndkEvents = Array.from(ndkEventSet)
orderEventsChronologically(ndkEvents) orderEventsChronologically(ndkEvents)
@ -179,56 +173,17 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
} }
/** /**
* Asynchronously retrieves multiple event from a set of relays based on a provided filter. * Asynchronously retrieves multiple event based on a provided filter.
* If no relays are specified, it defaults to using connected relays.
* *
* @param filter - The filter criteria to find the event. * @param filter - The filter criteria to find the event.
* @param relays - An optional array of relay URLs to search for the event.
* @returns Returns a promise that resolves to the found event or null if not found. * @returns Returns a promise that resolves to the found event or null if not found.
*/ */
const fetchEvents = async ( const fetchEvents = async (filter: NDKFilter): Promise<NDKEvent[]> => {
filter: NDKFilter,
relayUrls: string[] = []
): Promise<NDKEvent[]> => {
const relays = new Set<string>()
// add all the relays passed to relay set
relayUrls.forEach((relayUrl) => {
relays.add(relayUrl)
})
relays.add(import.meta.env.VITE_APP_RELAY)
const adminNpubs = import.meta.env.VITE_ADMIN_NPUBS.split(',')
const promises = adminNpubs.map((npub) => {
const hexKey = npubToHex(npub)
if (!hexKey) return null
return getRelayListForUser(hexKey, ndk)
.then((ndkRelayList) => {
if (ndkRelayList) {
ndkRelayList.writeRelayUrls.forEach((url) => relays.add(url))
}
})
.catch((err) => {
log(
true,
LogType.Error,
`❌ Error occurred in getting the instance of NDKRelayList for npub: ${npub}`,
err
)
})
})
await Promise.allSettled(promises)
return ndk return ndk
.fetchEvents( .fetchEvents(filter, {
filter, closeOnEose: true,
{ closeOnEose: true, cacheUsage: NDKSubscriptionCacheUsage.PARALLEL }, cacheUsage: NDKSubscriptionCacheUsage.PARALLEL
NDKRelaySet.fromRelayUrls(Array.from(relays), ndk, true) })
)
.then((ndkEventSet) => { .then((ndkEventSet) => {
const ndkEvents = Array.from(ndkEventSet) const ndkEvents = Array.from(ndkEventSet)
return orderEventsChronologically(ndkEvents) return orderEventsChronologically(ndkEvents)
@ -242,15 +197,13 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
} }
/** /**
* Asynchronously retrieves an event from a set of relays based on a provided filter. * Asynchronously retrieves an event based on a provided filter.
* If no relays are specified, it defaults to using connected relays.
* *
* @param filter - The filter criteria to find the event. * @param filter - The filter criteria to find the event.
* @param relaysUrls - An optional array of relay URLs to search for the event.
* @returns Returns a promise that resolves to the found event or null if not found. * @returns Returns a promise that resolves to the found event or null if not found.
*/ */
const fetchEvent = async (filter: NDKFilter, relayUrls: string[] = []) => { const fetchEvent = async (filter: NDKFilter) => {
const events = await fetchEvents(filter, relayUrls) const events = await fetchEvents(filter)
if (events.length === 0) return null if (events.length === 0) return null
return events[0] return events[0]
} }
@ -285,8 +238,22 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
return [] as string[] return [] as string[]
}) })
// Fetch the event from the user's relays using the provided filter and relay URLs return ndk
return fetchEvents(filter, relayUrls) .fetchEvents(
filter,
{ closeOnEose: true, cacheUsage: NDKSubscriptionCacheUsage.PARALLEL },
NDKRelaySet.fromRelayUrls(relayUrls, ndk, true)
)
.then((ndkEventSet) => {
const ndkEvents = Array.from(ndkEventSet)
return orderEventsChronologically(ndkEvents)
})
.catch((err) => {
// Log the error and show a notification if fetching fails
log(true, LogType.Error, 'An error occurred in fetching events', err)
toast.error('An error occurred in fetching events') // Show error notification
return [] // Return an empty array in case of an error
})
} }
/** /**

View File

@ -372,75 +372,6 @@ export class RelayController {
return publishedOnRelays return publishedOnRelays
} }
/**
* Subscribes to events from multiple relays.
*
* This method connects to the specified relay URLs and subscribes to events
* using the provided filter. It handles incoming events through the given
* `eventHandler` callback and manages the subscription lifecycle.
*
* @param filter - The filter criteria to apply when subscribing to events.
* @param relayUrls - An optional array of relay URLs to connect to. The default relay URL (`APP_RELAY`) is added automatically.
* @param eventHandler - A callback function to handle incoming events. It receives an `Event` object.
*
*/
subscribeForEvents = async (
filter: Filter,
relayUrls: string[] = [],
eventHandler: (event: Event) => void
) => {
const appRelay = import.meta.env.VITE_APP_RELAY
if (!relayUrls.includes(appRelay)) {
/**
* NOTE: To avoid side-effects on external relayUrls array passed as argument
* re-assigned relayUrls with added sigit relay instead of just appending to same array
*/
relayUrls = [...relayUrls, appRelay] // Add app relay to relays array if not exists already
}
// connect to all specified relays
const relayPromises = relayUrls.map((relayUrl) =>
this.connectRelay(relayUrl)
)
// Use Promise.allSettled to wait for all promises to settle
const results = await Promise.allSettled(relayPromises)
// Extract non-null values from fulfilled promises in a single pass
const relays = results.reduce<Relay[]>((acc, result) => {
if (result.status === 'fulfilled') {
const value = result.value
if (value) {
acc.push(value)
}
}
return acc
}, [])
// Check if any relays are connected
if (relays.length === 0) {
throw new Error('No relay is connected to fetch events!')
}
const processedEvents: string[] = [] // To keep track of processed events
// Create a promise for each relay subscription
const subscriptions = relays.map((relay) =>
relay.subscribe([filter], {
// Handle incoming events
onevent: (e) => {
// Process event only if it hasn't been processed before
if (!processedEvents.includes(e.id)) {
processedEvents.push(e.id)
eventHandler(e) // Call the event handler with the event
}
}
})
)
return subscriptions
}
getTotalZapAmount = async ( getTotalZapAmount = async (
user: string, user: string,
eTag: string, eTag: string,

View File

@ -392,7 +392,7 @@ const UsersResult = ({
} }
setIsFetching(true) setIsFetching(true)
fetchEvents(filter, ['wss://purplepag.es', 'wss://user.kindpag.es']) fetchEvents(filter)
.then((events) => { .then((events) => {
const results = events.map((event) => { const results = events.map((event) => {
const ndkEvent = new NDKEvent(undefined, event) const ndkEvent = new NDKEvent(undefined, event)