pummmp.fun/components/comments.tsx
2026-07-04 12:49:09 -07:00

152 lines
5.7 KiB
TypeScript

'use client'
import { useState } from 'react'
import useSWR, { mutate } from 'swr'
import Link from 'next/link'
import { useSession } from 'next-auth/react'
import { Button } from '@/components/ui/button'
import { Textarea } from '@/components/ui/textarea'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { formatDistanceToNow } from 'date-fns'
import { UserBadges } from './ui/user-badges'
import { toast } from 'sonner'
import { Trash2 } from 'lucide-react'
interface Comment {
_id: string
userId: string
userName: string
userImage: string
userVerified?: boolean
userIsAdmin?: boolean
userIsBetaTester?: boolean
userIsBugHunter?: boolean
text: string
isHolder: boolean
createdAt: string
}
const fetcher = (url: string) => fetch(url).then((res) => res.json())
export function Comments({ coinId, creatorId }: { coinId: string, creatorId?: string }) {
const { data: session } = useSession()
const { data: comments } = useSWR<Comment[]>(`/api/comments?coinId=${coinId}`, fetcher)
const [text, setText] = useState('')
const [loading, setLoading] = useState(false)
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (!text.trim() || !session) return
setLoading(true)
try {
await fetch('/api/comments', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ coinId, text }),
})
setText('')
mutate(`/api/comments?coinId=${coinId}`)
} catch (error) {
console.error('Failed to post comment', error)
toast.error('Failed to post comment')
} finally {
setLoading(false)
}
}
const handleDelete = async (commentId: string) => {
try {
const res = await fetch(`/api/comments?id=${commentId}`, { method: 'DELETE' });
if (res.ok) {
mutate(`/api/comments?coinId=${coinId}`)
toast.success('Comment deleted')
} else {
const data = await res.json()
toast.error(data.error || 'Failed to delete')
}
} catch (e) {
toast.error('Failed to delete comment')
}
}
return (
<div className="flex flex-col gap-6">
<div className="rounded-xl border border-border bg-card/30 p-4">
<h3 className="mb-4 font-semibold">Discussion</h3>
{session ? (
<form onSubmit={handleSubmit} className="mb-6 space-y-3">
<Textarea
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="What do you think about this coin?"
className="min-h-[80px] bg-muted/30"
/>
<div className="flex justify-end">
<Button type="submit" size="sm" disabled={loading || !text.trim()}>
{loading ? 'Posting...' : 'Post Comment'}
</Button>
</div>
</form>
) : (
<div className="mb-6 rounded-lg bg-muted/20 p-4 text-center text-sm text-muted-foreground">
Please sign in to join the discussion
</div>
)}
<div className="space-y-4">
{comments?.map((comment, i) => (
<div key={`${comment._id}-${i}`} className="flex gap-3 group">
<Avatar className="h-8 w-8">
<AvatarImage src={comment.userImage} />
<AvatarFallback>{comment.userName?.substring(0, 2)}</AvatarFallback>
</Avatar>
<div className="flex-1 space-y-1">
<div className="flex items-center gap-2">
<Link href={`/u/${comment.userName}`} className="text-sm font-semibold hover:underline">
{comment.userName}
</Link>
<UserBadges
isAdmin={comment.userIsAdmin}
isVerified={comment.userVerified}
isBugHunter={comment.userIsBugHunter}
isBetaTester={comment.userIsBetaTester}
onlyShowHighest
/>
{creatorId === comment.userId && (
<span className="rounded bg-blue-500/20 px-1.5 py-0.5 text-[10px] font-bold text-blue-500 border border-blue-500/30">
CREATOR
</span>
)}
{comment.isHolder && creatorId !== comment.userId && (
<span className="rounded bg-primary/20 px-1.5 py-0.5 text-[10px] font-medium text-primary">
HOLDER
</span>
)}
<span className="text-xs text-muted-foreground">
{formatDistanceToNow(new Date(comment.createdAt), { addSuffix: true })}
</span>
{session && (session.user.id === comment.userId || session.user.id === creatorId || (session.user as any).isAdmin) && (
<button
onClick={() => handleDelete(comment._id)}
className="opacity-0 group-hover:opacity-100 transition-opacity ml-auto text-muted-foreground hover:text-destructive p-1"
title="Delete comment"
>
<Trash2 className="w-3 h-3" />
</button>
)}
</div>
<p className="text-sm text-foreground/90 whitespace-pre-wrap">{comment.text}</p>
</div>
</div>
))}
{comments?.length === 0 && (
<p className="text-center text-sm text-muted-foreground">No comments yet. Be the first!</p>
)}
</div>
</div>
</div>
)
}