feat: support choosing between public and private mute
This commit is contained in:
parent
30a32ca94f
commit
ec1692c066
19 changed files with 473 additions and 120 deletions
|
|
@ -1,29 +1,43 @@
|
|||
import { Button } from '@/components/ui/button'
|
||||
import { Drawer, DrawerContent, DrawerTrigger } from '@/components/ui/drawer'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { useToast } from '@/hooks'
|
||||
import { useMuteList } from '@/providers/MuteListProvider'
|
||||
import { useNostr } from '@/providers/NostrProvider'
|
||||
import { Loader } from 'lucide-react'
|
||||
import { useScreenSize } from '@/providers/ScreenSizeProvider'
|
||||
import { BellOff, Loader } from 'lucide-react'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export default function MuteButton({ pubkey }: { pubkey: string }) {
|
||||
const { t } = useTranslation()
|
||||
const { isSmallScreen } = useScreenSize()
|
||||
const { toast } = useToast()
|
||||
const { pubkey: accountPubkey, checkLogin } = useNostr()
|
||||
const { mutePubkeys, mutePubkey, unmutePubkey } = useMuteList()
|
||||
const { mutePubkeys, changing, mutePubkeyPrivately, mutePubkeyPublicly, unmutePubkey } =
|
||||
useMuteList()
|
||||
const [updating, setUpdating] = useState(false)
|
||||
const isMuted = useMemo(() => mutePubkeys.includes(pubkey), [mutePubkeys, pubkey])
|
||||
|
||||
if (!accountPubkey || (pubkey && pubkey === accountPubkey)) return null
|
||||
|
||||
const handleMute = async (e: React.MouseEvent) => {
|
||||
const handleMute = async (e: React.MouseEvent, isPrivate = true) => {
|
||||
e.stopPropagation()
|
||||
checkLogin(async () => {
|
||||
if (isMuted) return
|
||||
|
||||
setUpdating(true)
|
||||
try {
|
||||
await mutePubkey(pubkey)
|
||||
if (isPrivate) {
|
||||
await mutePubkeyPrivately(pubkey)
|
||||
} else {
|
||||
await mutePubkeyPublicly(pubkey)
|
||||
}
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: t('Mute failed'),
|
||||
|
|
@ -56,23 +70,76 @@ export default function MuteButton({ pubkey }: { pubkey: string }) {
|
|||
})
|
||||
}
|
||||
|
||||
return isMuted ? (
|
||||
<Button
|
||||
className="w-20 min-w-20 rounded-full"
|
||||
variant="secondary"
|
||||
onClick={handleUnmute}
|
||||
disabled={updating}
|
||||
>
|
||||
{updating ? <Loader className="animate-spin" /> : t('Unmute')}
|
||||
</Button>
|
||||
) : (
|
||||
if (isMuted) {
|
||||
return (
|
||||
<Button
|
||||
className="w-20 min-w-20 rounded-full"
|
||||
variant="secondary"
|
||||
onClick={handleUnmute}
|
||||
disabled={updating || changing}
|
||||
>
|
||||
{updating ? <Loader className="animate-spin" /> : t('Unmute')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
const trigger = (
|
||||
<Button
|
||||
variant="destructive"
|
||||
className="w-20 min-w-20 rounded-full"
|
||||
onClick={handleMute}
|
||||
disabled={updating}
|
||||
disabled={updating || changing}
|
||||
>
|
||||
{updating ? <Loader className="animate-spin" /> : t('Mute')}
|
||||
</Button>
|
||||
)
|
||||
|
||||
if (isSmallScreen) {
|
||||
return (
|
||||
<Drawer>
|
||||
<DrawerTrigger asChild>{trigger}</DrawerTrigger>
|
||||
<DrawerContent>
|
||||
<div className="py-2">
|
||||
<Button
|
||||
className="w-full p-6 justify-start text-destructive text-lg gap-4 [&_svg]:size-5 focus:text-destructive"
|
||||
variant="ghost"
|
||||
onClick={(e) => handleMute(e, true)}
|
||||
disabled={updating || changing}
|
||||
>
|
||||
{updating ? <Loader className="animate-spin" /> : t('Mute user privately')}
|
||||
</Button>
|
||||
<Button
|
||||
className="w-full p-6 justify-start text-destructive text-lg gap-4 [&_svg]:size-5 focus:text-destructive"
|
||||
variant="ghost"
|
||||
onClick={(e) => handleMute(e, false)}
|
||||
disabled={updating || changing}
|
||||
>
|
||||
{updating ? <Loader className="animate-spin" /> : t('Mute user publicly')}
|
||||
</Button>
|
||||
</div>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => handleMute(e, true)}
|
||||
className="text-destructive focus:text-destructive"
|
||||
>
|
||||
<BellOff />
|
||||
{t('Mute user privately')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => handleMute(e, false)}
|
||||
className="text-destructive focus:text-destructive"
|
||||
>
|
||||
<BellOff />
|
||||
{t('Mute user publicly')}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export default function NoteOptions({ event, className }: { event: Event; classN
|
|||
const { pubkey } = useNostr()
|
||||
const [isRawEventDialogOpen, setIsRawEventDialogOpen] = useState(false)
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false)
|
||||
const { mutePubkey, unmutePubkey, mutePubkeys } = useMuteList()
|
||||
const { mutePubkeyPublicly, mutePubkeyPrivately, unmutePubkey, mutePubkeys } = useMuteList()
|
||||
const isMuted = useMemo(() => mutePubkeys.includes(event.pubkey), [mutePubkeys, event])
|
||||
|
||||
const trigger = (
|
||||
|
|
@ -97,23 +97,45 @@ export default function NoteOptions({ event, className }: { event: Event; classN
|
|||
<Code />
|
||||
{t('View raw event')}
|
||||
</Button>
|
||||
{pubkey && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setIsDrawerOpen(false)
|
||||
if (isMuted) {
|
||||
{pubkey &&
|
||||
(isMuted ? (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setIsDrawerOpen(false)
|
||||
unmutePubkey(event.pubkey)
|
||||
} else {
|
||||
mutePubkey(event.pubkey)
|
||||
}
|
||||
}}
|
||||
className="w-full p-6 justify-start text-destructive text-lg gap-4 [&_svg]:size-5 focus:text-destructive"
|
||||
variant="ghost"
|
||||
>
|
||||
{isMuted ? <Bell /> : <BellOff />}
|
||||
{isMuted ? t('Unmute user') : t('Mute user')}
|
||||
</Button>
|
||||
)}
|
||||
}}
|
||||
className="w-full p-6 justify-start text-destructive text-lg gap-4 [&_svg]:size-5 focus:text-destructive"
|
||||
variant="ghost"
|
||||
>
|
||||
<Bell />
|
||||
{t('Unmute user')}
|
||||
</Button>
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setIsDrawerOpen(false)
|
||||
mutePubkeyPrivately(event.pubkey)
|
||||
}}
|
||||
className="w-full p-6 justify-start text-destructive text-lg gap-4 [&_svg]:size-5 focus:text-destructive"
|
||||
variant="ghost"
|
||||
>
|
||||
<BellOff />
|
||||
{t('Mute user privately')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setIsDrawerOpen(false)
|
||||
mutePubkeyPublicly(event.pubkey)
|
||||
}}
|
||||
className="w-full p-6 justify-start text-destructive text-lg gap-4 [&_svg]:size-5 focus:text-destructive"
|
||||
variant="ghost"
|
||||
>
|
||||
<BellOff />
|
||||
{t('Mute user publicly')}
|
||||
</Button>
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
|
@ -155,13 +177,32 @@ export default function NoteOptions({ event, className }: { event: Event; classN
|
|||
{pubkey && (
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={() => (isMuted ? unmutePubkey(event.pubkey) : mutePubkey(event.pubkey))}
|
||||
className="text-destructive focus:text-destructive"
|
||||
>
|
||||
{isMuted ? <Bell /> : <BellOff />}
|
||||
{isMuted ? t('Unmute user') : t('Mute user')}
|
||||
</DropdownMenuItem>
|
||||
{isMuted ? (
|
||||
<DropdownMenuItem
|
||||
onClick={() => unmutePubkey(event.pubkey)}
|
||||
className="text-destructive focus:text-destructive"
|
||||
>
|
||||
<Bell />
|
||||
{t('Unmute user')}
|
||||
</DropdownMenuItem>
|
||||
) : (
|
||||
<>
|
||||
<DropdownMenuItem
|
||||
onClick={() => mutePubkeyPrivately(event.pubkey)}
|
||||
className="text-destructive focus:text-destructive"
|
||||
>
|
||||
<BellOff />
|
||||
{t('Mute user privately')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => mutePubkeyPublicly(event.pubkey)}
|
||||
className="text-destructive focus:text-destructive"
|
||||
>
|
||||
<BellOff />
|
||||
{t('Mute user publicly')}
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
|
|
|
|||
|
|
@ -14,10 +14,12 @@ import { useTranslation } from 'react-i18next'
|
|||
export default function ProfileOptions({ pubkey }: { pubkey: string }) {
|
||||
const { t } = useTranslation()
|
||||
const { pubkey: accountPubkey } = useNostr()
|
||||
const { mutePubkeys, mutePubkey, unmutePubkey } = useMuteList()
|
||||
const { mutePubkeys, mutePubkeyPrivately, mutePubkeyPublicly, unmutePubkey } = useMuteList()
|
||||
|
||||
if (pubkey === accountPubkey) return null
|
||||
|
||||
const isMuted = mutePubkeys.includes(pubkey)
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
|
|
@ -32,7 +34,7 @@ export default function ProfileOptions({ pubkey }: { pubkey: string }) {
|
|||
<Copy />
|
||||
{t('Copy user ID')}
|
||||
</DropdownMenuItem>
|
||||
{mutePubkeys.includes(pubkey) ? (
|
||||
{isMuted ? (
|
||||
<DropdownMenuItem
|
||||
onClick={() => unmutePubkey(pubkey)}
|
||||
className="text-destructive focus:text-destructive"
|
||||
|
|
@ -41,13 +43,22 @@ export default function ProfileOptions({ pubkey }: { pubkey: string }) {
|
|||
{t('Unmute user')}
|
||||
</DropdownMenuItem>
|
||||
) : (
|
||||
<DropdownMenuItem
|
||||
onClick={() => mutePubkey(pubkey)}
|
||||
className="text-destructive focus:text-destructive"
|
||||
>
|
||||
<BellOff />
|
||||
{t('Mute user')}
|
||||
</DropdownMenuItem>
|
||||
<>
|
||||
<DropdownMenuItem
|
||||
onClick={() => mutePubkeyPrivately(pubkey)}
|
||||
className="text-destructive focus:text-destructive"
|
||||
>
|
||||
<BellOff />
|
||||
{t('Mute user privately')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => mutePubkeyPublicly(pubkey)}
|
||||
className="text-destructive focus:text-destructive"
|
||||
>
|
||||
<BellOff />
|
||||
{t('Mute user publicly')}
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue