ReportDialog.tsx raw

   1  import { Button } from '@/components/ui/button'
   2  import {
   3    Dialog,
   4    DialogContent,
   5    DialogDescription,
   6    DialogHeader,
   7    DialogTitle
   8  } from '@/components/ui/dialog'
   9  import {
  10    Drawer,
  11    DrawerContent,
  12    DrawerDescription,
  13    DrawerHeader,
  14    DrawerTitle
  15  } from '@/components/ui/drawer'
  16  import { Label } from '@/components/ui/label'
  17  import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
  18  import { createReportDraftEvent } from '@/lib/draft-event'
  19  import { useNostr } from '@/providers/NostrProvider'
  20  import { useScreenSize } from '@/providers/ScreenSizeProvider'
  21  import { Loader } from 'lucide-react'
  22  import { NostrEvent } from 'nostr-tools'
  23  import { useState } from 'react'
  24  import { useTranslation } from 'react-i18next'
  25  import { toast } from 'sonner'
  26  
  27  export default function ReportDialog({
  28    event,
  29    isOpen,
  30    closeDialog
  31  }: {
  32    event: NostrEvent
  33    isOpen: boolean
  34    closeDialog: () => void
  35  }) {
  36    const { isSmallScreen } = useScreenSize()
  37  
  38    if (isSmallScreen) {
  39      return (
  40        <Drawer
  41          open={isOpen}
  42          onOpenChange={(open) => {
  43            if (!open) {
  44              closeDialog()
  45            }
  46          }}
  47        >
  48          <DrawerContent>
  49            <DrawerHeader>
  50              <DrawerTitle className="hidden" />
  51              <DrawerDescription className="hidden" />
  52            </DrawerHeader>
  53            <div className="p-4">
  54              <ReportContent event={event} closeDialog={closeDialog} />
  55            </div>
  56          </DrawerContent>
  57        </Drawer>
  58      )
  59    }
  60  
  61    return (
  62      <Dialog
  63        open={isOpen}
  64        onOpenChange={(open) => {
  65          if (!open) {
  66            closeDialog()
  67          }
  68        }}
  69      >
  70        <DialogContent>
  71          <DialogHeader>
  72            <DialogTitle className="hidden" />
  73            <DialogDescription className="hidden" />
  74          </DialogHeader>
  75          <ReportContent event={event} closeDialog={closeDialog} />
  76        </DialogContent>
  77      </Dialog>
  78    )
  79  }
  80  
  81  function ReportContent({ event, closeDialog }: { event: NostrEvent; closeDialog: () => void }) {
  82    const { t } = useTranslation()
  83    const { pubkey, publish } = useNostr()
  84    const [reason, setReason] = useState<string | null>(null)
  85    const [reporting, setReporting] = useState(false)
  86  
  87    const handleReport = async () => {
  88      if (!reason || !pubkey) return
  89  
  90      try {
  91        setReporting(true)
  92        const draftEvent = createReportDraftEvent(event, reason)
  93        await publish(draftEvent)
  94        toast.success(t('Successfully report'))
  95        closeDialog()
  96      } catch (error) {
  97        const errors = error instanceof AggregateError ? error.errors : [error]
  98        errors.forEach((err) => {
  99          toast.error(
 100            `${t('Failed to report')}: ${err instanceof Error ? err.message : String(err)}`,
 101            { duration: 10_000 }
 102          )
 103          console.error(err)
 104        })
 105        return
 106      } finally {
 107        setReporting(false)
 108      }
 109    }
 110  
 111    return (
 112      <div className="w-full space-y-4">
 113        <RadioGroup value={reason} onValueChange={setReason} className="space-y-2">
 114          {['nudity', 'malware', 'profanity', 'illegal', 'spam', 'other'].map((item) => (
 115            <div key={item} className="flex items-center space-x-2">
 116              <RadioGroupItem value={item} id={item} />
 117              <Label htmlFor={item} className="text-base">
 118                {t(item)}
 119              </Label>
 120            </div>
 121          ))}
 122        </RadioGroup>
 123        <Button
 124          variant="destructive"
 125          className="w-full"
 126          disabled={!reason || reporting}
 127          onClick={(e) => {
 128            e.stopPropagation()
 129            handleReport()
 130          }}
 131        >
 132          {reporting && <Loader className="animate-spin" />}
 133          {t('Report')}
 134        </Button>
 135      </div>
 136    )
 137  }
 138