import { useState, useEffect, useCallback } from 'react' import { createPortal } from 'react-dom' import { X, Send } from 'lucide-react' import { commentPost, getPosts } from '../utils/api' import { hapticFeedback } from '../utils/telegram' import { decodeHtmlEntities } from '../utils/htmlEntities' import './CommentsModal.css' export default function CommentsModal({ post, onClose, onUpdate }) { const [comment, setComment] = useState('') const [loading, setLoading] = useState(false) const [comments, setComments] = useState([]) const [fullPost, setFullPost] = useState(null) const [loadingPost, setLoadingPost] = useState(false) // Загрузить полные данные поста с комментариями const loadFullPost = useCallback(async () => { if (!post?._id) { setFullPost(post || null) setComments(post?.comments || []) return } try { setLoadingPost(true) const response = await getPosts() const foundPost = response.posts?.find(p => p._id === post._id) if (foundPost) { setFullPost(foundPost) setComments(foundPost.comments || []) } else { // Fallback на переданный post setFullPost(post) setComments(post?.comments || []) } } catch (error) { console.error('[CommentsModal] Ошибка загрузки поста:', error) // Fallback на переданный post setFullPost(post) setComments(post?.comments || []) } finally { setLoadingPost(false) } }, [post]) useEffect(() => { if (post) { setFullPost(post) setComments(post.comments || []) loadFullPost() } }, [post, loadFullPost]) // Проверка на существование поста ПОСЛЕ хуков if (!post) { return null } const displayPost = fullPost || post const handleSubmit = async () => { if (!comment.trim()) return try { setLoading(true) hapticFeedback('light') const result = await commentPost(post._id, comment) setComments(result.comments || []) setComment('') hapticFeedback('success') // Обновить полный пост await loadFullPost() onUpdate() } catch (error) { console.error('Ошибка добавления комментария:', error) hapticFeedback('error') } finally { setLoading(false) } } const formatDate = (date) => { const d = new Date(date) const now = new Date() const diff = Math.floor((now - d) / 1000) // секунды if (diff < 60) return 'только что' if (diff < 3600) return `${Math.floor(diff / 60)} мин` if (diff < 86400) return `${Math.floor(diff / 3600)} ч` return d.toLocaleDateString('ru-RU', { day: 'numeric', month: 'short' }) } const handleOverlayClick = (e) => { // Закрывать только при клике на overlay, не на контент if (e.target === e.currentTarget) { onClose() } } return createPortal(
e.stopPropagation()} onTouchStart={(e) => e.stopPropagation()} onClick={handleOverlayClick} >
e.stopPropagation()}> {/* Хедер */}

Комментарии

{/* Пост */} {loadingPost ? (

Загрузка...

) : (
{displayPost.author?.username { e.target.src = '/default-avatar.png' }} />
{displayPost.author?.firstName || ''} {displayPost.author?.lastName || ''} {!displayPost.author?.firstName && !displayPost.author?.lastName && 'Пользователь'}
@{displayPost.author?.username || displayPost.author?.firstName || 'user'}
{displayPost.content && (
{decodeHtmlEntities(displayPost.content)}
)} {((displayPost.images && displayPost.images.length > 0) || displayPost.imageUrl) && (
Post
)}
)} {/* Список комментариев */}
{comments.length === 0 ? (

Пока нет комментариев

Будьте первым!
) : ( comments .filter(c => c.author) // Фильтруем комментарии без автора .map((c, index) => (
{c.author?.username { e.target.src = '/default-avatar.png' }} />
{c.author?.firstName || ''} {c.author?.lastName || ''} {!c.author?.firstName && !c.author?.lastName && 'Пользователь'} {formatDate(c.createdAt)}

{decodeHtmlEntities(c.content)}

)) )}
{/* Форма добавления комментария */}
setComment(e.target.value)} onKeyPress={e => e.key === 'Enter' && handleSubmit()} maxLength={500} />
) }