@@ -6,13 +6,14 @@ import { useEffect, useState, useMemo, useCallback, memo } from "react"
66import type { NewsItem , Comment } from "@/lib/data"
77import { type VoteState } from "@/lib/types"
88import { handleVote , fetchUserVotesForResources } from "@/lib/voteHandler"
9+ import { deleteComment } from "@/lib/commentHandler"
910import { useAuth } from "@/contexts/auth-context"
1011import { getCachedJWT } from "@/lib/jwtCache"
1112import { PostCard } from "@/components/post-card"
1213import { avatars } from "@/lib/appwrite"
1314import { Avatar , AvatarFallback , AvatarImage } from "@/components/ui/avatar"
1415import { Button } from "@/components/ui/button"
15- import { Clock , TrendingUp , Reply , ChevronDown , ChevronUp } from "lucide-react"
16+ import { Clock , TrendingUp , Reply , ChevronDown , ChevronUp , Trash2 } from "lucide-react"
1617
1718interface ThreadClientPageProps {
1819 article : NewsItem
@@ -31,6 +32,8 @@ interface CommentItemProps {
3132 isOriginalPoster : ( commentUserId : string ) => boolean
3233 getCommentVoteState : ( commentId : string ) => VoteState
3334 getCommentChildren : ( commentId : string ) => Comment [ ]
35+ onDeleteComment ?: ( commentId : string ) => void // Add callback for deleting comment
36+ currentUserId ?: string // Add current user ID for ownership check
3437}
3538
3639const CommentItem = memo < CommentItemProps > ( ( {
@@ -42,7 +45,9 @@ const CommentItem = memo<CommentItemProps>(({
4245 isAuthenticated,
4346 isOriginalPoster,
4447 getCommentVoteState,
45- getCommentChildren
48+ getCommentChildren,
49+ onDeleteComment,
50+ currentUserId
4651} ) => {
4752 const canReply = depth < 2 // Allow replies up to depth 2 (3 levels total)
4853
@@ -86,6 +91,7 @@ const CommentItem = memo<CommentItemProps>(({
8691 < span className = "text-gray-500 text-xs" > original poster</ span >
8792 </ >
8893 ) }
94+ < span className = "text-gray-500" > • { comment . timeAgo } </ span >
8995 </ div >
9096 < div className = "text-gray-700 text-sm mb-3 whitespace-pre-wrap" > { comment . text } </ div >
9197
@@ -100,6 +106,18 @@ const CommentItem = memo<CommentItemProps>(({
100106 size = "sm"
101107 />
102108 < div className = "text-xs text-gray-500" > { comment . timeAgo } </ div >
109+ { /* Delete button - only show for comment owners, positioned before reply */ }
110+ { onDeleteComment && comment . userId && currentUserId && comment . userId === currentUserId && (
111+ < Button
112+ variant = "ghost"
113+ size = "sm"
114+ className = "h-auto p-1 text-xs text-gray-500 hover:text-red-600 flex items-center gap-1"
115+ onClick = { ( ) => onDeleteComment ( comment . id ) }
116+ >
117+ < Trash2 className = "w-3 h-3" />
118+ Delete
119+ </ Button >
120+ ) }
103121 { canReply && (
104122 < Button
105123 variant = "ghost"
@@ -128,6 +146,8 @@ const CommentItem = memo<CommentItemProps>(({
128146 isOriginalPoster = { isOriginalPoster }
129147 getCommentVoteState = { getCommentVoteState }
130148 getCommentChildren = { getCommentChildren }
149+ onDeleteComment = { onDeleteComment }
150+ currentUserId = { currentUserId }
131151 />
132152 ) ) }
133153 </ div >
@@ -430,6 +450,55 @@ export function ThreadClientPage({ article }: ThreadClientPageProps) {
430450 }
431451 } , [ isAuthenticated , isCommentVoting , getCommentVoteState , updateCommentVoteState ] )
432452
453+ const handleDeleteComment = useCallback ( async ( commentId : string ) => {
454+ if ( ! isAuthenticated || ! user ) {
455+ return
456+ }
457+
458+ if ( confirm ( 'Are you sure you want to delete this comment? This action cannot be undone.' ) ) {
459+ try {
460+ const result = await deleteComment ( commentId )
461+ if ( result . success ) {
462+ // Remove the comment from the comment map
463+ setCommentMap ( prev => {
464+ const newMap = new Map ( prev )
465+ newMap . delete ( commentId )
466+ return newMap
467+ } )
468+
469+ // Also remove any replies to this comment
470+ setCommentMap ( prev => {
471+ const newMap = new Map ( prev )
472+ const commentsToRemove : string [ ] = [ ]
473+
474+ // Find all replies to this comment
475+ newMap . forEach ( ( comment , id ) => {
476+ if ( comment . parentId === commentId ) {
477+ commentsToRemove . push ( id )
478+ }
479+ } )
480+
481+ // Remove all replies
482+ commentsToRemove . forEach ( id => newMap . delete ( id ) )
483+ return newMap
484+ } )
485+
486+ // Remove vote states for deleted comments
487+ setCommentVoteStates ( prev => {
488+ const newStates = { ...prev }
489+ delete newStates [ commentId ]
490+ return newStates
491+ } )
492+ } else {
493+ alert ( `Failed to delete comment: ${ result . error } ` )
494+ }
495+ } catch ( error ) {
496+ console . error ( 'Error deleting comment:' , error )
497+ alert ( 'Failed to delete comment' )
498+ }
499+ }
500+ } , [ isAuthenticated , user ] )
501+
433502 const handleSortChange = ( newSortType : SortType ) => {
434503 if ( sortType === newSortType ) {
435504 // Toggle direction if clicking the same sort type
@@ -547,6 +616,8 @@ export function ThreadClientPage({ article }: ThreadClientPageProps) {
547616 isOriginalPoster = { isOriginalPoster }
548617 getCommentVoteState = { getCommentVoteState }
549618 getCommentChildren = { getCommentChildren }
619+ onDeleteComment = { handleDeleteComment }
620+ currentUserId = { user ?. $id }
550621 />
551622 ) ) }
552623 </ div >
0 commit comments