index.tsx raw
1 import { Button } from '@/components/ui/button'
2 import { Drawer, DrawerContent, DrawerTrigger } from '@/components/ui/drawer'
3 import {
4 DropdownMenu,
5 DropdownMenuContent,
6 DropdownMenuItem,
7 DropdownMenuTrigger
8 } from '@/components/ui/dropdown-menu'
9 import { useMuteList } from '@/providers/MuteListProvider'
10 import { useNostr } from '@/providers/NostrProvider'
11 import { useScreenSize } from '@/providers/ScreenSizeProvider'
12 import { BellOff, Loader } from 'lucide-react'
13 import { useMemo, useState } from 'react'
14 import { useTranslation } from 'react-i18next'
15 import { toast } from 'sonner'
16
17 export default function MuteButton({ pubkey }: { pubkey: string }) {
18 const { t } = useTranslation()
19 const { isSmallScreen } = useScreenSize()
20 const { pubkey: accountPubkey, checkLogin } = useNostr()
21 const { mutePubkeySet, changing, mutePubkeyPrivately, mutePubkeyPublicly, unmutePubkey } =
22 useMuteList()
23 const [updating, setUpdating] = useState(false)
24 const isMuted = useMemo(() => mutePubkeySet.has(pubkey), [mutePubkeySet, pubkey])
25
26 if (!accountPubkey || (pubkey && pubkey === accountPubkey)) return null
27
28 const handleMute = async (e: React.MouseEvent, isPrivate = true) => {
29 e.stopPropagation()
30 checkLogin(async () => {
31 if (isMuted) return
32
33 setUpdating(true)
34 try {
35 if (isPrivate) {
36 await mutePubkeyPrivately(pubkey)
37 } else {
38 await mutePubkeyPublicly(pubkey)
39 }
40 } catch (error) {
41 toast.error(`${t('Mute failed')}: ${(error as Error).message}`)
42 } finally {
43 setUpdating(false)
44 }
45 })
46 }
47
48 const handleUnmute = async (e: React.MouseEvent) => {
49 e.stopPropagation()
50 checkLogin(async () => {
51 if (!isMuted) return
52
53 setUpdating(true)
54 try {
55 await unmutePubkey(pubkey)
56 } catch (error) {
57 toast.error(`${t('Unmute failed')}: ${(error as Error).message}`)
58 } finally {
59 setUpdating(false)
60 }
61 })
62 }
63
64 if (isMuted) {
65 return (
66 <Button
67 className="w-20 min-w-20 rounded-full"
68 variant="secondary"
69 onClick={handleUnmute}
70 disabled={updating || changing}
71 >
72 {updating ? <Loader className="animate-spin" /> : t('Unmute')}
73 </Button>
74 )
75 }
76
77 const trigger = (
78 <Button
79 variant="destructive"
80 className="w-20 min-w-20 rounded-full"
81 disabled={updating || changing}
82 >
83 {updating ? <Loader className="animate-spin" /> : t('Mute')}
84 </Button>
85 )
86
87 if (isSmallScreen) {
88 return (
89 <Drawer>
90 <DrawerTrigger asChild>{trigger}</DrawerTrigger>
91 <DrawerContent>
92 <div className="py-2">
93 <Button
94 className="w-full p-6 justify-start text-destructive text-lg gap-4 [&_svg]:size-5 focus:text-destructive"
95 variant="ghost"
96 onClick={(e) => handleMute(e, true)}
97 disabled={updating || changing}
98 >
99 {updating ? <Loader className="animate-spin" /> : t('Mute user privately')}
100 </Button>
101 <Button
102 className="w-full p-6 justify-start text-destructive text-lg gap-4 [&_svg]:size-5 focus:text-destructive"
103 variant="ghost"
104 onClick={(e) => handleMute(e, false)}
105 disabled={updating || changing}
106 >
107 {updating ? <Loader className="animate-spin" /> : t('Mute user publicly')}
108 </Button>
109 </div>
110 </DrawerContent>
111 </Drawer>
112 )
113 }
114
115 return (
116 <DropdownMenu>
117 <DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
118 <DropdownMenuContent>
119 <DropdownMenuItem
120 onClick={(e) => handleMute(e, true)}
121 className="text-destructive focus:text-destructive"
122 >
123 <BellOff />
124 {t('Mute user privately')}
125 </DropdownMenuItem>
126 <DropdownMenuItem
127 onClick={(e) => handleMute(e, false)}
128 className="text-destructive focus:text-destructive"
129 >
130 <BellOff />
131 {t('Mute user publicly')}
132 </DropdownMenuItem>
133 </DropdownMenuContent>
134 </DropdownMenu>
135 )
136 }
137