import { useState, useEffect, useCallback, useRef } from "react"; import { useParams, useNavigate } from "react-router-dom"; import { useTranslation } from 'react-i18next'; import { useChats } from "../hooks/useChats"; import { useChatSocket } from "../hooks/useChatSocket"; import { getPostForChat } from "../stores/chatPostStore"; import { setActiveChatId } from "../stores/activeChatStore"; import { useUnread } from "../context/UnreadContext"; import { useAuthContext } from "../context/AuthContext"; import { getImageUrl } from "../config/authConstants"; import ChatHeader from "../components/chat/ChatHeader"; import MessageBubble from "../components/chat/MessageBubble"; import ChatInputBar from "../components/chat/ChatInputBar"; import Toast from "../components/Toast"; export default function ChatPage() { const { t } = useTranslation(); const { chatId } = useParams(); const navigate = useNavigate(); const { fetchMessages, markAsRead } = useChats(); const { clearChatUnread } = useUnread(); const { user } = useAuthContext(); const [messages, setMessages] = useState([]); const [input, setInput] = useState(""); const [showEmoji, setShowEmoji] = useState(false); const [loadError, setLoadError] = useState(""); const [sendError, setSendError] = useState(""); const messagesEndRef = useRef(null); const myUserId = user?.id; /** Мержит новое сообщение по id — защита от дублей (REST + WS эхо). */ const addMessage = useCallback((msg) => { setMessages((prev) => { if (msg.id != null && prev.some((m) => m.id === msg.id)) return prev; return [...prev, msg]; }); }, []); const handleIncoming = useCallback((msg) => { addMessage(msg); }, [addMessage]); const { sendMessage, connected } = useChatSocket(chatId, handleIncoming); useEffect(() => { if (!chatId) return; setActiveChatId(chatId); setLoadError(""); fetchMessages(chatId) .then((list) => { // Мержим историю с уже пришедшими по сокету сообщениями, // а не затираем их — иначе можно потерять сообщение, пришедшее // по WS, пока REST-запрос ещё letел. setMessages((prev) => { const byId = new Map(list.map((m) => [m.id, m])); const extra = prev.filter((m) => !byId.has(m.id)); return [...list, ...extra]; }); }) .catch(() => setLoadError(t('chat.loadError'))); markAsRead(chatId); clearChatUnread(chatId); return () => setActiveChatId(null); }, [chatId, fetchMessages, markAsRead, clearChatUnread, t]); useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }, [messages]); const handleSend = useCallback((e) => { e.preventDefault(); const trimmed = input.trim(); if (!trimmed) return; const sent = sendMessage(trimmed); if (!sent) { setSendError(t('chat.sendError')); return; } setInput(""); }, [input, sendMessage, t]); const handleEmojiSelect = useCallback((emoji) => { setInput((prev) => prev + emoji.native); }, []); const post = getPostForChat(chatId); const postImg = getImageUrl(post?.images?.[0]); return (