Refactor to use NDK instead of custom relay and metadata controllers #89
@ -33,12 +33,8 @@ type FetchModsOptions = {
|
||||
interface NDKContextType {
|
||||
ndk: NDK
|
||||
fetchMods: (opts: FetchModsOptions) => Promise<ModDetails[]>
|
||||
fetchEvents: (filter: NDKFilter, relayUrls?: string[]) => Promise<NDKEvent[]>
|
||||
fetchEvent: (
|
||||
filter: NDKFilter,
|
||||
relayUrls?: string[]
|
||||
) => Promise<NDKEvent | null>
|
||||
|
||||
fetchEvents: (filter: NDKFilter) => Promise<NDKEvent[]>
|
||||
fetchEvent: (filter: NDKFilter) => Promise<NDKEvent | null>
|
||||
fetchEventsFromUserRelays: (
|
||||
filter: NDKFilter,
|
||||
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(() => {
|
||||
localStorage.setItem('debug', '*')
|
||||
const dexieAdapter = new NDKCacheAdapterDexie({ dbName: 'degmod-db' })
|
||||
@ -88,6 +109,7 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
],
|
||||
cacheAdapter: dexieAdapter
|
||||
})
|
||||
addAdminRelays(ndk)
|
||||
|
||||
ndk.connect()
|
||||
|
||||
@ -110,33 +132,6 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
since,
|
||||
limit
|
||||
}: 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
|
||||
const filter: NDKFilter = {
|
||||
kinds: [NDKKind.Classified], // Specify the kind of events to fetch
|
||||
@ -152,11 +147,10 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
}
|
||||
|
||||
return ndk
|
||||
.fetchEvents(
|
||||
filter,
|
||||
{ closeOnEose: true, cacheUsage: NDKSubscriptionCacheUsage.PARALLEL },
|
||||
NDKRelaySet.fromRelayUrls(Array.from(relays), ndk, true)
|
||||
)
|
||||
.fetchEvents(filter, {
|
||||
closeOnEose: true,
|
||||
cacheUsage: NDKSubscriptionCacheUsage.PARALLEL
|
||||
})
|
||||
.then((ndkEventSet) => {
|
||||
const ndkEvents = Array.from(ndkEventSet)
|
||||
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.
|
||||
* If no relays are specified, it defaults to using connected relays.
|
||||
* Asynchronously retrieves multiple event based on a provided filter.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
const fetchEvents = async (
|
||||
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)
|
||||
|
||||
const fetchEvents = async (filter: NDKFilter): Promise<NDKEvent[]> => {
|
||||
return ndk
|
||||
.fetchEvents(
|
||||
filter,
|
||||
{ closeOnEose: true, cacheUsage: NDKSubscriptionCacheUsage.PARALLEL },
|
||||
NDKRelaySet.fromRelayUrls(Array.from(relays), ndk, true)
|
||||
)
|
||||
.fetchEvents(filter, {
|
||||
closeOnEose: true,
|
||||
cacheUsage: NDKSubscriptionCacheUsage.PARALLEL
|
||||
})
|
||||
.then((ndkEventSet) => {
|
||||
const ndkEvents = Array.from(ndkEventSet)
|
||||
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.
|
||||
* If no relays are specified, it defaults to using connected relays.
|
||||
* Asynchronously retrieves an event based on a provided filter.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
const fetchEvent = async (filter: NDKFilter, relayUrls: string[] = []) => {
|
||||
const events = await fetchEvents(filter, relayUrls)
|
||||
const fetchEvent = async (filter: NDKFilter) => {
|
||||
const events = await fetchEvents(filter)
|
||||
if (events.length === 0) return null
|
||||
return events[0]
|
||||
}
|
||||
@ -285,8 +238,22 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
return [] as string[]
|
||||
})
|
||||
|
||||
// Fetch the event from the user's relays using the provided filter and relay URLs
|
||||
return fetchEvents(filter, relayUrls)
|
||||
return ndk
|
||||
.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
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -372,75 +372,6 @@ export class RelayController {
|
||||
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 (
|
||||
user: string,
|
||||
eTag: string,
|
||||
|
@ -392,7 +392,7 @@ const UsersResult = ({
|
||||
}
|
||||
|
||||
setIsFetching(true)
|
||||
fetchEvents(filter, ['wss://purplepag.es', 'wss://user.kindpag.es'])
|
||||
fetchEvents(filter)
|
||||
.then((events) => {
|
||||
const results = events.map((event) => {
|
||||
const ndkEvent = new NDKEvent(undefined, event)
|
||||
|
Loading…
Reference in New Issue
Block a user