0d93e16f3a
All checks were successful
Open PR on Staging / audit_and_check (pull_request) Successful in 41s
300 lines
8.9 KiB
TypeScript
300 lines
8.9 KiB
TypeScript
import NDK, {
|
|
getRelayListForUser,
|
|
Hexpubkey,
|
|
NDKEvent,
|
|
NDKFilter,
|
|
NDKRelayList,
|
|
NDKRelaySet,
|
|
NDKSubscriptionCacheUsage,
|
|
NDKSubscriptionOptions,
|
|
NDKUser,
|
|
NDKUserProfile
|
|
} from '@nostr-dev-kit/ndk'
|
|
|
|
import NDKCacheAdapterDexie from '@nostr-dev-kit/ndk-cache-dexie'
|
|
|
|
import { Dexie } from 'dexie'
|
|
import { createContext, ReactNode, useEffect, useMemo } from 'react'
|
|
import { toast } from 'react-toastify'
|
|
import { UserRelaysType } from '../types'
|
|
import {
|
|
DEFAULT_LOOK_UP_RELAY_LIST,
|
|
hexToNpub,
|
|
orderEventsChronologically,
|
|
SIGIT_RELAY,
|
|
timeout
|
|
} from '../utils'
|
|
|
|
export interface NDKContextType {
|
|
ndk: NDK
|
|
fetchEvents: (
|
|
filter: NDKFilter,
|
|
opts?: NDKSubscriptionOptions
|
|
) => Promise<NDKEvent[]>
|
|
fetchEvent: (
|
|
filter: NDKFilter,
|
|
opts?: NDKSubscriptionOptions
|
|
) => Promise<NDKEvent | null>
|
|
fetchEventsFromUserRelays: (
|
|
filter: NDKFilter | NDKFilter[],
|
|
hexKey: string,
|
|
userRelaysType: UserRelaysType,
|
|
opts?: NDKSubscriptionOptions
|
|
) => Promise<NDKEvent[]>
|
|
fetchEventFromUserRelays: (
|
|
filter: NDKFilter | NDKFilter[],
|
|
hexKey: string,
|
|
userRelaysType: UserRelaysType,
|
|
opts?: NDKSubscriptionOptions
|
|
) => Promise<NDKEvent | null>
|
|
findMetadata: (
|
|
pubkey: string,
|
|
opts?: NDKSubscriptionOptions
|
|
) => Promise<NDKUserProfile | null>
|
|
getNDKRelayList: (pubkey: Hexpubkey) => Promise<NDKRelayList>
|
|
publish: (event: NDKEvent, explicitRelayUrls?: string[]) => Promise<string[]>
|
|
}
|
|
|
|
// Create the context with an initial value of `null`
|
|
export const NDKContext = createContext<NDKContextType | null>(null)
|
|
|
|
// Create a provider component to wrap around parts of your app
|
|
export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
|
|
useEffect(() => {
|
|
window.onunhandledrejection = async (event: PromiseRejectionEvent) => {
|
|
event.preventDefault()
|
|
if (event.reason?.name === Dexie.errnames.DatabaseClosed) {
|
|
console.log(
|
|
'Could not open Dexie DB, probably version change. Deleting old DB and reloading...'
|
|
)
|
|
await Dexie.delete('degmod-db')
|
|
// Must reload to open a brand new DB
|
|
window.location.reload()
|
|
}
|
|
}
|
|
}, [])
|
|
|
|
const ndk = useMemo(() => {
|
|
if (import.meta.env.MODE === 'development') {
|
|
localStorage.setItem('debug', '*')
|
|
}
|
|
const dexieAdapter = new NDKCacheAdapterDexie({ dbName: 'sigit-db' })
|
|
dexieAdapter.locking = true
|
|
const ndk = new NDK({
|
|
enableOutboxModel: true,
|
|
autoConnectUserRelays: true,
|
|
autoFetchUserMutelist: true,
|
|
explicitRelayUrls: [...DEFAULT_LOOK_UP_RELAY_LIST],
|
|
cacheAdapter: dexieAdapter
|
|
})
|
|
ndk.connect()
|
|
|
|
return ndk
|
|
}, [])
|
|
|
|
/**
|
|
* Asynchronously retrieves multiple event based on a provided filter.
|
|
*
|
|
* @param filter - The filter criteria to find the event.
|
|
* @returns Returns a promise that resolves to the found event or null if not found.
|
|
*/
|
|
const fetchEvents = async (
|
|
filter: NDKFilter,
|
|
opts?: NDKSubscriptionOptions
|
|
): Promise<NDKEvent[]> => {
|
|
return ndk
|
|
.fetchEvents(filter, {
|
|
closeOnEose: true,
|
|
cacheUsage: NDKSubscriptionCacheUsage.PARALLEL,
|
|
...opts
|
|
})
|
|
.then((ndkEventSet) => {
|
|
const ndkEvents = Array.from(ndkEventSet)
|
|
return orderEventsChronologically(ndkEvents)
|
|
})
|
|
.catch((err) => {
|
|
// Log the error and show a notification if fetching fails
|
|
console.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
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Asynchronously retrieves an event based on a provided filter.
|
|
*
|
|
* @param filter - The filter criteria to find the event.
|
|
* @returns Returns a promise that resolves to the found event or null if not found.
|
|
*/
|
|
const fetchEvent = async (
|
|
filter: NDKFilter,
|
|
opts?: NDKSubscriptionOptions
|
|
) => {
|
|
const events = await fetchEvents(filter, opts)
|
|
if (events.length === 0) return null
|
|
return events[0]
|
|
}
|
|
|
|
/**
|
|
* Asynchronously retrieves multiple events from the user's relays based on a specified filter.
|
|
* The function first retrieves the user's relays, and then fetches the events using the provided filter.
|
|
*
|
|
* @param filter - The event filter to use when fetching the event (e.g., kinds, authors).
|
|
* @param hexKey - The hexadecimal representation of the user's public key.
|
|
* @param userRelaysType - The type of relays to search (e.g., write, read).
|
|
* @returns A promise that resolves with an array of events.
|
|
*/
|
|
const fetchEventsFromUserRelays = async (
|
|
filter: NDKFilter | NDKFilter[],
|
|
hexKey: string,
|
|
userRelaysType: UserRelaysType,
|
|
opts?: NDKSubscriptionOptions
|
|
): Promise<NDKEvent[]> => {
|
|
// Find the user's relays (10s timeout).
|
|
const relayUrls = await Promise.race([
|
|
getRelayListForUser(hexKey, ndk),
|
|
timeout(3000)
|
|
])
|
|
.then((ndkRelayList) => {
|
|
if (ndkRelayList) return ndkRelayList[userRelaysType]
|
|
return [] // Return an empty array if ndkRelayList is undefined
|
|
})
|
|
.catch((err) => {
|
|
console.error(
|
|
`An error occurred in fetching user's (${hexKey}) ${userRelaysType}`,
|
|
err
|
|
)
|
|
return [] as string[]
|
|
})
|
|
|
|
if (!relayUrls.includes(SIGIT_RELAY)) {
|
|
relayUrls.push(SIGIT_RELAY)
|
|
}
|
|
|
|
return ndk
|
|
.fetchEvents(
|
|
filter,
|
|
{
|
|
closeOnEose: true,
|
|
cacheUsage: NDKSubscriptionCacheUsage.PARALLEL,
|
|
...opts
|
|
},
|
|
relayUrls.length
|
|
? NDKRelaySet.fromRelayUrls(relayUrls, ndk, true)
|
|
: undefined
|
|
)
|
|
.then((ndkEventSet) => {
|
|
const ndkEvents = Array.from(ndkEventSet)
|
|
return orderEventsChronologically(ndkEvents)
|
|
})
|
|
.catch((err) => {
|
|
// Log the error and show a notification if fetching fails
|
|
console.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
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Fetches an event from the user's relays based on a specified filter.
|
|
* The function first retrieves the user's relays, and then fetches the event using the provided filter.
|
|
*
|
|
* @param filter - The event filter to use when fetching the event (e.g., kinds, authors).
|
|
* @param hexKey - The hexadecimal representation of the user's public key.
|
|
* @param userRelaysType - The type of relays to search (e.g., write, read).
|
|
* @returns A promise that resolves to the fetched event or null if the operation fails.
|
|
*/
|
|
const fetchEventFromUserRelays = async (
|
|
filter: NDKFilter | NDKFilter[],
|
|
hexKey: string,
|
|
userRelaysType: UserRelaysType,
|
|
opts?: NDKSubscriptionOptions
|
|
) => {
|
|
const events = await fetchEventsFromUserRelays(
|
|
filter,
|
|
hexKey,
|
|
userRelaysType,
|
|
opts
|
|
)
|
|
if (events.length === 0) return null
|
|
return events[0]
|
|
}
|
|
|
|
/**
|
|
* Finds metadata for a given pubkey.
|
|
*
|
|
* @param hexKey - The pubkey to search for metadata.
|
|
* @returns A promise that resolves to the metadata event.
|
|
*/
|
|
const findMetadata = async (
|
|
pubkey: string,
|
|
opts?: NDKSubscriptionOptions
|
|
): Promise<NDKUserProfile | null> => {
|
|
const npub = hexToNpub(pubkey)
|
|
|
|
const user = new NDKUser({ npub })
|
|
user.ndk = ndk
|
|
|
|
return await user.fetchProfile({
|
|
cacheUsage: NDKSubscriptionCacheUsage.PARALLEL,
|
|
...(opts || {})
|
|
})
|
|
}
|
|
|
|
const getNDKRelayList = async (pubkey: Hexpubkey) => {
|
|
const ndkRelayList = await Promise.race([
|
|
getRelayListForUser(pubkey, ndk),
|
|
timeout(10000)
|
|
]).catch(() => {
|
|
const relayList = new NDKRelayList(ndk)
|
|
relayList.bothRelayUrls = [SIGIT_RELAY]
|
|
return relayList
|
|
})
|
|
|
|
return ndkRelayList
|
|
}
|
|
|
|
const publish = async (
|
|
event: NDKEvent,
|
|
explicitRelayUrls?: string[]
|
|
): Promise<string[]> => {
|
|
if (!event.sig) throw new Error('Before publishing first sign the event!')
|
|
|
|
let ndkRelaySet: NDKRelaySet | undefined
|
|
|
|
if (explicitRelayUrls && explicitRelayUrls.length > 0) {
|
|
if (!explicitRelayUrls.includes(SIGIT_RELAY)) {
|
|
explicitRelayUrls = [...explicitRelayUrls, SIGIT_RELAY]
|
|
}
|
|
ndkRelaySet = NDKRelaySet.fromRelayUrls(explicitRelayUrls, ndk)
|
|
}
|
|
|
|
return await Promise.race([event.publish(ndkRelaySet), timeout(3000)])
|
|
.then((res) => {
|
|
const relaysPublishedOn = Array.from(res)
|
|
return relaysPublishedOn.map((relay) => relay.url)
|
|
})
|
|
.catch((err) => {
|
|
console.error(`An error occurred in publishing event`, err)
|
|
return []
|
|
})
|
|
}
|
|
|
|
return (
|
|
<NDKContext.Provider
|
|
value={{
|
|
ndk,
|
|
fetchEvents,
|
|
fetchEvent,
|
|
fetchEventsFromUserRelays,
|
|
fetchEventFromUserRelays,
|
|
findMetadata,
|
|
getNDKRelayList,
|
|
publish
|
|
}}
|
|
>
|
|
{children}
|
|
</NDKContext.Provider>
|
|
)
|
|
}
|