import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { useFetchRelayInfo } from '@/hooks'
import { getRelayIconOverrideSrc, relayUrlFingerprintColors } from '@/lib/relay-icon-source'
import { cn } from '@/lib/utils'
import logger from '@/lib/logger'
import { Server } from 'lucide-react'
import { useMemo } from 'react'
/**
* Resolve an image URL from NIP-11. Handles:
* - Absolute HTTP(S) URLs → used as-is
* - Relative paths (e.g. "/logo.png") → resolved against the relay's base HTTP URL
* - ws(s):// URLs some relays mistakenly return → ignored
*
* We do not fetch `https://host/favicon.ico` as a fallback: many relays return HTML/404 there,
* which triggers Firefox Opaque Response Blocking noise and broken `
` loads.
*/
function resolveRelayImageUrl(raw: string, relayUrl: string): string | undefined {
if (!raw) return undefined
if (raw.startsWith('https://') || raw.startsWith('http://')) return raw
if (raw.startsWith('/')) {
try {
const base = relayUrl.replace(/^wss?:\/\//i, 'https://').replace(/^ws:\/\//i, 'http://')
const u = new URL(base)
return `${u.protocol}//${u.host}${raw}`
} catch {
return undefined
}
}
return undefined
}
export default function RelayIcon({
url,
className,
iconSize = 14,
/** When true, do not hit NIP-11 (parent already fetches relay info, or icon-only row). */
skipRelayInfoFetch = false
}: {
url?: string
className?: string
iconSize?: number
skipRelayInfoFetch?: boolean
}) {
const { relayInfo } = useFetchRelayInfo(skipRelayInfoFetch ? undefined : url)
const iconUrl = useMemo(() => {
if (!url) return undefined
const override = getRelayIconOverrideSrc(url)
if (override) {
logger.debug('[RelayIcon] using override icon', { url, override })
return override
}
// Prefer the NIP-11 icon field
const rawIcon = relayInfo?.icon && typeof relayInfo.icon === 'string' ? relayInfo.icon : undefined
const nip11Icon = rawIcon ? resolveRelayImageUrl(rawIcon, url) : undefined
if (nip11Icon) {
logger.debug('[RelayIcon] using NIP-11 icon', { url, rawIcon, nip11Icon })
return nip11Icon
}
return undefined
}, [url, relayInfo])
const fallbackColors = useMemo(() => relayUrlFingerprintColors(url), [url])
return (
{iconUrl && }
)
}