diff --git a/src/components/ProfileSection.tsx b/src/components/ProfileSection.tsx index fa4d6e8..72d1ee9 100644 --- a/src/components/ProfileSection.tsx +++ b/src/components/ProfileSection.tsx @@ -4,12 +4,8 @@ import { QRCodeSVG } from 'qrcode.react' import { useState } from 'react' import { Link } from 'react-router-dom' import { toast } from 'react-toastify' -import { - MetadataController, - RelayController, - UserRelaysType -} from '../controllers' -import { useAppSelector, useDidMount } from '../hooks' +import { RelayController, UserRelaysType } from '../controllers' +import { useAppSelector, useDidMount, useNDKContext } from '../hooks' import { appRoutes, getProfilePageRoute } from '../routes' import '../styles/author.css' import '../styles/innerPage.css' @@ -31,11 +27,11 @@ type Props = { } export const ProfileSection = ({ pubkey }: Props) => { + const { findMetadata } = useNDKContext() const [profile, setProfile] = useState() - useDidMount(async () => { - const metadataController = await MetadataController.getInstance() - metadataController.findMetadata(pubkey).then((res) => { + useDidMount(() => { + findMetadata(pubkey).then((res) => { setProfile(res) }) }) @@ -371,6 +367,7 @@ type FollowButtonProps = { } const FollowButton = ({ pubkey }: FollowButtonProps) => { + const { fetchEventFromUserRelays } = useNDKContext() const [isFollowing, setIsFollowing] = useState(false) const [isLoading, setIsLoading] = useState(false) @@ -409,12 +406,11 @@ const FollowButton = ({ pubkey }: FollowButtonProps) => { authors: [userHexKey] } - const contactListEvent = - await RelayController.getInstance().fetchEventFromUserRelays( - filter, - userHexKey, - UserRelaysType.Both - ) + const contactListEvent = await fetchEventFromUserRelays( + filter, + userHexKey, + UserRelaysType.Both + ) if (!contactListEvent) return { @@ -513,12 +509,11 @@ const FollowButton = ({ pubkey }: FollowButtonProps) => { authors: [userHexKey] } - const contactListEvent = - await RelayController.getInstance().fetchEventFromUserRelays( - filter, - userHexKey, - UserRelaysType.Both - ) + const contactListEvent = await fetchEventFromUserRelays( + filter, + userHexKey, + UserRelaysType.Both + ) if ( !contactListEvent || diff --git a/src/components/Zap.tsx b/src/components/Zap.tsx index 7273465..e7c7005 100644 --- a/src/components/Zap.tsx +++ b/src/components/Zap.tsx @@ -10,7 +10,7 @@ import React, { import Countdown, { CountdownRenderProps } from 'react-countdown' import { toast } from 'react-toastify' import { MetadataController, ZapController } from '../controllers' -import { useAppSelector, useDidMount } from '../hooks' +import { useAppSelector, useDidMount, useNDKContext } from '../hooks' import '../styles/popup.css' import { PaymentRequest, UserProfile } from '../types' import { @@ -133,9 +133,11 @@ export const ZapQR = React.memo( setTotalZapAmount, setHasZapped }: ZapQRProps) => { + const { ndk } = useNDKContext() + useDidMount(() => { ZapController.getInstance() - .pollZapReceipt(paymentRequest) + .pollZapReceipt(paymentRequest, ndk) .then((zapReceipt) => { toast.success(`Successfully sent sats!`) if (setTotalZapAmount) { @@ -249,6 +251,7 @@ export const ZapPopUp = ({ setHasZapped, handleClose }: ZapPopUpProps) => { + const { findMetadata } = useNDKContext() const [isLoading, setIsLoading] = useState(false) const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('') const [amount, setAmount] = useState(0) @@ -282,9 +285,8 @@ export const ZapPopUp = ({ } setLoadingSpinnerDesc('finding receiver metadata') - const metadataController = await MetadataController.getInstance() - const receiverMetadata = await metadataController.findMetadata(receiver) + const receiverMetadata = await findMetadata(receiver) if (!receiverMetadata?.lud16) { setIsLoading(false) @@ -480,6 +482,7 @@ export const ZapSplit = ({ setHasZapped, handleClose }: ZapSplitProps) => { + const { findMetadata } = useNDKContext() const [isLoading, setIsLoading] = useState(false) const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('') const [amount, setAmount] = useState(0) @@ -495,12 +498,12 @@ export const ZapSplit = ({ const [invoices, setInvoices] = useState>() useDidMount(async () => { - const metadataController = await MetadataController.getInstance() - metadataController.findMetadata(pubkey).then((res) => { + findMetadata(pubkey).then((res) => { setAuthor(res) }) - metadataController.findAdminMetadata().then((res) => { + const metadataController = await MetadataController.getInstance() + findMetadata(metadataController.adminNpubs[0]).then((res) => { setAdmin(res) }) }) diff --git a/src/contexts/NDKContext.tsx b/src/contexts/NDKContext.tsx index 5482710..0ec3771 100644 --- a/src/contexts/NDKContext.tsx +++ b/src/contexts/NDKContext.tsx @@ -4,16 +4,19 @@ import NDK, { NDKFilter, NDKKind, NDKRelaySet, - NDKSubscriptionCacheUsage + NDKSubscriptionCacheUsage, + NDKUser } from '@nostr-dev-kit/ndk' import NDKCacheAdapterDexie from '@nostr-dev-kit/ndk-cache-dexie' import { MOD_FILTER_LIMIT, T_TAG_VALUE } from 'constants.ts' +import { UserRelaysType } from 'controllers' import { Dexie } from 'dexie' import { createContext, ReactNode, useEffect, useMemo } from 'react' import { toast } from 'react-toastify' -import { ModDetails } from 'types' +import { ModDetails, UserProfile } from 'types' import { constructModListFromEvents, + hexToNpub, log, LogType, npubToHex, @@ -31,6 +34,22 @@ interface NDKContextType { ndk: NDK fetchMods: (opts: FetchModsOptions) => Promise fetchEvents: (filter: NDKFilter, relayUrls?: string[]) => Promise + fetchEvent: ( + filter: NDKFilter, + relayUrls?: string[] + ) => Promise + + fetchEventsFromUserRelays: ( + filter: NDKFilter, + hexKey: string, + userRelaysType: UserRelaysType + ) => Promise + fetchEventFromUserRelays: ( + filter: NDKFilter, + hexKey: string, + userRelaysType: UserRelaysType + ) => Promise + findMetadata: (pubkey: string) => Promise } // Create the context with an initial value of `null` @@ -56,6 +75,7 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => { const ndk = useMemo(() => { localStorage.setItem('debug', '*') const dexieAdapter = new NDKCacheAdapterDexie({ dbName: 'degmod-db' }) + dexieAdapter.locking = true const ndk = new NDK({ enableOutboxModel: true, autoConnectUserRelays: true, @@ -158,6 +178,14 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => { }) } + /** + * Asynchronously retrieves multiple event from a set of relays based on a provided filter. + * If no relays are specified, it defaults to using connected relays. + * + * @param filter - The filter criteria to find the event. + * @param relays - An optional array of relay URLs to search for the event. + * @returns Returns a promise that resolves to the found event or null if not found. + */ const fetchEvents = async ( filter: NDKFilter, relayUrls: string[] = [] @@ -207,19 +235,112 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => { }) .catch((err) => { // Log the error and show a notification if fetching fails - log( - true, - LogType.Error, - 'An error occurred in fetching mods from relays', - err - ) - toast.error('An error occurred in fetching mods from relays') // Show error notification + log(true, LogType.Error, 'An error occurred in fetching events', err) + toast.error('An error occurred in fetching events') // Show error notification return [] // Return an empty array in case of an error }) } + /** + * Asynchronously retrieves an event from a set of relays based on a provided filter. + * If no relays are specified, it defaults to using connected relays. + * + * @param filter - The filter criteria to find the event. + * @param relaysUrls - An optional array of relay URLs to search for the event. + * @returns Returns a promise that resolves to the found event or null if not found. + */ + const fetchEvent = async (filter: NDKFilter, relayUrls: string[] = []) => { + const events = await fetchEvents(filter, relayUrls) + if (events.length === 0) return null + return events[0] + } + + /** + * Asynchronously retrieves multiple events from the user's relays based on a specified filter. + * The function first retrieves the user's relays, and then fetches the events using the provided filter. + * + * @param filter - The event filter to use when fetching the event (e.g., kinds, authors). + * @param hexKey - The hexadecimal representation of the user's public key. + * @param userRelaysType - The type of relays to search (e.g., write, read). + * @returns A promise that resolves with an array of events. + */ + const fetchEventsFromUserRelays = async ( + filter: NDKFilter, + hexKey: string, + userRelaysType: UserRelaysType + ) => { + // Find the user's relays. + const relayUrls = await getRelayListForUser(hexKey, ndk) + .then((ndkRelayList) => { + if (ndkRelayList) return ndkRelayList[userRelaysType] + return [] // Return an empty array if ndkRelayList is undefined + }) + .catch((err) => { + log( + true, + LogType.Error, + `An error occurred in fetching user's (${hexKey}) ${userRelaysType}`, + err + ) + return [] as string[] + }) + + // Fetch the event from the user's relays using the provided filter and relay URLs + return fetchEvents(filter, relayUrls) + } + + /** + * Fetches an event from the user's relays based on a specified filter. + * The function first retrieves the user's relays, and then fetches the event using the provided filter. + * + * @param filter - The event filter to use when fetching the event (e.g., kinds, authors). + * @param hexKey - The hexadecimal representation of the user's public key. + * @param userRelaysType - The type of relays to search (e.g., write, read). + * @returns A promise that resolves to the fetched event or null if the operation fails. + */ + const fetchEventFromUserRelays = async ( + filter: NDKFilter, + hexKey: string, + userRelaysType: UserRelaysType + ) => { + const events = await fetchEventsFromUserRelays( + filter, + hexKey, + userRelaysType + ) + if (events.length === 0) return null + return events[0] + } + + /** + * Finds metadata for a given pubkey. + * + * @param hexKey - The pubkey to search for metadata. + * @returns A promise that resolves to the metadata event. + */ + const findMetadata = async (pubkey: string): Promise => { + const npub = hexToNpub(pubkey) + + const user = new NDKUser({ npub }) + user.ndk = ndk + + const userProfile = await user.fetchProfile() + + return userProfile + } + return ( - + {children} ) diff --git a/src/controllers/metadata.ts b/src/controllers/metadata.ts index 6cebdcb..98967d4 100644 --- a/src/controllers/metadata.ts +++ b/src/controllers/metadata.ts @@ -1,8 +1,7 @@ -import NDK, { getRelayListForUser, NDKList, NDKUser } from '@nostr-dev-kit/ndk' +import NDK, { getRelayListForUser, NDKList } from '@nostr-dev-kit/ndk' import { kinds } from 'nostr-tools' import { MuteLists } from '../types' -import { UserProfile } from '../types/user' -import { hexToNpub, log, LogType, npubToHex, timeout } from '../utils' +import { log, LogType, npubToHex, timeout } from '../utils' export enum UserRelaysType { Read = 'readRelayUrls', @@ -16,7 +15,6 @@ export enum UserRelaysType { export class MetadataController { private static instance: MetadataController private ndk: NDK - private usersMetadata = new Map() public adminNpubs: string[] public adminRelays = new Set() public reportingNpub: string @@ -84,40 +82,6 @@ export class MetadataController { return MetadataController.instance } - /** - * Finds metadata for a given pubkey. - * - * @param hexKey - The pubkey to search for metadata. - * @returns A promise that resolves to the metadata event. - */ - public findMetadata = async (pubkey: string): Promise => { - const npub = hexToNpub(pubkey) - - const cachedMetadata = this.usersMetadata.get(npub) - if (cachedMetadata) { - return cachedMetadata - } - - const user = new NDKUser({ npub }) - user.ndk = this.ndk - - const userProfile = await user.fetchProfile() - if (userProfile) { - this.usersMetadata.set(npub, userProfile) - } - - return userProfile - } - - /** - * Finds metadata for admin user. - * - * @returns A promise that resolves to the metadata event. - */ - public findAdminMetadata = async (): Promise => { - return this.findMetadata(this.adminNpubs[0]) - } - public findUserRelays = async ( hexKey: string, userRelaysType: UserRelaysType = UserRelaysType.Both diff --git a/src/controllers/relay.ts b/src/controllers/relay.ts index bc347bc..2fdd6ae 100644 --- a/src/controllers/relay.ts +++ b/src/controllers/relay.ts @@ -372,217 +372,6 @@ export class RelayController { return publishedOnRelays } - /** - * Asynchronously retrieves multiple event from a set of relays based on a provided filter. - * If no relays are specified, it defaults to using connected relays. - * - * @param {Filter} filter - The filter criteria to find the event. - * @param {string[]} [relays] - An optional array of relay URLs to search for the event. - * @returns {Promise} - Returns a promise that resolves to the found event or null if not found. - */ - fetchEvents = async ( - filter: Filter, - relayUrls: string[] = [] - ): Promise => { - const relaySet = new Set() - - // add all the relays passed to relay set - relayUrls.forEach((relayUrl) => { - relaySet.add(relayUrl) - }) - - relaySet.add(import.meta.env.VITE_APP_RELAY) - - const metadataController = await MetadataController.getInstance() - // add admin relays to relays array - metadataController.adminRelays.forEach((relayUrl) => { - relaySet.add(relayUrl) - }) - - relayUrls = Array.from(relaySet) - - // 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) { - throw new Error('No relay is connected to fetch events!') - } - - const events: Event[] = [] - const eventIds = new Set() // To keep track of event IDs and avoid duplicates - - // Create a promise for each relay subscription - const subPromises = relays.map((relay) => { - return new Promise((resolve) => { - // Subscribe to the relay with the specified filter - const sub = relay.subscribe([filter], { - // Handle incoming events - onevent: (e) => { - // Add the event to the array if it's not a duplicate - if (!eventIds.has(e.id)) { - eventIds.add(e.id) // Record the event ID - events.push(e) // Add the event to the array - } - }, - // 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) - - // It is possible that different relays will send different events and events array may contain more events then specified limit in filter - // To fix this issue we'll first sort these events and then return only limited events - if (filter.limit) { - // Sort events by creation date in descending order - events.sort((a, b) => b.created_at - a.created_at) - - return events.slice(0, filter.limit) - } - - return events - } - - /** - * Asynchronously retrieves an event from a set of relays based on a provided filter. - * If no relays are specified, it defaults to using connected relays. - * - * @param {Filter} filter - The filter criteria to find the event. - * @param {string[]} [relays] - An optional array of relay URLs to search for the event. - * @returns {Promise} - Returns a promise that resolves to the found event or null if not found. - */ - fetchEvent = async ( - filter: Filter, - relays: string[] = [] - ): Promise => { - // first check if event is present in cached map then return that - // otherwise query relays - if (filter['#a']) { - const aTag = filter['#a'][0] - const cachedEvent = this.events.get(aTag) - - if (cachedEvent) return cachedEvent - } - - const events = await this.fetchEvents(filter, relays) - - // Sort events by creation date in descending order - events.sort((a, b) => b.created_at - a.created_at) - - if (events.length > 0) { - const event = events[0] - - // if the aTag was specified in filter then cache the fetched event before returning - if (filter['#a']) { - const aTag = filter['#a'][0] - this.events.set(aTag, event) - } - - // return the event - return event - } - - // return null if event array is empty - return null - } - - /** - * Asynchronously retrieves multiple events from the user's relays based on a specified filter. - * The function first retrieves the user's relays, and then fetches the events using the provided filter. - * - * @param filter - The event filter to use when fetching the event (e.g., kinds, authors). - * @param hexKey - The hexadecimal representation of the user's public key. - * @param userRelaysType - The type of relays to search (e.g., write, read). - * @returns A promise that resolves with an array of events. - */ - fetchEventsFromUserRelays = async ( - filter: Filter, - hexKey: string, - userRelaysType: UserRelaysType - ): Promise => { - // Get an instance of the MetadataController, which manages user metadata and relays - const metadataController = await MetadataController.getInstance() - - // Find the user's relays using the MetadataController. - const relayUrls = await metadataController.findUserRelays( - hexKey, - userRelaysType - ) - - // Fetch the event from the user's relays using the provided filter and relay URLs - return this.fetchEvents(filter, relayUrls) - } - - /** - * Fetches an event from the user's relays based on a specified filter. - * The function first retrieves the user's relays, and then fetches the event using the provided filter. - * - * @param filter - The event filter to use when fetching the event (e.g., kinds, authors). - * @param hexKey - The hexadecimal representation of the user's public key. - * @param userRelaysType - The type of relays to search (e.g., write, read). - * @returns A promise that resolves to the fetched event or null if the operation fails. - */ - fetchEventFromUserRelays = async ( - filter: Filter, - hexKey: string, - userRelaysType: UserRelaysType - ): Promise => { - // first check if event is present in cached map then return that - // otherwise query relays - if (filter['#a']) { - const aTag = filter['#a'][0] - const cachedEvent = this.events.get(aTag) - - if (cachedEvent) return cachedEvent - } - - const events = await this.fetchEventsFromUserRelays( - filter, - hexKey, - userRelaysType - ) - // Sort events by creation date in descending order - events.sort((a, b) => b.created_at - a.created_at) - - if (events.length > 0) { - const event = events[0] - - // if the aTag was specified in filter then cache the fetched event before returning - if (filter['#a']) { - const aTag = filter['#a'][0] - this.events.set(aTag, event) - } - - // return the event - return event - } - - // return null if event array is empty - return null - } - /** * Subscribes to events from multiple relays. * diff --git a/src/controllers/zap.ts b/src/controllers/zap.ts index 5bbaf51..8b74dd7 100644 --- a/src/controllers/zap.ts +++ b/src/controllers/zap.ts @@ -1,6 +1,12 @@ import { Invoice } from '@getalby/lightning-tools' +import NDK, { + NDKFilter, + NDKKind, + NDKRelaySet, + NDKSubscriptionCacheUsage +} from '@nostr-dev-kit/ndk' import axios, { AxiosInstance } from 'axios' -import { Filter, kinds } from 'nostr-tools' +import { kinds } from 'nostr-tools' import { requestProvider, SendPaymentResponse, WebLNProvider } from 'webln' import { isLnurlResponse, @@ -11,7 +17,6 @@ import { ZapRequest } from '../types' import { log, LogType, npubToHex } from '../utils' -import { RelayController } from './relay' import { MetadataController, UserRelaysType } from './metadata' /** @@ -134,6 +139,7 @@ export class ZapController { */ async pollZapReceipt( paymentRequest: PaymentRequest, + ndk: NDK, pollingTimeout?: number ) { const { pr, ...zapRequest } = paymentRequest @@ -148,7 +154,7 @@ export class ZapController { const cleanup = () => { clearTimeout(timeout) - subscriptions.forEach((subscription) => subscription.close()) + subscription.stop() } // Polling timeout @@ -168,32 +174,35 @@ export class ZapController { const relayUrls = relaysTag.slice(1) // filter relay for event of kind 9735 - const filter: Filter = { - kinds: [kinds.Zap], + const filter: NDKFilter = { + kinds: [NDKKind.Zap], since: created_at } - const subscriptions = - await RelayController.getInstance().subscribeForEvents( - filter, - relayUrls, - async (event) => { - // get description tag of the event - const description = event.tags.filter( - (tag) => tag[0] === 'description' - )[0] + const subscription = ndk.subscribe( + filter, + { + closeOnEose: false, + cacheUsage: NDKSubscriptionCacheUsage.ONLY_RELAY + }, + NDKRelaySet.fromRelayUrls(relayUrls, ndk, true) + ) - // compare description tag of the event with stringified zap request - if (description[1] === zapRequestStringified) { - // validate zap receipt - if (await this.validateZapReceipt(pr, event as ZapReceipt)) { - cleanup() + subscription.on('event', async (ndkEvent) => { + // compare description tag of the event with stringified zap request + if (ndkEvent.tagValue('description') === zapRequestStringified) { + // validate zap receipt + if ( + await this.validateZapReceipt(pr, ndkEvent.rawEvent() as ZapReceipt) + ) { + cleanup() - resolve(event as ZapReceipt) - } - } + resolve(ndkEvent.rawEvent() as ZapReceipt) } - ) + } + }) + + subscription.start() }) } diff --git a/src/hooks/useComments.ts b/src/hooks/useComments.ts index 808e67d..5dd120a 100644 --- a/src/hooks/useComments.ts +++ b/src/hooks/useComments.ts @@ -1,43 +1,87 @@ import { - MetadataController, - RelayController, - UserRelaysType -} from 'controllers' -import { Filter, kinds } from 'nostr-tools' -import { useState } from 'react' + getRelayListForUser, + NDKFilter, + NDKKind, + NDKRelaySet, + NDKSubscription, + NDKSubscriptionCacheUsage +} from '@nostr-dev-kit/ndk' +import { UserRelaysType } from 'controllers' +import { useEffect, useState } from 'react' import { CommentEvent, ModDetails } from 'types' -import { useDidMount } from './useDidMount' +import { log, LogType } from 'utils' +import { useNDKContext } from './useNDKContext' export const useComments = (mod: ModDetails) => { + const { ndk } = useNDKContext() const [commentEvents, setCommentEvents] = useState([]) - useDidMount(async () => { - const metadataController = await MetadataController.getInstance() + useEffect(() => { + let subscription: NDKSubscription // Define the subscription variable here for cleanup - const authorReadRelays = await metadataController.findUserRelays( - mod.author, - UserRelaysType.Read - ) + const setupSubscription = async () => { + // Find the mod author's relays. + const authorReadRelays = await getRelayListForUser(mod.author, ndk) + .then((ndkRelayList) => { + if (ndkRelayList) return ndkRelayList[UserRelaysType.Read] + return [] // Return an empty array if ndkRelayList is undefined + }) + .catch((err) => { + log( + true, + LogType.Error, + `An error occurred in fetching user's (${mod.author}) ${UserRelaysType.Read}`, + err + ) + return [] as string[] + }) - const filter: Filter = { - kinds: [kinds.ShortTextNote], - '#a': [mod.aTag] - } + const filter: NDKFilter = { + kinds: [NDKKind.Text], + '#a': [mod.aTag] + } - RelayController.getInstance().subscribeForEvents( - filter, - authorReadRelays, - (event) => { + subscription = ndk.subscribe( + filter, + { + closeOnEose: false, + cacheUsage: NDKSubscriptionCacheUsage.CACHE_FIRST + }, + NDKRelaySet.fromRelayUrls(authorReadRelays, ndk, true) + ) + + subscription.on('event', (ndkEvent) => { setCommentEvents((prev) => { - if (prev.find((e) => e.id === event.id)) { + if (prev.find((e) => e.id === ndkEvent.id)) { return [...prev] } - return [event, ...prev] + const commentEvent: CommentEvent = { + kind: NDKKind.Text, + tags: ndkEvent.tags, + content: ndkEvent.content, + created_at: ndkEvent.created_at!, + pubkey: ndkEvent.pubkey, + id: ndkEvent.id, + sig: ndkEvent.sig! + } + + return [commentEvent, ...prev] }) + }) + + subscription.start() + } + + setupSubscription() + + // Cleanup function to stop the subscription on unmount + return () => { + if (subscription) { + subscription.stop() } - ) - }) + } + }, [mod.aTag, mod.author, ndk]) return { commentEvents, diff --git a/src/hooks/useNDKContext.ts b/src/hooks/useNDKContext.ts index 9c2f340..b551d5e 100644 --- a/src/hooks/useNDKContext.ts +++ b/src/hooks/useNDKContext.ts @@ -9,11 +9,23 @@ export const useNDKContext = () => { 'NDKContext should not be used in out component tree hierarchy' ) - const { ndk, fetchEvents, fetchMods } = ndkContext + const { + ndk, + fetchEvents, + fetchEvent, + fetchEventsFromUserRelays, + fetchEventFromUserRelays, + fetchMods, + findMetadata + } = ndkContext return { ndk, fetchEvents, - fetchMods + fetchEvent, + fetchEventsFromUserRelays, + fetchEventFromUserRelays, + fetchMods, + findMetadata } } diff --git a/src/hooks/useReactions.ts b/src/hooks/useReactions.ts index 5ce1716..9ebc63f 100644 --- a/src/hooks/useReactions.ts +++ b/src/hooks/useReactions.ts @@ -1,9 +1,10 @@ -import { useState, useMemo } from 'react' -import { toast } from 'react-toastify' +import { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk' import { REACTIONS } from 'constants.ts' import { RelayController, UserRelaysType } from 'controllers' -import { useAppSelector, useDidMount } from 'hooks' -import { Event, Filter, UnsignedEvent, kinds } from 'nostr-tools' +import { useAppSelector, useDidMount, useNDKContext } from 'hooks' +import { Event, kinds, UnsignedEvent } from 'nostr-tools' +import { useMemo, useState } from 'react' +import { toast } from 'react-toastify' import { abbreviateNumber, log, LogType, now } from 'utils' type UseReactionsParams = { @@ -13,14 +14,15 @@ type UseReactionsParams = { } export const useReactions = (params: UseReactionsParams) => { + const { ndk, fetchEventsFromUserRelays } = useNDKContext() const [isReactionInProgress, setIsReactionInProgress] = useState(false) const [isDataLoaded, setIsDataLoaded] = useState(false) - const [reactionEvents, setReactionEvents] = useState([]) + const [reactionEvents, setReactionEvents] = useState([]) const userState = useAppSelector((state) => state.user) useDidMount(() => { - const filter: Filter = { + const filter: NDKFilter = { kinds: [kinds.Reaction] } @@ -30,8 +32,7 @@ export const useReactions = (params: UseReactionsParams) => { filter['#e'] = [params.eTag] } - RelayController.getInstance() - .fetchEventsFromUserRelays(filter, params.pubkey, UserRelaysType.Read) + fetchEventsFromUserRelays(filter, params.pubkey, UserRelaysType.Read) .then((events) => { setReactionEvents(events) }) @@ -118,7 +119,7 @@ export const useReactions = (params: UseReactionsParams) => { if (!signedEvent) return - setReactionEvents((prev) => [...prev, signedEvent]) + setReactionEvents((prev) => [...prev, new NDKEvent(ndk, signedEvent)]) const publishedOnRelays = await RelayController.getInstance().publish( signedEvent as Event, diff --git a/src/layout/header.tsx b/src/layout/header.tsx index 16382a7..049d748 100644 --- a/src/layout/header.tsx +++ b/src/layout/header.tsx @@ -7,7 +7,12 @@ import { Link } from 'react-router-dom' import { Banner } from '../components/Banner' import { ZapPopUp } from '../components/Zap' import { MetadataController } from '../controllers' -import { useAppDispatch, useAppSelector, useDidMount } from '../hooks' +import { + useAppDispatch, + useAppSelector, + useDidMount, + useNDKContext +} from '../hooks' import { appRoutes } from '../routes' import { setAuth, setUser } from '../store/reducers/user' import mainStyles from '../styles//main.module.scss' @@ -17,6 +22,7 @@ import { npubToHex } from '../utils' export const Header = () => { const dispatch = useAppDispatch() + const { findMetadata } = useNDKContext() const userState = useAppSelector((state) => state.user) useEffect(() => { @@ -41,23 +47,21 @@ export const Header = () => { pubkey: npubToHex(npub)! }) ) - MetadataController.getInstance().then((metadataController) => { - metadataController.findMetadata(npub).then((userProfile) => { - if (userProfile) { - dispatch( - setUser({ - npub, - pubkey: npubToHex(npub)!, - ...userProfile - }) - ) - } - }) + findMetadata(npub).then((userProfile) => { + if (userProfile) { + dispatch( + setUser({ + npub, + pubkey: npubToHex(npub)!, + ...userProfile + }) + ) + } }) } } }) - }, [dispatch]) + }, [dispatch, findMetadata]) const handleLogin = () => { launchNostrLoginDialog() @@ -357,8 +361,14 @@ const RegisterButtonWithDialog = () => {

Once you create your "account" on any of these ( - Here's a quick video guide - ), come back and click login, then sign-in with extension. + + Here's a quick video guide + + ), come back and click login, then sign-in with + extension.

{ const params = useParams() const { name: gameName } = params + const { ndk } = useNDKContext() const muteLists = useMuteLists() const nsfwList = useNSFWList() @@ -38,8 +40,6 @@ export const GamePage = () => { }) const [mods, setMods] = useState([]) - const hasEffectRun = useRef(false) - const [isSubscribing, setIsSubscribing] = useState(false) const [currentPage, setCurrentPage] = useState(1) const userState = useAppSelector((state) => state.user) @@ -66,57 +66,40 @@ export const GamePage = () => { } useEffect(() => { - if (hasEffectRun.current) { - return - } - - hasEffectRun.current = true // Set it so the effect doesn't run again - - const filter: Filter = { - kinds: [kinds.ClassifiedListing], + const filter: NDKFilter = { + kinds: [NDKKind.Classified], '#t': [T_TAG_VALUE] } - setIsSubscribing(true) + const subscription = ndk.subscribe(filter, { + cacheUsage: NDKSubscriptionCacheUsage.PARALLEL, + closeOnEose: true + }) - let subscriptions: Subscription[] = [] + subscription.on('event', (ndkEvent) => { + if (isModDataComplete(ndkEvent)) { + const mod = extractModData(ndkEvent) + if (mod.game === gameName) + setMods((prev) => { + if (prev.find((e) => e.aTag === mod.aTag)) return [...prev] - RelayController.getInstance() - .subscribeForEvents(filter, [], (event) => { - if (isModDataComplete(event)) { - const mod = extractModData(event) - if (mod.game === gameName) setMods((prev) => [...prev, mod]) - } - }) - .then((subs) => { - subscriptions = subs - }) - .catch((err) => { - log( - true, - LogType.Error, - 'An error occurred in subscribing to relays.', - err - ) - toast.error(err.message || err) - }) - .finally(() => { - setIsSubscribing(false) - }) + return [...prev, mod] + }) + } + }) - // Cleanup function to stop all subscriptions + subscription.start() + + // Cleanup function to stop subscription return () => { - subscriptions.forEach((sub) => sub.close()) // close each subscription + subscription.stop() } - }, [gameName]) + }, [gameName, ndk]) if (!gameName) return null return ( <> - {isSubscribing && ( - - )}
diff --git a/src/pages/home.tsx b/src/pages/home.tsx index fdb65f1..7778250 100644 --- a/src/pages/home.tsx +++ b/src/pages/home.tsx @@ -146,7 +146,7 @@ type SlideContentProps = { const SlideContent = ({ naddr }: SlideContentProps) => { const navigate = useNavigate() - const { fetchEvents } = useNDKContext() + const { fetchEvent } = useNDKContext() const [mod, setMod] = useState() useDidMount(() => { @@ -159,10 +159,9 @@ const SlideContent = ({ naddr }: SlideContentProps) => { kinds: [kind] } - fetchEvents(ndkFilter, relays) - .then((ndkEvents) => { - if (ndkEvents.length > 0) { - const ndkEvent = ndkEvents[0] + fetchEvent(ndkFilter, relays) + .then((ndkEvent) => { + if (ndkEvent) { const extracted = extractModData(ndkEvent) setMod(extracted) } @@ -221,7 +220,7 @@ type DisplayModProps = { const DisplayMod = ({ naddr }: DisplayModProps) => { const [mod, setMod] = useState() - const { fetchEvents } = useNDKContext() + const { fetchEvent } = useNDKContext() useDidMount(() => { const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`) @@ -233,10 +232,9 @@ const DisplayMod = ({ naddr }: DisplayModProps) => { kinds: [kind] } - fetchEvents(ndkFilter, relays) - .then((ndkEvents) => { - if (ndkEvents.length > 0) { - const ndkEvent = ndkEvents[0] + fetchEvent(ndkFilter, relays) + .then((ndkEvent) => { + if (ndkEvent) { const extracted = extractModData(ndkEvent) setMod(extracted) } diff --git a/src/pages/mod/index.tsx b/src/pages/mod/index.tsx index 1c3cee6..301823c 100644 --- a/src/pages/mod/index.tsx +++ b/src/pages/mod/index.tsx @@ -1,21 +1,18 @@ +import { NDKFilter, NDKKind } from '@nostr-dev-kit/ndk' import Link from '@tiptap/extension-link' import { EditorContent, useEditor } from '@tiptap/react' import StarterKit from '@tiptap/starter-kit' import { formatDate } from 'date-fns' import FsLightbox from 'fslightbox-react' -import { Filter, kinds, nip19, UnsignedEvent } from 'nostr-tools' +import { nip19, UnsignedEvent } from 'nostr-tools' import { useEffect, useRef, useState } from 'react' import { Link as ReactRouterLink, useParams } from 'react-router-dom' import { toast } from 'react-toastify' import { BlogCard } from '../../components/BlogCard' import { LoadingSpinner } from '../../components/LoadingSpinner' import { ProfileSection } from '../../components/ProfileSection' -import { - MetadataController, - RelayController, - UserRelaysType -} from '../../controllers' -import { useAppSelector, useDidMount } from '../../hooks' +import { MetadataController, UserRelaysType } from '../../controllers' +import { useAppSelector, useDidMount, useNDKContext } from '../../hooks' import { getGamePageRoute, getModsEditPageRoute } from '../../routes' import '../../styles/comments.css' import '../../styles/downloads.css' @@ -47,6 +44,7 @@ import { Zap } from './internal/zap' export const ModPage = () => { const { naddr } = useParams() + const { fetchEvent } = useNDKContext() const [modData, setModData] = useState() const [isFetching, setIsFetching] = useState(true) const [commentCount, setCommentCount] = useState(0) @@ -56,14 +54,13 @@ export const ModPage = () => { const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`) const { identifier, kind, pubkey, relays = [] } = decoded.data - const filter: Filter = { + const filter: NDKFilter = { '#a': [identifier], authors: [pubkey], kinds: [kind] } - RelayController.getInstance() - .fetchEvent(filter, relays) + fetchEvent(filter, relays) .then((event) => { if (event) { const extracted = extractModData(event) @@ -214,6 +211,8 @@ type GameProps = { } const Game = ({ naddr, game, author, aTag }: GameProps) => { + const { fetchEventFromUserRelays } = useNDKContext() + const userState = useAppSelector((state) => state.user) const [isLoading, setIsLoading] = useState(false) const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('') @@ -225,56 +224,54 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => { if (userState.auth && userState.user?.pubkey) { const pubkey = userState.user.pubkey as string - const muteListFilter: Filter = { - kinds: [kinds.Mutelist], + const muteListFilter: NDKFilter = { + kinds: [NDKKind.MuteList], authors: [pubkey] } - RelayController.getInstance() - .fetchEventFromUserRelays(muteListFilter, pubkey, UserRelaysType.Write) - .then((event) => { - if (event) { - // get a list of tags - const tags = event.tags - const blocked = - tags.findIndex((item) => item[0] === 'a' && item[1] === aTag) !== - -1 + fetchEventFromUserRelays( + muteListFilter, + pubkey, + UserRelaysType.Write + ).then((event) => { + if (event) { + // get a list of tags + const tags = event.tags + const blocked = + tags.findIndex((item) => item[0] === 'a' && item[1] === aTag) !== -1 - setIsBlocked(blocked) - } - }) + setIsBlocked(blocked) + } + }) if ( userState.user.npub && userState.user.npub === import.meta.env.VITE_REPORTING_NPUB ) { - const nsfwListFilter: Filter = { - kinds: [kinds.Curationsets], + const nsfwListFilter: NDKFilter = { + kinds: [NDKKind.ArticleCurationSet], authors: [pubkey], '#d': ['nsfw'] } - RelayController.getInstance() - .fetchEventFromUserRelays( - nsfwListFilter, - pubkey, - UserRelaysType.Write - ) - .then((event) => { - if (event) { - // get a list of tags - const tags = event.tags - const existsInNSFWList = - tags.findIndex( - (item) => item[0] === 'a' && item[1] === aTag - ) !== -1 + fetchEventFromUserRelays( + nsfwListFilter, + pubkey, + UserRelaysType.Write + ).then((event) => { + if (event) { + // get a list of tags + const tags = event.tags + const existsInNSFWList = + tags.findIndex((item) => item[0] === 'a' && item[1] === aTag) !== + -1 - setIsAddedToNSFW(existsInNSFWList) - } - }) + setIsAddedToNSFW(existsInNSFWList) + } + }) } } - }, [userState, aTag]) + }, [userState, aTag, fetchEventFromUserRelays]) const handleBlock = async () => { let hexPubkey: string @@ -298,18 +295,17 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => { // Define the event filter to search for the user's mute list events. // We look for events of a specific kind (Mutelist) authored by the given hexPubkey. - const filter: Filter = { - kinds: [kinds.Mutelist], + const filter: NDKFilter = { + kinds: [NDKKind.MuteList], authors: [hexPubkey] } // Fetch the mute list event from the relays. This returns the event containing the user's mute list. - const muteListEvent = - await RelayController.getInstance().fetchEventFromUserRelays( - filter, - hexPubkey, - UserRelaysType.Write - ) + const muteListEvent = await fetchEventFromUserRelays( + filter, + hexPubkey, + UserRelaysType.Write + ) let unsignedEvent: UnsignedEvent @@ -329,7 +325,7 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => { unsignedEvent = { pubkey: muteListEvent.pubkey, - kind: muteListEvent.kind, + kind: NDKKind.MuteList, content: muteListEvent.content, created_at: now(), tags: [...tags] @@ -337,7 +333,7 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => { } else { unsignedEvent = { pubkey: hexPubkey, - kind: kinds.Mutelist, + kind: NDKKind.MuteList, content: '', created_at: now(), tags: [['a', aTag]] @@ -356,8 +352,8 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => { const handleUnblock = async () => { const pubkey = userState.user?.pubkey as string - const filter: Filter = { - kinds: [kinds.Mutelist], + const filter: NDKFilter = { + kinds: [NDKKind.MuteList], authors: [pubkey] } @@ -365,12 +361,11 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => { setLoadingSpinnerDesc(`Finding user's mute list`) // Fetch the mute list event from the relays. This returns the event containing the user's mute list. - const muteListEvent = - await RelayController.getInstance().fetchEventFromUserRelays( - filter, - pubkey, - UserRelaysType.Write - ) + const muteListEvent = await fetchEventFromUserRelays( + filter, + pubkey, + UserRelaysType.Write + ) if (!muteListEvent) { toast.error(`Couldn't get user's mute list event from relays`) @@ -381,7 +376,7 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => { const unsignedEvent: UnsignedEvent = { pubkey: muteListEvent.pubkey, - kind: muteListEvent.kind, + kind: NDKKind.MuteList, content: muteListEvent.content, created_at: now(), tags: tags.filter((item) => item[0] !== 'a' || item[1] !== aTag) @@ -401,8 +396,8 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => { if (!pubkey) return - const filter: Filter = { - kinds: [kinds.Curationsets], + const filter: NDKFilter = { + kinds: [NDKKind.ArticleCurationSet], authors: [pubkey], '#d': ['nsfw'] } @@ -410,12 +405,11 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => { setIsLoading(true) setLoadingSpinnerDesc('Finding NSFW list') - const nsfwListEvent = - await RelayController.getInstance().fetchEventFromUserRelays( - filter, - pubkey, - UserRelaysType.Write - ) + const nsfwListEvent = await fetchEventFromUserRelays( + filter, + pubkey, + UserRelaysType.Write + ) let unsignedEvent: UnsignedEvent @@ -435,7 +429,7 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => { unsignedEvent = { pubkey: nsfwListEvent.pubkey, - kind: nsfwListEvent.kind, + kind: NDKKind.ArticleCurationSet, content: nsfwListEvent.content, created_at: now(), tags: [...tags] @@ -443,7 +437,7 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => { } else { unsignedEvent = { pubkey: pubkey, - kind: kinds.Curationsets, + kind: NDKKind.ArticleCurationSet, content: '', created_at: now(), tags: [ @@ -465,8 +459,8 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => { const handleUnblockNSFW = async () => { const pubkey = userState.user?.pubkey as string - const filter: Filter = { - kinds: [kinds.Curationsets], + const filter: NDKFilter = { + kinds: [NDKKind.ArticleCurationSet], authors: [pubkey], '#d': ['nsfw'] } @@ -474,12 +468,11 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => { setIsLoading(true) setLoadingSpinnerDesc('Finding NSFW list') - const nsfwListEvent = - await RelayController.getInstance().fetchEventFromUserRelays( - filter, - pubkey, - UserRelaysType.Write - ) + const nsfwListEvent = await fetchEventFromUserRelays( + filter, + pubkey, + UserRelaysType.Write + ) if (!nsfwListEvent) { toast.error(`Couldn't get nsfw list event from relays`) @@ -490,7 +483,7 @@ const Game = ({ naddr, game, author, aTag }: GameProps) => { const unsignedEvent: UnsignedEvent = { pubkey: nsfwListEvent.pubkey, - kind: nsfwListEvent.kind, + kind: NDKKind.ArticleCurationSet, content: nsfwListEvent.content, created_at: now(), tags: tags.filter((item) => item[0] !== 'a' || item[1] !== aTag) @@ -667,6 +660,7 @@ type ReportPopupProps = { } const ReportPopup = ({ aTag, handleClose }: ReportPopupProps) => { + const { fetchEventFromUserRelays } = useNDKContext() const userState = useAppSelector((state) => state.user) const [selectedOptions, setSelectedOptions] = useState({ actuallyCP: false, @@ -720,18 +714,17 @@ const ReportPopup = ({ aTag, handleClose }: ReportPopupProps) => { setLoadingSpinnerDesc(`Finding user's mute list`) // Define the event filter to search for the user's mute list events. // We look for events of a specific kind (Mutelist) authored by the given hexPubkey. - const filter: Filter = { - kinds: [kinds.Mutelist], + const filter: NDKFilter = { + kinds: [NDKKind.MuteList], authors: [hexPubkey] } // Fetch the mute list event from the relays. This returns the event containing the user's mute list. - const muteListEvent = - await RelayController.getInstance().fetchEventFromUserRelays( - filter, - hexPubkey, - UserRelaysType.Write - ) + const muteListEvent = await fetchEventFromUserRelays( + filter, + hexPubkey, + UserRelaysType.Write + ) let unsignedEvent: UnsignedEvent @@ -750,7 +743,7 @@ const ReportPopup = ({ aTag, handleClose }: ReportPopupProps) => { unsignedEvent = { pubkey: muteListEvent.pubkey, - kind: muteListEvent.kind, + kind: NDKKind.MuteList, content: muteListEvent.content, created_at: now(), tags: [...tags] @@ -758,7 +751,7 @@ const ReportPopup = ({ aTag, handleClose }: ReportPopupProps) => { } else { unsignedEvent = { pubkey: hexPubkey, - kind: kinds.Mutelist, + kind: NDKKind.MuteList, content: '', created_at: now(), tags: [['a', aTag]] diff --git a/src/pages/mod/internal/comment/index.tsx b/src/pages/mod/internal/comment/index.tsx index 62c5099..2f5b721 100644 --- a/src/pages/mod/internal/comment/index.tsx +++ b/src/pages/mod/internal/comment/index.tsx @@ -5,7 +5,7 @@ import { UserRelaysType } from 'controllers' import { formatDate } from 'date-fns' -import { useAppSelector, useDidMount, useReactions } from 'hooks' +import { useAppSelector, useDidMount, useNDKContext, useReactions } from 'hooks' import { useComments } from 'hooks/useComments' import { Event, kinds, nip19, UnsignedEvent } from 'nostr-tools' import React, { @@ -322,11 +322,11 @@ const Filter = React.memo( ) const Comment = (props: CommentEvent) => { + const { findMetadata } = useNDKContext() const [profile, setProfile] = useState() - useDidMount(async () => { - const metadataController = await MetadataController.getInstance() - metadataController.findMetadata(props.pubkey).then((res) => { + useDidMount(() => { + findMetadata(props.pubkey).then((res) => { setProfile(res) }) }) diff --git a/src/pages/search.tsx b/src/pages/search.tsx index 7b60cb2..41aebcb 100644 --- a/src/pages/search.tsx +++ b/src/pages/search.tsx @@ -1,4 +1,11 @@ -import { NDKEvent, NDKUserProfile, profileFromEvent } from '@nostr-dev-kit/ndk' +import { + NDKEvent, + NDKFilter, + NDKKind, + NDKSubscriptionCacheUsage, + NDKUserProfile, + profileFromEvent +} from '@nostr-dev-kit/ndk' import { ErrorBoundary } from 'components/ErrorBoundary' import { GameCard } from 'components/GameCard' import { LoadingSpinner } from 'components/LoadingSpinner' @@ -11,16 +18,14 @@ import { MAX_MODS_PER_PAGE, T_TAG_VALUE } from 'constants.ts' -import { RelayController } from 'controllers' import { useAppSelector, useFilteredMods, useGames, useMuteLists, + useNDKContext, useNSFWList } from 'hooks' -import { Filter, kinds } from 'nostr-tools' -import { Subscription } from 'nostr-tools/abstract-relay' import React, { Dispatch, SetStateAction, @@ -30,7 +35,6 @@ import React, { useState } from 'react' import { useSearchParams } from 'react-router-dom' -import { toast } from 'react-toastify' import { FilterOptions, ModDetails, @@ -267,56 +271,38 @@ const ModsResult = ({ muteLists, nsfwList }: ModsResultProps) => { - const hasEffectRun = useRef(false) - const [isSubscribing, setIsSubscribing] = useState(false) + const { ndk } = useNDKContext() const [mods, setMods] = useState([]) const [page, setPage] = useState(1) const userState = useAppSelector((state) => state.user) useEffect(() => { - if (hasEffectRun.current) { - return - } - - hasEffectRun.current = true // Set it so the effect doesn't run again - - const filter: Filter = { - kinds: [kinds.ClassifiedListing], + const filter: NDKFilter = { + kinds: [NDKKind.Classified], '#t': [T_TAG_VALUE] } - setIsSubscribing(true) + const subscription = ndk.subscribe(filter, { + cacheUsage: NDKSubscriptionCacheUsage.PARALLEL, + closeOnEose: true + }) - let subscriptions: Subscription[] = [] + subscription.on('event', (ndkEvent) => { + if (isModDataComplete(ndkEvent)) { + const mod = extractModData(ndkEvent) + setMods((prev) => { + if (prev.find((e) => e.aTag === mod.aTag)) return [...prev] - RelayController.getInstance() - .subscribeForEvents(filter, [], (event) => { - if (isModDataComplete(event)) { - const mod = extractModData(event) - setMods((prev) => [...prev, mod]) - } - }) - .then((subs) => { - subscriptions = subs - }) - .catch((err) => { - log( - true, - LogType.Error, - 'An error occurred in subscribing to relays.', - err - ) - toast.error(err.message || err) - }) - .finally(() => { - setIsSubscribing(false) - }) + return [...prev, mod] + }) + } + }) // Cleanup function to stop all subscriptions return () => { - subscriptions.forEach((sub) => sub.close()) // close each subscription + subscription.stop() } - }, []) + }, [ndk]) useEffect(() => { setPage(1) @@ -357,9 +343,6 @@ const ModsResult = ({ return ( <> - {isSubscribing && ( - - )}
{filteredModList @@ -393,6 +376,7 @@ const UsersResult = ({ moderationFilter, muteLists }: UsersResultProps) => { + const { fetchEvents } = useNDKContext() const [isFetching, setIsFetching] = useState(false) const [profiles, setProfiles] = useState([]) @@ -402,14 +386,13 @@ const UsersResult = ({ if (searchTerm === '') { setProfiles([]) } else { - const filter: Filter = { - kinds: [kinds.Metadata], + const filter: NDKFilter = { + kinds: [NDKKind.Metadata], search: searchTerm } setIsFetching(true) - RelayController.getInstance() - .fetchEvents(filter, ['wss://purplepag.es', 'wss://user.kindpag.es']) + fetchEvents(filter, ['wss://purplepag.es', 'wss://user.kindpag.es']) .then((events) => { const results = events.map((event) => { const ndkEvent = new NDKEvent(undefined, event) @@ -425,7 +408,7 @@ const UsersResult = ({ setIsFetching(false) }) } - }, [searchTerm]) + }, [searchTerm, fetchEvents]) const filteredProfiles = useMemo(() => { let filtered = [...profiles] diff --git a/src/pages/submitMod.tsx b/src/pages/submitMod.tsx index 1a07fcc..226b896 100644 --- a/src/pages/submitMod.tsx +++ b/src/pages/submitMod.tsx @@ -1,21 +1,22 @@ +import { NDKFilter } from '@nostr-dev-kit/ndk' +import { nip19 } from 'nostr-tools' +import { useState } from 'react' import { useLocation, useParams } from 'react-router-dom' +import { toast } from 'react-toastify' +import { LoadingSpinner } from '../components/LoadingSpinner' import { ModForm } from '../components/ModForm' import { ProfileSection } from '../components/ProfileSection' +import { useAppSelector, useDidMount, useNDKContext } from '../hooks' import '../styles/innerPage.css' import '../styles/styles.css' import '../styles/write.css' -import { Filter, nip19 } from 'nostr-tools' -import { RelayController } from '../controllers' -import { extractModData, log, LogType } from '../utils' import { ModDetails } from '../types' -import { toast } from 'react-toastify' -import { useState } from 'react' -import { LoadingSpinner } from '../components/LoadingSpinner' -import { useAppSelector, useDidMount } from '../hooks' +import { extractModData, log, LogType } from '../utils' export const SubmitModPage = () => { const location = useLocation() const { naddr } = useParams() + const { fetchEvent } = useNDKContext() const [modData, setModData] = useState() const [isFetching, setIsFetching] = useState(false) @@ -30,15 +31,15 @@ export const SubmitModPage = () => { const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`) const { identifier, kind, pubkey, relays = [] } = decoded.data - const filter: Filter = { + const filter: NDKFilter = { '#a': [identifier], authors: [pubkey], kinds: [kind] } setIsFetching(true) - RelayController.getInstance() - .fetchEvent(filter, relays) + + fetchEvent(filter, relays) .then((event) => { if (event) { const extracted = extractModData(event)