mirror of
https://github.com/t4t5/nostr-react.git
synced 2025-05-05 19:40:14 +02:00
Add new useProfile hook
It should batch requests when possible to prevent the "Max connections open" error
This commit is contained in:
parent
f572ae9cdc
commit
c606193c82
@ -68,6 +68,7 @@
|
|||||||
"typescript": "^4.9.4"
|
"typescript": "^4.9.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"jotai": "^1.12.1",
|
||||||
"nostr-tools": "^1.1.0"
|
"nostr-tools": "^1.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
225
src/core.tsx
Normal file
225
src/core.tsx
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
import {
|
||||||
|
createContext,
|
||||||
|
ReactNode,
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from "react"
|
||||||
|
import { Provider as JotaiProvider } from "jotai"
|
||||||
|
|
||||||
|
import { Relay, Filter, Event as NostrEvent, relayInit, Sub } from "nostr-tools"
|
||||||
|
|
||||||
|
import { uniqBy } from "./utils"
|
||||||
|
|
||||||
|
type OnConnectFunc = (relay: Relay) => void
|
||||||
|
type OnDisconnectFunc = (relay: Relay) => void
|
||||||
|
type OnEventFunc = (event: NostrEvent) => void
|
||||||
|
type OnSubscribeFunc = (sub: Sub, relay: Relay) => void
|
||||||
|
|
||||||
|
interface NostrContextType {
|
||||||
|
isLoading: boolean
|
||||||
|
debug?: boolean
|
||||||
|
connectedRelays: Relay[]
|
||||||
|
onConnect: (_onConnectCallback?: OnConnectFunc) => void
|
||||||
|
onDisconnect: (_onDisconnectCallback?: OnDisconnectFunc) => void
|
||||||
|
publish: (event: NostrEvent) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const NostrContext = createContext<NostrContextType>({
|
||||||
|
isLoading: true,
|
||||||
|
connectedRelays: [],
|
||||||
|
onConnect: () => null,
|
||||||
|
onDisconnect: () => null,
|
||||||
|
publish: () => null,
|
||||||
|
})
|
||||||
|
|
||||||
|
const log = (
|
||||||
|
isOn: boolean | undefined,
|
||||||
|
type: "info" | "error" | "warn",
|
||||||
|
...args: unknown[]
|
||||||
|
) => {
|
||||||
|
if (!isOn) return
|
||||||
|
console[type](...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NostrProvider({
|
||||||
|
children,
|
||||||
|
relayUrls,
|
||||||
|
debug,
|
||||||
|
}: {
|
||||||
|
children: ReactNode
|
||||||
|
relayUrls: string[]
|
||||||
|
debug?: boolean
|
||||||
|
}) {
|
||||||
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
|
const [connectedRelays, setConnectedRelays] = useState<Relay[]>([])
|
||||||
|
|
||||||
|
let onConnectCallback: null | OnConnectFunc = null
|
||||||
|
let onDisconnectCallback: null | OnDisconnectFunc = null
|
||||||
|
|
||||||
|
const isFirstRender = useRef(true)
|
||||||
|
|
||||||
|
const connectToRelays = useCallback(() => {
|
||||||
|
relayUrls.forEach(async (relayUrl) => {
|
||||||
|
const relay = relayInit(relayUrl)
|
||||||
|
relay.connect()
|
||||||
|
|
||||||
|
relay.on("connect", () => {
|
||||||
|
log(debug, "info", `✅ nostr (${relayUrl}): Connected!`)
|
||||||
|
setIsLoading(false)
|
||||||
|
onConnectCallback?.(relay)
|
||||||
|
setConnectedRelays((prev) => uniqBy([...prev, relay], "url"))
|
||||||
|
})
|
||||||
|
|
||||||
|
relay.on("disconnect", () => {
|
||||||
|
log(debug, "warn", `🚪 nostr (${relayUrl}): Connection closed.`)
|
||||||
|
onDisconnectCallback?.(relay)
|
||||||
|
setConnectedRelays((prev) => prev.filter((r) => r.url !== relayUrl))
|
||||||
|
})
|
||||||
|
|
||||||
|
relay.on("error", () => {
|
||||||
|
log(debug, "error", `❌ nostr (${relayUrl}): Connection error!`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Make sure we only start the relays once (even in strict-mode)
|
||||||
|
if (isFirstRender.current) {
|
||||||
|
isFirstRender.current = false
|
||||||
|
connectToRelays()
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const publish = (event: NostrEvent) => {
|
||||||
|
return connectedRelays.map((relay) => {
|
||||||
|
log(debug, "info", `⬆️ nostr (${relay.url}): Sending event:`, event)
|
||||||
|
|
||||||
|
return relay.publish(event)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const value: NostrContextType = {
|
||||||
|
debug,
|
||||||
|
isLoading,
|
||||||
|
connectedRelays,
|
||||||
|
publish,
|
||||||
|
onConnect: (_onConnectCallback?: OnConnectFunc) => {
|
||||||
|
if (_onConnectCallback) {
|
||||||
|
onConnectCallback = _onConnectCallback
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDisconnect: (_onDisconnectCallback?: OnDisconnectFunc) => {
|
||||||
|
if (_onDisconnectCallback) {
|
||||||
|
onDisconnectCallback = _onDisconnectCallback
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return <NostrContext.Provider value={value}>{children}</NostrContext.Provider>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useNostr() {
|
||||||
|
return useContext(NostrContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useNostrEvents({
|
||||||
|
filter,
|
||||||
|
enabled = true,
|
||||||
|
}: {
|
||||||
|
filter: Filter
|
||||||
|
enabled?: boolean
|
||||||
|
}) {
|
||||||
|
const { isLoading, onConnect, debug, connectedRelays } = useNostr()
|
||||||
|
const [events, setEvents] = useState<NostrEvent[]>([])
|
||||||
|
const [unsubscribe, setUnsubscribe] = useState<() => void | void>(() => {
|
||||||
|
return
|
||||||
|
})
|
||||||
|
|
||||||
|
let onEventCallback: null | OnEventFunc = null
|
||||||
|
let onSubscribeCallback: null | OnSubscribeFunc = null
|
||||||
|
|
||||||
|
// Lets us detect changes in the nested filter object for the useEffect hook
|
||||||
|
const filterBase64 =
|
||||||
|
typeof window !== "undefined" ? window.btoa(JSON.stringify(filter)) : null
|
||||||
|
|
||||||
|
const _unsubscribe = (sub: Sub, relay: Relay) => {
|
||||||
|
log(
|
||||||
|
debug,
|
||||||
|
"info",
|
||||||
|
`🙉 nostr (${relay.url}): Unsubscribing from filter:`,
|
||||||
|
filter,
|
||||||
|
)
|
||||||
|
return sub.unsub()
|
||||||
|
}
|
||||||
|
|
||||||
|
const subscribe = useCallback((relay: Relay, filter: Filter) => {
|
||||||
|
log(
|
||||||
|
debug,
|
||||||
|
"info",
|
||||||
|
`👂 nostr (${relay.url}): Subscribing to filter:`,
|
||||||
|
filter,
|
||||||
|
)
|
||||||
|
const sub = relay.sub([filter])
|
||||||
|
|
||||||
|
const unsubscribeFunc = () => {
|
||||||
|
_unsubscribe(sub, relay)
|
||||||
|
}
|
||||||
|
|
||||||
|
setUnsubscribe(() => unsubscribeFunc)
|
||||||
|
|
||||||
|
sub.on("event", (event: NostrEvent) => {
|
||||||
|
log(debug, "info", `⬇️ nostr (${relay.url}): Received event:`, event)
|
||||||
|
onEventCallback?.(event)
|
||||||
|
setEvents((_events) => {
|
||||||
|
return [event, ..._events]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return sub
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!enabled) return
|
||||||
|
|
||||||
|
const relaySubs = connectedRelays.map((relay) => {
|
||||||
|
const sub = subscribe(relay, filter)
|
||||||
|
|
||||||
|
onSubscribeCallback?.(sub, relay)
|
||||||
|
|
||||||
|
return {
|
||||||
|
sub,
|
||||||
|
relay,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
relaySubs.forEach(({ sub, relay }) => {
|
||||||
|
_unsubscribe(sub, relay)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [connectedRelays, filterBase64, enabled])
|
||||||
|
|
||||||
|
const uniqEvents = events.length > 0 ? uniqBy(events, "id") : []
|
||||||
|
const sortedEvents = uniqEvents.sort((a, b) => b.created_at - a.created_at)
|
||||||
|
|
||||||
|
return {
|
||||||
|
isLoading,
|
||||||
|
events: sortedEvents,
|
||||||
|
onConnect,
|
||||||
|
connectedRelays,
|
||||||
|
unsubscribe,
|
||||||
|
onSubscribe: (_onSubscribeCallback: OnSubscribeFunc) => {
|
||||||
|
if (_onSubscribeCallback) {
|
||||||
|
onSubscribeCallback = _onSubscribeCallback
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onEvent: (_onEventCallback: OnEventFunc) => {
|
||||||
|
if (_onEventCallback) {
|
||||||
|
onEventCallback = _onEventCallback
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
228
src/index.tsx
228
src/index.tsx
@ -1,225 +1,3 @@
|
|||||||
import {
|
export * from "./utils"
|
||||||
createContext,
|
export * from "./core"
|
||||||
ReactNode,
|
export * from "./useProfile"
|
||||||
useCallback,
|
|
||||||
useContext,
|
|
||||||
useEffect,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from "react"
|
|
||||||
|
|
||||||
import { Relay, Filter, Event as NostrEvent, relayInit, Sub } from "nostr-tools"
|
|
||||||
|
|
||||||
import { uniqBy } from "./utils"
|
|
||||||
export { dateToUnix } from "./utils"
|
|
||||||
|
|
||||||
type OnConnectFunc = (relay: Relay) => void
|
|
||||||
type OnDisconnectFunc = (relay: Relay) => void
|
|
||||||
type OnEventFunc = (event: NostrEvent) => void
|
|
||||||
type OnSubscribeFunc = (sub: Sub, relay: Relay) => void
|
|
||||||
|
|
||||||
interface NostrContextType {
|
|
||||||
isLoading: boolean
|
|
||||||
debug?: boolean
|
|
||||||
connectedRelays: Relay[]
|
|
||||||
onConnect: (_onConnectCallback?: OnConnectFunc) => void
|
|
||||||
onDisconnect: (_onDisconnectCallback?: OnDisconnectFunc) => void
|
|
||||||
publish: (event: NostrEvent) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const NostrContext = createContext<NostrContextType>({
|
|
||||||
isLoading: true,
|
|
||||||
connectedRelays: [],
|
|
||||||
onConnect: () => null,
|
|
||||||
onDisconnect: () => null,
|
|
||||||
publish: () => null,
|
|
||||||
})
|
|
||||||
|
|
||||||
const log = (
|
|
||||||
isOn: boolean | undefined,
|
|
||||||
type: "info" | "error" | "warn",
|
|
||||||
...args: unknown[]
|
|
||||||
) => {
|
|
||||||
if (!isOn) return
|
|
||||||
console[type](...args)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function NostrProvider({
|
|
||||||
children,
|
|
||||||
relayUrls,
|
|
||||||
debug,
|
|
||||||
}: {
|
|
||||||
children: ReactNode
|
|
||||||
relayUrls: string[]
|
|
||||||
debug?: boolean
|
|
||||||
}) {
|
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
|
||||||
const [connectedRelays, setConnectedRelays] = useState<Relay[]>([])
|
|
||||||
|
|
||||||
let onConnectCallback: null | OnConnectFunc = null
|
|
||||||
let onDisconnectCallback: null | OnDisconnectFunc = null
|
|
||||||
|
|
||||||
const isFirstRender = useRef(true)
|
|
||||||
|
|
||||||
const connectToRelays = useCallback(() => {
|
|
||||||
relayUrls.forEach(async (relayUrl) => {
|
|
||||||
const relay = relayInit(relayUrl)
|
|
||||||
relay.connect()
|
|
||||||
|
|
||||||
relay.on("connect", () => {
|
|
||||||
log(debug, "info", `✅ nostr (${relayUrl}): Connected!`)
|
|
||||||
setIsLoading(false)
|
|
||||||
onConnectCallback?.(relay)
|
|
||||||
setConnectedRelays((prev) => uniqBy([...prev, relay], "url"))
|
|
||||||
})
|
|
||||||
|
|
||||||
relay.on("disconnect", () => {
|
|
||||||
log(debug, "warn", `🚪 nostr (${relayUrl}): Connection closed.`)
|
|
||||||
onDisconnectCallback?.(relay)
|
|
||||||
setConnectedRelays((prev) => prev.filter((r) => r.url !== relayUrl))
|
|
||||||
})
|
|
||||||
|
|
||||||
relay.on("error", () => {
|
|
||||||
log(debug, "error", `❌ nostr (${relayUrl}): Connection error!`)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Make sure we only start the relays once (even in strict-mode)
|
|
||||||
if (isFirstRender.current) {
|
|
||||||
isFirstRender.current = false
|
|
||||||
connectToRelays()
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const publish = (event: NostrEvent) => {
|
|
||||||
return connectedRelays.map((relay) => {
|
|
||||||
log(debug, "info", `⬆️ nostr (${relay.url}): Sending event:`, event)
|
|
||||||
|
|
||||||
return relay.publish(event)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const value: NostrContextType = {
|
|
||||||
debug,
|
|
||||||
isLoading,
|
|
||||||
connectedRelays,
|
|
||||||
publish,
|
|
||||||
onConnect: (_onConnectCallback?: OnConnectFunc) => {
|
|
||||||
if (_onConnectCallback) {
|
|
||||||
onConnectCallback = _onConnectCallback
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDisconnect: (_onDisconnectCallback?: OnDisconnectFunc) => {
|
|
||||||
if (_onDisconnectCallback) {
|
|
||||||
onDisconnectCallback = _onDisconnectCallback
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return <NostrContext.Provider value={value}>{children}</NostrContext.Provider>
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useNostr() {
|
|
||||||
return useContext(NostrContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useNostrEvents({
|
|
||||||
filter,
|
|
||||||
enabled = true,
|
|
||||||
}: {
|
|
||||||
filter: Filter
|
|
||||||
enabled?: boolean
|
|
||||||
}) {
|
|
||||||
const { isLoading, onConnect, debug, connectedRelays } = useNostr()
|
|
||||||
const [events, setEvents] = useState<NostrEvent[]>([])
|
|
||||||
const [unsubscribe, setUnsubscribe] = useState<() => void | void>(() => {
|
|
||||||
return
|
|
||||||
})
|
|
||||||
|
|
||||||
let onEventCallback: null | OnEventFunc = null
|
|
||||||
let onSubscribeCallback: null | OnSubscribeFunc = null
|
|
||||||
|
|
||||||
// Lets us detect changes in the nested filter object for the useEffect hook
|
|
||||||
const filterBase64 =
|
|
||||||
typeof window !== "undefined" ? window.btoa(JSON.stringify(filter)) : null
|
|
||||||
|
|
||||||
const _unsubscribe = (sub: Sub, relay: Relay) => {
|
|
||||||
log(
|
|
||||||
debug,
|
|
||||||
"info",
|
|
||||||
`🙉 nostr (${relay.url}): Unsubscribing from filter:`,
|
|
||||||
filter,
|
|
||||||
)
|
|
||||||
return sub.unsub()
|
|
||||||
}
|
|
||||||
|
|
||||||
const subscribe = useCallback((relay: Relay, filter: Filter) => {
|
|
||||||
log(
|
|
||||||
debug,
|
|
||||||
"info",
|
|
||||||
`👂 nostr (${relay.url}): Subscribing to filter:`,
|
|
||||||
filter,
|
|
||||||
)
|
|
||||||
const sub = relay.sub([filter])
|
|
||||||
|
|
||||||
const unsubscribeFunc = () => {
|
|
||||||
_unsubscribe(sub, relay)
|
|
||||||
}
|
|
||||||
|
|
||||||
setUnsubscribe(() => unsubscribeFunc)
|
|
||||||
|
|
||||||
sub.on("event", (event: NostrEvent) => {
|
|
||||||
log(debug, "info", `⬇️ nostr (${relay.url}): Received event:`, event)
|
|
||||||
onEventCallback?.(event)
|
|
||||||
setEvents((_events) => {
|
|
||||||
return [event, ..._events]
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
return sub
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!enabled) return
|
|
||||||
|
|
||||||
const relaySubs = connectedRelays.map((relay) => {
|
|
||||||
const sub = subscribe(relay, filter)
|
|
||||||
|
|
||||||
onSubscribeCallback?.(sub, relay)
|
|
||||||
|
|
||||||
return {
|
|
||||||
sub,
|
|
||||||
relay,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
relaySubs.forEach(({ sub, relay }) => {
|
|
||||||
_unsubscribe(sub, relay)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [connectedRelays, filterBase64, enabled])
|
|
||||||
|
|
||||||
const uniqEvents = events.length > 0 ? uniqBy(events, "id") : []
|
|
||||||
const sortedEvents = uniqEvents.sort((a, b) => b.created_at - a.created_at)
|
|
||||||
|
|
||||||
return {
|
|
||||||
isLoading,
|
|
||||||
events: sortedEvents,
|
|
||||||
onConnect,
|
|
||||||
connectedRelays,
|
|
||||||
unsubscribe,
|
|
||||||
onSubscribe: (_onSubscribeCallback: OnSubscribeFunc) => {
|
|
||||||
if (_onSubscribeCallback) {
|
|
||||||
onSubscribeCallback = _onSubscribeCallback
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onEvent: (_onEventCallback: OnEventFunc) => {
|
|
||||||
if (_onEventCallback) {
|
|
||||||
onEventCallback = _onEventCallback
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
111
src/useProfile.tsx
Normal file
111
src/useProfile.tsx
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import { atom, useAtom } from "jotai"
|
||||||
|
import { nip19 } from "nostr-tools"
|
||||||
|
import { useEffect, useState } from "react"
|
||||||
|
|
||||||
|
import { useNostrEvents } from "./core"
|
||||||
|
import { uniqValues } from "./utils"
|
||||||
|
|
||||||
|
interface Metadata {
|
||||||
|
name?: string
|
||||||
|
display_name?: string
|
||||||
|
picture?: string
|
||||||
|
about?: string
|
||||||
|
website?: string
|
||||||
|
lud06?: string
|
||||||
|
lud16?: string
|
||||||
|
nip06?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const QUEUE_DEBOUNCE_DURATION = 100
|
||||||
|
|
||||||
|
let timer: NodeJS.Timeout | undefined = undefined
|
||||||
|
|
||||||
|
const queuedPubkeysAtom = atom<string[]>([])
|
||||||
|
const requestedPubkeysAtom = atom<string[]>([])
|
||||||
|
const fetchedProfilesAtom = atom<Record<string, Metadata>>({})
|
||||||
|
|
||||||
|
function useProfileQueue({ pubkey }: { pubkey: string }) {
|
||||||
|
const [isReadyToFetch, setIsReadyToFetch] = useState(false)
|
||||||
|
|
||||||
|
const [queuedPubkeys, setQueuedPubkeys] = useAtom(queuedPubkeysAtom)
|
||||||
|
|
||||||
|
const [requestedPubkeys] = useAtom(requestedPubkeysAtom)
|
||||||
|
const alreadyRequested = !!requestedPubkeys.includes(pubkey)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (alreadyRequested) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
clearTimeout(timer)
|
||||||
|
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
setIsReadyToFetch(true)
|
||||||
|
}, QUEUE_DEBOUNCE_DURATION)
|
||||||
|
|
||||||
|
setQueuedPubkeys((_pubkeys: string[]) => {
|
||||||
|
// Unique values only:
|
||||||
|
const arr = [..._pubkeys, pubkey].filter(uniqValues).filter((_pubkey) => {
|
||||||
|
return !requestedPubkeys.includes(_pubkey)
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log("ARR", arr)
|
||||||
|
|
||||||
|
return arr
|
||||||
|
})
|
||||||
|
}, [pubkey, setQueuedPubkeys, alreadyRequested, requestedPubkeys])
|
||||||
|
|
||||||
|
return {
|
||||||
|
pubkeysToFetch: isReadyToFetch ? queuedPubkeys : [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useProfile({ pubkey }: { pubkey: string }) {
|
||||||
|
const [, setRequestedPubkeys] = useAtom(requestedPubkeysAtom)
|
||||||
|
const { pubkeysToFetch } = useProfileQueue({ pubkey })
|
||||||
|
const enabled = !!pubkeysToFetch.length
|
||||||
|
|
||||||
|
const [fetchedProfiles, setFetchedProfiles] = useAtom(fetchedProfilesAtom)
|
||||||
|
|
||||||
|
const { onEvent, onSubscribe } = useNostrEvents({
|
||||||
|
filter: {
|
||||||
|
kinds: [0],
|
||||||
|
authors: pubkeysToFetch,
|
||||||
|
},
|
||||||
|
enabled,
|
||||||
|
})
|
||||||
|
|
||||||
|
onSubscribe(() => {
|
||||||
|
// Reset list
|
||||||
|
// (We've already opened a subscription to these pubkeys now)
|
||||||
|
setRequestedPubkeys((_pubkeys) => {
|
||||||
|
return [..._pubkeys, ...pubkeysToFetch].filter(uniqValues)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
onEvent((rawMetadata) => {
|
||||||
|
try {
|
||||||
|
const metadata: Metadata = JSON.parse(rawMetadata.content)
|
||||||
|
const metaPubkey = rawMetadata.pubkey
|
||||||
|
|
||||||
|
if (metadata) {
|
||||||
|
setFetchedProfiles((_profiles: Record<string, Metadata>) => {
|
||||||
|
return {
|
||||||
|
..._profiles,
|
||||||
|
[metaPubkey]: metadata,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err, rawMetadata)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const metadata = fetchedProfiles[pubkey]
|
||||||
|
const npub = nip19.npubEncode(pubkey)
|
||||||
|
|
||||||
|
return {
|
||||||
|
...metadata,
|
||||||
|
npub,
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,10 @@ export const uniqBy = <T>(arr: T[], key: keyof T): T[] => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const uniqValues = (value: string, index: number, self: string[]) => {
|
||||||
|
return self.indexOf(value) === index
|
||||||
|
}
|
||||||
|
|
||||||
export const dateToUnix = (_date?: Date) => {
|
export const dateToUnix = (_date?: Date) => {
|
||||||
const date = _date || new Date()
|
const date = _date || new Date()
|
||||||
|
|
||||||
|
@ -4220,6 +4220,11 @@ jest@^27.4.7:
|
|||||||
import-local "^3.0.2"
|
import-local "^3.0.2"
|
||||||
jest-cli "^27.5.1"
|
jest-cli "^27.5.1"
|
||||||
|
|
||||||
|
jotai@^1.12.1:
|
||||||
|
version "1.12.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/jotai/-/jotai-1.12.1.tgz#18808724c96f8c038f6ee8b75f3ac37e1ac35608"
|
||||||
|
integrity sha512-t6gsYM1WkQHMOazaZYLykCA+fh9KPDGrA+tDYzDeV0268QsCqmX6S4lO46uswgt1LGUeG0EDFGuMd9ac8cWNTA==
|
||||||
|
|
||||||
jpjs@^1.2.1:
|
jpjs@^1.2.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.npmjs.org/jpjs/-/jpjs-1.2.1.tgz"
|
resolved "https://registry.npmjs.org/jpjs/-/jpjs-1.2.1.tgz"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user