You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

86 lines
2.6 KiB

import { DEFAULT_FAVORITE_RELAYS, FAST_READ_RELAY_URLS } from '@/constants'
import { normalizeAnyRelayUrl } from '@/lib/url'
import { useNostr } from '@/providers/NostrProvider'
import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider'
import client from '@/services/client.service'
import { useEffect, useMemo, useState } from 'react'
const POLL_MS = 1500
function canon(url: string): string {
return (normalizeAnyRelayUrl(url) || url).trim().toLowerCase()
}
function mergeUniquePreserveOrder(...lists: (readonly string[] | undefined)[]): string[] {
const seen = new Set<string>()
const out: string[] = []
for (const list of lists) {
if (!list?.length) continue
for (const raw of list) {
const n = normalizeAnyRelayUrl(raw) || raw
const k = canon(n)
if (!k || seen.has(k)) continue
seen.add(k)
out.push(n)
}
}
return out
}
export type TRelayConnectionRow = {
url: string
/** WebSocket in the pool is open. */
connected: boolean
}
/**
* Relays to show in “active relays” UI: favorites + NIP-65 read/write + defaults + fast-read,
* then any pool-connected URL not already listed. {@link row.connected} reflects the live WebSocket.
*/
export function useRelayConnectionRows(): {
rows: TRelayConnectionRow[]
/** Relays that currently have an open WebSocket connection. */
connectedCount: number
} {
const { relayList } = useNostr()
const { favoriteRelays } = useFavoriteRelays()
const [connectedCanon, setConnectedCanon] = useState<Set<string>>(() =>
new Set(client.getConnectedRelayUrls().map(canon))
)
useEffect(() => {
const tick = () => setConnectedCanon(new Set(client.getConnectedRelayUrls().map(canon)))
tick()
const id = window.setInterval(tick, POLL_MS)
return () => clearInterval(id)
}, [])
return useMemo(() => {
const inbox = [...(relayList?.read ?? []), ...(relayList?.write ?? [])]
const base = mergeUniquePreserveOrder(
favoriteRelays,
inbox,
DEFAULT_FAVORITE_RELAYS,
FAST_READ_RELAY_URLS
)
const baseCanon = new Set(base.map(canon))
const rowFor = (url: string, socketConnected: boolean): TRelayConnectionRow => ({
url,
connected: socketConnected
})
const rows: TRelayConnectionRow[] = base.map((url) =>
rowFor(url, connectedCanon.has(canon(url)))
)
for (const url of client.getConnectedRelayUrls()) {
const k = canon(url)
if (baseCanon.has(k)) continue
rows.push(rowFor(url, true))
}
const connectedCount = rows.filter((r) => r.connected).length
return { rows, connectedCount }
}, [favoriteRelays, relayList?.read, relayList?.write, connectedCanon])
}