feat: added caching using browsers built in index db #96
@ -14,71 +14,130 @@ import { NostrController } from '.'
|
|||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { queryNip05 } from '../utils'
|
import { queryNip05 } from '../utils'
|
||||||
import NDK, { NDKEvent, NDKSubscription } from '@nostr-dev-kit/ndk'
|
import NDK, { NDKEvent, NDKSubscription } from '@nostr-dev-kit/ndk'
|
||||||
|
import { EventEmitter } from 'tseep'
|
||||||
|
import { localCache } from '../services'
|
||||||
|
|
||||||
export class MetadataController {
|
export class MetadataController extends EventEmitter {
|
||||||
private nostrController: NostrController
|
private nostrController: NostrController
|
||||||
private specialMetadataRelay = 'wss://purplepag.es'
|
private specialMetadataRelay = 'wss://purplepag.es'
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
super()
|
||||||
this.nostrController = NostrController.getInstance()
|
this.nostrController = NostrController.getInstance()
|
||||||
}
|
}
|
||||||
|
|
||||||
public getEmptyMetadataEvent = (): Event => {
|
/**
|
||||||
return {
|
* Asynchronously checks for more recent metadata events authored by a specific key.
|
||||||
content: '',
|
* If a more recent metadata event is found, it is handled and returned.
|
||||||
created_at: new Date().valueOf(),
|
* If no more recent event is found, the current event is returned.
|
||||||
id: '',
|
* @param hexKey The hexadecimal key of the author to filter metadata events.
|
||||||
kind: 0,
|
* @param currentEvent The current metadata event, if any, to compare with newer events.
|
||||||
pubkey: '',
|
* @returns A promise resolving to the most recent metadata event found, or null if none is found.
|
||||||
sig: '',
|
*/
|
||||||
tags: []
|
private async checkForMoreRecentMetadata(
|
||||||
}
|
hexKey: string,
|
||||||
}
|
currentEvent: Event | null
|
||||||
|
): Promise<Event | null> {
|
||||||
public findMetadata = async (hexKey: string) => {
|
// Define the event filter to only include metadata events authored by the given key
|
||||||
const eventFilter: Filter = {
|
const eventFilter: Filter = {
|
||||||
kinds: [kinds.Metadata],
|
kinds: [kinds.Metadata], // Only metadata events
|
||||||
authors: [hexKey]
|
authors: [hexKey] // Authored by the specified key
|
||||||
}
|
}
|
||||||
|
|
||||||
const pool = new SimplePool()
|
const pool = new SimplePool()
|
||||||
|
|
||||||
|
// Try to get the metadata event from a special relay (wss://purplepag.es)
|
||||||
const metadataEvent = await pool
|
const metadataEvent = await pool
|
||||||
.get([this.specialMetadataRelay], eventFilter)
|
.get([this.specialMetadataRelay], eventFilter)
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error(err)
|
console.error(err) // Log any errors
|
||||||
return null
|
return null // Return null if an error occurs
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// If a valid metadata event is found from the special relay
|
||||||
if (
|
if (
|
||||||
metadataEvent &&
|
metadataEvent &&
|
||||||
validateEvent(metadataEvent) &&
|
validateEvent(metadataEvent) && // Validate the event
|
||||||
verifyEvent(metadataEvent)
|
verifyEvent(metadataEvent) // Verify the event's authenticity
|
||||||
) {
|
) {
|
||||||
return metadataEvent
|
// If there's no current event or the new metadata event is more recent
|
||||||
|
if (!currentEvent || metadataEvent.created_at > currentEvent.created_at) {
|
||||||
|
// Handle the new metadata event
|
||||||
|
this.handleNewMetadataEvent(metadataEvent)
|
||||||
|
return metadataEvent
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If no valid metadata event is found from the special relay, get the most popular relays
|
||||||
const mostPopularRelays = await this.nostrController.getMostPopularRelays()
|
const mostPopularRelays = await this.nostrController.getMostPopularRelays()
|
||||||
|
|
||||||
|
// Query the most popular relays for metadata events
|
||||||
const events = await pool
|
const events = await pool
|
||||||
.querySync(mostPopularRelays, eventFilter)
|
.querySync(mostPopularRelays, eventFilter)
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error(err)
|
console.error(err) // Log any errors
|
||||||
|
return null // Return null if an error occurs
|
||||||
return null
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// If events are found from the popular relays
|
||||||
if (events && events.length) {
|
if (events && events.length) {
|
||||||
events.sort((a, b) => b.created_at - a.created_at)
|
events.sort((a, b) => b.created_at - a.created_at) // Sort events by creation date (descending)
|
||||||
|
|
||||||
|
// Iterate through the events
|
||||||
for (const event of events) {
|
for (const event of events) {
|
||||||
if (validateEvent(event) && verifyEvent(event)) {
|
// If the event is valid, authentic, and more recent than the current event
|
||||||
|
if (
|
||||||
|
validateEvent(event) &&
|
||||||
|
verifyEvent(event) &&
|
||||||
|
(!currentEvent || event.created_at > currentEvent.created_at)
|
||||||
|
) {
|
||||||
|
// Handle the new metadata event
|
||||||
|
this.handleNewMetadataEvent(event)
|
||||||
return event
|
return event
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('Mo metadata found.')
|
return currentEvent // Return the current event if no newer event is found
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle new metadata events and emit them to subscribers
|
||||||
|
*/
|
||||||
|
private async handleNewMetadataEvent(event: VerifiedEvent) {
|
||||||
|
// update the event in local cache
|
||||||
|
localCache.addUserMetadata(event)
|
||||||
|
// Emit the event to subscribers.
|
||||||
|
this.emit(event.pubkey, event.kind, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds metadata for a given hexadecimal key.
|
||||||
|
*
|
||||||
|
* @param hexKey - The hexadecimal key to search for metadata.
|
||||||
|
* @returns A promise that resolves to the metadata event.
|
||||||
|
*/
|
||||||
|
public findMetadata = async (hexKey: string): Promise<Event | null> => {
|
||||||
|
// Attempt to retrieve the metadata event from the local cache
|
||||||
|
const cachedMetadataEvent = await localCache.getUserMetadata(hexKey)
|
||||||
|
|
||||||
|
// If cached metadata is found, check its validity
|
||||||
|
if (cachedMetadataEvent) {
|
||||||
|
const oneDayInMS = 24 * 60 * 60 * 1000 // Number of milliseconds in one day
|
||||||
|
|
||||||
|
// Check if the cached metadata is older than one day
|
||||||
|
if (Date.now() - cachedMetadataEvent.cachedAt > oneDayInMS) {
|
||||||
|
// If older than one day, find the metadata from relays in background
|
||||||
|
|
||||||
|
this.checkForMoreRecentMetadata(hexKey, cachedMetadataEvent.event)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the cached metadata event
|
||||||
|
return cachedMetadataEvent.event
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no cached metadata is found, retrieve it from relays
|
||||||
|
return this.checkForMoreRecentMetadata(hexKey, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
public findRelayListMetadata = async (hexKey: string) => {
|
public findRelayListMetadata = async (hexKey: string) => {
|
||||||
@ -142,7 +201,7 @@ export class MetadataController {
|
|||||||
throw new Error('No relay list metadata found.')
|
throw new Error('No relay list metadata found.')
|
||||||
}
|
}
|
||||||
|
|
||||||
public extractProfileMetadataContent = (event: VerifiedEvent) => {
|
public extractProfileMetadataContent = (event: Event) => {
|
||||||
try {
|
try {
|
||||||
if (!event.content) return {}
|
if (!event.content) return {}
|
||||||
return JSON.parse(event.content) as ProfileMetadata
|
return JSON.parse(event.content) as ProfileMetadata
|
||||||
@ -175,6 +234,7 @@ export class MetadataController {
|
|||||||
.publishEvent(signedMetadataEvent, [this.specialMetadataRelay])
|
.publishEvent(signedMetadataEvent, [this.specialMetadataRelay])
|
||||||
.then((relays) => {
|
.then((relays) => {
|
||||||
toast.success(`Metadata event published on: ${relays.join('\n')}`)
|
toast.success(`Metadata event published on: ${relays.join('\n')}`)
|
||||||
|
this.handleNewMetadataEvent(signedMetadataEvent as VerifiedEvent)
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
toast.error(err.message)
|
toast.error(err.message)
|
||||||
@ -193,6 +253,8 @@ export class MetadataController {
|
|||||||
userRelays.push(...relaySet.write)
|
userRelays.push(...relaySet.write)
|
||||||
} else {
|
} else {
|
||||||
const metadata = await this.findMetadata(hexKey)
|
const metadata = await this.findMetadata(hexKey)
|
||||||
|
if (!metadata) return null
|
||||||
|
|
||||||
const metadataContent = this.extractProfileMetadataContent(metadata)
|
const metadataContent = this.extractProfileMetadataContent(metadata)
|
||||||
|
|
||||||
if (metadataContent?.nip05) {
|
if (metadataContent?.nip05) {
|
||||||
@ -306,4 +368,16 @@ export class MetadataController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public validate = (event: Event) => validateEvent(event) && verifyEvent(event)
|
public validate = (event: Event) => validateEvent(event) && verifyEvent(event)
|
||||||
|
|
||||||
|
public getEmptyMetadataEvent = (): Event => {
|
||||||
|
return {
|
||||||
|
content: '',
|
||||||
|
created_at: new Date().valueOf(),
|
||||||
|
id: '',
|
||||||
|
kind: 0,
|
||||||
|
pubkey: '',
|
||||||
|
sig: '',
|
||||||
|
tags: []
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,7 @@ import { MetadataController, NostrController } from '../controllers'
|
|||||||
import { LoginMethods } from '../store/auth/types'
|
import { LoginMethods } from '../store/auth/types'
|
||||||
import { setUserRobotImage } from '../store/userRobotImage/action'
|
import { setUserRobotImage } from '../store/userRobotImage/action'
|
||||||
import { State } from '../store/rootReducer'
|
import { State } from '../store/rootReducer'
|
||||||
|
import { Event, kinds } from 'nostr-tools'
|
||||||
const metadataController = new MetadataController()
|
|
||||||
|
|
||||||
export const MainLayout = () => {
|
export const MainLayout = () => {
|
||||||
const dispatch: Dispatch = useDispatch()
|
const dispatch: Dispatch = useDispatch()
|
||||||
@ -27,6 +26,8 @@ export const MainLayout = () => {
|
|||||||
const authState = useSelector((state: State) => state.auth)
|
const authState = useSelector((state: State) => state.auth)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const metadataController = new MetadataController()
|
||||||
|
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
dispatch(
|
dispatch(
|
||||||
setAuthState({
|
setAuthState({
|
||||||
@ -68,6 +69,20 @@ export const MainLayout = () => {
|
|||||||
nostrController.createNsecBunkerSigner(usersPubkey)
|
nostrController.createNsecBunkerSigner(usersPubkey)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleMetadataEvent = (event: Event) => {
|
||||||
|
dispatch(setMetadataEvent(event))
|
||||||
|
}
|
||||||
|
|
||||||
|
metadataController.on(usersPubkey, (kind: number, event: Event) => {
|
||||||
|
if (kind === kinds.Metadata) {
|
||||||
|
handleMetadataEvent(event)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
metadataController.findMetadata(usersPubkey).then((metadataEvent) => {
|
||||||
|
if (metadataEvent) handleMetadataEvent(metadataEvent)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +49,7 @@ import type { Identifier, XYCoord } from 'dnd-core'
|
|||||||
import { useDrag, useDrop } from 'react-dnd'
|
import { useDrag, useDrop } from 'react-dnd'
|
||||||
import saveAs from 'file-saver'
|
import saveAs from 'file-saver'
|
||||||
import CopyModal from '../../components/copyModal'
|
import CopyModal from '../../components/copyModal'
|
||||||
|
import { Event, kinds } from 'nostr-tools'
|
||||||
|
|
||||||
export const CreatePage = () => {
|
export const CreatePage = () => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
@ -517,16 +518,26 @@ const DisplayUser = ({
|
|||||||
if (!(user.pubkey in metadata)) {
|
if (!(user.pubkey in metadata)) {
|
||||||
const metadataController = new MetadataController()
|
const metadataController = new MetadataController()
|
||||||
|
|
||||||
|
const handleMetadataEvent = (event: Event) => {
|
||||||
|
const metadataContent =
|
||||||
|
metadataController.extractProfileMetadataContent(event)
|
||||||
|
if (metadataContent)
|
||||||
|
setMetadata((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[user.pubkey]: metadataContent
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
metadataController.on(user.pubkey, (kind: number, event: Event) => {
|
||||||
|
if (kind === kinds.Metadata) {
|
||||||
|
handleMetadataEvent(event)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
metadataController
|
metadataController
|
||||||
.findMetadata(user.pubkey)
|
.findMetadata(user.pubkey)
|
||||||
.then((metadataEvent) => {
|
.then((metadataEvent) => {
|
||||||
const metadataContent =
|
if (metadataEvent) handleMetadataEvent(metadataEvent)
|
||||||
metadataController.extractProfileMetadataContent(metadataEvent)
|
|
||||||
if (metadataContent)
|
|
||||||
setMetadata((prev) => ({
|
|
||||||
...prev,
|
|
||||||
[user.pubkey]: metadataContent
|
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error(
|
console.error(
|
||||||
|
@ -2,7 +2,7 @@ import ContentCopyIcon from '@mui/icons-material/ContentCopy'
|
|||||||
import EditIcon from '@mui/icons-material/Edit'
|
import EditIcon from '@mui/icons-material/Edit'
|
||||||
import { Box, IconButton, SxProps, Theme, Typography } from '@mui/material'
|
import { Box, IconButton, SxProps, Theme, Typography } from '@mui/material'
|
||||||
import { truncate } from 'lodash'
|
import { truncate } from 'lodash'
|
||||||
import { VerifiedEvent, nip19 } from 'nostr-tools'
|
import { Event, VerifiedEvent, kinds, nip19 } from 'nostr-tools'
|
||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import { Link, useNavigate, useParams } from 'react-router-dom'
|
import { Link, useNavigate, useParams } from 'react-router-dom'
|
||||||
@ -75,6 +75,20 @@ export const ProfilePage = () => {
|
|||||||
|
|
||||||
if (pubkey) {
|
if (pubkey) {
|
||||||
const getMetadata = async (pubkey: string) => {
|
const getMetadata = async (pubkey: string) => {
|
||||||
|
const handleMetadataEvent = (event: Event) => {
|
||||||
|
const metadataContent =
|
||||||
|
metadataController.extractProfileMetadataContent(event)
|
||||||
|
if (metadataContent) {
|
||||||
|
setProfileMetadata(metadataContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
metadataController.on(pubkey, (kind: number, event: Event) => {
|
||||||
|
if (kind === kinds.Metadata) {
|
||||||
|
handleMetadataEvent(event)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const metadataEvent = await metadataController
|
const metadataEvent = await metadataController
|
||||||
.findMetadata(pubkey)
|
.findMetadata(pubkey)
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
@ -82,13 +96,7 @@ export const ProfilePage = () => {
|
|||||||
return null
|
return null
|
||||||
})
|
})
|
||||||
|
|
||||||
if (metadataEvent) {
|
if (metadataEvent) handleMetadataEvent(metadataEvent)
|
||||||
const metadataContent =
|
|
||||||
metadataController.extractProfileMetadataContent(metadataEvent)
|
|
||||||
if (metadataContent) {
|
|
||||||
setProfileMetadata(metadataContent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
useTheme
|
useTheme
|
||||||
} from '@mui/material'
|
} from '@mui/material'
|
||||||
import { UnsignedEvent, nip19, kinds, VerifiedEvent } from 'nostr-tools'
|
import { UnsignedEvent, nip19, kinds, VerifiedEvent, Event } from 'nostr-tools'
|
||||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { Link, useParams } from 'react-router-dom'
|
import { Link, useParams } from 'react-router-dom'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
@ -95,6 +95,20 @@ export const ProfileSettingsPage = () => {
|
|||||||
|
|
||||||
if (pubkey) {
|
if (pubkey) {
|
||||||
const getMetadata = async (pubkey: string) => {
|
const getMetadata = async (pubkey: string) => {
|
||||||
|
const handleMetadataEvent = (event: Event) => {
|
||||||
|
const metadataContent =
|
||||||
|
metadataController.extractProfileMetadataContent(event)
|
||||||
|
if (metadataContent) {
|
||||||
|
setProfileMetadata(metadataContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
metadataController.on(pubkey, (kind: number, event: Event) => {
|
||||||
|
if (kind === kinds.Metadata) {
|
||||||
|
handleMetadataEvent(event)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const metadataEvent = await metadataController
|
const metadataEvent = await metadataController
|
||||||
.findMetadata(pubkey)
|
.findMetadata(pubkey)
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
@ -102,13 +116,7 @@ export const ProfileSettingsPage = () => {
|
|||||||
return null
|
return null
|
||||||
})
|
})
|
||||||
|
|
||||||
if (metadataEvent) {
|
if (metadataEvent) handleMetadataEvent(metadataEvent)
|
||||||
const metadataContent =
|
|
||||||
metadataController.extractProfileMetadataContent(metadataEvent)
|
|
||||||
if (metadataContent) {
|
|
||||||
setProfileMetadata(metadataContent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ import saveAs from 'file-saver'
|
|||||||
import JSZip from 'jszip'
|
import JSZip from 'jszip'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import { MuiFileInput } from 'mui-file-input'
|
import { MuiFileInput } from 'mui-file-input'
|
||||||
import { Event, verifyEvent } from 'nostr-tools'
|
import { Event, kinds, verifyEvent } from 'nostr-tools'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import { useNavigate, useSearchParams } from 'react-router-dom'
|
import { useNavigate, useSearchParams } from 'react-router-dom'
|
||||||
@ -846,17 +846,27 @@ const DisplayMeta = ({
|
|||||||
|
|
||||||
hexKeys.forEach((key) => {
|
hexKeys.forEach((key) => {
|
||||||
if (!(key in metadata)) {
|
if (!(key in metadata)) {
|
||||||
|
const handleMetadataEvent = (event: Event) => {
|
||||||
|
const metadataContent =
|
||||||
|
metadataController.extractProfileMetadataContent(event)
|
||||||
|
|
||||||
|
if (metadataContent)
|
||||||
|
setMetadata((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[key]: metadataContent
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
metadataController.on(key, (kind: number, event: Event) => {
|
||||||
|
if (kind === kinds.Metadata) {
|
||||||
|
handleMetadataEvent(event)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
metadataController
|
metadataController
|
||||||
.findMetadata(key)
|
.findMetadata(key)
|
||||||
.then((metadataEvent) => {
|
.then((metadataEvent) => {
|
||||||
const metadataContent =
|
if (metadataEvent) handleMetadataEvent(metadataEvent)
|
||||||
metadataController.extractProfileMetadataContent(metadataEvent)
|
|
||||||
|
|
||||||
if (metadataContent)
|
|
||||||
setMetadata((prev) => ({
|
|
||||||
...prev,
|
|
||||||
[key]: metadataContent
|
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error(`error occurred in finding metadata for: ${key}`, err)
|
console.error(`error occurred in finding metadata for: ${key}`, err)
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
} from '@mui/material'
|
} from '@mui/material'
|
||||||
import JSZip from 'jszip'
|
import JSZip from 'jszip'
|
||||||
import { MuiFileInput } from 'mui-file-input'
|
import { MuiFileInput } from 'mui-file-input'
|
||||||
import { Event, verifyEvent } from 'nostr-tools'
|
import { Event, kinds, verifyEvent } from 'nostr-tools'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import { LoadingSpinner } from '../../components/LoadingSpinner'
|
import { LoadingSpinner } from '../../components/LoadingSpinner'
|
||||||
@ -107,16 +107,26 @@ export const VerifyPage = () => {
|
|||||||
const pubkey = npubToHex(user)!
|
const pubkey = npubToHex(user)!
|
||||||
|
|
||||||
if (!(pubkey in metadata)) {
|
if (!(pubkey in metadata)) {
|
||||||
|
const handleMetadataEvent = (event: Event) => {
|
||||||
|
const metadataContent =
|
||||||
|
metadataController.extractProfileMetadataContent(event)
|
||||||
|
if (metadataContent)
|
||||||
|
setMetadata((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[pubkey]: metadataContent
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
metadataController.on(pubkey, (kind: number, event: Event) => {
|
||||||
|
if (kind === kinds.Metadata) {
|
||||||
|
handleMetadataEvent(event)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
metadataController
|
metadataController
|
||||||
.findMetadata(pubkey)
|
.findMetadata(pubkey)
|
||||||
.then((metadataEvent) => {
|
.then((metadataEvent) => {
|
||||||
const metadataContent =
|
if (metadataEvent) handleMetadataEvent(metadataEvent)
|
||||||
metadataController.extractProfileMetadataContent(metadataEvent)
|
|
||||||
if (metadataContent)
|
|
||||||
setMetadata((prev) => ({
|
|
||||||
...prev,
|
|
||||||
[pubkey]: metadataContent
|
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error(
|
console.error(
|
||||||
|
Loading…
Reference in New Issue
Block a user