23 changed files with 685 additions and 17 deletions
@ -0,0 +1,129 @@
@@ -0,0 +1,129 @@
|
||||
import { Button } from '@/components/ui/button' |
||||
import { |
||||
Dialog, |
||||
DialogContent, |
||||
DialogDescription, |
||||
DialogHeader, |
||||
DialogTitle |
||||
} from '@/components/ui/dialog' |
||||
import { |
||||
Drawer, |
||||
DrawerContent, |
||||
DrawerDescription, |
||||
DrawerHeader, |
||||
DrawerTitle |
||||
} from '@/components/ui/drawer' |
||||
import { Label } from '@/components/ui/label' |
||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group' |
||||
import { createReportDraftEvent } from '@/lib/draft-event' |
||||
import { useNostr } from '@/providers/NostrProvider' |
||||
import { useScreenSize } from '@/providers/ScreenSizeProvider' |
||||
import { Loader } from 'lucide-react' |
||||
import { NostrEvent } from 'nostr-tools' |
||||
import { useState } from 'react' |
||||
import { useTranslation } from 'react-i18next' |
||||
import { toast } from 'sonner' |
||||
|
||||
export default function ReportDialog({ |
||||
event, |
||||
isOpen, |
||||
closeDialog |
||||
}: { |
||||
event: NostrEvent |
||||
isOpen: boolean |
||||
closeDialog: () => void |
||||
}) { |
||||
const { isSmallScreen } = useScreenSize() |
||||
|
||||
if (isSmallScreen) { |
||||
return ( |
||||
<Drawer |
||||
open={isOpen} |
||||
onOpenChange={(open) => { |
||||
if (!open) { |
||||
closeDialog() |
||||
} |
||||
}} |
||||
> |
||||
<DrawerContent> |
||||
<DrawerHeader> |
||||
<DrawerTitle className="hidden" /> |
||||
<DrawerDescription className="hidden" /> |
||||
</DrawerHeader> |
||||
<div className="p-4"> |
||||
<ReportContent event={event} closeDialog={closeDialog} /> |
||||
</div> |
||||
</DrawerContent> |
||||
</Drawer> |
||||
) |
||||
} |
||||
|
||||
return ( |
||||
<Dialog |
||||
open={isOpen} |
||||
onOpenChange={(open) => { |
||||
if (!open) { |
||||
closeDialog() |
||||
} |
||||
}} |
||||
> |
||||
<DialogContent> |
||||
<DialogHeader> |
||||
<DialogTitle className="hidden" /> |
||||
<DialogDescription className="hidden" /> |
||||
</DialogHeader> |
||||
<ReportContent event={event} closeDialog={closeDialog} /> |
||||
</DialogContent> |
||||
</Dialog> |
||||
) |
||||
} |
||||
|
||||
function ReportContent({ event, closeDialog }: { event: NostrEvent; closeDialog: () => void }) { |
||||
const { t } = useTranslation() |
||||
const { pubkey, publish } = useNostr() |
||||
const [reason, setReason] = useState<string | null>(null) |
||||
const [reporting, setReporting] = useState(false) |
||||
|
||||
const handleReport = async () => { |
||||
if (!reason || !pubkey) return |
||||
|
||||
try { |
||||
setReporting(true) |
||||
const draftEvent = createReportDraftEvent(event, reason) |
||||
await publish(draftEvent) |
||||
toast.success(t('Successfully report')) |
||||
closeDialog() |
||||
} catch (error) { |
||||
toast.error(t('Failed to report') + ': ' + (error as Error).message) |
||||
} finally { |
||||
setReporting(false) |
||||
} |
||||
} |
||||
|
||||
return ( |
||||
<div className="w-full space-y-4"> |
||||
<RadioGroup value={reason} onValueChange={setReason} className="space-y-2"> |
||||
{['nudity', 'malware', 'profanity', 'illegal', 'spam', 'other'].map((item) => ( |
||||
<div key={item} className="flex items-center space-x-2"> |
||||
<RadioGroupItem value={item} id={item} /> |
||||
<Label htmlFor={item} className="text-base"> |
||||
{t(item)} |
||||
</Label> |
||||
</div> |
||||
))} |
||||
</RadioGroup> |
||||
<Button |
||||
variant="destructive" |
||||
className="w-full" |
||||
disabled={!reason || reporting} |
||||
onClick={(e) => { |
||||
e.stopPropagation() |
||||
handleReport() |
||||
}} |
||||
> |
||||
{reporting && <Loader className="animate-spin" />} |
||||
{t('Report')} |
||||
</Button> |
||||
</div> |
||||
) |
||||
} |
||||
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
import * as React from 'react' |
||||
import * as RadioGroupPrimitive from '@radix-ui/react-radio-group' |
||||
import { Circle } from 'lucide-react' |
||||
|
||||
import { cn } from '@/lib/utils' |
||||
|
||||
const RadioGroup = React.forwardRef< |
||||
React.ElementRef<typeof RadioGroupPrimitive.Root>, |
||||
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root> |
||||
>(({ className, ...props }, ref) => { |
||||
return <RadioGroupPrimitive.Root className={cn('grid gap-2', className)} {...props} ref={ref} /> |
||||
}) |
||||
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName |
||||
|
||||
const RadioGroupItem = React.forwardRef< |
||||
React.ElementRef<typeof RadioGroupPrimitive.Item>, |
||||
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item> |
||||
>(({ className, ...props }, ref) => { |
||||
return ( |
||||
<RadioGroupPrimitive.Item |
||||
ref={ref} |
||||
className={cn( |
||||
'aspect-square h-4 w-4 rounded-full border focus:outline-none focus-visible:ring-1 focus-visible:ring-foreground disabled:cursor-not-allowed disabled:opacity-50', |
||||
className |
||||
)} |
||||
{...props} |
||||
> |
||||
<RadioGroupPrimitive.Indicator className="flex items-center justify-center"> |
||||
<Circle className="h-2.5 w-2.5 fill-foreground" /> |
||||
</RadioGroupPrimitive.Indicator> |
||||
</RadioGroupPrimitive.Item> |
||||
) |
||||
}) |
||||
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName |
||||
|
||||
export { RadioGroup, RadioGroupItem } |
||||
Loading…
Reference in new issue