style: adjust the style of NoteStats (#222)

This commit is contained in:
Cody Tseng
2025-03-07 23:39:46 +08:00
committed by GitHub
parent 71895e3a0f
commit accf3319e7
16 changed files with 417 additions and 186 deletions

View File

@@ -0,0 +1,34 @@
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle
} from '@/components/ui/dialog'
import { ScrollArea, ScrollBar } from '@/components/ui/scroll-area'
import { Event } from 'nostr-tools'
export default function RawEventDialog({
event,
isOpen,
onClose
}: {
event: Event
isOpen: boolean
onClose: () => void
}) {
return (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="h-[60vh]">
<DialogHeader>
<DialogTitle>Raw Event</DialogTitle>
<DialogDescription className="hidden" />
</DialogHeader>
<ScrollArea className="h-full">
<pre className="text-sm text-muted-foreground">{JSON.stringify(event, null, 2)}</pre>
<ScrollBar orientation="horizontal" />
</ScrollArea>
</DialogContent>
</Dialog>
)
}

View File

@@ -0,0 +1,152 @@
import { Button } from '@/components/ui/button'
import { Drawer, DrawerContent, DrawerOverlay } from '@/components/ui/drawer'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger
} from '@/components/ui/dropdown-menu'
import { getSharableEventId } from '@/lib/event'
import { pubkeyToNpub } from '@/lib/pubkey'
import { useMuteList } from '@/providers/MuteListProvider'
import { useNostr } from '@/providers/NostrProvider'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { Bell, BellOff, Code, Copy, Ellipsis } from 'lucide-react'
import { Event } from 'nostr-tools'
import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import RawEventDialog from './RawEventDialog'
export default function NoteOptions({ event, className }: { event: Event; className?: string }) {
const { t } = useTranslation()
const { isSmallScreen } = useScreenSize()
const { pubkey } = useNostr()
const [isRawEventDialogOpen, setIsRawEventDialogOpen] = useState(false)
const [isDrawerOpen, setIsDrawerOpen] = useState(false)
const { mutePubkey, unmutePubkey, mutePubkeys } = useMuteList()
const isMuted = useMemo(() => mutePubkeys.includes(event.pubkey), [mutePubkeys, event])
const trigger = (
<button
className="flex items-center text-muted-foreground hover:text-foreground pl-3 h-full"
onClick={() => setIsDrawerOpen(true)}
>
<Ellipsis />
</button>
)
const rawEventDialog = (
<RawEventDialog
event={event}
isOpen={isRawEventDialogOpen}
onClose={() => setIsRawEventDialogOpen(false)}
/>
)
if (isSmallScreen) {
return (
<div className={className} onClick={(e) => e.stopPropagation()}>
{trigger}
<Drawer open={isDrawerOpen} onOpenChange={setIsDrawerOpen}>
<DrawerOverlay onClick={() => setIsDrawerOpen(false)} />
<DrawerContent hideOverlay>
<div className="py-2">
<Button
onClick={() => {
setIsDrawerOpen(false)
navigator.clipboard.writeText(getSharableEventId(event))
}}
className="w-full p-6 justify-start text-lg gap-4 [&_svg]:size-5"
variant="ghost"
>
<Copy />
{t('Copy event ID')}
</Button>
<Button
onClick={() => {
navigator.clipboard.writeText(pubkeyToNpub(event.pubkey) ?? '')
setIsDrawerOpen(false)
}}
className="w-full p-6 justify-start text-lg gap-4 [&_svg]:size-5"
variant="ghost"
>
<Copy />
{t('Copy user ID')}
</Button>
<Button
onClick={() => {
setIsDrawerOpen(false)
setIsRawEventDialogOpen(true)
}}
className="w-full p-6 justify-start text-lg gap-4 [&_svg]:size-5"
variant="ghost"
>
<Code />
{t('View raw event')}
</Button>
{pubkey && (
<Button
onClick={() => {
setIsDrawerOpen(false)
if (isMuted) {
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>
)}
</div>
</DrawerContent>
</Drawer>
{rawEventDialog}
</div>
)
}
return (
<div className={className} onClick={(e) => e.stopPropagation()}>
<DropdownMenu>
<DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
<DropdownMenuContent collisionPadding={8} className="min-w-52">
<DropdownMenuItem
onClick={() => navigator.clipboard.writeText(getSharableEventId(event))}
>
<Copy />
{t('Copy event ID')}
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => navigator.clipboard.writeText(pubkeyToNpub(event.pubkey) ?? '')}
>
<Copy />
{t('Copy user ID')}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => setIsRawEventDialogOpen(true)}>
<Code />
{t('View raw event')}
</DropdownMenuItem>
{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>
</>
)}
</DropdownMenuContent>
</DropdownMenu>
{rawEventDialog}
</div>
)
}