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