index.tsx raw
1 import {
2 AlertDialog,
3 AlertDialogAction,
4 AlertDialogCancel,
5 AlertDialogContent,
6 AlertDialogDescription,
7 AlertDialogFooter,
8 AlertDialogHeader,
9 AlertDialogTitle,
10 AlertDialogTrigger
11 } from '@/components/ui/alert-dialog'
12 import { Button } from '@/components/ui/button'
13 import { useFollowList } from '@/providers/FollowListProvider'
14 import { useNostr } from '@/providers/NostrProvider'
15 import { Loader } from 'lucide-react'
16 import { useMemo, useState } from 'react'
17 import { useTranslation } from 'react-i18next'
18 import { toast } from 'sonner'
19
20 export default function FollowButton({ pubkey }: { pubkey: string }) {
21 const { t } = useTranslation()
22 const { pubkey: accountPubkey, checkLogin } = useNostr()
23 const { followingSet, follow, unfollow } = useFollowList()
24 const [updating, setUpdating] = useState(false)
25 const [hover, setHover] = useState(false)
26 const isFollowing = useMemo(() => followingSet.has(pubkey), [followingSet, pubkey])
27
28 if (!accountPubkey || (pubkey && pubkey === accountPubkey)) return null
29
30 const handleFollow = async (e: React.MouseEvent) => {
31 e.stopPropagation()
32 checkLogin(async () => {
33 if (isFollowing) return
34
35 setUpdating(true)
36 try {
37 await follow(pubkey)
38 } catch (error) {
39 toast.error(t('Follow failed') + ': ' + (error as Error).message)
40 } finally {
41 setUpdating(false)
42 }
43 })
44 }
45
46 const handleUnfollow = async (e: React.MouseEvent) => {
47 e.stopPropagation()
48 checkLogin(async () => {
49 if (!isFollowing) return
50
51 setUpdating(true)
52 try {
53 await unfollow(pubkey)
54 } catch (error) {
55 toast.error(t('Unfollow failed') + ': ' + (error as Error).message)
56 } finally {
57 setUpdating(false)
58 }
59 })
60 }
61
62 return isFollowing ? (
63 <div onClick={(e) => e.stopPropagation()}>
64 <AlertDialog>
65 <AlertDialogTrigger asChild>
66 <Button
67 className="rounded-full min-w-28"
68 variant={hover ? 'destructive' : 'secondary'}
69 disabled={updating}
70 onMouseEnter={() => setHover(true)}
71 onMouseLeave={() => setHover(false)}
72 >
73 {updating ? (
74 <Loader className="animate-spin" />
75 ) : hover ? (
76 t('Unfollow')
77 ) : (
78 t('buttonFollowing')
79 )}
80 </Button>
81 </AlertDialogTrigger>
82 <AlertDialogContent>
83 <AlertDialogHeader>
84 <AlertDialogTitle>{t('Unfollow')}?</AlertDialogTitle>
85 <AlertDialogDescription>
86 {t('Are you sure you want to unfollow this user?')}
87 </AlertDialogDescription>
88 </AlertDialogHeader>
89 <AlertDialogFooter>
90 <AlertDialogCancel>{t('Cancel')}</AlertDialogCancel>
91 <AlertDialogAction onClick={handleUnfollow} variant="destructive">
92 {t('Unfollow')}
93 </AlertDialogAction>
94 </AlertDialogFooter>
95 </AlertDialogContent>
96 </AlertDialog>
97 </div>
98 ) : (
99 <Button className="rounded-full min-w-28" onClick={handleFollow} disabled={updating}>
100 {updating ? <Loader className="animate-spin" /> : t('Follow')}
101 </Button>
102 )
103 }
104