index.tsx raw
1 import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card'
2 import { Skeleton } from '@/components/ui/skeleton'
3 import { useFetchProfile } from '@/hooks'
4 import { toProfile } from '@/lib/link'
5 import { cn, isTouchDevice } from '@/lib/utils'
6 import { SecondaryPageLink } from '@/PageManager'
7 import { useMemo } from 'react'
8 import NpubQrCode from '../NpubQrCode'
9 import ProfileCard from '../ProfileCard'
10 import TextWithEmojis from '../TextWithEmojis'
11
12 export default function Username({
13 userId,
14 showAt = false,
15 className,
16 skeletonClassName,
17 withoutSkeleton = false,
18 showQrCode = true
19 }: {
20 userId: string
21 showAt?: boolean
22 className?: string
23 skeletonClassName?: string
24 withoutSkeleton?: boolean
25 showQrCode?: boolean
26 }) {
27 const { profile, isFetching } = useFetchProfile(userId)
28 const supportTouch = useMemo(() => isTouchDevice(), [])
29 if (!profile && isFetching && !withoutSkeleton) {
30 return (
31 <div className="py-1">
32 <Skeleton className={cn('w-16', skeletonClassName)} />
33 </div>
34 )
35 }
36 if (!profile) return null
37
38 const usernameLink = (
39 <SecondaryPageLink
40 to={toProfile(userId)}
41 className="truncate hover:underline"
42 onClick={(e) => e.stopPropagation()}
43 >
44 {showAt && '@'}
45 <TextWithEmojis text={profile.username} emojis={profile.emojis} emojiClassName="mb-1" />
46 </SecondaryPageLink>
47 )
48
49 const trigger = (
50 <div className={cn('flex items-center gap-1', className)}>
51 {usernameLink}
52 {showQrCode && <NpubQrCode pubkey={userId} />}
53 </div>
54 )
55
56 if (supportTouch) {
57 return trigger
58 }
59
60 return (
61 <HoverCard>
62 <HoverCardTrigger asChild>{trigger}</HoverCardTrigger>
63 <HoverCardContent className="w-80">
64 <ProfileCard userId={userId} />
65 </HoverCardContent>
66 </HoverCard>
67 )
68 }
69
70 export function SimpleUsername({
71 userId,
72 showAt = false,
73 className,
74 skeletonClassName,
75 withoutSkeleton = false,
76 showQrCode = true
77 }: {
78 userId: string
79 showAt?: boolean
80 className?: string
81 skeletonClassName?: string
82 withoutSkeleton?: boolean
83 showQrCode?: boolean
84 }) {
85 const { profile, isFetching } = useFetchProfile(userId)
86 if (!profile && isFetching && !withoutSkeleton) {
87 return (
88 <div className="py-1">
89 <Skeleton className={cn('w-16', skeletonClassName)} />
90 </div>
91 )
92 }
93 if (!profile) return null
94
95 const { username, emojis } = profile
96
97 return (
98 <div className={cn('flex items-center gap-1', className)}>
99 <span className="truncate">
100 {showAt && '@'}
101 <TextWithEmojis text={username} emojis={emojis} emojiClassName="mb-1" />
102 </span>
103 {showQrCode && <NpubQrCode pubkey={userId} />}
104 </div>
105 )
106 }
107