import React, { useEffect, useState, useCallback } from 'react'; import { Link } from 'react-router-dom'; import { jwtDecode } from 'jwt-decode'; import * as Kubikon3DApi from '../api/Kubikon3DService'; import { formatRelative } from '../utils/kubikonTime'; import EmailConfirmNotice from '../components/EmailConfirmNotice/EmailConfirmNotice'; /** * KubikonComments — список комментариев под игрой + форма ответа. * * Используется в KubikonPlayer на отдельной панели (открывается по кнопке «💬»). * В первой версии — без вложенных ответов, плоский список. * * Авторизация: гости не могут оставлять комменты — модалка регистрации. * * Props: * projectId — id игры * projectOwnerId — id автора игры (он может удалять чужие комменты) * onClose — закрыть панель * onRequestAuth — позвать модалку «нужна регистрация» */ const KubikonComments = ({ projectId, projectOwnerId, onClose, onRequestAuth }) => { const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [text, setText] = useState(''); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); const [emailNotice, setEmailNotice] = useState(false); // окно «подтвердите email» const userInfo = (() => { try { const t = localStorage.getItem('Authorization'); if (!t) return null; const p = jwtDecode(t); return { id: p?.id || p?.user_id || null, username: p?.firstName || p?.first_name || p?.email || '', }; } catch (e) { return null; } })(); const userId = userInfo?.id || null; const isOwnerOfProject = userId && projectOwnerId && userId === projectOwnerId; const load = useCallback(async () => { setLoading(true); try { const res = await Kubikon3DApi.getProjectComments(projectId); setItems(res.data?.comments || []); } catch (e) { setItems([]); } setLoading(false); }, [projectId]); useEffect(() => { load(); }, [load]); const send = async () => { if (!userId) { onRequestAuth?.('оставлять комментарии'); return; } const t = text.trim(); if (!t) { setError('Напиши хотя бы что-нибудь.'); return; } setSubmitting(true); setError(null); try { const res = await Kubikon3DApi.createProjectComment(projectId, { user_id: userId, username: userInfo?.username || '', text: t, }); // Оптимистичное добавление в начало const newComment = res.data?.comment; if (newComment) setItems(prev => [newComment, ...prev]); setText(''); } catch (e) { const code = e?.response?.data?.error; const msg = e?.response?.data?.message; if (code === 'too_frequent') { setError(msg || 'Слишком часто. Подожди пару секунд.'); } else if (code === 'rate_limit') { setError(msg || 'Слишком много комментариев — притормози.'); } else if (code === 'login_required') { onRequestAuth?.('оставлять комментарии'); } else if (code === 'email_not_confirmed') { setEmailNotice(true); } else { setError(msg || code || 'Ошибка отправки'); } } setSubmitting(false); }; const removeMine = async (cid) => { try { await Kubikon3DApi.deleteProjectComment(cid, userId); setItems(prev => prev.filter(x => x.id !== cid)); } catch (e) { /* ignore */ } }; return (
💬 Комментарии
{items.length}
{loading ? (
⏳ Загрузка…
) : items.length === 0 ? (
💭 Пока нет комментариев. Будь первым!
) : (
{items.map(c => ( removeMine(c.id)} /> ))}
)}
{error && (
⚠️ {error}
)} {!userId ? (
Войди в аккаунт, чтобы оставить комментарий
) : ( <>