chore: remove dvm utils file and use useDvm hook instead

This commit is contained in:
daniyal 2024-12-22 21:53:00 +05:00
parent 3615de70ad
commit 0ea6ba0033
4 changed files with 52 additions and 344 deletions

View File

@ -193,70 +193,6 @@ export class RelayController {
return events[0] || null
}
/**
* 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 (`SIGIT_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
) => {
if (!relayUrls.includes(SIGIT_RELAY)) {
/**
* 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, SIGIT_RELAY] // Add app relay to relays array if not exists already
}
// connect to all specified relays
const relays = await settleAllFullfilfedPromises(
relayUrls,
this.connectRelay
)
// 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 subPromises = relays.map((relay) => {
return new Promise<void>((resolve) => {
// Subscribe to the relay with the specified filter
const sub = 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
}
},
// Handle the End-Of-Stream (EOSE) message
oneose: () => {
sub.close() // Close the subscription
resolve() // Resolve the promise when EOSE is received
}
})
})
})
// Wait for all subscriptions to complete
await Promise.allSettled(subPromises)
}
publish = async (
event: Event,
relayUrls: string[] = []

View File

@ -13,15 +13,17 @@ import {
createAndSaveAuthToken,
getAuthToken,
getEmptyMetadataEvent,
getRelayMap,
getRelayMapFromNDKRelayList,
unixNow
} from '../utils'
import { useAppDispatch, useAppSelector } from './store'
import { useNDKContext } from './useNDKContext'
import { useDvm } from './useDvm'
export const useAuth = () => {
const dispatch = useAppDispatch()
const { findMetadata } = useNDKContext()
const { getRelayInfo } = useDvm()
const { findMetadata, getNDKRelayList } = useNDKContext()
const { auth: authState, relays: relaysState } = useAppSelector(
(state) => state
@ -101,23 +103,32 @@ export const useAuth = () => {
})
)
const relayMap = await getRelayMap(pubkey)
const ndkRelayList = await getNDKRelayList(pubkey)
const relays = ndkRelayList.relays
if (Object.keys(relayMap).length < 1) {
if (relays.length < 1) {
// Navigate user to relays page if relay map is empty
return appPrivateRoutes.relays
}
if (
authState.loggedIn &&
!compareObjects(relaysState?.map, relayMap.map)
) {
dispatch(setRelayMapAction(relayMap.map))
getRelayInfo(relays)
const relayMap = getRelayMapFromNDKRelayList(ndkRelayList)
if (authState.loggedIn && !compareObjects(relaysState?.map, relayMap)) {
dispatch(setRelayMapAction(relayMap))
}
return appPrivateRoutes.homePage
},
[dispatch, findMetadata, authState, relaysState]
[
dispatch,
findMetadata,
getNDKRelayList,
getRelayInfo,
authState,
relaysState
]
)
return {

View File

@ -1,98 +0,0 @@
import NDK, { NDKEvent, NDKSubscription } from '@nostr-dev-kit/ndk'
import { EventTemplate } from 'nostr-tools'
import { compareObjects, unixNow } from '.'
import { NostrController, relayController } from '../controllers'
import { setRelayInfoAction } from '../store/actions'
import store from '../store/store'
import { RelayInfoObject } from '../types'
/**
* Sets information about relays into relays.info app state.
* @param relayURIs - relay URIs to get information about
*/
export const getRelayInfo = async (relayURIs: string[]) => {
// initialize job request
const jobEventTemplate: EventTemplate = {
content: '',
created_at: unixNow(),
kind: 68001,
tags: [
['i', `${JSON.stringify(relayURIs)}`],
['j', 'relay-info']
]
}
const nostrController = NostrController.getInstance()
// sign job request event
const jobSignedEvent = await nostrController.signEvent(jobEventTemplate)
const relays = [
'wss://relay.damus.io',
'wss://relay.primal.net',
'wss://relayable.org'
]
// publish job request
await relayController.publish(jobSignedEvent, relays)
console.log('jobSignedEvent :>> ', jobSignedEvent)
const subscribeWithTimeout = (
subscription: NDKSubscription,
timeoutMs: number
): Promise<string> => {
return new Promise((resolve, reject) => {
const eventHandler = (event: NDKEvent) => {
subscription.stop()
resolve(event.content)
}
subscription.on('event', eventHandler)
// Set up a timeout to stop the subscription after a specified time
const timeout = setTimeout(() => {
subscription.stop() // Stop the subscription
reject(new Error('Subscription timed out')) // Reject the promise with a timeout error
}, timeoutMs)
// Handle subscription close event
subscription.on('close', () => clearTimeout(timeout))
})
}
const dvmNDK = new NDK({
explicitRelayUrls: relays
})
await dvmNDK.connect(2000)
// filter for getting DVM job's result
const sub = dvmNDK.subscribe({
kinds: [68002 as number],
'#e': [jobSignedEvent.id],
'#p': [jobSignedEvent.pubkey]
})
// asynchronously get block number from dvm job with 20 seconds timeout
const dvmJobResult = await subscribeWithTimeout(sub, 20000)
if (!dvmJobResult) {
return Promise.reject(`Relay(s) information wasn't received`)
}
let relaysInfo: RelayInfoObject
try {
relaysInfo = JSON.parse(dvmJobResult)
} catch (error) {
return Promise.reject(`Invalid relay(s) information.`)
}
if (
relaysInfo &&
!compareObjects(store.getState().relays?.info, relaysInfo)
) {
store.dispatch(setRelayInfoAction(relaysInfo))
}
}

View File

@ -1,172 +1,43 @@
import { Event, Filter, kinds, UnsignedEvent } from 'nostr-tools'
import { RelayList } from 'nostr-tools/kinds'
import { getRelayInfo, unixNow } from '.'
import { NostrController, relayController } from '../controllers'
import { localCache } from '../services'
import { RelayMap, RelaySet } from '../types'
import {
DEFAULT_LOOK_UP_RELAY_LIST,
ONE_DAY_IN_MS,
ONE_WEEK_IN_MS,
SIGIT_RELAY
} from './const'
import NDK, { NDKEvent } from '@nostr-dev-kit/ndk'
import NDK, { NDKEvent, NDKRelayList } from '@nostr-dev-kit/ndk'
import { kinds, UnsignedEvent } from 'nostr-tools'
import { normalizeWebSocketURL, unixNow } from '.'
import { NostrController } from '../controllers'
import { RelayMap } from '../types'
import { SIGIT_RELAY } from './const'
const READ_MARKER = 'read'
const WRITE_MARKER = 'write'
export const getRelayMapFromNDKRelayList = (ndkRelayList: NDKRelayList) => {
const relayMap: RelayMap = {}
/**
* Attempts to find a relay list from the provided lookUpRelays.
* If the relay list is found, it will be added to the user relay list metadata.
* @param lookUpRelays
* @param hexKey
* @return found relay list or null
*/
const findRelayListAndUpdateCache = async (
lookUpRelays: string[],
hexKey: string
): Promise<Event | null> => {
try {
const eventFilter: Filter = {
kinds: [RelayList],
authors: [hexKey]
ndkRelayList.readRelayUrls.forEach((relayUrl) => {
const normalizedUrl = normalizeWebSocketURL(relayUrl)
relayMap[normalizedUrl] = {
read: true,
write: false
}
})
const event = await relayController.fetchEvent(eventFilter, lookUpRelays)
if (event) {
await localCache.addUserRelayListMetadata(event)
ndkRelayList.writeRelayUrls.forEach((relayUrl) => {
const normalizedUrl = normalizeWebSocketURL(relayUrl)
const existing = relayMap[normalizedUrl]
if (existing) {
existing.write = true
} else {
relayMap[normalizedUrl] = {
read: false,
write: true
}
}
return event
} catch (error) {
console.error(error)
return null
}
})
return relayMap
}
/**
* Attempts to find a relay list in cache. If it is present, it will check that the cached event is not
* older than one week.
* @param hexKey
* @return RelayList event if it's not older than a week; otherwise null
*/
const findRelayListInCache = async (hexKey: string): Promise<Event | null> => {
try {
// Attempt to retrieve the metadata event from the local cache
const cachedRelayListMetadataEvent =
await localCache.getUserRelayListMetadata(hexKey)
// Check if the cached event is not older than one week
if (
cachedRelayListMetadataEvent &&
!isOlderThanOneWeek(cachedRelayListMetadataEvent.cachedAt)
) {
return cachedRelayListMetadataEvent.event
}
return null
} catch (error) {
console.error(error)
return null
}
}
/**
* Transforms a list of relay tags from a Nostr Event to a RelaySet.
* @param tags
*/
const getUserRelaySet = (tags: string[][]): RelaySet => {
return tags
.filter(isRelayTag)
.reduce<RelaySet>(toRelaySet, getDefaultRelaySet())
}
const getDefaultRelaySet = (): RelaySet => ({
read: [SIGIT_RELAY],
write: [SIGIT_RELAY]
})
const getDefaultRelayMap = (): RelayMap => ({
export const getDefaultRelayMap = (): RelayMap => ({
[SIGIT_RELAY]: { write: true, read: true }
})
const isOlderThanOneWeek = (cachedAt: number) => {
return Date.now() - cachedAt > ONE_WEEK_IN_MS
}
const isOlderThanOneDay = (cachedAt: number) => {
return Date.now() - cachedAt > ONE_DAY_IN_MS
}
const isRelayTag = (tag: string[]): boolean => tag[0] === 'r'
const toRelaySet = (obj: RelaySet, tag: string[]): RelaySet => {
if (tag.length >= 3) {
const marker = tag[2]
if (marker === READ_MARKER) {
obj.read.push(tag[1])
} else if (marker === WRITE_MARKER) {
obj.write.push(tag[1])
}
}
if (tag.length === 2) {
obj.read.push(tag[1])
obj.write.push(tag[1])
}
return obj
}
/**
* Provides relay map.
* @param npub - user's npub
* @returns - promise that resolves into relay map and a timestamp when it has been updated.
*/
const getRelayMap = async (
npub: string
): Promise<{ map: RelayMap; mapUpdated?: number }> => {
// More info about this kind of event available https://github.com/nostr-protocol/nips/blob/master/65.md
const eventFilter: Filter = {
kinds: [kinds.RelayList],
authors: [npub]
}
const event = await relayController
.fetchEvent(eventFilter, DEFAULT_LOOK_UP_RELAY_LIST)
.catch((err) => {
return Promise.reject(err)
})
if (event) {
// Handle founded 10002 event
const relaysMap: RelayMap = {}
// 'r' stands for 'relay'
const relayTags = event.tags.filter((tag) => tag[0] === 'r')
relayTags.forEach((tag) => {
const uri = tag[1]
const relayType = tag[2]
// if 3rd element of relay tag is undefined, relay is WRITE and READ
relaysMap[uri] = {
write: relayType ? relayType === 'write' : true,
read: relayType ? relayType === 'read' : true
}
})
Object.keys(relaysMap).forEach((relayUrl) =>
relayController.connectRelay(relayUrl)
)
getRelayInfo(Object.keys(relaysMap))
return Promise.resolve({ map: relaysMap, mapUpdated: event.created_at })
} else {
return Promise.resolve({ map: getDefaultRelayMap() })
}
}
/**
* Publishes relay map.
* @param relayMap - relay map.
@ -174,7 +45,7 @@ const getRelayMap = async (
* @param extraRelaysToPublish - optional relays to publish relay map.
* @returns - promise that resolves into a string representing publishing result.
*/
const publishRelayMap = async (
export const publishRelayMap = async (
relayMap: RelayMap,
npub: string,
ndk: NDK,
@ -218,15 +89,3 @@ const publishRelayMap = async (
return Promise.reject('Publishing updated relay map was unsuccessful.')
}
export {
findRelayListAndUpdateCache,
findRelayListInCache,
getDefaultRelayMap,
getDefaultRelaySet,
getRelayMap,
getUserRelaySet,
isOlderThanOneDay,
isOlderThanOneWeek,
publishRelayMap
}