![]() 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/ |
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
};
};