diff --git a/src/components/ModForm.tsx b/src/components/ModForm.tsx
index 6e90b1a..763a3f9 100644
--- a/src/components/ModForm.tsx
+++ b/src/components/ModForm.tsx
@@ -13,8 +13,7 @@ import { toast } from 'react-toastify'
import { FixedSizeList as List } from 'react-window'
import { v4 as uuidv4 } from 'uuid'
import { T_TAG_VALUE } from '../constants'
-import { RelayController } from '../controllers'
-import { useAppSelector, useGames } from '../hooks'
+import { useAppSelector, useGames, useNDKContext } from '../hooks'
import { appRoutes, getModPageRoute } from '../routes'
import '../styles/styles.css'
import { DownloadUrl, ModDetails, ModFormState } from '../types'
@@ -29,6 +28,7 @@ import {
} from '../utils'
import { CheckboxField, InputError, InputField } from './Inputs'
import { LoadingSpinner } from './LoadingSpinner'
+import { NDKEvent } from '@nostr-dev-kit/ndk'
interface FormErrors {
game?: string
@@ -54,6 +54,7 @@ type ModFormProps = {
export const ModForm = ({ existingModData }: ModFormProps) => {
const location = useLocation()
const navigate = useNavigate()
+ const { ndk, publish } = useNDKContext()
const games = useGames()
const userState = useAppSelector((state) => state.user)
@@ -243,9 +244,8 @@ export const ModForm = ({ existingModData }: ModFormProps) => {
return
}
- const publishedOnRelays = await RelayController.getInstance().publish(
- signedEvent as Event
- )
+ const ndkEvent = new NDKEvent(ndk, signedEvent)
+ const publishedOnRelays = await publish(ndkEvent)
// Handle cases where publishing failed or succeeded
if (publishedOnRelays.length === 0) {
@@ -763,8 +763,9 @@ const GameDropdown = ({
- Can't find the game you're looking for? You can temporarily publish the mod under '(Unlisted Game)' and
- later edit it with the proper game name once we add it.
+ Can't find the game you're looking for? You can temporarily publish the
+ mod under '(Unlisted Game)' and later edit it with the proper game name
+ once we add it.
@@ -825,10 +826,12 @@ const GameDropdown = ({
-
+
{error && }
-
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.
+
+ 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.
)
diff --git a/src/components/ProfileSection.tsx b/src/components/ProfileSection.tsx
index 170c1e4..972c8ef 100644
--- a/src/components/ProfileSection.tsx
+++ b/src/components/ProfileSection.tsx
@@ -4,7 +4,7 @@ import { QRCodeSVG } from 'qrcode.react'
import { useState } from 'react'
import { Link } from 'react-router-dom'
import { toast } from 'react-toastify'
-import { RelayController, UserRelaysType } from '../controllers'
+import { UserRelaysType } from '../controllers'
import { useAppSelector, useDidMount, useNDKContext } from '../hooks'
import { appRoutes, getProfilePageRoute } from '../routes'
import '../styles/author.css'
@@ -22,6 +22,7 @@ import {
import { LoadingSpinner } from './LoadingSpinner'
import { ZapPopUp } from './Zap'
import placeholder from '../assets/img/DEGMods Placeholder Img.png'
+import { NDKEvent } from '@nostr-dev-kit/ndk'
type Props = {
pubkey: string
@@ -368,7 +369,7 @@ type FollowButtonProps = {
}
const FollowButton = ({ pubkey }: FollowButtonProps) => {
- const { fetchEventFromUserRelays } = useNDKContext()
+ const { ndk, fetchEventFromUserRelays, publish } = useNDKContext()
const [isFollowing, setIsFollowing] = useState(false)
const [isLoading, setIsLoading] = useState(false)
@@ -441,9 +442,8 @@ const FollowButton = ({ pubkey }: FollowButtonProps) => {
if (!signedEvent) return false
- const publishedOnRelays = await RelayController.getInstance().publish(
- signedEvent as Event
- )
+ const ndkEvent = new NDKEvent(ndk, signedEvent)
+ const publishedOnRelays = await publish(ndkEvent)
if (publishedOnRelays.length === 0) {
toast.error('Failed to publish event on any relay')
diff --git a/src/contexts/NDKContext.tsx b/src/contexts/NDKContext.tsx
index da9c2c4..0e92db9 100644
--- a/src/contexts/NDKContext.tsx
+++ b/src/contexts/NDKContext.tsx
@@ -56,6 +56,7 @@ interface NDKContextType {
accumulatedZapAmount: number
hasZapped: boolean
}>
+ publish: (event: NDKEvent) => Promise
}
// 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 => {
+ 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 (
{
fetchEventsFromUserRelays,
fetchEventFromUserRelays,
findMetadata,
- getTotalZapAmount
+ getTotalZapAmount,
+ publish
}}
>
{children}
diff --git a/src/controllers/index.ts b/src/controllers/index.ts
index b7b89e4..b028b93 100644
--- a/src/controllers/index.ts
+++ b/src/controllers/index.ts
@@ -1,3 +1,2 @@
export * from './metadata'
-export * from './relay'
export * from './zap'
diff --git a/src/controllers/relay.ts b/src/controllers/relay.ts
deleted file mode 100644
index e8855c6..0000000
--- a/src/controllers/relay.ts
+++ /dev/null
@@ -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()
- 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 => {
- // 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((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 => {
- // 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 => {
- 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((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
- }
-}
diff --git a/src/hooks/useReactions.ts b/src/hooks/useReactions.ts
index 9ebc63f..60406c2 100644
--- a/src/hooks/useReactions.ts
+++ b/src/hooks/useReactions.ts
@@ -1,6 +1,6 @@
import { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk'
import { REACTIONS } from 'constants.ts'
-import { RelayController, UserRelaysType } from 'controllers'
+import { UserRelaysType } from 'controllers'
import { useAppSelector, useDidMount, useNDKContext } from 'hooks'
import { Event, kinds, UnsignedEvent } from 'nostr-tools'
import { useMemo, useState } from 'react'
@@ -14,7 +14,7 @@ type UseReactionsParams = {
}
export const useReactions = (params: UseReactionsParams) => {
- const { ndk, fetchEventsFromUserRelays } = useNDKContext()
+ const { ndk, fetchEventsFromUserRelays, publish } = useNDKContext()
const [isReactionInProgress, setIsReactionInProgress] = useState(false)
const [isDataLoaded, setIsDataLoaded] = useState(false)
const [reactionEvents, setReactionEvents] = useState([])
@@ -119,13 +119,11 @@ export const useReactions = (params: UseReactionsParams) => {
if (!signedEvent) return
- setReactionEvents((prev) => [...prev, new NDKEvent(ndk, signedEvent)])
+ const ndkEvent = new NDKEvent(ndk, signedEvent)
- const publishedOnRelays = await RelayController.getInstance().publish(
- signedEvent as Event,
- params.pubkey,
- UserRelaysType.Read
- )
+ setReactionEvents((prev) => [...prev, ndkEvent])
+
+ const publishedOnRelays = await publish(ndkEvent)
if (publishedOnRelays.length === 0) {
log(
diff --git a/src/pages/mod/index.tsx b/src/pages/mod/index.tsx
index a85d716..dd678fd 100644
--- a/src/pages/mod/index.tsx
+++ b/src/pages/mod/index.tsx
@@ -53,7 +53,7 @@ export const ModPage = () => {
useDidMount(async () => {
if (naddr) {
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 = {
'#a': [identifier],
@@ -61,7 +61,7 @@ export const ModPage = () => {
kinds: [kind]
}
- fetchEvent(filter, relays)
+ fetchEvent(filter)
.then((event) => {
if (event) {
const extracted = extractModData(event)
@@ -212,7 +212,7 @@ type GameProps = {
}
const Game = ({ naddr, game, author, aTag }: GameProps) => {
- const { fetchEventFromUserRelays } = useNDKContext()
+ const { ndk, fetchEventFromUserRelays, publish } = useNDKContext()
const userState = useAppSelector((state) => state.user)
const [isLoading, setIsLoading] = useState(false)
@@ -343,7 +343,7 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => {
setLoadingSpinnerDesc('Updating mute list event')
- const isUpdated = await signAndPublish(unsignedEvent)
+ const isUpdated = await signAndPublish(unsignedEvent, ndk, publish)
if (isUpdated) {
setIsBlocked(true)
}
@@ -384,7 +384,7 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => {
}
setLoadingSpinnerDesc('Updating mute list event')
- const isUpdated = await signAndPublish(unsignedEvent)
+ const isUpdated = await signAndPublish(unsignedEvent, ndk, publish)
if (isUpdated) {
setIsBlocked(false)
}
@@ -450,7 +450,7 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => {
setLoadingSpinnerDesc('Updating nsfw list event')
- const isUpdated = await signAndPublish(unsignedEvent)
+ const isUpdated = await signAndPublish(unsignedEvent, ndk, publish)
if (isUpdated) {
setIsAddedToNSFW(true)
}
@@ -491,7 +491,7 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => {
}
setLoadingSpinnerDesc('Updating nsfw list event')
- const isUpdated = await signAndPublish(unsignedEvent)
+ const isUpdated = await signAndPublish(unsignedEvent, ndk, publish)
if (isUpdated) {
setIsAddedToNSFW(false)
}
@@ -661,7 +661,7 @@ type ReportPopupProps = {
}
const ReportPopup = ({ aTag, handleClose }: ReportPopupProps) => {
- const { fetchEventFromUserRelays } = useNDKContext()
+ const { ndk, fetchEventFromUserRelays, publish } = useNDKContext()
const userState = useAppSelector((state) => state.user)
const [selectedOptions, setSelectedOptions] = useState({
actuallyCP: false,
@@ -760,7 +760,7 @@ const ReportPopup = ({ aTag, handleClose }: ReportPopupProps) => {
}
setLoadingSpinnerDesc('Updating mute list event')
- const isUpdated = await signAndPublish(unsignedEvent)
+ const isUpdated = await signAndPublish(unsignedEvent, ndk, publish)
if (isUpdated) handleClose()
} else {
const href = window.location.href
@@ -773,7 +773,12 @@ const ReportPopup = ({ aTag, handleClose }: ReportPopupProps) => {
})
setLoadingSpinnerDesc('Sending report')
- const isSent = await sendDMUsingRandomKey(message, reportingPubkey!)
+ const isSent = await sendDMUsingRandomKey(
+ message,
+ reportingPubkey!,
+ ndk,
+ publish
+ )
if (isSent) handleClose()
}
setIsLoading(false)
diff --git a/src/pages/mod/internal/comment/index.tsx b/src/pages/mod/internal/comment/index.tsx
index aeeaf7b..a82c87d 100644
--- a/src/pages/mod/internal/comment/index.tsx
+++ b/src/pages/mod/internal/comment/index.tsx
@@ -1,9 +1,5 @@
+import { NDKEvent } from '@nostr-dev-kit/ndk'
import { ZapPopUp } from 'components/Zap'
-import {
- MetadataController,
- RelayController,
- UserRelaysType
-} from 'controllers'
import { formatDate } from 'date-fns'
import { useAppSelector, useDidMount, useNDKContext, useReactions } from 'hooks'
import { useComments } from 'hooks/useComments'
@@ -47,6 +43,7 @@ type Props = {
}
export const Comments = ({ modDetails, setCommentCount }: Props) => {
+ const { ndk, publish } = useNDKContext()
const { commentEvents, setCommentEvents } = useComments(modDetails)
const [filterOptions, setFilterOptions] = useState({
sort: SortByEnum.Latest,
@@ -82,7 +79,8 @@ export const Comments = ({ modDetails, setCommentCount }: Props) => {
created_at: now(),
tags: [
['e', modDetails.id],
- ['a', modDetails.aTag]
+ ['a', modDetails.aTag],
+ ['p', modDetails.author]
]
}
@@ -105,28 +103,52 @@ export const Comments = ({ modDetails, setCommentCount }: Props) => {
...prev
])
- const publish = async () => {
- const metadataController = await MetadataController.getInstance()
- const modAuthorReadRelays = await metadataController.findUserRelays(
- modDetails.author,
- UserRelaysType.Read
- )
- const commentatorWriteRelays = await metadataController.findUserRelays(
- pubkey,
- UserRelaysType.Write
- )
+ const ndkEvent = new NDKEvent(ndk, signedEvent)
+ publish(ndkEvent)
+ .then((publishedOnRelays) => {
+ if (publishedOnRelays.length === 0) {
+ setCommentEvents((prev) =>
+ prev.map((event) => {
+ if (event.id === signedEvent.id) {
+ return {
+ ...event,
+ status: CommentEventStatus.Failed
+ }
+ }
- const combinedRelays = [
- ...new Set(...modAuthorReadRelays, ...commentatorWriteRelays)
- ]
+ return event
+ })
+ )
+ } else {
+ setCommentEvents((prev) =>
+ prev.map((event) => {
+ if (event.id === signedEvent.id) {
+ return {
+ ...event,
+ status: CommentEventStatus.Published
+ }
+ }
- const publishedOnRelays =
- await RelayController.getInstance().publishOnRelays(
- signedEvent,
- combinedRelays
- )
+ return event
+ })
+ )
+ }
- if (publishedOnRelays.length === 0) {
+ // when an event is successfully published remove the status from it after 15 seconds
+ setTimeout(() => {
+ setCommentEvents((prev) =>
+ prev.map((event) => {
+ if (event.id === signedEvent.id) {
+ delete event.status
+ }
+
+ return event
+ })
+ )
+ }, 15000)
+ })
+ .catch((err) => {
+ console.error('An error occurred in publishing comment', err)
setCommentEvents((prev) =>
prev.map((event) => {
if (event.id === signedEvent.id) {
@@ -139,36 +161,7 @@ export const Comments = ({ modDetails, setCommentCount }: Props) => {
return event
})
)
- } else {
- setCommentEvents((prev) =>
- prev.map((event) => {
- if (event.id === signedEvent.id) {
- return {
- ...event,
- status: CommentEventStatus.Published
- }
- }
-
- return event
- })
- )
- }
-
- // when an event is successfully published remove the status from it after 15 seconds
- setTimeout(() => {
- setCommentEvents((prev) =>
- prev.map((event) => {
- if (event.id === signedEvent.id) {
- delete event.status
- }
-
- return event
- })
- )
- }, 15000)
- }
-
- publish()
+ })
return true
}
diff --git a/src/pages/settings/profile.tsx b/src/pages/settings/profile.tsx
index 52f69f7..907d0f0 100644
--- a/src/pages/settings/profile.tsx
+++ b/src/pages/settings/profile.tsx
@@ -1,6 +1,6 @@
import { InputField } from 'components/Inputs'
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 { useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
@@ -14,7 +14,6 @@ import {
profileFromEvent,
serializeProfile
} from '@nostr-dev-kit/ndk'
-import { RelayController } from 'controllers'
import { LoadingSpinner } from 'components/LoadingSpinner'
import { setUser } from 'store/reducers/user'
import placeholderMod from '../../assets/img/DEGMods Placeholder Img.png'
@@ -43,6 +42,7 @@ const defaultFormState: FormState = {
export const ProfileSettings = () => {
const dispatch = useAppDispatch()
const userState = useAppSelector((state) => state.user)
+ const { ndk, publish } = useNDKContext()
const [isPublishing, setIsPublishing] = useState(false)
const [formState, setFormState] = useState(defaultFormState)
@@ -163,9 +163,8 @@ export const ProfileSettings = () => {
return
}
- const publishedOnRelays = await RelayController.getInstance().publish(
- signedEvent as Event
- )
+ const ndkEvent = new NDKEvent(ndk, signedEvent)
+ const publishedOnRelays = await publish(ndkEvent)
// Handle cases where publishing failed or succeeded
if (publishedOnRelays.length === 0) {
diff --git a/src/pages/settings/relay.tsx b/src/pages/settings/relay.tsx
index ee79d94..90beed2 100644
--- a/src/pages/settings/relay.tsx
+++ b/src/pages/settings/relay.tsx
@@ -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 { LoadingSpinner } from 'components/LoadingSpinner'
-import {
- MetadataController,
- RelayController,
- UserRelaysType
-} from 'controllers'
-import { useAppSelector, useDidMount } from 'hooks'
+import { MetadataController, UserRelaysType } from 'controllers'
+import { useAppSelector, useDidMount, useNDKContext } from 'hooks'
import { Event, kinds, UnsignedEvent } from 'nostr-tools'
import { useEffect, useState } from 'react'
import { toast } from 'react-toastify'
@@ -16,6 +12,7 @@ const READ_MARKER = 'read'
const WRITE_MARKER = 'write'
export const RelaySettings = () => {
+ const { ndk, publish } = useNDKContext()
const userState = useAppSelector((state) => state.user)
const [ndkRelayList, setNDKRelayList] = useState(null)
const [isPublishing, setIsPublishing] = useState(false)
@@ -78,11 +75,8 @@ export const RelaySettings = () => {
return
}
- const publishedOnRelays =
- await RelayController.getInstance().publishOnRelays(
- signedEvent,
- ndkRelayList.writeRelayUrls
- )
+ const ndkEvent = new NDKEvent(ndk, signedEvent)
+ const publishedOnRelays = await publish(ndkEvent)
// Handle cases where publishing failed or succeeded
if (publishedOnRelays.length === 0) {
@@ -140,11 +134,8 @@ export const RelaySettings = () => {
return
}
- const publishedOnRelays =
- await RelayController.getInstance().publishOnRelays(
- signedEvent,
- ndkRelayList.writeRelayUrls
- )
+ const ndkEvent = new NDKEvent(ndk, signedEvent)
+ const publishedOnRelays = await publish(ndkEvent)
// Handle cases where publishing failed or succeeded
if (publishedOnRelays.length === 0) {
@@ -214,11 +205,8 @@ export const RelaySettings = () => {
return
}
- const publishedOnRelays =
- await RelayController.getInstance().publishOnRelays(
- signedEvent,
- ndkRelayList.writeRelayUrls
- )
+ const ndkEvent = new NDKEvent(ndk, signedEvent)
+ const publishedOnRelays = await publish(ndkEvent)
// Handle cases where publishing failed or succeeded
if (publishedOnRelays.length === 0) {
@@ -382,17 +370,29 @@ const RelayListItem = ({
changeRelayType
}: RelayItemProps) => {
const [isConnected, setIsConnected] = useState(false)
+ const { ndk } = useNDKContext()
useDidMount(() => {
- RelayController.getInstance()
- .connectRelay(relayUrl)
- .then((relay) => {
- if (relay && relay.connected) {
- setIsConnected(true)
- } else {
- setIsConnected(false)
- }
- })
+ const ndkPool = ndk.pool
+
+ ndkPool.on('relay:connect', (relay) => {
+ 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)
+ } else {
+ setIsConnected(false)
+ }
})
return (
diff --git a/src/utils/nostr.ts b/src/utils/nostr.ts
index f3711b0..130d023 100644
--- a/src/utils/nostr.ts
+++ b/src/utils/nostr.ts
@@ -9,9 +9,8 @@ import {
UnsignedEvent
} from 'nostr-tools'
import { toast } from 'react-toastify'
-import { RelayController } from '../controllers'
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).
@@ -123,7 +122,11 @@ export const extractZapAmount = (event: Event): number => {
* @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
*/
-export const signAndPublish = async (unsignedEvent: UnsignedEvent) => {
+export const signAndPublish = async (
+ unsignedEvent: UnsignedEvent,
+ ndk: NDK,
+ publish: (event: NDKEvent) => Promise
+) => {
// Sign the event. This returns a signed event or null if signing fails.
const signedEvent = await window.nostr
?.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 (!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.
- const publishedOnRelays = await RelayController.getInstance().publish(
- signedEvent as Event
- )
+ const ndkEvent = new NDKEvent(ndk, signedEvent)
+ const publishedOnRelays = await publish(ndkEvent)
// Handle cases where publishing to the relays failed
if (publishedOnRelays.length === 0) {
@@ -170,7 +172,9 @@ export const signAndPublish = async (unsignedEvent: UnsignedEvent) => {
*/
export const sendDMUsingRandomKey = async (
message: string,
- receiver: string
+ receiver: string,
+ ndk: NDK,
+ publish: (event: NDKEvent) => Promise
) => {
// Generate a random secret key for encrypting the message
const secretKey = generateSecretKey()
@@ -201,11 +205,8 @@ export const sendDMUsingRandomKey = async (
// Finalize and sign the event using the generated secret key
const signedEvent = finalizeEvent(unsignedEvent, secretKey)
- // Publish the signed event (the encrypted DM) to the relays
- const publishedOnRelays = await RelayController.getInstance().publishDM(
- signedEvent,
- receiver
- )
+ const ndkEvent = new NDKEvent(ndk, signedEvent)
+ const publishedOnRelays = await publish(ndkEvent)
// Handle cases where publishing to the relays failed
if (publishedOnRelays.length === 0) {