diff --git a/src/components/ProfileSection.tsx b/src/components/ProfileSection.tsx index 972c8ef..298ae9d 100644 --- a/src/components/ProfileSection.tsx +++ b/src/components/ProfileSection.tsx @@ -4,13 +4,12 @@ import { QRCodeSVG } from 'qrcode.react' import { useState } from 'react' import { Link } from 'react-router-dom' import { toast } from 'react-toastify' -import { UserRelaysType } from '../controllers' import { useAppSelector, useDidMount, useNDKContext } from '../hooks' import { appRoutes, getProfilePageRoute } from '../routes' import '../styles/author.css' import '../styles/innerPage.css' import '../styles/socialPosts.css' -import { UserProfile } from '../types' +import { UserProfile, UserRelaysType } from '../types' import { copyTextToClipboard, hexToNpub, diff --git a/src/components/Zap.tsx b/src/components/Zap.tsx index e7c7005..80ec0fa 100644 --- a/src/components/Zap.tsx +++ b/src/components/Zap.tsx @@ -1,3 +1,4 @@ +import { getRelayListForUser } from '@nostr-dev-kit/ndk' import { QRCodeSVG } from 'qrcode.react' import React, { Dispatch, @@ -9,7 +10,7 @@ import React, { } from 'react' import Countdown, { CountdownRenderProps } from 'react-countdown' import { toast } from 'react-toastify' -import { MetadataController, ZapController } from '../controllers' +import { ZapController } from '../controllers' import { useAppSelector, useDidMount, useNDKContext } from '../hooks' import '../styles/popup.css' import { PaymentRequest, UserProfile } from '../types' @@ -251,7 +252,7 @@ export const ZapPopUp = ({ setHasZapped, handleClose }: ZapPopUpProps) => { - const { findMetadata } = useNDKContext() + const { ndk, findMetadata } = useNDKContext() const [isLoading, setIsLoading] = useState(false) const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('') const [amount, setAmount] = useState(0) @@ -300,6 +301,20 @@ export const ZapPopUp = ({ return null } + // Find the receiver's read relays. + const receiverRelays = await getRelayListForUser(receiver, ndk) + .then((ndkRelayList) => { + if (ndkRelayList) return ndkRelayList.readRelayUrls + return [] // Return an empty array if ndkRelayList is undefined + }) + .catch((err) => { + console.error( + `An error occurred in getting zap receiver's read relays`, + err + ) + return [] as string[] + }) + const zapController = ZapController.getInstance() setLoadingSpinnerDesc('Creating zap request') @@ -308,6 +323,7 @@ export const ZapPopUp = ({ receiverMetadata.lud16, amount, receiverMetadata.pubkey as string, + receiverRelays, userHexKey, message, eventId, @@ -482,7 +498,7 @@ export const ZapSplit = ({ setHasZapped, handleClose }: ZapSplitProps) => { - const { findMetadata } = useNDKContext() + const { ndk, findMetadata } = useNDKContext() const [isLoading, setIsLoading] = useState(false) const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('') const [amount, setAmount] = useState(0) @@ -502,8 +518,8 @@ export const ZapSplit = ({ setAuthor(res) }) - const metadataController = await MetadataController.getInstance() - findMetadata(metadataController.adminNpubs[0]).then((res) => { + const adminNpubs = import.meta.env.VITE_ADMIN_NPUBS.split(',') + findMetadata(adminNpubs[0]).then((res) => { setAdmin(res) }) }) @@ -557,12 +573,30 @@ export const ZapSplit = ({ const invoices = new Map() if (authorShare > 0 && author?.pubkey && author?.lud16) { + // Find the receiver's read relays. + const authorRelays = await getRelayListForUser( + author.pubkey as string, + ndk + ) + .then((ndkRelayList) => { + if (ndkRelayList) return ndkRelayList.readRelayUrls + return [] // Return an empty array if ndkRelayList is undefined + }) + .catch((err) => { + console.error( + `An error occurred in getting zap receiver's read relays`, + err + ) + return [] as string[] + }) + setLoadingSpinnerDesc('Generating invoice for author') const invoice = await zapController .getLightningPaymentRequest( author.lud16, authorShare, author.pubkey as string, + authorRelays, userHexKey, message, eventId, @@ -579,12 +613,27 @@ export const ZapSplit = ({ } if (adminShare > 0 && admin?.pubkey && admin?.lud16) { + // Find the receiver's read relays. + const adminRelays = await getRelayListForUser(admin.pubkey as string, ndk) + .then((ndkRelayList) => { + if (ndkRelayList) return ndkRelayList.readRelayUrls + return [] // Return an empty array if ndkRelayList is undefined + }) + .catch((err) => { + console.error( + `An error occurred in getting zap receiver's read relays`, + err + ) + return [] as string[] + }) + setLoadingSpinnerDesc('Generating invoice for site owner') const invoice = await zapController .getLightningPaymentRequest( admin.lud16, adminShare, admin.pubkey as string, + adminRelays, userHexKey, message, eventId, diff --git a/src/contexts/NDKContext.tsx b/src/contexts/NDKContext.tsx index 0e92db9..33e35da 100644 --- a/src/contexts/NDKContext.tsx +++ b/src/contexts/NDKContext.tsx @@ -3,6 +3,7 @@ import NDK, { NDKEvent, NDKFilter, NDKKind, + NDKList, NDKRelaySet, NDKSubscriptionCacheUsage, NDKUser, @@ -10,11 +11,10 @@ import NDK, { } 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, UserProfile } from 'types' +import { ModDetails, MuteLists, UserProfile, UserRelaysType } from 'types' import { constructModListFromEvents, hexToNpub, @@ -57,6 +57,11 @@ interface NDKContextType { hasZapped: boolean }> publish: (event: NDKEvent) => Promise + getNSFWList: () => Promise + getMuteLists: (pubkey?: string) => Promise<{ + admin: MuteLists + user: MuteLists + }> } // Create the context with an initial value of `null` @@ -368,6 +373,117 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => { }) } + /** + * Retrieves a list of NSFW (Not Safe For Work) posts that were not specified as NSFW by post author but marked as NSFW by admin. + * + * @returns {Promise} - A promise that resolves to an array of NSFW post identifiers (e.g., URLs or IDs). + */ + const getNSFWList = async (): Promise => { + // Initialize an array to store the NSFW post identifiers + const nsfwPosts: string[] = [] + + const reportingNpub = import.meta.env.VITE_REPORTING_NPUB + + // Convert the public key (npub) to a hexadecimal format + const hexKey = npubToHex(reportingNpub) + + // If the conversion is successful and we have a hexKey + if (hexKey) { + // Fetch the event that contains the NSFW list + const nsfwListEvent = await fetchEvent({ + kinds: [NDKKind.ArticleCurationSet], + authors: [hexKey], + '#d': ['nsfw'] + }) + + if (nsfwListEvent) { + // Convert the event data to an NDKList, which is a structured list format + const list = NDKList.from(nsfwListEvent) + + // Iterate through the items in the list + list.items.forEach((item) => { + if (item[0] === 'a') { + // Add the identifier of the NSFW post to the nsfwPosts array + nsfwPosts.push(item[1]) + } + }) + } + } + + // Return the array of NSFW post identifiers + return nsfwPosts + } + + const getMuteLists = async ( + pubkey?: string + ): Promise<{ + admin: MuteLists + user: MuteLists + }> => { + const adminMutedAuthors = new Set() + const adminMutedPosts = new Set() + + const reportingNpub = import.meta.env.VITE_REPORTING_NPUB + + const adminHexKey = npubToHex(reportingNpub) + + if (adminHexKey) { + const muteListEvent = await fetchEvent({ + kinds: [NDKKind.MuteList], + authors: [adminHexKey] + }) + + if (muteListEvent) { + const list = NDKList.from(muteListEvent) + + list.items.forEach((item) => { + if (item[0] === 'p') { + adminMutedAuthors.add(item[1]) + } else if (item[0] === 'a') { + adminMutedPosts.add(item[1]) + } + }) + } + } + + const userMutedAuthors = new Set() + const userMutedPosts = new Set() + + if (pubkey) { + const userHexKey = npubToHex(pubkey) + + if (userHexKey) { + const muteListEvent = await fetchEvent({ + kinds: [NDKKind.MuteList], + authors: [userHexKey] + }) + + if (muteListEvent) { + const list = NDKList.from(muteListEvent) + + list.items.forEach((item) => { + if (item[0] === 'p') { + userMutedAuthors.add(item[1]) + } else if (item[0] === 'a') { + userMutedPosts.add(item[1]) + } + }) + } + } + } + + return { + admin: { + authors: Array.from(adminMutedAuthors), + replaceableEvents: Array.from(adminMutedPosts) + }, + user: { + authors: Array.from(userMutedAuthors), + replaceableEvents: Array.from(userMutedPosts) + } + } + } + return ( { fetchEventFromUserRelays, findMetadata, getTotalZapAmount, - publish + publish, + getNSFWList, + getMuteLists }} > {children} diff --git a/src/controllers/index.ts b/src/controllers/index.ts index b028b93..4e84779 100644 --- a/src/controllers/index.ts +++ b/src/controllers/index.ts @@ -1,2 +1 @@ -export * from './metadata' export * from './zap' diff --git a/src/controllers/metadata.ts b/src/controllers/metadata.ts deleted file mode 100644 index 98967d4..0000000 --- a/src/controllers/metadata.ts +++ /dev/null @@ -1,217 +0,0 @@ -import NDK, { getRelayListForUser, NDKList } from '@nostr-dev-kit/ndk' -import { kinds } from 'nostr-tools' -import { MuteLists } from '../types' -import { log, LogType, npubToHex, timeout } from '../utils' - -export enum UserRelaysType { - Read = 'readRelayUrls', - Write = 'writeRelayUrls', - Both = 'bothRelayUrls' -} - -/** - * Singleton class to manage metadata operations using NDK. - */ -export class MetadataController { - private static instance: MetadataController - private ndk: NDK - public adminNpubs: string[] - public adminRelays = new Set() - public reportingNpub: string - - private constructor() { - this.ndk = new NDK({ - explicitRelayUrls: [ - 'wss://user.kindpag.es', - 'wss://purplepag.es', - 'wss://relay.damus.io/', - import.meta.env.VITE_APP_RELAY - ] - }) - - this.ndk - .connect() - .then(() => { - console.log('NDK connected') - }) - .catch((err) => { - console.log('error in ndk connection', err) - }) - - this.adminNpubs = import.meta.env.VITE_ADMIN_NPUBS.split(',') - this.reportingNpub = import.meta.env.VITE_REPORTING_NPUB - } - - private setAdminRelays = async () => { - const promises = this.adminNpubs.map((npub) => { - const hexKey = npubToHex(npub) - if (!hexKey) return null - - return getRelayListForUser(hexKey, this.ndk) - .then((ndkRelayList) => { - if (ndkRelayList) { - ndkRelayList.writeRelayUrls.forEach((url) => - this.adminRelays.add(url) - ) - } - }) - .catch((err) => { - log( - true, - LogType.Error, - `❌ Error occurred in getting the instance of NDKRelayList for npub: ${npub}`, - err - ) - }) - }) - - await Promise.allSettled(promises) - } - - /** - * Provides the singleton instance of MetadataController. - * - * @returns The singleton instance of MetadataController. - */ - public static async getInstance(): Promise { - if (!MetadataController.instance) { - MetadataController.instance = new MetadataController() - - await MetadataController.instance.setAdminRelays() - } - return MetadataController.instance - } - - public findUserRelays = async ( - hexKey: string, - userRelaysType: UserRelaysType = UserRelaysType.Both - ): Promise => { - log(true, LogType.Info, `ℹ Finding user's relays`, hexKey, userRelaysType) - - const ndkRelayListPromise = getRelayListForUser(hexKey, this.ndk) - - // Use Promise.race to either get the NDKRelayList instance or handle the timeout - return await Promise.race([ - ndkRelayListPromise, - timeout() // Custom timeout function that rejects after a specified time - ]) - .then((ndkRelayList) => { - if (ndkRelayList) return ndkRelayList[userRelaysType] - return [] // Return an empty array if ndkRelayList is undefined - }) - .catch((err) => { - log(true, LogType.Error, err) - return [] // Return an empty array if an error occurs - }) - } - - public getNDKRelayList = async (hexKey: string) => - getRelayListForUser(hexKey, this.ndk) - - public getMuteLists = async ( - pubkey?: string - ): Promise<{ - admin: MuteLists - user: MuteLists - }> => { - const adminMutedAuthors = new Set() - const adminMutedPosts = new Set() - - const adminHexKey = npubToHex(this.reportingNpub) - - if (adminHexKey) { - const muteListEvent = await this.ndk.fetchEvent({ - kinds: [kinds.Mutelist], - authors: [adminHexKey] - }) - - if (muteListEvent) { - const list = NDKList.from(muteListEvent) - - list.items.forEach((item) => { - if (item[0] === 'p') { - adminMutedAuthors.add(item[1]) - } else if (item[0] === 'a') { - adminMutedPosts.add(item[1]) - } - }) - } - } - - const userMutedAuthors = new Set() - const userMutedPosts = new Set() - - if (pubkey) { - const userHexKey = npubToHex(pubkey) - - if (userHexKey) { - const muteListEvent = await this.ndk.fetchEvent({ - kinds: [kinds.Mutelist], - authors: [userHexKey] - }) - - if (muteListEvent) { - const list = NDKList.from(muteListEvent) - - list.items.forEach((item) => { - if (item[0] === 'p') { - userMutedAuthors.add(item[1]) - } else if (item[0] === 'a') { - userMutedPosts.add(item[1]) - } - }) - } - } - } - - return { - admin: { - authors: Array.from(adminMutedAuthors), - replaceableEvents: Array.from(adminMutedPosts) - }, - user: { - authors: Array.from(userMutedAuthors), - replaceableEvents: Array.from(userMutedPosts) - } - } - } - - /** - * Retrieves a list of NSFW (Not Safe For Work) posts that were not specified as NSFW by post author but marked as NSFW by admin. - * - * @returns {Promise} - A promise that resolves to an array of NSFW post identifiers (e.g., URLs or IDs). - */ - public getNSFWList = async (): Promise => { - // Initialize an array to store the NSFW post identifiers - const nsfwPosts: string[] = [] - - // Convert the public key (npub) to a hexadecimal format - const hexKey = npubToHex(this.reportingNpub) - - // If the conversion is successful and we have a hexKey - if (hexKey) { - // Fetch the event that contains the NSFW list - const nsfwListEvent = await this.ndk.fetchEvent({ - kinds: [kinds.Curationsets], - authors: [hexKey], - '#d': ['nsfw'] - }) - - if (nsfwListEvent) { - // Convert the event data to an NDKList, which is a structured list format - const list = NDKList.from(nsfwListEvent) - - // Iterate through the items in the list - list.items.forEach((item) => { - if (item[0] === 'a') { - // Add the identifier of the NSFW post to the nsfwPosts array - nsfwPosts.push(item[1]) - } - }) - } - } - - // Return the array of NSFW post identifiers - return nsfwPosts - } -} diff --git a/src/controllers/zap.ts b/src/controllers/zap.ts index 8b74dd7..0ff300a 100644 --- a/src/controllers/zap.ts +++ b/src/controllers/zap.ts @@ -17,7 +17,6 @@ import { ZapRequest } from '../types' import { log, LogType, npubToHex } from '../utils' -import { MetadataController, UserRelaysType } from './metadata' /** * Singleton class to manage zap related operations. @@ -48,6 +47,7 @@ export class ZapController { * @param lud16 - LUD-16 of the recipient. * @param amount - payment amount (will be multiplied by 1000 to represent sats). * @param recipientPubKey - pubKey of the recipient. + * @param recipientRelays - relays on which zap receipt will be published. * @param senderPubkey - pubKey of of the sender. * @param content - optional content (comment). * @param eventId - event id, if zapping an event. @@ -59,6 +59,7 @@ export class ZapController { lud16: string, amount: number, recipientPubKey: string, + recipientRelays: string[], senderPubkey: string, content?: string, eventId?: string, @@ -88,6 +89,7 @@ export class ZapController { amount, content, recipientPubKey, + recipientRelays, senderPubkey, eventId, aTag @@ -273,6 +275,7 @@ export class ZapController { * @param amount - request amount (sats). * @param content - comment. * @param recipientPubKey - pubKey of the recipient. + * @param recipientRelays - relays on which zap receipt will be published. * @param senderPubkey - pubKey of of the sender. * @param eventId - event id, if zapping an event. * @param aTag - value of `a` tag. @@ -282,6 +285,7 @@ export class ZapController { amount: number, content = '', recipientPubKey: string, + recipientRelays: string[], senderPubkey: string, eventId?: string, aTag?: string @@ -290,21 +294,15 @@ export class ZapController { if (!recipientHexKey) throw 'Invalid recipient pubKey.' - const metadataController = await MetadataController.getInstance() - const receiverReadRelays = await metadataController.findUserRelays( - recipientHexKey, - UserRelaysType.Read - ) - - if (!receiverReadRelays.includes(this.appRelay)) { - receiverReadRelays.push(this.appRelay) + if (!recipientRelays.includes(this.appRelay)) { + recipientRelays.push(this.appRelay) } const zapRequest: ZapRequest = { kind: kinds.ZapRequest, content, tags: [ - ['relays', ...receiverReadRelays], + ['relays', ...recipientRelays], ['amount', `${amount}`], ['p', recipientHexKey] ], diff --git a/src/hooks/useComments.ts b/src/hooks/useComments.ts index 5dd120a..6be5932 100644 --- a/src/hooks/useComments.ts +++ b/src/hooks/useComments.ts @@ -6,9 +6,8 @@ import { NDKSubscription, NDKSubscriptionCacheUsage } from '@nostr-dev-kit/ndk' -import { UserRelaysType } from 'controllers' import { useEffect, useState } from 'react' -import { CommentEvent, ModDetails } from 'types' +import { CommentEvent, ModDetails, UserRelaysType } from 'types' import { log, LogType } from 'utils' import { useNDKContext } from './useNDKContext' diff --git a/src/hooks/useMuteLists.ts b/src/hooks/useMuteLists.ts index 558bcb7..803da9e 100644 --- a/src/hooks/useMuteLists.ts +++ b/src/hooks/useMuteLists.ts @@ -1,9 +1,10 @@ import { useEffect, useState } from 'react' import { MuteLists } from 'types' import { useAppSelector } from './redux' -import { MetadataController } from 'controllers' +import { useNDKContext } from './useNDKContext' export const useMuteLists = () => { + const { getMuteLists } = useNDKContext() const [muteLists, setMuteLists] = useState<{ admin: MuteLists user: MuteLists @@ -21,17 +22,11 @@ export const useMuteLists = () => { const userState = useAppSelector((state) => state.user) useEffect(() => { - const getMuteLists = async () => { - const pubkey = userState.user?.pubkey as string | undefined - - const metadataController = await MetadataController.getInstance() - metadataController.getMuteLists(pubkey).then((lists) => { - setMuteLists(lists) - }) - } - - getMuteLists() - }, [userState]) + const pubkey = userState.user?.pubkey as string | undefined + getMuteLists(pubkey).then((lists) => { + setMuteLists(lists) + }) + }, [userState, getMuteLists]) return muteLists } diff --git a/src/hooks/useNSFWList.ts b/src/hooks/useNSFWList.ts index 0712da6..9da98db 100644 --- a/src/hooks/useNSFWList.ts +++ b/src/hooks/useNSFWList.ts @@ -1,14 +1,13 @@ -import { MetadataController } from 'controllers' import { useState } from 'react' import { useDidMount } from './useDidMount' +import { useNDKContext } from './useNDKContext' export const useNSFWList = () => { + const { getNSFWList } = useNDKContext() const [nsfwList, setNSFWList] = useState([]) useDidMount(async () => { - const metadataController = await MetadataController.getInstance() - - metadataController.getNSFWList().then((list) => { + getNSFWList().then((list) => { setNSFWList(list) }) }) diff --git a/src/hooks/useReactions.ts b/src/hooks/useReactions.ts index 60406c2..574c3eb 100644 --- a/src/hooks/useReactions.ts +++ b/src/hooks/useReactions.ts @@ -1,10 +1,10 @@ import { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk' import { REACTIONS } from 'constants.ts' -import { UserRelaysType } from 'controllers' import { useAppSelector, useDidMount, useNDKContext } from 'hooks' import { Event, kinds, UnsignedEvent } from 'nostr-tools' import { useMemo, useState } from 'react' import { toast } from 'react-toastify' +import { UserRelaysType } from 'types' import { abbreviateNumber, log, LogType, now } from 'utils' type UseReactionsParams = { diff --git a/src/layout/header.tsx b/src/layout/header.tsx index bc4c2b8..c6c4498 100644 --- a/src/layout/header.tsx +++ b/src/layout/header.tsx @@ -6,7 +6,6 @@ import React, { useEffect, useState } from 'react' import { Link } from 'react-router-dom' import { Banner } from '../components/Banner' import { ZapPopUp } from '../components/Zap' -import { MetadataController } from '../controllers' import { useAppDispatch, useAppSelector, @@ -261,8 +260,8 @@ const TipButtonWithDialog = React.memo(() => { const [isOpen, setIsOpen] = useState(false) useDidMount(async () => { - const metadataController = await MetadataController.getInstance() - setAdminNpub(metadataController.adminNpubs[0]) + const adminNpubs = import.meta.env.VITE_ADMIN_NPUBS.split(',') + setAdminNpub(adminNpubs[0]) }) return ( diff --git a/src/pages/home.tsx b/src/pages/home.tsx index 79e5a15..5c15e25 100644 --- a/src/pages/home.tsx +++ b/src/pages/home.tsx @@ -152,7 +152,7 @@ const SlideContent = ({ naddr }: SlideContentProps) => { useDidMount(() => { const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`) - const { identifier, kind, pubkey, relays = [] } = decoded.data + const { identifier, kind, pubkey } = decoded.data const ndkFilter: NDKFilter = { '#a': [identifier], @@ -160,7 +160,7 @@ const SlideContent = ({ naddr }: SlideContentProps) => { kinds: [kind] } - fetchEvent(ndkFilter, relays) + fetchEvent(ndkFilter) .then((ndkEvent) => { if (ndkEvent) { const extracted = extractModData(ndkEvent) @@ -225,7 +225,7 @@ const DisplayMod = ({ naddr }: DisplayModProps) => { useDidMount(() => { const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`) - const { identifier, kind, pubkey, relays = [] } = decoded.data + const { identifier, kind, pubkey } = decoded.data const ndkFilter: NDKFilter = { '#a': [identifier], @@ -233,7 +233,7 @@ const DisplayMod = ({ naddr }: DisplayModProps) => { kinds: [kind] } - fetchEvent(ndkFilter, relays) + fetchEvent(ndkFilter) .then((ndkEvent) => { if (ndkEvent) { const extracted = extractModData(ndkEvent) diff --git a/src/pages/mod/index.tsx b/src/pages/mod/index.tsx index dd678fd..a10acc8 100644 --- a/src/pages/mod/index.tsx +++ b/src/pages/mod/index.tsx @@ -11,7 +11,6 @@ import { toast } from 'react-toastify' import { BlogCard } from '../../components/BlogCard' import { LoadingSpinner } from '../../components/LoadingSpinner' import { ProfileSection } from '../../components/ProfileSection' -import { MetadataController, UserRelaysType } from '../../controllers' import { useAppSelector, useDidMount, useNDKContext } from '../../hooks' import { getGamePageRoute, getModsEditPageRoute } from '../../routes' import '../../styles/comments.css' @@ -24,7 +23,7 @@ import '../../styles/styles.css' import '../../styles/tabs.css' import '../../styles/tags.css' import '../../styles/write.css' -import { DownloadUrl, ModDetails } from '../../types' +import { DownloadUrl, ModDetails, UserRelaysType } from '../../types' import { abbreviateNumber, copyTextToClipboard, @@ -708,8 +707,8 @@ const ReportPopup = ({ aTag, handleClose }: ReportPopupProps) => { return } - const metadataController = await MetadataController.getInstance() - const reportingPubkey = npubToHex(metadataController.reportingNpub) + const reportingNpub = import.meta.env.VITE_REPORTING_NPUB + const reportingPubkey = npubToHex(reportingNpub) if (reportingPubkey === hexPubkey) { setLoadingSpinnerDesc(`Finding user's mute list`) diff --git a/src/pages/settings/index.tsx b/src/pages/settings/index.tsx index 60aa2c8..4c0f618 100644 --- a/src/pages/settings/index.tsx +++ b/src/pages/settings/index.tsx @@ -1,5 +1,4 @@ import { AdminSVG, PreferenceSVG, ProfileSVG, RelaySVG } from 'components/SVGs' -import { MetadataController } from 'controllers' import { useAppSelector } from 'hooks' import { logout } from 'nostr-login' import { useEffect, useState } from 'react' @@ -57,15 +56,12 @@ const SettingTabs = () => { const userState = useAppSelector((state) => state.user) useEffect(() => { - MetadataController.getInstance().then((controller) => { - if (userState.auth && userState.user?.npub) { - setIsAdmin( - controller.adminNpubs.includes(userState.user.npub as string) - ) - } else { - setIsAdmin(false) - } - }) + const adminNpubs = import.meta.env.VITE_ADMIN_NPUBS.split(',') + if (userState.auth && userState.user?.npub) { + setIsAdmin(adminNpubs.includes(userState.user.npub as string)) + } else { + setIsAdmin(false) + } }, [userState]) const handleSignOut = () => { diff --git a/src/pages/settings/relay.tsx b/src/pages/settings/relay.tsx index 90beed2..1a32d28 100644 --- a/src/pages/settings/relay.tsx +++ b/src/pages/settings/relay.tsx @@ -1,11 +1,16 @@ -import { NDKEvent, NDKRelayList, NDKRelayStatus } from '@nostr-dev-kit/ndk' +import { + getRelayListForUser, + NDKEvent, + NDKRelayList, + NDKRelayStatus +} from '@nostr-dev-kit/ndk' import { InputField } from 'components/Inputs' import { LoadingSpinner } from 'components/LoadingSpinner' -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' +import { UserRelaysType } from 'types' import { log, LogType, normalizeWebSocketURL, now } from 'utils' const READ_MARKER = 'read' @@ -20,10 +25,8 @@ export const RelaySettings = () => { const [inputValue, setInputValue] = useState('') useEffect(() => { - const fetchRelayList = async (pubkey: string) => { - const metadataController = await MetadataController.getInstance() - metadataController - .getNDKRelayList(pubkey) + if (userState.auth && userState.user?.pubkey) { + getRelayListForUser(userState.user.pubkey as string, ndk) .then((res) => { setNDKRelayList(res) }) @@ -35,14 +38,10 @@ export const RelaySettings = () => { ) setNDKRelayList(null) }) - } - - if (userState.auth && userState.user?.pubkey) { - fetchRelayList(userState.user.pubkey as string) } else { setNDKRelayList(null) } - }, [userState]) + }, [userState, ndk]) const handleAdd = async (relayUrl: string) => { if (!ndkRelayList) return diff --git a/src/pages/submitMod.tsx b/src/pages/submitMod.tsx index 226b896..ba79874 100644 --- a/src/pages/submitMod.tsx +++ b/src/pages/submitMod.tsx @@ -29,7 +29,7 @@ export const SubmitModPage = () => { 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], @@ -39,7 +39,7 @@ export const SubmitModPage = () => { setIsFetching(true) - fetchEvent(filter, relays) + fetchEvent(filter) .then((event) => { if (event) { const extracted = extractModData(event) diff --git a/src/types/user.ts b/src/types/user.ts index 551bd1f..059ba84 100644 --- a/src/types/user.ts +++ b/src/types/user.ts @@ -1,3 +1,9 @@ import { NDKUserProfile } from '@nostr-dev-kit/ndk' export type UserProfile = NDKUserProfile | null + +export enum UserRelaysType { + Read = 'readRelayUrls', + Write = 'writeRelayUrls', + Both = 'bothRelayUrls' +}