T.ME/BIBIL_0DAY
CasperSecurity


Server : Apache/2
System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64
User : gositeme ( 1004)
PHP Version : 8.2.29
Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Directory :  /home/gositeme/domains/lavocat.ca/public_html/src/hooks/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/lavocat.ca/public_html/src/hooks/useComments.ts
import { useState, useEffect, useCallback } from 'react';
import { toast } from 'react-hot-toast';

interface CommentUser {
  id: string;
  name: string;
  profilePicture?: string;
  role: string;
  isVerified?: boolean;
}

interface CommentAttachment {
  id: string;
  name: string;
  url: string;
  type: string;
  size: number;
}

interface CommentReaction {
  id: string;
  reactionType: string;
  user: {
    id: string;
    name: string;
  };
}

interface Comment {
  id: string;
  content: string;
  createdAt: string;
  updatedAt: string;
  isEdited: boolean;
  isDeleted: boolean;
  user: CommentUser;
  attachments: CommentAttachment[];
  reactions: CommentReaction[];
  _count: {
    replies: number;
    reactions: number;
  };
}

interface PaginationInfo {
  page: number;
  limit: number;
  total: number;
  pages: number;
}

interface UseCommentsReturn {
  comments: Comment[];
  loading: boolean;
  posting: boolean;
  hasMore: boolean;
  loadingMore: boolean;
  pagination: PaginationInfo | null;
  fetchComments: (reset?: boolean) => Promise<void>;
  postComment: (content: string, caseId: string, parentId?: string) => Promise<Comment | null>;
  editComment: (commentId: string, content: string) => Promise<boolean>;
  deleteComment: (commentId: string) => Promise<boolean>;
  loadMore: () => Promise<void>;
}

export const useComments = (caseId: string): UseCommentsReturn => {
  const [comments, setComments] = useState<Comment[]>([]);
  const [loading, setLoading] = useState(true);
  const [posting, setPosting] = useState(false);
  const [page, setPage] = useState(1);
  const [hasMore, setHasMore] = useState(true);
  const [loadingMore, setLoadingMore] = useState(false);
  const [pagination, setPagination] = useState<PaginationInfo | null>(null);

  const fetchComments = useCallback(async (reset = false) => {
    try {
      if (reset) {
        setPage(1);
        setComments([]);
        setHasMore(true);
      }
      
      setLoading(true);
      const params = new URLSearchParams({
        caseId,
        page: reset ? '1' : String(page),
        limit: '20'
      });
      
      const response = await fetch(`/api/comments?${params}`);
      if (!response.ok) throw new Error('Failed to fetch comments');
      
      const data = await response.json();
      const newComments = data.comments;
      
      if (reset) {
        setComments(newComments);
      } else {
        setComments(prev => {
          const existingIds = new Set(prev.map(c => c.id));
          const uniqueNewComments = newComments.filter((c: Comment) => !existingIds.has(c.id));
          return [...prev, ...uniqueNewComments];
        });
      }
      
      setPagination(data.pagination);
      setHasMore(data.pagination.page < data.pagination.pages);
    } catch (error) {
      console.error('Error fetching comments:', error);
      toast.error('Failed to load comments');
    } finally {
      setLoading(false);
      setLoadingMore(false);
    }
  }, [caseId, page]);

  const postComment = useCallback(async (content: string, caseId: string, parentId?: string): Promise<Comment | null> => {
    setPosting(true);
    try {
      const response = await fetch('/api/comments', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          content: content.trim(),
          caseId,
          parentId
        })
      });

      if (!response.ok) throw new Error('Failed to post comment');
      
      const data = await response.json();
      const newComment = data.comment;
      
      // Add to comments optimistically
      setComments(prev => [newComment, ...prev]);
      
      // Update reply count if this is a reply
      if (parentId) {
        setComments(prev => prev.map(comment => {
          if (comment.id === parentId) {
            return {
              ...comment,
              _count: {
                ...comment._count,
                replies: comment._count.replies + 1
              }
            };
          }
          return comment;
        }));
      }
      
      toast.success(parentId ? 'Reply posted successfully' : 'Comment posted successfully');
      return newComment;
    } catch (error) {
      console.error('Error posting comment:', error);
      toast.error('Failed to post comment');
      return null;
    } finally {
      setPosting(false);
    }
  }, []);

  const editComment = useCallback(async (commentId: string, content: string): Promise<boolean> => {
    try {
      const response = await fetch(`/api/comments?commentId=${commentId}`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          content: content.trim()
        })
      });

      if (!response.ok) throw new Error('Failed to update comment');
      
      const data = await response.json();
      
      // Update comment in state
      setComments(prev => prev.map(comment => 
        comment.id === commentId ? data.comment : comment
      ));
      
      toast.success('Comment updated successfully');
      return true;
    } catch (error) {
      console.error('Error updating comment:', error);
      toast.error('Failed to update comment');
      return false;
    }
  }, []);

  const deleteComment = useCallback(async (commentId: string): Promise<boolean> => {
    try {
      const response = await fetch(`/api/comments?commentId=${commentId}`, {
        method: 'DELETE'
      });

      if (!response.ok) throw new Error('Failed to delete comment');
      
      // Remove from state
      setComments(prev => prev.filter(comment => comment.id !== commentId));
      
      toast.success('Comment deleted successfully');
      return true;
    } catch (error) {
      console.error('Error deleting comment:', error);
      toast.error('Failed to delete comment');
      return false;
    }
  }, []);

  const loadMore = useCallback(async () => {
    if (loadingMore || !hasMore) return;
    
    setLoadingMore(true);
    setPage(prev => prev + 1);
  }, [loadingMore, hasMore]);

  // Initial load
  useEffect(() => {
    fetchComments(true);
  }, [caseId]);

  // Load more when page changes
  useEffect(() => {
    if (page > 1) {
      fetchComments(false);
    }
  }, [page]);

  return {
    comments,
    loading,
    posting,
    hasMore,
    loadingMore,
    pagination,
    fetchComments,
    postComment,
    editComment,
    deleteComment,
    loadMore
  };
}; 

CasperSecurity Mini