import { createSignal, For, Show, onMount } from "solid-js"; import CommentLock from "./CommentLock"; import CommentForm from "./CommentForm"; import type { CommentWithReplies } from "../../types/comments"; interface CommentsProps { postSlug: string; } interface ApiComment { id: string; post_slug: string; user: string; content: string; parent?: string | null; is_verified: boolean; status: "pending" | "published" | "spam"; created: string; updated: string; expand?: { user?: { id: string; firstName: string; email: string; avatar?: string; }; }; } interface ToastMessage { type: "success" | "error"; message: string; } export default function Comments(props: CommentsProps) { const [isAuthenticated, setIsAuthenticated] = createSignal(false); const [currentUser, setCurrentUser] = createSignal<{ name: string; email: string; avatar?: string; } | undefined>(undefined); const [isLoading, setIsLoading] = createSignal(true); const [comments, setComments] = createSignal([]); const [replyTo, setReplyTo] = createSignal(null); const [commentAuthors, setCommentAuthors] = createSignal>({}); const [toastVisible, setToastVisible] = createSignal(null); const [toast, setToast] = createSignal(null); const showToast = (message: ToastMessage): void => { setToast(message); setTimeout(() => setToast(null), 3000); }; onMount(async () => { try { const response = await fetch("/api/auth/me", { method: "GET", credentials: "include", }); const data = await response.json(); if (data.authenticated && data.user) { setIsAuthenticated(true); setCurrentUser({ name: data.user.name || "Пользователь", email: data.user.email, avatar: data.user.avatar, }); } } catch (error) { console.error("[Comments] Ошибка проверки авторизации:", error); } finally { setIsLoading(false); } }); const loadComments = async () => { try { const response = await fetch( `/api/comments?post_slug=${encodeURIComponent(props.postSlug)}&parent=null`, { method: "GET", credentials: "include", } ); if (!response.ok) { throw new Error("Failed to fetch comments"); } const data = await response.json(); const authors: Record = {}; data.items.forEach((comment: ApiComment) => { if (comment.user) { authors[comment.id] = comment.user; } }); setCommentAuthors(authors); const commentsWithReplies: CommentWithReplies[] = await Promise.all( data.items.map(async (comment: ApiComment) => { const repliesResponse = await fetch( `/api/comments?post_slug=${encodeURIComponent(props.postSlug)}&parent=${comment.id}`, { method: "GET", credentials: "include", } ); const repliesData = await repliesResponse.json(); return { ...comment, replies: repliesData.items || [], }; }) ); setComments(commentsWithReplies); } catch (error) { console.error("[Comments] Ошибка загрузки комментариев:", error); } }; onMount(() => { if (props.postSlug) { loadComments(); } }); const handleNewComment = async (data: { content: string }) => { try { const response = await fetch("/api/comments", { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ content: data.content, post_slug: props.postSlug, parent: null, }), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || "Failed to create comment"); } await loadComments(); } catch (error) { console.error("[Comments] Ошибка создания комментария:", error); } }; const handleReply = async (commentId: string, data: { content: string }) => { try { const response = await fetch("/api/comments", { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ content: data.content, post_slug: props.postSlug, parent: commentId, }), }); if (!response.ok) { const errorData = await response.json(); if (errorData.error) { showToast({ type: "error", message: errorData.error }); return; } throw new Error(errorData.error || "Failed to create reply"); } showToast({ type: "success", message: "Ответ успешно отправлен!" }); await loadComments(); setReplyTo(null); } catch (error) { console.error("[Comments] Ошибка создания ответа:", error); showToast({ type: "error", message: "Не удалось создать ответ. Попробуйте позже." }); } }; const handleReplyClick = (commentId: string) => { const checkAuthAndReply = async () => { try { const response = await fetch("/api/auth/me", { credentials: "include", }); const data = await response.json(); const currentUserId = data.user?.id; const commentAuthorId = commentAuthors()[commentId]; if (commentAuthorId && currentUserId === commentAuthorId) { setToastVisible(commentId); setTimeout(() => setToastVisible(null), 3000); return; } setReplyTo(replyTo() === commentId ? null : commentId); } catch (error) { console.error("[handleReplyClick] Ошибка проверки автора:", error); setReplyTo(replyTo() === commentId ? null : commentId); } }; checkAuthAndReply(); }; const Avatar = (props: { author: string; avatar?: string; size?: "sm" | "md" }) => { const size = props.size || "md"; const sizeClasses = { sm: "w-8 h-8 text-sm", md: "w-12 h-12 text-lg" }; return ( {props.author.charAt(0)} } > {props.author} ); }; const formatDate = (dateString: string) => { const date = new Date(dateString); return date.toLocaleDateString("ru-RU", { day: "numeric", month: "long", year: "numeric", hour: "2-digit", minute: "2-digit", }); }; return ( <> {(t) => (
{t().message}
)}
{isLoading() ? (

Комментарии

0

Загрузка...

) : ( }>

Комментарии

{comments().length}
0} fallback={

Пока нет комментариев. Будьте первым!

}>
{(comment) => (
{comment.expand?.user?.firstName || "Аноним"} {formatDate(comment.created)}

{comment.content}

Нельзя ответить на свой комментарий
handleReply(comment.id, data)} onCancel={() => setReplyTo(null)} user={currentUser()} />
0}>
{(reply) => (
{reply.expand?.user?.firstName || "Аноним"} {formatDate(reply.created)}

{reply.content}

)}
)}
)} ); }