import { Skeleton } from '@/components/ui/skeleton'
import { useFetchProfile } from '@/hooks'
import { toProfile } from '@/lib/link'
import { formatPubkey, userIdToPubkey, pubkeyToNpub, formatNpub } from '@/lib/pubkey'
import { cn } from '@/lib/utils'
import { useSmartProfileNavigationOptional } from '@/PageManager'
import { useMemo } from 'react'
export default function Username({
userId,
showAt = false,
className,
skeletonClassName,
withoutSkeleton = false,
style,
onNavigate
}: {
userId: string
showAt?: boolean
className?: string
skeletonClassName?: string
withoutSkeleton?: boolean
style?: React.CSSProperties
onNavigate?: () => void
}) {
const { profile, isFetching } = useFetchProfile(userId)
const { navigateToProfile } = useSmartProfileNavigationOptional()
// Get pubkey from userId (works even if profile isn't loaded)
const pubkey = useMemo(() => {
if (profile?.pubkey) return profile.pubkey
return userIdToPubkey(userId) || ''
}, [userId, profile?.pubkey])
// Never block on profile fetch when we can already show npub/hex fallback (feeds batch-fetch profiles).
const canShowWithoutProfile = Boolean(pubkey)
if (isFetching && !withoutSkeleton && !canShowWithoutProfile) {
return (
)
}
// If we have a profile, show the username
if (profile) {
const { username, pubkey: profilePubkey } = profile
return (
{
e.stopPropagation()
onNavigate?.()
navigateToProfile(toProfile(profilePubkey))
}}
>
{showAt && '@'}
{username}
)
}
// Fallback: show formatted npub (bech32) if we have a pubkey (even if profile fetch failed)
if (pubkey) {
// Convert to npub (bech32) format for display
const npub = pubkeyToNpub(pubkey)
const displayName = npub ? formatNpub(npub) : formatPubkey(pubkey)
return (
{
e.stopPropagation()
onNavigate?.()
navigateToProfile(toProfile(pubkey))
}}
>
{showAt && '@'}
{displayName}
)
}
// No pubkey available - return null or skeleton based on withoutSkeleton
if (!withoutSkeleton) {
return (
)
}
return null
}
export function SimpleUsername({
userId,
showAt = false,
className,
skeletonClassName,
withoutSkeleton = false,
style
}: {
userId: string
showAt?: boolean
className?: string
skeletonClassName?: string
withoutSkeleton?: boolean
style?: React.CSSProperties
}) {
const { profile, isFetching } = useFetchProfile(userId)
// Get pubkey from userId (works even if profile isn't loaded)
const pubkey = useMemo(() => {
if (profile?.pubkey) return profile.pubkey
return userIdToPubkey(userId) || ''
}, [userId, profile?.pubkey])
const canShowWithoutProfile = Boolean(pubkey)
if (isFetching && !withoutSkeleton && !canShowWithoutProfile) {
return (
)
}
// If we have a profile, show the username
if (profile) {
const { username } = profile
return (
{showAt && '@'}
{username}
)
}
// Fallback: show formatted npub (bech32) if we have a pubkey (even if profile fetch failed)
if (pubkey) {
// Convert to npub (bech32) format for display
const npub = pubkeyToNpub(pubkey)
const displayName = npub ? formatNpub(npub) : formatPubkey(pubkey)
return (
{showAt && '@'}
{displayName}
)
}
// No pubkey available - return null or skeleton based on withoutSkeleton
if (!withoutSkeleton) {
return (
)
}
return null
}