Files
nahodka-front/src/pages/ChatPage.jsx
SlimusMinus 7f59ee1f38 fixed code
2026-08-11 00:24:07 +03:00

143 lines
4.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 (
<div className="f-app">
<div className="chat-page">
<ChatHeader
title={post?.title || t('chat.title')}
onBack={() => navigate("/chats")}
avatar={postImg ? (
<img src={postImg} alt="" className="chat-header-avatar-img" />
) : (
post?.emoji || "📋"
)}
statusLabel={connected ? t('chat.online') : t('chat.offline')}
statusConnected={connected}
/>
<div className="chat-messages">
{loadError && <div className="chat-empty">{loadError}</div>}
{!loadError && messages.length === 0 && (
<div className="chat-empty">{t('chat.noMessages')}</div>
)}
{messages.map((m) => (
<MessageBubble key={m.id} message={m} isMine={m.senderId === myUserId} />
))}
<div ref={messagesEndRef} />
</div>
<ChatInputBar
input={input}
onInputChange={setInput}
onSend={handleSend}
onEmojiSelect={handleEmojiSelect}
showEmoji={showEmoji}
onToggleEmoji={() => setShowEmoji((v) => !v)}
onCloseEmoji={() => setShowEmoji(false)}
connected={connected}
placeholder={t('chat.messagePlaceholder')}
/>
</div>
{sendError && (
<Toast
message={sendError}
type="error"
onDismiss={() => setSendError("")}
/>
)}
</div>
);
}