feat(notificiations): initial changes to check notifications per each user
This commit is contained in:
parent
b965623a02
commit
de7a8a868a
@ -2,7 +2,8 @@ import { Event, Filter, Relay } from 'nostr-tools'
|
||||
import {
|
||||
settleAllFullfilfedPromises,
|
||||
normalizeWebSocketURL,
|
||||
timeout
|
||||
publishToRelay,
|
||||
isPromiseFulfilled
|
||||
} from '../utils'
|
||||
import { SIGIT_RELAY } from '../utils/const'
|
||||
|
||||
@ -261,17 +262,17 @@ export class RelayController {
|
||||
event: Event,
|
||||
relayUrls: string[] = []
|
||||
): Promise<string[]> => {
|
||||
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
|
||||
}
|
||||
/**
|
||||
* Ensure that the default Sigit Relay is included.
|
||||
* Copy the array instead of mutating it.
|
||||
*/
|
||||
const updatedRelayUrls = !relayUrls.includes(SIGIT_RELAY)
|
||||
? [...relayUrls, SIGIT_RELAY]
|
||||
: [...relayUrls]
|
||||
|
||||
// connect to all specified relays
|
||||
const relays = await settleAllFullfilfedPromises(
|
||||
relayUrls,
|
||||
updatedRelayUrls,
|
||||
this.connectRelay
|
||||
)
|
||||
|
||||
@ -280,26 +281,15 @@ export class RelayController {
|
||||
throw new Error('No relay is connected to publish event!')
|
||||
}
|
||||
|
||||
const publishedOnRelays: string[] = [] // List to track which relays successfully published the event
|
||||
|
||||
// Create a promise for publishing the event to each connected relay
|
||||
const publishPromises = relays.map(async (relay) => {
|
||||
try {
|
||||
await Promise.race([
|
||||
relay.publish(event), // Publish the event to the relay
|
||||
timeout(20 * 1000) // Set a timeout to handle cases where publishing takes too long
|
||||
])
|
||||
publishedOnRelays.push(relay.url) // Add the relay URL to the list of successfully published relays
|
||||
} catch (err) {
|
||||
console.error(`Failed to publish event on relay: ${relay.url}`, err)
|
||||
}
|
||||
})
|
||||
|
||||
// Wait for all publish operations to complete (either fulfilled or rejected)
|
||||
await Promise.allSettled(publishPromises)
|
||||
const settledPromises: PromiseSettledResult<string>[] =
|
||||
await Promise.allSettled(
|
||||
relays.map(async (relay) => publishToRelay(relay, event))
|
||||
)
|
||||
|
||||
// Return the list of relay URLs where the event was published
|
||||
return publishedOnRelays
|
||||
return settledPromises
|
||||
.filter(isPromiseFulfilled)
|
||||
.map((res) => res.value) as string[]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,9 @@ import {
|
||||
shorten,
|
||||
signEventForMetaFile,
|
||||
updateUsersAppData,
|
||||
uploadToFileStorage
|
||||
uploadToFileStorage,
|
||||
isPromiseFulfilled,
|
||||
isPromiseRejected
|
||||
} from '../../utils'
|
||||
import { Container } from '../../components/Container'
|
||||
import styles from './style.module.scss'
|
||||
@ -720,7 +722,7 @@ export const CreatePage = () => {
|
||||
: viewers.map((viewer) => viewer.pubkey)
|
||||
).filter((receiver) => receiver !== usersPubkey)
|
||||
|
||||
return receivers.map((receiver) => sendNotification(receiver, meta))
|
||||
return receivers.map(async (receiver) => sendNotification(receiver, meta))
|
||||
}
|
||||
|
||||
const handleCreate = async () => {
|
||||
@ -784,15 +786,25 @@ export const CreatePage = () => {
|
||||
if (!event) return
|
||||
|
||||
setLoadingSpinnerDesc('Sending notifications to counterparties')
|
||||
const promises = sendNotifications(meta)
|
||||
const notifications = await Promise.allSettled(sendNotifications(meta))
|
||||
if (notifications.every(isPromiseFulfilled)) {
|
||||
toast.success('finished processing notifications!')
|
||||
} else {
|
||||
notifications
|
||||
.filter(isPromiseRejected)
|
||||
.map((res: PromiseRejectedResult) => res.reason)
|
||||
.forEach((res) => console.log('rejected result: ', res))
|
||||
}
|
||||
|
||||
await Promise.all(promises)
|
||||
.then(() => {
|
||||
toast.success('Notifications sent successfully')
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error('Failed to publish notifications')
|
||||
})
|
||||
// const promises = sendNotifications(meta)
|
||||
|
||||
// await Promise.all(promises)
|
||||
// .then(() => {
|
||||
// toast.success('Notifications sent successfully')
|
||||
// })
|
||||
// .catch(() => {
|
||||
// toast.error('Failed to publish notifications')
|
||||
// })
|
||||
|
||||
navigate(appPrivateRoutes.sign, { state: { meta: meta } })
|
||||
} else {
|
||||
|
@ -701,17 +701,19 @@ export const SignPage = () => {
|
||||
|
||||
setLoadingSpinnerDesc('Sending notifications')
|
||||
const users = Array.from(userSet)
|
||||
const promises = users.map((user) =>
|
||||
sendNotification(npubToHex(user)!, meta)
|
||||
console.log('users: ', users)
|
||||
const publishedRelays = await Promise.all(
|
||||
users.map(async (user) => sendNotification(npubToHex(user)!, meta))
|
||||
)
|
||||
await Promise.all(promises)
|
||||
.then(() => {
|
||||
toast.success('Notifications sent successfully')
|
||||
setMeta(meta)
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error('Failed to publish notifications')
|
||||
})
|
||||
console.log('published relays: ', publishedRelays)
|
||||
// await Promise.all(promises)
|
||||
// .then(() => {
|
||||
// toast.success('Notifications sent successfully')
|
||||
// setMeta(meta)
|
||||
// })
|
||||
// .catch(() => {
|
||||
// toast.error('Failed to publish notifications')
|
||||
// })
|
||||
|
||||
setIsLoading(false)
|
||||
}
|
||||
|
@ -916,50 +916,33 @@ const processReceivedEvent = async (event: Event, difficulty: number = 5) => {
|
||||
* @param meta - Metadata associated with the notification.
|
||||
*/
|
||||
export const sendNotification = async (receiver: string, meta: Meta) => {
|
||||
// Retrieve the user's public key from the state
|
||||
const usersPubkey = (store.getState().auth as AuthState).usersPubkey!
|
||||
try {
|
||||
// Retrieve the user's public key from the state
|
||||
const usersPubkey = (store.getState().auth as AuthState).usersPubkey!
|
||||
// Create an unsigned event object with the provided metadata
|
||||
const unsignedEvent: UnsignedEvent = {
|
||||
kind: 938,
|
||||
pubkey: usersPubkey,
|
||||
content: JSON.stringify(meta),
|
||||
tags: [],
|
||||
created_at: unixNow()
|
||||
}
|
||||
|
||||
// Create an unsigned event object with the provided metadata
|
||||
const unsignedEvent: UnsignedEvent = {
|
||||
kind: 938,
|
||||
pubkey: usersPubkey,
|
||||
content: JSON.stringify(meta),
|
||||
tags: [],
|
||||
created_at: unixNow()
|
||||
}
|
||||
// Wrap the unsigned event with the receiver's information
|
||||
const wrappedEvent = createWrap(unsignedEvent, receiver)
|
||||
|
||||
// Wrap the unsigned event with the receiver's information
|
||||
const wrappedEvent = createWrap(unsignedEvent, receiver)
|
||||
// Instantiate the MetadataController to retrieve relay list metadata
|
||||
const metadataController = new MetadataController()
|
||||
|
||||
// Instantiate the MetadataController to retrieve relay list metadata
|
||||
const metadataController = new MetadataController()
|
||||
const relaySet = await metadataController
|
||||
.findRelayListMetadata(receiver)
|
||||
.catch((err) => {
|
||||
// Log an error if retrieving relay list metadata fails
|
||||
console.log(
|
||||
`An error occurred while finding relay list metadata for ${hexToNpub(receiver)}`,
|
||||
err
|
||||
)
|
||||
return null
|
||||
const relaySet = await metadataController.findRelayListMetadata(receiver)
|
||||
|
||||
return await Promise.race([
|
||||
relayController.publish(wrappedEvent, relaySet.read),
|
||||
timeout(40 * 1000)
|
||||
])
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to publish a notification for ${receiver}`, {
|
||||
cause: error
|
||||
})
|
||||
|
||||
// Return if metadata retrieval failed
|
||||
if (!relaySet) return
|
||||
|
||||
// Ensure relay list is not empty
|
||||
if (relaySet.read.length === 0) return
|
||||
|
||||
// Publish the notification event to the recipient's read relays
|
||||
await Promise.race([
|
||||
relayController.publish(wrappedEvent, relaySet.read),
|
||||
timeout(40 * 1000)
|
||||
]).catch((err) => {
|
||||
// Log an error if publishing the notification event fails
|
||||
console.log(
|
||||
`An error occurred while publishing notification event for ${hexToNpub(receiver)}`,
|
||||
err
|
||||
)
|
||||
throw err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Event, Filter, kinds, UnsignedEvent } from 'nostr-tools'
|
||||
import { Event, Filter, kinds, Relay, UnsignedEvent } from 'nostr-tools'
|
||||
import { RelayList } from 'nostr-tools/kinds'
|
||||
import { getRelayInfo, unixNow } from '.'
|
||||
import { getRelayInfo, timeout, unixNow } from '.'
|
||||
import { NostrController, relayController } from '../controllers'
|
||||
import { localCache } from '../services'
|
||||
import { RelayMap, RelaySet } from '../types'
|
||||
@ -229,6 +229,16 @@ const publishRelayMap = async (
|
||||
return Promise.reject('Publishing updated relay map was unsuccessful.')
|
||||
}
|
||||
|
||||
const publishToRelay = async (relay: Relay, event: Event): Promise<string> => {
|
||||
try {
|
||||
await Promise.race([relay.publish(event), timeout(20 * 1000)])
|
||||
return relay.url
|
||||
} catch (err) {
|
||||
console.error(`Failed to publish event on relay: ${relay.url}`, err)
|
||||
throw new Error(`Failed to publish event on relay: ${relay.url}`)
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
findRelayListAndUpdateCache,
|
||||
findRelayListInCache,
|
||||
@ -238,5 +248,6 @@ export {
|
||||
getUserRelaySet,
|
||||
isOlderThanOneDay,
|
||||
isOlderThanOneWeek,
|
||||
publishRelayMap
|
||||
publishRelayMap,
|
||||
publishToRelay
|
||||
}
|
||||
|
@ -118,3 +118,15 @@ export const settleAllFullfilfedPromises = async <Item, FulfilledItem = Item>(
|
||||
return acc
|
||||
}, [])
|
||||
}
|
||||
|
||||
export const isPromiseFulfilled = <T>(
|
||||
result: PromiseSettledResult<T>
|
||||
): result is PromiseFulfilledResult<T> => {
|
||||
return result.status === 'fulfilled'
|
||||
}
|
||||
|
||||
export const isPromiseRejected = <T>(
|
||||
result: PromiseSettledResult<T>
|
||||
): result is PromiseRejectedResult => {
|
||||
return result.status === 'rejected'
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user