Relay operations refactored with NDK for publishing events (+ more. Wrapped up refactoring), pagination scroll up on click, body scroll disable/enable when popups appear/disappear, nsfw tag shown on mod cards if mod post is nsfw #92

Merged
freakoverse merged 19 commits from staging into master 2024-10-21 14:17:02 +00:00
17 changed files with 229 additions and 291 deletions
Showing only changes of commit b0ebe7154a - Show all commits

View File

@ -4,13 +4,12 @@ import { QRCodeSVG } from 'qrcode.react'
import { useState } from 'react' import { useState } from 'react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { UserRelaysType } from '../controllers'
import { useAppSelector, useDidMount, useNDKContext } from '../hooks' import { useAppSelector, useDidMount, useNDKContext } from '../hooks'
import { appRoutes, getProfilePageRoute } from '../routes' import { appRoutes, getProfilePageRoute } from '../routes'
import '../styles/author.css' import '../styles/author.css'
import '../styles/innerPage.css' import '../styles/innerPage.css'
import '../styles/socialPosts.css' import '../styles/socialPosts.css'
import { UserProfile } from '../types' import { UserProfile, UserRelaysType } from '../types'
import { import {
copyTextToClipboard, copyTextToClipboard,
hexToNpub, hexToNpub,

View File

@ -1,3 +1,4 @@
import { getRelayListForUser } from '@nostr-dev-kit/ndk'
import { QRCodeSVG } from 'qrcode.react' import { QRCodeSVG } from 'qrcode.react'
import React, { import React, {
Dispatch, Dispatch,
@ -9,7 +10,7 @@ import React, {
} from 'react' } from 'react'
import Countdown, { CountdownRenderProps } from 'react-countdown' import Countdown, { CountdownRenderProps } from 'react-countdown'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { MetadataController, ZapController } from '../controllers' import { ZapController } from '../controllers'
import { useAppSelector, useDidMount, useNDKContext } from '../hooks' import { useAppSelector, useDidMount, useNDKContext } from '../hooks'
import '../styles/popup.css' import '../styles/popup.css'
import { PaymentRequest, UserProfile } from '../types' import { PaymentRequest, UserProfile } from '../types'
@ -251,7 +252,7 @@ export const ZapPopUp = ({
setHasZapped, setHasZapped,
handleClose handleClose
}: ZapPopUpProps) => { }: ZapPopUpProps) => {
const { findMetadata } = useNDKContext() const { ndk, findMetadata } = useNDKContext()
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('') const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('')
const [amount, setAmount] = useState<number>(0) const [amount, setAmount] = useState<number>(0)
@ -300,6 +301,20 @@ export const ZapPopUp = ({
return null 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() const zapController = ZapController.getInstance()
setLoadingSpinnerDesc('Creating zap request') setLoadingSpinnerDesc('Creating zap request')
@ -308,6 +323,7 @@ export const ZapPopUp = ({
receiverMetadata.lud16, receiverMetadata.lud16,
amount, amount,
receiverMetadata.pubkey as string, receiverMetadata.pubkey as string,
receiverRelays,
userHexKey, userHexKey,
message, message,
eventId, eventId,
@ -482,7 +498,7 @@ export const ZapSplit = ({
setHasZapped, setHasZapped,
handleClose handleClose
}: ZapSplitProps) => { }: ZapSplitProps) => {
const { findMetadata } = useNDKContext() const { ndk, findMetadata } = useNDKContext()
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('') const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('')
const [amount, setAmount] = useState<number>(0) const [amount, setAmount] = useState<number>(0)
@ -502,8 +518,8 @@ export const ZapSplit = ({
setAuthor(res) setAuthor(res)
}) })
const metadataController = await MetadataController.getInstance() const adminNpubs = import.meta.env.VITE_ADMIN_NPUBS.split(',')
findMetadata(metadataController.adminNpubs[0]).then((res) => { findMetadata(adminNpubs[0]).then((res) => {
setAdmin(res) setAdmin(res)
}) })
}) })
@ -557,12 +573,30 @@ export const ZapSplit = ({
const invoices = new Map<string, PaymentRequest>() const invoices = new Map<string, PaymentRequest>()
if (authorShare > 0 && author?.pubkey && author?.lud16) { 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') setLoadingSpinnerDesc('Generating invoice for author')
const invoice = await zapController const invoice = await zapController
.getLightningPaymentRequest( .getLightningPaymentRequest(
author.lud16, author.lud16,
authorShare, authorShare,
author.pubkey as string, author.pubkey as string,
authorRelays,
userHexKey, userHexKey,
message, message,
eventId, eventId,
@ -579,12 +613,27 @@ export const ZapSplit = ({
} }
if (adminShare > 0 && admin?.pubkey && admin?.lud16) { 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') setLoadingSpinnerDesc('Generating invoice for site owner')
const invoice = await zapController const invoice = await zapController
.getLightningPaymentRequest( .getLightningPaymentRequest(
admin.lud16, admin.lud16,
adminShare, adminShare,
admin.pubkey as string, admin.pubkey as string,
adminRelays,
userHexKey, userHexKey,
message, message,
eventId, eventId,

View File

@ -3,6 +3,7 @@ import NDK, {
NDKEvent, NDKEvent,
NDKFilter, NDKFilter,
NDKKind, NDKKind,
NDKList,
NDKRelaySet, NDKRelaySet,
NDKSubscriptionCacheUsage, NDKSubscriptionCacheUsage,
NDKUser, NDKUser,
@ -10,11 +11,10 @@ import NDK, {
} from '@nostr-dev-kit/ndk' } from '@nostr-dev-kit/ndk'
import NDKCacheAdapterDexie from '@nostr-dev-kit/ndk-cache-dexie' import NDKCacheAdapterDexie from '@nostr-dev-kit/ndk-cache-dexie'
import { MOD_FILTER_LIMIT, T_TAG_VALUE } from 'constants.ts' import { MOD_FILTER_LIMIT, T_TAG_VALUE } from 'constants.ts'
import { UserRelaysType } from 'controllers'
import { Dexie } from 'dexie' import { Dexie } from 'dexie'
import { createContext, ReactNode, useEffect, useMemo } from 'react' import { createContext, ReactNode, useEffect, useMemo } from 'react'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { ModDetails, UserProfile } from 'types' import { ModDetails, MuteLists, UserProfile, UserRelaysType } from 'types'
import { import {
constructModListFromEvents, constructModListFromEvents,
hexToNpub, hexToNpub,
@ -57,6 +57,11 @@ interface NDKContextType {
hasZapped: boolean hasZapped: boolean
}> }>
publish: (event: NDKEvent) => Promise<string[]> publish: (event: NDKEvent) => Promise<string[]>
getNSFWList: () => Promise<string[]>
getMuteLists: (pubkey?: string) => Promise<{
admin: MuteLists
user: MuteLists
}>
} }
// Create the context with an initial value of `null` // 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<string[]>} - A promise that resolves to an array of NSFW post identifiers (e.g., URLs or IDs).
*/
const getNSFWList = async (): Promise<string[]> => {
// 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<string>()
const adminMutedPosts = new Set<string>()
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<string>()
const userMutedPosts = new Set<string>()
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 ( return (
<NDKContext.Provider <NDKContext.Provider
value={{ value={{
@ -379,7 +495,9 @@ export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
fetchEventFromUserRelays, fetchEventFromUserRelays,
findMetadata, findMetadata,
getTotalZapAmount, getTotalZapAmount,
publish publish,
getNSFWList,
getMuteLists
}} }}
> >
{children} {children}

View File

@ -1,2 +1 @@
export * from './metadata'
export * from './zap' export * from './zap'

View File

@ -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<string>()
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<MetadataController> {
if (!MetadataController.instance) {
MetadataController.instance = new MetadataController()
await MetadataController.instance.setAdminRelays()
}
return MetadataController.instance
}
public findUserRelays = async (
hexKey: string,
userRelaysType: UserRelaysType = UserRelaysType.Both
): Promise<string[]> => {
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<string>()
const adminMutedPosts = new Set<string>()
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<string>()
const userMutedPosts = new Set<string>()
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<string[]>} - A promise that resolves to an array of NSFW post identifiers (e.g., URLs or IDs).
*/
public getNSFWList = async (): Promise<string[]> => {
// 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
}
}

View File

@ -17,7 +17,6 @@ import {
ZapRequest ZapRequest
} from '../types' } from '../types'
import { log, LogType, npubToHex } from '../utils' import { log, LogType, npubToHex } from '../utils'
import { MetadataController, UserRelaysType } from './metadata'
/** /**
* Singleton class to manage zap related operations. * Singleton class to manage zap related operations.
@ -48,6 +47,7 @@ export class ZapController {
* @param lud16 - LUD-16 of the recipient. * @param lud16 - LUD-16 of the recipient.
* @param amount - payment amount (will be multiplied by 1000 to represent sats). * @param amount - payment amount (will be multiplied by 1000 to represent sats).
* @param recipientPubKey - pubKey of the recipient. * @param recipientPubKey - pubKey of the recipient.
* @param recipientRelays - relays on which zap receipt will be published.
* @param senderPubkey - pubKey of of the sender. * @param senderPubkey - pubKey of of the sender.
* @param content - optional content (comment). * @param content - optional content (comment).
* @param eventId - event id, if zapping an event. * @param eventId - event id, if zapping an event.
@ -59,6 +59,7 @@ export class ZapController {
lud16: string, lud16: string,
amount: number, amount: number,
recipientPubKey: string, recipientPubKey: string,
recipientRelays: string[],
senderPubkey: string, senderPubkey: string,
content?: string, content?: string,
eventId?: string, eventId?: string,
@ -88,6 +89,7 @@ export class ZapController {
amount, amount,
content, content,
recipientPubKey, recipientPubKey,
recipientRelays,
senderPubkey, senderPubkey,
eventId, eventId,
aTag aTag
@ -273,6 +275,7 @@ export class ZapController {
* @param amount - request amount (sats). * @param amount - request amount (sats).
* @param content - comment. * @param content - comment.
* @param recipientPubKey - pubKey of the recipient. * @param recipientPubKey - pubKey of the recipient.
* @param recipientRelays - relays on which zap receipt will be published.
* @param senderPubkey - pubKey of of the sender. * @param senderPubkey - pubKey of of the sender.
* @param eventId - event id, if zapping an event. * @param eventId - event id, if zapping an event.
* @param aTag - value of `a` tag. * @param aTag - value of `a` tag.
@ -282,6 +285,7 @@ export class ZapController {
amount: number, amount: number,
content = '', content = '',
recipientPubKey: string, recipientPubKey: string,
recipientRelays: string[],
senderPubkey: string, senderPubkey: string,
eventId?: string, eventId?: string,
aTag?: string aTag?: string
@ -290,21 +294,15 @@ export class ZapController {
if (!recipientHexKey) throw 'Invalid recipient pubKey.' if (!recipientHexKey) throw 'Invalid recipient pubKey.'
const metadataController = await MetadataController.getInstance() if (!recipientRelays.includes(this.appRelay)) {
const receiverReadRelays = await metadataController.findUserRelays( recipientRelays.push(this.appRelay)
recipientHexKey,
UserRelaysType.Read
)
if (!receiverReadRelays.includes(this.appRelay)) {
receiverReadRelays.push(this.appRelay)
} }
const zapRequest: ZapRequest = { const zapRequest: ZapRequest = {
kind: kinds.ZapRequest, kind: kinds.ZapRequest,
content, content,
tags: [ tags: [
['relays', ...receiverReadRelays], ['relays', ...recipientRelays],
['amount', `${amount}`], ['amount', `${amount}`],
['p', recipientHexKey] ['p', recipientHexKey]
], ],

View File

@ -6,9 +6,8 @@ import {
NDKSubscription, NDKSubscription,
NDKSubscriptionCacheUsage NDKSubscriptionCacheUsage
} from '@nostr-dev-kit/ndk' } from '@nostr-dev-kit/ndk'
import { UserRelaysType } from 'controllers'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { CommentEvent, ModDetails } from 'types' import { CommentEvent, ModDetails, UserRelaysType } from 'types'
import { log, LogType } from 'utils' import { log, LogType } from 'utils'
import { useNDKContext } from './useNDKContext' import { useNDKContext } from './useNDKContext'

View File

@ -1,9 +1,10 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { MuteLists } from 'types' import { MuteLists } from 'types'
import { useAppSelector } from './redux' import { useAppSelector } from './redux'
import { MetadataController } from 'controllers' import { useNDKContext } from './useNDKContext'
export const useMuteLists = () => { export const useMuteLists = () => {
const { getMuteLists } = useNDKContext()
const [muteLists, setMuteLists] = useState<{ const [muteLists, setMuteLists] = useState<{
admin: MuteLists admin: MuteLists
user: MuteLists user: MuteLists
@ -21,17 +22,11 @@ export const useMuteLists = () => {
const userState = useAppSelector((state) => state.user) const userState = useAppSelector((state) => state.user)
useEffect(() => { useEffect(() => {
const getMuteLists = async () => {
const pubkey = userState.user?.pubkey as string | undefined const pubkey = userState.user?.pubkey as string | undefined
getMuteLists(pubkey).then((lists) => {
const metadataController = await MetadataController.getInstance()
metadataController.getMuteLists(pubkey).then((lists) => {
setMuteLists(lists) setMuteLists(lists)
}) })
} }, [userState, getMuteLists])
getMuteLists()
}, [userState])
return muteLists return muteLists
} }

View File

@ -1,14 +1,13 @@
import { MetadataController } from 'controllers'
import { useState } from 'react' import { useState } from 'react'
import { useDidMount } from './useDidMount' import { useDidMount } from './useDidMount'
import { useNDKContext } from './useNDKContext'
export const useNSFWList = () => { export const useNSFWList = () => {
const { getNSFWList } = useNDKContext()
const [nsfwList, setNSFWList] = useState<string[]>([]) const [nsfwList, setNSFWList] = useState<string[]>([])
useDidMount(async () => { useDidMount(async () => {
const metadataController = await MetadataController.getInstance() getNSFWList().then((list) => {
metadataController.getNSFWList().then((list) => {
setNSFWList(list) setNSFWList(list)
}) })
}) })

View File

@ -1,10 +1,10 @@
import { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk' import { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk'
import { REACTIONS } from 'constants.ts' import { REACTIONS } from 'constants.ts'
import { UserRelaysType } from 'controllers'
import { useAppSelector, useDidMount, useNDKContext } from 'hooks' import { useAppSelector, useDidMount, useNDKContext } from 'hooks'
import { Event, kinds, UnsignedEvent } from 'nostr-tools' import { Event, kinds, UnsignedEvent } from 'nostr-tools'
import { useMemo, useState } from 'react' import { useMemo, useState } from 'react'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { UserRelaysType } from 'types'
import { abbreviateNumber, log, LogType, now } from 'utils' import { abbreviateNumber, log, LogType, now } from 'utils'
type UseReactionsParams = { type UseReactionsParams = {

View File

@ -6,7 +6,6 @@ import React, { useEffect, useState } from 'react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { Banner } from '../components/Banner' import { Banner } from '../components/Banner'
import { ZapPopUp } from '../components/Zap' import { ZapPopUp } from '../components/Zap'
import { MetadataController } from '../controllers'
import { import {
useAppDispatch, useAppDispatch,
useAppSelector, useAppSelector,
@ -261,8 +260,8 @@ const TipButtonWithDialog = React.memo(() => {
const [isOpen, setIsOpen] = useState(false) const [isOpen, setIsOpen] = useState(false)
useDidMount(async () => { useDidMount(async () => {
const metadataController = await MetadataController.getInstance() const adminNpubs = import.meta.env.VITE_ADMIN_NPUBS.split(',')
setAdminNpub(metadataController.adminNpubs[0]) setAdminNpub(adminNpubs[0])
}) })
return ( return (

View File

@ -152,7 +152,7 @@ const SlideContent = ({ naddr }: SlideContentProps) => {
useDidMount(() => { useDidMount(() => {
const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`) const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`)
const { identifier, kind, pubkey, relays = [] } = decoded.data const { identifier, kind, pubkey } = decoded.data
const ndkFilter: NDKFilter = { const ndkFilter: NDKFilter = {
'#a': [identifier], '#a': [identifier],
@ -160,7 +160,7 @@ const SlideContent = ({ naddr }: SlideContentProps) => {
kinds: [kind] kinds: [kind]
} }
fetchEvent(ndkFilter, relays) fetchEvent(ndkFilter)
.then((ndkEvent) => { .then((ndkEvent) => {
if (ndkEvent) { if (ndkEvent) {
const extracted = extractModData(ndkEvent) const extracted = extractModData(ndkEvent)
@ -225,7 +225,7 @@ const DisplayMod = ({ naddr }: DisplayModProps) => {
useDidMount(() => { useDidMount(() => {
const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`) const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`)
const { identifier, kind, pubkey, relays = [] } = decoded.data const { identifier, kind, pubkey } = decoded.data
const ndkFilter: NDKFilter = { const ndkFilter: NDKFilter = {
'#a': [identifier], '#a': [identifier],
@ -233,7 +233,7 @@ const DisplayMod = ({ naddr }: DisplayModProps) => {
kinds: [kind] kinds: [kind]
} }
fetchEvent(ndkFilter, relays) fetchEvent(ndkFilter)
.then((ndkEvent) => { .then((ndkEvent) => {
if (ndkEvent) { if (ndkEvent) {
const extracted = extractModData(ndkEvent) const extracted = extractModData(ndkEvent)

View File

@ -11,7 +11,6 @@ import { toast } from 'react-toastify'
import { BlogCard } from '../../components/BlogCard' import { BlogCard } from '../../components/BlogCard'
import { LoadingSpinner } from '../../components/LoadingSpinner' import { LoadingSpinner } from '../../components/LoadingSpinner'
import { ProfileSection } from '../../components/ProfileSection' import { ProfileSection } from '../../components/ProfileSection'
import { MetadataController, UserRelaysType } from '../../controllers'
import { useAppSelector, useDidMount, useNDKContext } from '../../hooks' import { useAppSelector, useDidMount, useNDKContext } from '../../hooks'
import { getGamePageRoute, getModsEditPageRoute } from '../../routes' import { getGamePageRoute, getModsEditPageRoute } from '../../routes'
import '../../styles/comments.css' import '../../styles/comments.css'
@ -24,7 +23,7 @@ import '../../styles/styles.css'
import '../../styles/tabs.css' import '../../styles/tabs.css'
import '../../styles/tags.css' import '../../styles/tags.css'
import '../../styles/write.css' import '../../styles/write.css'
import { DownloadUrl, ModDetails } from '../../types' import { DownloadUrl, ModDetails, UserRelaysType } from '../../types'
import { import {
abbreviateNumber, abbreviateNumber,
copyTextToClipboard, copyTextToClipboard,
@ -708,8 +707,8 @@ const ReportPopup = ({ aTag, handleClose }: ReportPopupProps) => {
return return
} }
const metadataController = await MetadataController.getInstance() const reportingNpub = import.meta.env.VITE_REPORTING_NPUB
const reportingPubkey = npubToHex(metadataController.reportingNpub) const reportingPubkey = npubToHex(reportingNpub)
if (reportingPubkey === hexPubkey) { if (reportingPubkey === hexPubkey) {
setLoadingSpinnerDesc(`Finding user's mute list`) setLoadingSpinnerDesc(`Finding user's mute list`)

View File

@ -1,5 +1,4 @@
import { AdminSVG, PreferenceSVG, ProfileSVG, RelaySVG } from 'components/SVGs' import { AdminSVG, PreferenceSVG, ProfileSVG, RelaySVG } from 'components/SVGs'
import { MetadataController } from 'controllers'
import { useAppSelector } from 'hooks' import { useAppSelector } from 'hooks'
import { logout } from 'nostr-login' import { logout } from 'nostr-login'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
@ -57,15 +56,12 @@ const SettingTabs = () => {
const userState = useAppSelector((state) => state.user) const userState = useAppSelector((state) => state.user)
useEffect(() => { useEffect(() => {
MetadataController.getInstance().then((controller) => { const adminNpubs = import.meta.env.VITE_ADMIN_NPUBS.split(',')
if (userState.auth && userState.user?.npub) { if (userState.auth && userState.user?.npub) {
setIsAdmin( setIsAdmin(adminNpubs.includes(userState.user.npub as string))
controller.adminNpubs.includes(userState.user.npub as string)
)
} else { } else {
setIsAdmin(false) setIsAdmin(false)
} }
})
}, [userState]) }, [userState])
const handleSignOut = () => { const handleSignOut = () => {

View File

@ -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 { InputField } from 'components/Inputs'
import { LoadingSpinner } from 'components/LoadingSpinner' import { LoadingSpinner } from 'components/LoadingSpinner'
import { MetadataController, UserRelaysType } from 'controllers'
import { useAppSelector, useDidMount, useNDKContext } from 'hooks' import { useAppSelector, useDidMount, useNDKContext } from 'hooks'
import { Event, kinds, UnsignedEvent } from 'nostr-tools' import { Event, kinds, UnsignedEvent } from 'nostr-tools'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { UserRelaysType } from 'types'
import { log, LogType, normalizeWebSocketURL, now } from 'utils' import { log, LogType, normalizeWebSocketURL, now } from 'utils'
const READ_MARKER = 'read' const READ_MARKER = 'read'
@ -20,10 +25,8 @@ export const RelaySettings = () => {
const [inputValue, setInputValue] = useState('') const [inputValue, setInputValue] = useState('')
useEffect(() => { useEffect(() => {
const fetchRelayList = async (pubkey: string) => { if (userState.auth && userState.user?.pubkey) {
const metadataController = await MetadataController.getInstance() getRelayListForUser(userState.user.pubkey as string, ndk)
metadataController
.getNDKRelayList(pubkey)
.then((res) => { .then((res) => {
setNDKRelayList(res) setNDKRelayList(res)
}) })
@ -35,14 +38,10 @@ export const RelaySettings = () => {
) )
setNDKRelayList(null) setNDKRelayList(null)
}) })
}
if (userState.auth && userState.user?.pubkey) {
fetchRelayList(userState.user.pubkey as string)
} else { } else {
setNDKRelayList(null) setNDKRelayList(null)
} }
}, [userState]) }, [userState, ndk])
const handleAdd = async (relayUrl: string) => { const handleAdd = async (relayUrl: string) => {
if (!ndkRelayList) return if (!ndkRelayList) return

View File

@ -29,7 +29,7 @@ export const SubmitModPage = () => {
useDidMount(async () => { useDidMount(async () => {
if (naddr) { if (naddr) {
const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`) const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`)
const { identifier, kind, pubkey, relays = [] } = decoded.data const { identifier, kind, pubkey } = decoded.data
const filter: NDKFilter = { const filter: NDKFilter = {
'#a': [identifier], '#a': [identifier],
@ -39,7 +39,7 @@ export const SubmitModPage = () => {
setIsFetching(true) setIsFetching(true)
fetchEvent(filter, relays) fetchEvent(filter)
.then((event) => { .then((event) => {
if (event) { if (event) {
const extracted = extractModData(event) const extracted = extractModData(event)

View File

@ -1,3 +1,9 @@
import { NDKUserProfile } from '@nostr-dev-kit/ndk' import { NDKUserProfile } from '@nostr-dev-kit/ndk'
export type UserProfile = NDKUserProfile | null export type UserProfile = NDKUserProfile | null
export enum UserRelaysType {
Read = 'readRelayUrls',
Write = 'writeRelayUrls',
Both = 'bothRelayUrls'
}