nakama/frontend/src/components/CommentsModal.jsx

154 lines
5.4 KiB
React
Raw Normal View History

2025-11-03 21:52:13 +00:00
import { useState } from 'react'
2025-11-03 20:35:01 +00:00
import { X, Send } from 'lucide-react'
import { commentPost } from '../utils/api'
2025-11-03 21:52:13 +00:00
import { hapticFeedback } from '../utils/telegram'
2025-12-04 20:00:39 +00:00
import { decodeHtmlEntities } from '../utils/htmlEntities'
2025-11-03 20:35:01 +00:00
import './CommentsModal.css'
export default function CommentsModal({ post, onClose, onUpdate }) {
const [comment, setComment] = useState('')
const [loading, setLoading] = useState(false)
const [comments, setComments] = useState(post.comments || [])
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')
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' })
}
2025-11-03 22:41:34 +00:00
const handleOverlayClick = (e) => {
// Закрывать только при клике на overlay, не на контент
if (e.target === e.currentTarget) {
onClose()
}
}
2025-11-03 20:35:01 +00:00
return (
2025-12-04 20:11:28 +00:00
<div
className="comments-modal-overlay"
onMouseDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onClick={handleOverlayClick}
>
2025-11-03 22:41:34 +00:00
<div className="comments-modal" onClick={(e) => e.stopPropagation()}>
2025-11-03 20:35:01 +00:00
{/* Хедер */}
<div className="modal-header">
<button className="close-btn" onClick={onClose}>
<X size={24} />
</button>
2025-11-03 21:57:35 +00:00
<h2>Комментарии</h2>
<div style={{ width: 40 }} />
</div>
{/* Пост */}
<div className="post-preview">
<div className="preview-author">
<img
2025-12-01 05:40:27 +00:00
src={post.author?.photoUrl || '/default-avatar.png'}
alt={post.author?.username || post.author?.firstName || 'User'}
2025-11-03 21:57:35 +00:00
className="preview-avatar"
2025-12-01 05:40:27 +00:00
onError={(e) => { e.target.src = '/default-avatar.png' }}
2025-11-03 21:57:35 +00:00
/>
<div>
<div className="preview-name">
2025-12-01 05:40:27 +00:00
{post.author?.firstName || ''} {post.author?.lastName || ''}
{!post.author?.firstName && !post.author?.lastName && 'Пользователь'}
2025-11-03 21:57:35 +00:00
</div>
2025-12-01 05:40:27 +00:00
<div className="preview-username">@{post.author?.username || post.author?.firstName || 'user'}</div>
2025-11-03 21:57:35 +00:00
</div>
</div>
{post.content && (
2025-12-04 20:00:39 +00:00
<div className="preview-content">{decodeHtmlEntities(post.content)}</div>
2025-11-03 21:57:35 +00:00
)}
2025-12-04 20:00:39 +00:00
{((post.images && post.images.length > 0) || post.imageUrl) && (
2025-11-03 21:57:35 +00:00
<div className="preview-image">
2025-12-04 20:00:39 +00:00
<img src={post.images?.[0] || post.imageUrl} alt="Post" />
2025-11-03 21:57:35 +00:00
</div>
)}
2025-11-03 20:35:01 +00:00
</div>
{/* Список комментариев */}
<div className="comments-list">
{comments.length === 0 ? (
<div className="empty-comments">
<p>Пока нет комментариев</p>
<span>Будьте первым!</span>
</div>
) : (
2025-12-01 05:40:27 +00:00
comments
.filter(c => c.author) // Фильтруем комментарии без автора
.map((c, index) => (
2025-11-03 20:35:01 +00:00
<div key={index} className="comment-item fade-in">
<img
2025-12-01 05:40:27 +00:00
src={c.author?.photoUrl || '/default-avatar.png'}
alt={c.author?.username || c.author?.firstName || 'User'}
2025-11-03 20:35:01 +00:00
className="comment-avatar"
2025-12-01 05:40:27 +00:00
onError={(e) => { e.target.src = '/default-avatar.png' }}
2025-11-03 20:35:01 +00:00
/>
<div className="comment-content">
<div className="comment-header">
<span className="comment-author">
2025-12-01 05:40:27 +00:00
{c.author?.firstName || ''} {c.author?.lastName || ''}
{!c.author?.firstName && !c.author?.lastName && 'Пользователь'}
2025-11-03 20:35:01 +00:00
</span>
<span className="comment-time">{formatDate(c.createdAt)}</span>
</div>
2025-12-04 20:00:39 +00:00
<p className="comment-text">{decodeHtmlEntities(c.content)}</p>
2025-11-03 20:35:01 +00:00
</div>
</div>
))
)}
</div>
{/* Форма добавления комментария */}
<div className="comment-form">
<input
type="text"
placeholder="Написать комментарий..."
value={comment}
onChange={e => setComment(e.target.value)}
onKeyPress={e => e.key === 'Enter' && handleSubmit()}
maxLength={500}
/>
<button
onClick={handleSubmit}
disabled={loading || !comment.trim()}
className="send-btn"
>
<Send size={20} />
</button>
</div>
</div>
</div>
)
}