Relay operations refactored with NDK for publishing events (+ more. Wrapped up refactoring), pagination scroll up on click, body scroll disable/enable when popups appear/disappear, nsfw tag shown on mod cards if mod post is nsfw #92
@ -13,8 +13,7 @@ import { toast } from 'react-toastify'
|
|||||||
import { FixedSizeList as List } from 'react-window'
|
import { FixedSizeList as List } from 'react-window'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { T_TAG_VALUE } from '../constants'
|
import { T_TAG_VALUE } from '../constants'
|
||||||
import { RelayController } from '../controllers'
|
import { useAppSelector, useGames, useNDKContext } from '../hooks'
|
||||||
import { useAppSelector, useGames } from '../hooks'
|
|
||||||
import { appRoutes, getModPageRoute } from '../routes'
|
import { appRoutes, getModPageRoute } from '../routes'
|
||||||
import '../styles/styles.css'
|
import '../styles/styles.css'
|
||||||
import { DownloadUrl, ModDetails, ModFormState } from '../types'
|
import { DownloadUrl, ModDetails, ModFormState } from '../types'
|
||||||
@ -29,6 +28,7 @@ import {
|
|||||||
} from '../utils'
|
} from '../utils'
|
||||||
import { CheckboxField, InputError, InputField } from './Inputs'
|
import { CheckboxField, InputError, InputField } from './Inputs'
|
||||||
import { LoadingSpinner } from './LoadingSpinner'
|
import { LoadingSpinner } from './LoadingSpinner'
|
||||||
|
import { NDKEvent } from '@nostr-dev-kit/ndk'
|
||||||
|
|
||||||
interface FormErrors {
|
interface FormErrors {
|
||||||
game?: string
|
game?: string
|
||||||
@ -54,6 +54,7 @@ type ModFormProps = {
|
|||||||
export const ModForm = ({ existingModData }: ModFormProps) => {
|
export const ModForm = ({ existingModData }: ModFormProps) => {
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
const { ndk, publish } = useNDKContext()
|
||||||
const games = useGames()
|
const games = useGames()
|
||||||
const userState = useAppSelector((state) => state.user)
|
const userState = useAppSelector((state) => state.user)
|
||||||
|
|
||||||
@ -243,9 +244,8 @@ export const ModForm = ({ existingModData }: ModFormProps) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const publishedOnRelays = await RelayController.getInstance().publish(
|
const ndkEvent = new NDKEvent(ndk, signedEvent)
|
||||||
signedEvent as Event
|
const publishedOnRelays = await publish(ndkEvent)
|
||||||
)
|
|
||||||
|
|
||||||
// Handle cases where publishing failed or succeeded
|
// Handle cases where publishing failed or succeeded
|
||||||
if (publishedOnRelays.length === 0) {
|
if (publishedOnRelays.length === 0) {
|
||||||
@ -763,8 +763,9 @@ const GameDropdown = ({
|
|||||||
<div className='inputLabelWrapperMain'>
|
<div className='inputLabelWrapperMain'>
|
||||||
<label className='form-label labelMain'>Game</label>
|
<label className='form-label labelMain'>Game</label>
|
||||||
<p className='labelDescriptionMain'>
|
<p className='labelDescriptionMain'>
|
||||||
Can't find the game you're looking for? You can temporarily publish the mod under '(Unlisted Game)' and
|
Can't find the game you're looking for? You can temporarily publish the
|
||||||
later edit it with the proper game name once we add it.
|
mod under '(Unlisted Game)' and later edit it with the proper game name
|
||||||
|
once we add it.
|
||||||
</p>
|
</p>
|
||||||
<div className='dropdown dropdownMain'>
|
<div className='dropdown dropdownMain'>
|
||||||
<div className='inputWrapperMain inputWrapperMainAlt'>
|
<div className='inputWrapperMain inputWrapperMainAlt'>
|
||||||
@ -827,8 +828,10 @@ const GameDropdown = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{error && <InputError message={error} />}
|
{error && <InputError message={error} />}
|
||||||
<p className='labelDescriptionMain'>Note: Please mention the game name in the body text of your mod post (e.g., 'This is a mod for Game Name')
|
<p className='labelDescriptionMain'>
|
||||||
so we know what to look for and add.
|
Note: Please mention the game name in the body text of your mod post
|
||||||
|
(e.g., 'This is a mod for Game Name') so we know what to look for and
|
||||||
|
add.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -4,7 +4,7 @@ import { QRCodeSVG } from 'qrcode.react'
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { RelayController, UserRelaysType } from '../controllers'
|
import { UserRelaysType } from '../controllers'
|
||||||
import { useAppSelector, useDidMount, useNDKContext } from '../hooks'
|
import { useAppSelector, useDidMount, useNDKContext } from '../hooks'
|
||||||
import { appRoutes, getProfilePageRoute } from '../routes'
|
import { appRoutes, getProfilePageRoute } from '../routes'
|
||||||
import '../styles/author.css'
|
import '../styles/author.css'
|
||||||
@ -22,6 +22,7 @@ import {
|
|||||||
import { LoadingSpinner } from './LoadingSpinner'
|
import { LoadingSpinner } from './LoadingSpinner'
|
||||||
import { ZapPopUp } from './Zap'
|
import { ZapPopUp } from './Zap'
|
||||||
import placeholder from '../assets/img/DEGMods Placeholder Img.png'
|
import placeholder from '../assets/img/DEGMods Placeholder Img.png'
|
||||||
|
import { NDKEvent } from '@nostr-dev-kit/ndk'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
pubkey: string
|
pubkey: string
|
||||||
@ -368,7 +369,7 @@ type FollowButtonProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const FollowButton = ({ pubkey }: FollowButtonProps) => {
|
const FollowButton = ({ pubkey }: FollowButtonProps) => {
|
||||||
const { fetchEventFromUserRelays } = useNDKContext()
|
const { ndk, fetchEventFromUserRelays, publish } = useNDKContext()
|
||||||
const [isFollowing, setIsFollowing] = useState(false)
|
const [isFollowing, setIsFollowing] = useState(false)
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
@ -441,9 +442,8 @@ const FollowButton = ({ pubkey }: FollowButtonProps) => {
|
|||||||
|
|
||||||
if (!signedEvent) return false
|
if (!signedEvent) return false
|
||||||
|
|
||||||
const publishedOnRelays = await RelayController.getInstance().publish(
|
const ndkEvent = new NDKEvent(ndk, signedEvent)
|
||||||
signedEvent as Event
|
const publishedOnRelays = await publish(ndkEvent)
|
||||||
)
|
|
||||||
|
|
||||||
if (publishedOnRelays.length === 0) {
|
if (publishedOnRelays.length === 0) {
|
||||||
toast.error('Failed to publish event on any relay')
|
toast.error('Failed to publish event on any relay')
|
||||||
|
@ -56,6 +56,7 @@ interface NDKContextType {
|
|||||||
accumulatedZapAmount: number
|
accumulatedZapAmount: number
|
||||||
hasZapped: boolean
|
hasZapped: boolean
|
||||||
}>
|
}>
|
||||||
|
publish: (event: NDKEvent) => Promise<string[]>
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the context with an initial value of `null`
|
// Create the context with an initial value of `null`
|
||||||
@ -352,6 +353,21 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const publish = async (event: NDKEvent): Promise<string[]> => {
|
||||||
|
if (!event.sig) throw new Error('Before publishing first sign the event!')
|
||||||
|
|
||||||
|
return event
|
||||||
|
.publish(undefined, 30000)
|
||||||
|
.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 (
|
return (
|
||||||
<NDKContext.Provider
|
<NDKContext.Provider
|
||||||
value={{
|
value={{
|
||||||
@ -362,7 +378,8 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
fetchEventsFromUserRelays,
|
fetchEventsFromUserRelays,
|
||||||
fetchEventFromUserRelays,
|
fetchEventFromUserRelays,
|
||||||
findMetadata,
|
findMetadata,
|
||||||
getTotalZapAmount
|
getTotalZapAmount,
|
||||||
|
publish
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
export * from './metadata'
|
export * from './metadata'
|
||||||
export * from './relay'
|
|
||||||
export * from './zap'
|
export * from './zap'
|
||||||
|
@ -1,368 +0,0 @@
|
|||||||
import { Event, Relay } from 'nostr-tools'
|
|
||||||
import { log, LogType, normalizeWebSocketURL, timeout } from '../utils'
|
|
||||||
import { MetadataController, UserRelaysType } from './metadata'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Singleton class to manage relay operations.
|
|
||||||
*/
|
|
||||||
export class RelayController {
|
|
||||||
private static instance: RelayController
|
|
||||||
private events = new Map<string, Event>()
|
|
||||||
private debug = true
|
|
||||||
public connectedRelays: Relay[] = []
|
|
||||||
|
|
||||||
private constructor() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides the singleton instance of RelayController.
|
|
||||||
*
|
|
||||||
* @returns The singleton instance of RelayController.
|
|
||||||
*/
|
|
||||||
public static getInstance(): RelayController {
|
|
||||||
if (!RelayController.instance) {
|
|
||||||
RelayController.instance = new RelayController()
|
|
||||||
}
|
|
||||||
return RelayController.instance
|
|
||||||
}
|
|
||||||
|
|
||||||
public connectRelay = async (relayUrl: string) => {
|
|
||||||
const relay = this.connectedRelays.find(
|
|
||||||
(relay) =>
|
|
||||||
normalizeWebSocketURL(relay.url) === normalizeWebSocketURL(relayUrl)
|
|
||||||
)
|
|
||||||
if (relay) {
|
|
||||||
// already connected, skip
|
|
||||||
return relay
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Relay.connect(relayUrl)
|
|
||||||
.then((relay) => {
|
|
||||||
log(this.debug, LogType.Info, `✅ nostr (${relayUrl}): Connected!`)
|
|
||||||
this.connectedRelays.push(relay)
|
|
||||||
return relay
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
log(
|
|
||||||
this.debug,
|
|
||||||
LogType.Error,
|
|
||||||
`❌ nostr (${relayUrl}): Connection error!`,
|
|
||||||
err
|
|
||||||
)
|
|
||||||
return null
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Publishes an event to multiple relays.
|
|
||||||
*
|
|
||||||
* This method establishes a connection to the application relay specified by
|
|
||||||
* an environment variable and a set of relays obtained from the
|
|
||||||
* `MetadataController`. It attempts to publish the event to all connected
|
|
||||||
* relays and returns a list of URLs of relays where the event was successfully
|
|
||||||
* published.
|
|
||||||
*
|
|
||||||
* If the process of finding relays or publishing the event takes too long,
|
|
||||||
* it handles the timeout to prevent blocking the operation.
|
|
||||||
*
|
|
||||||
* @param event - The event to be published.
|
|
||||||
* @param userHexKey - The user's hexadecimal public key, used to retrieve their relays.
|
|
||||||
* If not provided, the event's public key will be used.
|
|
||||||
* @param userRelaysType - The type of relays to be retrieved (e.g., write relays).
|
|
||||||
* Defaults to `UserRelaysType.Write`.
|
|
||||||
* @returns A promise that resolves to an array of URLs of relays where the event
|
|
||||||
* was published, or an empty array if no relays were connected or the
|
|
||||||
* event could not be published.
|
|
||||||
*/
|
|
||||||
publish = async (
|
|
||||||
event: Event,
|
|
||||||
userHexKey?: string,
|
|
||||||
userRelaysType?: UserRelaysType
|
|
||||||
): Promise<string[]> => {
|
|
||||||
// Connect to the application relay specified by an environment variable
|
|
||||||
const appRelayPromise = this.connectRelay(import.meta.env.VITE_APP_RELAY)
|
|
||||||
|
|
||||||
// TODO: Implement logic to retrieve relays using `window.nostr.getRelays()` once it becomes available in nostr-login.
|
|
||||||
|
|
||||||
// Retrieve an instance of MetadataController to find user relays
|
|
||||||
const metadataController = await MetadataController.getInstance()
|
|
||||||
|
|
||||||
// Retrieve the list of relays for the specified user's public key
|
|
||||||
const relayUrls = await metadataController.findUserRelays(
|
|
||||||
userHexKey || event.pubkey,
|
|
||||||
userRelaysType || UserRelaysType.Write
|
|
||||||
)
|
|
||||||
|
|
||||||
// Add admin relay URLs from the metadata controller to the list of relay URLs
|
|
||||||
metadataController.adminRelays.forEach((url) => {
|
|
||||||
relayUrls.push(url)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Attempt to connect to all write relays obtained from MetadataController
|
|
||||||
const relayPromises = relayUrls.map((relayUrl) =>
|
|
||||||
this.connectRelay(relayUrl)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Wait for all relay connection attempts to settle (either fulfilled or rejected)
|
|
||||||
const results = await Promise.allSettled([
|
|
||||||
appRelayPromise,
|
|
||||||
...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
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
// If no relays are connected, log an error and return an empty array
|
|
||||||
if (relays.length === 0) {
|
|
||||||
log(this.debug, LogType.Error, 'No relay is connected!')
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
const publishedOnRelays: string[] = [] // Track relays where the event was successfully published
|
|
||||||
|
|
||||||
// Create promises to publish the event to each connected relay
|
|
||||||
const publishPromises = relays.map((relay) => {
|
|
||||||
log(
|
|
||||||
this.debug,
|
|
||||||
LogType.Info,
|
|
||||||
`⬆️ nostr (${relay.url}): Sending event:`,
|
|
||||||
event
|
|
||||||
)
|
|
||||||
|
|
||||||
return Promise.race([
|
|
||||||
relay.publish(event), // Publish the event to the relay
|
|
||||||
timeout(30000) // Set a timeout to handle slow publishing operations
|
|
||||||
])
|
|
||||||
.then((res) => {
|
|
||||||
log(
|
|
||||||
this.debug,
|
|
||||||
LogType.Info,
|
|
||||||
`⬆️ nostr (${relay.url}): Publish result:`,
|
|
||||||
res
|
|
||||||
)
|
|
||||||
publishedOnRelays.push(relay.url) // Add successful relay URL to the list
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
log(
|
|
||||||
this.debug,
|
|
||||||
LogType.Error,
|
|
||||||
`❌ nostr (${relay.url}): Publish error!`,
|
|
||||||
err
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Wait for all publish operations to complete (either fulfilled or rejected)
|
|
||||||
await Promise.allSettled(publishPromises)
|
|
||||||
|
|
||||||
if (publishedOnRelays.length > 0) {
|
|
||||||
// If the event was successfully published to any relays, check if it contains an `aTag`
|
|
||||||
// If the `aTag` is present, cache the event locally
|
|
||||||
const aTag = event.tags.find((item) => item[0] === 'a')
|
|
||||||
if (aTag && aTag[1]) {
|
|
||||||
this.events.set(aTag[1], event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the list of relay URLs where the event was successfully published
|
|
||||||
return publishedOnRelays
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Publishes an encrypted DM to receiver's read relays.
|
|
||||||
*
|
|
||||||
* This method connects to the application relay and a set of receiver's read relays
|
|
||||||
* obtained from the `MetadataController`. It then publishes the event to
|
|
||||||
* all connected relays and returns a list of relays where the event was successfully published.
|
|
||||||
*
|
|
||||||
* @param event - The event to be published.
|
|
||||||
* @returns A promise that resolves to an array of URLs of relays where the event was published,
|
|
||||||
* or an empty array if no relays were connected or the event could not be published.
|
|
||||||
*/
|
|
||||||
publishDM = async (event: Event, receiver: string): Promise<string[]> => {
|
|
||||||
// Connect to the application relay specified by environment variable
|
|
||||||
const appRelayPromise = this.connectRelay(import.meta.env.VITE_APP_RELAY)
|
|
||||||
|
|
||||||
// todo: window.nostr.getRelays() is not implemented yet in nostr-login, implement the logic once its done
|
|
||||||
|
|
||||||
const metadataController = await MetadataController.getInstance()
|
|
||||||
|
|
||||||
// Retrieve the list of read relays for the receiver
|
|
||||||
const readRelayUrls = await metadataController.findUserRelays(
|
|
||||||
receiver,
|
|
||||||
UserRelaysType.Read
|
|
||||||
)
|
|
||||||
|
|
||||||
// push admin relay urls obtained from metadata controller to readRelayUrls list
|
|
||||||
metadataController.adminRelays.forEach((url) => {
|
|
||||||
readRelayUrls.push(url)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Connect to all write relays obtained from MetadataController
|
|
||||||
const relayPromises = readRelayUrls.map((relayUrl) =>
|
|
||||||
this.connectRelay(relayUrl)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Wait for all relay connections to settle (either fulfilled or rejected)
|
|
||||||
await Promise.allSettled([appRelayPromise, ...relayPromises])
|
|
||||||
|
|
||||||
// Check if any relays are connected; if not, log an error and return null
|
|
||||||
if (this.connectedRelays.length === 0) {
|
|
||||||
log(this.debug, LogType.Error, 'No relay is connected!')
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = this.connectedRelays.map((relay) => {
|
|
||||||
log(
|
|
||||||
this.debug,
|
|
||||||
LogType.Info,
|
|
||||||
`⬆️ nostr (${relay.url}): Sending event:`,
|
|
||||||
event
|
|
||||||
)
|
|
||||||
|
|
||||||
return Promise.race([
|
|
||||||
relay.publish(event), // Publish the event to the relay
|
|
||||||
timeout(30000) // Set a timeout to handle cases where publishing takes too long
|
|
||||||
])
|
|
||||||
.then((res) => {
|
|
||||||
log(
|
|
||||||
this.debug,
|
|
||||||
LogType.Info,
|
|
||||||
`⬆️ nostr (${relay.url}): Publish result:`,
|
|
||||||
res
|
|
||||||
)
|
|
||||||
publishedOnRelays.push(relay.url) // Add the relay URL to the list of successfully published relays
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
log(
|
|
||||||
this.debug,
|
|
||||||
LogType.Error,
|
|
||||||
`❌ nostr (${relay.url}): Publish error!`,
|
|
||||||
err
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Wait for all publish operations to complete (either fulfilled or rejected)
|
|
||||||
await Promise.allSettled(publishPromises)
|
|
||||||
|
|
||||||
// Return the list of relay URLs where the event was published
|
|
||||||
return publishedOnRelays
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Publishes an event to multiple relays.
|
|
||||||
*
|
|
||||||
* This method establishes a connection to the application relay specified by
|
|
||||||
* an environment variable and a set of relays provided as argument.
|
|
||||||
* It attempts to publish the event to all connected relays
|
|
||||||
* and returns a list of URLs of relays where the event was successfully published.
|
|
||||||
*
|
|
||||||
* If the process of publishing the event takes too long,
|
|
||||||
* it handles the timeout to prevent blocking the operation.
|
|
||||||
*
|
|
||||||
* @param event - The event to be published.
|
|
||||||
* @param relayUrls - The array of relayUrl where event should be published
|
|
||||||
* @returns A promise that resolves to an array of URLs of relays where the event
|
|
||||||
* was published, or an empty array if no relays were connected or the
|
|
||||||
* event could not be published.
|
|
||||||
*/
|
|
||||||
publishOnRelays = async (
|
|
||||||
event: Event,
|
|
||||||
relayUrls: string[]
|
|
||||||
): Promise<string[]> => {
|
|
||||||
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) {
|
|
||||||
log(this.debug, LogType.Error, 'No relay is connected!')
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
const publishedOnRelays: string[] = [] // Track relays where the event was successfully published
|
|
||||||
|
|
||||||
// Create promises to publish the event to each connected relay
|
|
||||||
const publishPromises = relays.map((relay) => {
|
|
||||||
log(
|
|
||||||
this.debug,
|
|
||||||
LogType.Info,
|
|
||||||
`⬆️ nostr (${relay.url}): Sending event:`,
|
|
||||||
event
|
|
||||||
)
|
|
||||||
|
|
||||||
return Promise.race([
|
|
||||||
relay.publish(event), // Publish the event to the relay
|
|
||||||
timeout(30000) // Set a timeout to handle slow publishing operations
|
|
||||||
])
|
|
||||||
.then((res) => {
|
|
||||||
log(
|
|
||||||
this.debug,
|
|
||||||
LogType.Info,
|
|
||||||
`⬆️ nostr (${relay.url}): Publish result:`,
|
|
||||||
res
|
|
||||||
)
|
|
||||||
publishedOnRelays.push(relay.url) // Add successful relay URL to the list
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
log(
|
|
||||||
this.debug,
|
|
||||||
LogType.Error,
|
|
||||||
`❌ nostr (${relay.url}): Publish error!`,
|
|
||||||
err
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Wait for all publish operations to complete (either fulfilled or rejected)
|
|
||||||
await Promise.allSettled(publishPromises)
|
|
||||||
|
|
||||||
if (publishedOnRelays.length > 0) {
|
|
||||||
// If the event was successfully published to any relays, check if it contains an `aTag`
|
|
||||||
// If the `aTag` is present, cache the event locally
|
|
||||||
const aTag = event.tags.find((item) => item[0] === 'a')
|
|
||||||
if (aTag && aTag[1]) {
|
|
||||||
this.events.set(aTag[1], event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the list of relay URLs where the event was successfully published
|
|
||||||
return publishedOnRelays
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
import { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk'
|
import { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk'
|
||||||
import { REACTIONS } from 'constants.ts'
|
import { REACTIONS } from 'constants.ts'
|
||||||
import { RelayController, UserRelaysType } from 'controllers'
|
import { UserRelaysType } from 'controllers'
|
||||||
import { useAppSelector, useDidMount, useNDKContext } from 'hooks'
|
import { useAppSelector, useDidMount, useNDKContext } from 'hooks'
|
||||||
import { Event, kinds, UnsignedEvent } from 'nostr-tools'
|
import { Event, kinds, UnsignedEvent } from 'nostr-tools'
|
||||||
import { useMemo, useState } from 'react'
|
import { useMemo, useState } from 'react'
|
||||||
@ -14,7 +14,7 @@ type UseReactionsParams = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useReactions = (params: UseReactionsParams) => {
|
export const useReactions = (params: UseReactionsParams) => {
|
||||||
const { ndk, fetchEventsFromUserRelays } = useNDKContext()
|
const { ndk, fetchEventsFromUserRelays, publish } = useNDKContext()
|
||||||
const [isReactionInProgress, setIsReactionInProgress] = useState(false)
|
const [isReactionInProgress, setIsReactionInProgress] = useState(false)
|
||||||
const [isDataLoaded, setIsDataLoaded] = useState(false)
|
const [isDataLoaded, setIsDataLoaded] = useState(false)
|
||||||
const [reactionEvents, setReactionEvents] = useState<NDKEvent[]>([])
|
const [reactionEvents, setReactionEvents] = useState<NDKEvent[]>([])
|
||||||
@ -119,13 +119,11 @@ export const useReactions = (params: UseReactionsParams) => {
|
|||||||
|
|
||||||
if (!signedEvent) return
|
if (!signedEvent) return
|
||||||
|
|
||||||
setReactionEvents((prev) => [...prev, new NDKEvent(ndk, signedEvent)])
|
const ndkEvent = new NDKEvent(ndk, signedEvent)
|
||||||
|
|
||||||
const publishedOnRelays = await RelayController.getInstance().publish(
|
setReactionEvents((prev) => [...prev, ndkEvent])
|
||||||
signedEvent as Event,
|
|
||||||
params.pubkey,
|
const publishedOnRelays = await publish(ndkEvent)
|
||||||
UserRelaysType.Read
|
|
||||||
)
|
|
||||||
|
|
||||||
if (publishedOnRelays.length === 0) {
|
if (publishedOnRelays.length === 0) {
|
||||||
log(
|
log(
|
||||||
|
@ -53,7 +53,7 @@ export const ModPage = () => {
|
|||||||
useDidMount(async () => {
|
useDidMount(async () => {
|
||||||
if (naddr) {
|
if (naddr) {
|
||||||
const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`)
|
const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`)
|
||||||
const { identifier, kind, pubkey, relays = [] } = decoded.data
|
const { identifier, kind, pubkey } = decoded.data
|
||||||
|
|
||||||
const filter: NDKFilter = {
|
const filter: NDKFilter = {
|
||||||
'#a': [identifier],
|
'#a': [identifier],
|
||||||
@ -61,7 +61,7 @@ export const ModPage = () => {
|
|||||||
kinds: [kind]
|
kinds: [kind]
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchEvent(filter, relays)
|
fetchEvent(filter)
|
||||||
.then((event) => {
|
.then((event) => {
|
||||||
if (event) {
|
if (event) {
|
||||||
const extracted = extractModData(event)
|
const extracted = extractModData(event)
|
||||||
@ -212,7 +212,7 @@ type GameProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Game = ({ naddr, game, author, aTag }: GameProps) => {
|
const Game = ({ naddr, game, author, aTag }: GameProps) => {
|
||||||
const { fetchEventFromUserRelays } = useNDKContext()
|
const { ndk, fetchEventFromUserRelays, publish } = useNDKContext()
|
||||||
|
|
||||||
const userState = useAppSelector((state) => state.user)
|
const userState = useAppSelector((state) => state.user)
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
@ -343,7 +343,7 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => {
|
|||||||
|
|
||||||
setLoadingSpinnerDesc('Updating mute list event')
|
setLoadingSpinnerDesc('Updating mute list event')
|
||||||
|
|
||||||
const isUpdated = await signAndPublish(unsignedEvent)
|
const isUpdated = await signAndPublish(unsignedEvent, ndk, publish)
|
||||||
if (isUpdated) {
|
if (isUpdated) {
|
||||||
setIsBlocked(true)
|
setIsBlocked(true)
|
||||||
}
|
}
|
||||||
@ -384,7 +384,7 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setLoadingSpinnerDesc('Updating mute list event')
|
setLoadingSpinnerDesc('Updating mute list event')
|
||||||
const isUpdated = await signAndPublish(unsignedEvent)
|
const isUpdated = await signAndPublish(unsignedEvent, ndk, publish)
|
||||||
if (isUpdated) {
|
if (isUpdated) {
|
||||||
setIsBlocked(false)
|
setIsBlocked(false)
|
||||||
}
|
}
|
||||||
@ -450,7 +450,7 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => {
|
|||||||
|
|
||||||
setLoadingSpinnerDesc('Updating nsfw list event')
|
setLoadingSpinnerDesc('Updating nsfw list event')
|
||||||
|
|
||||||
const isUpdated = await signAndPublish(unsignedEvent)
|
const isUpdated = await signAndPublish(unsignedEvent, ndk, publish)
|
||||||
if (isUpdated) {
|
if (isUpdated) {
|
||||||
setIsAddedToNSFW(true)
|
setIsAddedToNSFW(true)
|
||||||
}
|
}
|
||||||
@ -491,7 +491,7 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setLoadingSpinnerDesc('Updating nsfw list event')
|
setLoadingSpinnerDesc('Updating nsfw list event')
|
||||||
const isUpdated = await signAndPublish(unsignedEvent)
|
const isUpdated = await signAndPublish(unsignedEvent, ndk, publish)
|
||||||
if (isUpdated) {
|
if (isUpdated) {
|
||||||
setIsAddedToNSFW(false)
|
setIsAddedToNSFW(false)
|
||||||
}
|
}
|
||||||
@ -661,7 +661,7 @@ type ReportPopupProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ReportPopup = ({ aTag, handleClose }: ReportPopupProps) => {
|
const ReportPopup = ({ aTag, handleClose }: ReportPopupProps) => {
|
||||||
const { fetchEventFromUserRelays } = useNDKContext()
|
const { ndk, fetchEventFromUserRelays, publish } = useNDKContext()
|
||||||
const userState = useAppSelector((state) => state.user)
|
const userState = useAppSelector((state) => state.user)
|
||||||
const [selectedOptions, setSelectedOptions] = useState({
|
const [selectedOptions, setSelectedOptions] = useState({
|
||||||
actuallyCP: false,
|
actuallyCP: false,
|
||||||
@ -760,7 +760,7 @@ const ReportPopup = ({ aTag, handleClose }: ReportPopupProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setLoadingSpinnerDesc('Updating mute list event')
|
setLoadingSpinnerDesc('Updating mute list event')
|
||||||
const isUpdated = await signAndPublish(unsignedEvent)
|
const isUpdated = await signAndPublish(unsignedEvent, ndk, publish)
|
||||||
if (isUpdated) handleClose()
|
if (isUpdated) handleClose()
|
||||||
} else {
|
} else {
|
||||||
const href = window.location.href
|
const href = window.location.href
|
||||||
@ -773,7 +773,12 @@ const ReportPopup = ({ aTag, handleClose }: ReportPopupProps) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
setLoadingSpinnerDesc('Sending report')
|
setLoadingSpinnerDesc('Sending report')
|
||||||
const isSent = await sendDMUsingRandomKey(message, reportingPubkey!)
|
const isSent = await sendDMUsingRandomKey(
|
||||||
|
message,
|
||||||
|
reportingPubkey!,
|
||||||
|
ndk,
|
||||||
|
publish
|
||||||
|
)
|
||||||
if (isSent) handleClose()
|
if (isSent) handleClose()
|
||||||
}
|
}
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
|
import { NDKEvent } from '@nostr-dev-kit/ndk'
|
||||||
import { ZapPopUp } from 'components/Zap'
|
import { ZapPopUp } from 'components/Zap'
|
||||||
import {
|
|
||||||
MetadataController,
|
|
||||||
RelayController,
|
|
||||||
UserRelaysType
|
|
||||||
} from 'controllers'
|
|
||||||
import { formatDate } from 'date-fns'
|
import { formatDate } from 'date-fns'
|
||||||
import { useAppSelector, useDidMount, useNDKContext, useReactions } from 'hooks'
|
import { useAppSelector, useDidMount, useNDKContext, useReactions } from 'hooks'
|
||||||
import { useComments } from 'hooks/useComments'
|
import { useComments } from 'hooks/useComments'
|
||||||
@ -47,6 +43,7 @@ type Props = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const Comments = ({ modDetails, setCommentCount }: Props) => {
|
export const Comments = ({ modDetails, setCommentCount }: Props) => {
|
||||||
|
const { ndk, publish } = useNDKContext()
|
||||||
const { commentEvents, setCommentEvents } = useComments(modDetails)
|
const { commentEvents, setCommentEvents } = useComments(modDetails)
|
||||||
const [filterOptions, setFilterOptions] = useState<FilterOptions>({
|
const [filterOptions, setFilterOptions] = useState<FilterOptions>({
|
||||||
sort: SortByEnum.Latest,
|
sort: SortByEnum.Latest,
|
||||||
@ -82,7 +79,8 @@ export const Comments = ({ modDetails, setCommentCount }: Props) => {
|
|||||||
created_at: now(),
|
created_at: now(),
|
||||||
tags: [
|
tags: [
|
||||||
['e', modDetails.id],
|
['e', modDetails.id],
|
||||||
['a', modDetails.aTag]
|
['a', modDetails.aTag],
|
||||||
|
['p', modDetails.author]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,27 +103,9 @@ export const Comments = ({ modDetails, setCommentCount }: Props) => {
|
|||||||
...prev
|
...prev
|
||||||
])
|
])
|
||||||
|
|
||||||
const publish = async () => {
|
const ndkEvent = new NDKEvent(ndk, signedEvent)
|
||||||
const metadataController = await MetadataController.getInstance()
|
publish(ndkEvent)
|
||||||
const modAuthorReadRelays = await metadataController.findUserRelays(
|
.then((publishedOnRelays) => {
|
||||||
modDetails.author,
|
|
||||||
UserRelaysType.Read
|
|
||||||
)
|
|
||||||
const commentatorWriteRelays = await metadataController.findUserRelays(
|
|
||||||
pubkey,
|
|
||||||
UserRelaysType.Write
|
|
||||||
)
|
|
||||||
|
|
||||||
const combinedRelays = [
|
|
||||||
...new Set(...modAuthorReadRelays, ...commentatorWriteRelays)
|
|
||||||
]
|
|
||||||
|
|
||||||
const publishedOnRelays =
|
|
||||||
await RelayController.getInstance().publishOnRelays(
|
|
||||||
signedEvent,
|
|
||||||
combinedRelays
|
|
||||||
)
|
|
||||||
|
|
||||||
if (publishedOnRelays.length === 0) {
|
if (publishedOnRelays.length === 0) {
|
||||||
setCommentEvents((prev) =>
|
setCommentEvents((prev) =>
|
||||||
prev.map((event) => {
|
prev.map((event) => {
|
||||||
@ -166,9 +146,22 @@ export const Comments = ({ modDetails, setCommentCount }: Props) => {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
}, 15000)
|
}, 15000)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error('An error occurred in publishing comment', err)
|
||||||
|
setCommentEvents((prev) =>
|
||||||
|
prev.map((event) => {
|
||||||
|
if (event.id === signedEvent.id) {
|
||||||
|
return {
|
||||||
|
...event,
|
||||||
|
status: CommentEventStatus.Failed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
publish()
|
return event
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { InputField } from 'components/Inputs'
|
import { InputField } from 'components/Inputs'
|
||||||
import { ProfileQRButtonWithPopUp } from 'components/ProfileSection'
|
import { ProfileQRButtonWithPopUp } from 'components/ProfileSection'
|
||||||
import { useAppDispatch, useAppSelector } from 'hooks'
|
import { useAppDispatch, useAppSelector, useNDKContext } from 'hooks'
|
||||||
import { kinds, nip19, UnsignedEvent, Event } from 'nostr-tools'
|
import { kinds, nip19, UnsignedEvent, Event } from 'nostr-tools'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
@ -14,7 +14,6 @@ import {
|
|||||||
profileFromEvent,
|
profileFromEvent,
|
||||||
serializeProfile
|
serializeProfile
|
||||||
} from '@nostr-dev-kit/ndk'
|
} from '@nostr-dev-kit/ndk'
|
||||||
import { RelayController } from 'controllers'
|
|
||||||
import { LoadingSpinner } from 'components/LoadingSpinner'
|
import { LoadingSpinner } from 'components/LoadingSpinner'
|
||||||
import { setUser } from 'store/reducers/user'
|
import { setUser } from 'store/reducers/user'
|
||||||
import placeholderMod from '../../assets/img/DEGMods Placeholder Img.png'
|
import placeholderMod from '../../assets/img/DEGMods Placeholder Img.png'
|
||||||
@ -43,6 +42,7 @@ const defaultFormState: FormState = {
|
|||||||
export const ProfileSettings = () => {
|
export const ProfileSettings = () => {
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const userState = useAppSelector((state) => state.user)
|
const userState = useAppSelector((state) => state.user)
|
||||||
|
const { ndk, publish } = useNDKContext()
|
||||||
|
|
||||||
const [isPublishing, setIsPublishing] = useState(false)
|
const [isPublishing, setIsPublishing] = useState(false)
|
||||||
const [formState, setFormState] = useState<FormState>(defaultFormState)
|
const [formState, setFormState] = useState<FormState>(defaultFormState)
|
||||||
@ -163,9 +163,8 @@ export const ProfileSettings = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const publishedOnRelays = await RelayController.getInstance().publish(
|
const ndkEvent = new NDKEvent(ndk, signedEvent)
|
||||||
signedEvent as Event
|
const publishedOnRelays = await publish(ndkEvent)
|
||||||
)
|
|
||||||
|
|
||||||
// Handle cases where publishing failed or succeeded
|
// Handle cases where publishing failed or succeeded
|
||||||
if (publishedOnRelays.length === 0) {
|
if (publishedOnRelays.length === 0) {
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
import { NDKRelayList } from '@nostr-dev-kit/ndk'
|
import { NDKEvent, NDKRelayList, NDKRelayStatus } from '@nostr-dev-kit/ndk'
|
||||||
import { InputField } from 'components/Inputs'
|
import { InputField } from 'components/Inputs'
|
||||||
import { LoadingSpinner } from 'components/LoadingSpinner'
|
import { LoadingSpinner } from 'components/LoadingSpinner'
|
||||||
import {
|
import { MetadataController, UserRelaysType } from 'controllers'
|
||||||
MetadataController,
|
import { useAppSelector, useDidMount, useNDKContext } from 'hooks'
|
||||||
RelayController,
|
|
||||||
UserRelaysType
|
|
||||||
} from 'controllers'
|
|
||||||
import { useAppSelector, useDidMount } from 'hooks'
|
|
||||||
import { Event, kinds, UnsignedEvent } from 'nostr-tools'
|
import { Event, kinds, UnsignedEvent } from 'nostr-tools'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
@ -16,6 +12,7 @@ const READ_MARKER = 'read'
|
|||||||
const WRITE_MARKER = 'write'
|
const WRITE_MARKER = 'write'
|
||||||
|
|
||||||
export const RelaySettings = () => {
|
export const RelaySettings = () => {
|
||||||
|
const { ndk, publish } = useNDKContext()
|
||||||
const userState = useAppSelector((state) => state.user)
|
const userState = useAppSelector((state) => state.user)
|
||||||
const [ndkRelayList, setNDKRelayList] = useState<NDKRelayList | null>(null)
|
const [ndkRelayList, setNDKRelayList] = useState<NDKRelayList | null>(null)
|
||||||
const [isPublishing, setIsPublishing] = useState(false)
|
const [isPublishing, setIsPublishing] = useState(false)
|
||||||
@ -78,11 +75,8 @@ export const RelaySettings = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const publishedOnRelays =
|
const ndkEvent = new NDKEvent(ndk, signedEvent)
|
||||||
await RelayController.getInstance().publishOnRelays(
|
const publishedOnRelays = await publish(ndkEvent)
|
||||||
signedEvent,
|
|
||||||
ndkRelayList.writeRelayUrls
|
|
||||||
)
|
|
||||||
|
|
||||||
// Handle cases where publishing failed or succeeded
|
// Handle cases where publishing failed or succeeded
|
||||||
if (publishedOnRelays.length === 0) {
|
if (publishedOnRelays.length === 0) {
|
||||||
@ -140,11 +134,8 @@ export const RelaySettings = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const publishedOnRelays =
|
const ndkEvent = new NDKEvent(ndk, signedEvent)
|
||||||
await RelayController.getInstance().publishOnRelays(
|
const publishedOnRelays = await publish(ndkEvent)
|
||||||
signedEvent,
|
|
||||||
ndkRelayList.writeRelayUrls
|
|
||||||
)
|
|
||||||
|
|
||||||
// Handle cases where publishing failed or succeeded
|
// Handle cases where publishing failed or succeeded
|
||||||
if (publishedOnRelays.length === 0) {
|
if (publishedOnRelays.length === 0) {
|
||||||
@ -214,11 +205,8 @@ export const RelaySettings = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const publishedOnRelays =
|
const ndkEvent = new NDKEvent(ndk, signedEvent)
|
||||||
await RelayController.getInstance().publishOnRelays(
|
const publishedOnRelays = await publish(ndkEvent)
|
||||||
signedEvent,
|
|
||||||
ndkRelayList.writeRelayUrls
|
|
||||||
)
|
|
||||||
|
|
||||||
// Handle cases where publishing failed or succeeded
|
// Handle cases where publishing failed or succeeded
|
||||||
if (publishedOnRelays.length === 0) {
|
if (publishedOnRelays.length === 0) {
|
||||||
@ -382,18 +370,30 @@ const RelayListItem = ({
|
|||||||
changeRelayType
|
changeRelayType
|
||||||
}: RelayItemProps) => {
|
}: RelayItemProps) => {
|
||||||
const [isConnected, setIsConnected] = useState(false)
|
const [isConnected, setIsConnected] = useState(false)
|
||||||
|
const { ndk } = useNDKContext()
|
||||||
|
|
||||||
useDidMount(() => {
|
useDidMount(() => {
|
||||||
RelayController.getInstance()
|
const ndkPool = ndk.pool
|
||||||
.connectRelay(relayUrl)
|
|
||||||
.then((relay) => {
|
ndkPool.on('relay:connect', (relay) => {
|
||||||
if (relay && relay.connected) {
|
if (relay.url === relayUrl) {
|
||||||
|
setIsConnected(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
ndkPool.on('relay:disconnect', (relay) => {
|
||||||
|
if (relay.url === relayUrl) {
|
||||||
|
setIsConnected(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const relay = ndkPool.relays.get(relayUrl)
|
||||||
|
if (relay && relay.status >= NDKRelayStatus.CONNECTED) {
|
||||||
setIsConnected(true)
|
setIsConnected(true)
|
||||||
} else {
|
} else {
|
||||||
setIsConnected(false)
|
setIsConnected(false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='relayListItem'>
|
<div className='relayListItem'>
|
||||||
|
@ -9,9 +9,8 @@ import {
|
|||||||
UnsignedEvent
|
UnsignedEvent
|
||||||
} from 'nostr-tools'
|
} from 'nostr-tools'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { RelayController } from '../controllers'
|
|
||||||
import { log, LogType } from './utils'
|
import { log, LogType } from './utils'
|
||||||
import { NDKEvent } from '@nostr-dev-kit/ndk'
|
import NDK, { NDKEvent } from '@nostr-dev-kit/ndk'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current time in seconds since the Unix epoch (January 1, 1970).
|
* Get the current time in seconds since the Unix epoch (January 1, 1970).
|
||||||
@ -123,7 +122,11 @@ export const extractZapAmount = (event: Event): number => {
|
|||||||
* @param unsignedEvent - The event object which needs to be signed before publishing.
|
* @param unsignedEvent - The event object which needs to be signed before publishing.
|
||||||
* @returns - A promise that resolves to boolean indicating whether the event was successfully signed and published
|
* @returns - A promise that resolves to boolean indicating whether the event was successfully signed and published
|
||||||
*/
|
*/
|
||||||
export const signAndPublish = async (unsignedEvent: UnsignedEvent) => {
|
export const signAndPublish = async (
|
||||||
|
unsignedEvent: UnsignedEvent,
|
||||||
|
ndk: NDK,
|
||||||
|
publish: (event: NDKEvent) => Promise<string[]>
|
||||||
|
) => {
|
||||||
// Sign the event. This returns a signed event or null if signing fails.
|
// Sign the event. This returns a signed event or null if signing fails.
|
||||||
const signedEvent = await window.nostr
|
const signedEvent = await window.nostr
|
||||||
?.signEvent(unsignedEvent)
|
?.signEvent(unsignedEvent)
|
||||||
@ -138,11 +141,10 @@ export const signAndPublish = async (unsignedEvent: UnsignedEvent) => {
|
|||||||
// If the event couldn't be signed, exit the function and return null.
|
// If the event couldn't be signed, exit the function and return null.
|
||||||
if (!signedEvent) return false
|
if (!signedEvent) return false
|
||||||
|
|
||||||
// Publish the signed event to the relays using the RelayController.
|
// Publish the signed event to the relays.
|
||||||
// This returns an array of relay URLs where the event was successfully published.
|
// This returns an array of relay URLs where the event was successfully published.
|
||||||
const publishedOnRelays = await RelayController.getInstance().publish(
|
const ndkEvent = new NDKEvent(ndk, signedEvent)
|
||||||
signedEvent as Event
|
const publishedOnRelays = await publish(ndkEvent)
|
||||||
)
|
|
||||||
|
|
||||||
// Handle cases where publishing to the relays failed
|
// Handle cases where publishing to the relays failed
|
||||||
if (publishedOnRelays.length === 0) {
|
if (publishedOnRelays.length === 0) {
|
||||||
@ -170,7 +172,9 @@ export const signAndPublish = async (unsignedEvent: UnsignedEvent) => {
|
|||||||
*/
|
*/
|
||||||
export const sendDMUsingRandomKey = async (
|
export const sendDMUsingRandomKey = async (
|
||||||
message: string,
|
message: string,
|
||||||
receiver: string
|
receiver: string,
|
||||||
|
ndk: NDK,
|
||||||
|
publish: (event: NDKEvent) => Promise<string[]>
|
||||||
) => {
|
) => {
|
||||||
// Generate a random secret key for encrypting the message
|
// Generate a random secret key for encrypting the message
|
||||||
const secretKey = generateSecretKey()
|
const secretKey = generateSecretKey()
|
||||||
@ -201,11 +205,8 @@ export const sendDMUsingRandomKey = async (
|
|||||||
// Finalize and sign the event using the generated secret key
|
// Finalize and sign the event using the generated secret key
|
||||||
const signedEvent = finalizeEvent(unsignedEvent, secretKey)
|
const signedEvent = finalizeEvent(unsignedEvent, secretKey)
|
||||||
|
|
||||||
// Publish the signed event (the encrypted DM) to the relays
|
const ndkEvent = new NDKEvent(ndk, signedEvent)
|
||||||
const publishedOnRelays = await RelayController.getInstance().publishDM(
|
const publishedOnRelays = await publish(ndkEvent)
|
||||||
signedEvent,
|
|
||||||
receiver
|
|
||||||
)
|
|
||||||
|
|
||||||
// Handle cases where publishing to the relays failed
|
// Handle cases where publishing to the relays failed
|
||||||
if (publishedOnRelays.length === 0) {
|
if (publishedOnRelays.length === 0) {
|
||||||
|
Loading…
Reference in New Issue
Block a user