From 66ff0dc5bfe1ce457d4a46874f41b8bb93cdfbcd Mon Sep 17 00:00:00 2001 From: SlimusMinus Date: Thu, 13 Aug 2026 00:37:41 +0300 Subject: [PATCH] fixed image and name chat --- src/components/chat/ChatListItem.jsx | 6 ++-- src/context/AuthContext.jsx | 10 +++---- src/hooks/useContactSeller.js | 1 + src/pages/ChatListPage.jsx | 13 ++++++++- src/stores/chatPostStore.js | 41 +++++++++++++++++++++++++++- src/utils/chatDisplay.jsx | 12 ++++++-- 6 files changed, 70 insertions(+), 13 deletions(-) diff --git a/src/components/chat/ChatListItem.jsx b/src/components/chat/ChatListItem.jsx index f567d24..7ba62a6 100644 --- a/src/components/chat/ChatListItem.jsx +++ b/src/components/chat/ChatListItem.jsx @@ -2,7 +2,7 @@ import { getChatDisplayInfo } from "../../utils/chatDisplay"; import TrashIcon from "../icons/TrashIcon"; export default function ChatListItem({ chat, post, unreadMap, t, onOpen, onDelete }) { - const { title, imgSrc, emoji, unreadCount } = getChatDisplayInfo(chat, post, unreadMap, t); + const { title, imgSrc, emoji, unreadCount, preview } = getChatDisplayInfo(chat, post, unreadMap, t); return (
) : ( - emoji || chat.otherUser?.name?.[0] || "?" + emoji || chat.otherUserName?.[0] || chat.otherUser?.name?.[0] || "?" )}
{title}
- {chat.lastMessage?.content || t('chat.noMessages')} + {preview}
{unreadCount > 0 && ( diff --git a/src/context/AuthContext.jsx b/src/context/AuthContext.jsx index 71abba3..a342873 100644 --- a/src/context/AuthContext.jsx +++ b/src/context/AuthContext.jsx @@ -1,7 +1,6 @@ // src/context/AuthContext.jsx import { createContext, useContext, useState, useCallback } from "react"; import { saveTokens, clearTokens, getAccessToken } from "../api/apiClient"; -import { clearPostCache } from "../stores/chatPostStore"; const AuthContext = createContext(null); @@ -35,14 +34,13 @@ export function AuthProvider({ children }) { }, []); /** - * Очищает все данные аутентификации (access, refresh, user) и модульные - * кеши, привязанные к сессии пользователя (например, chatPostStore), — - * иначе данные предыдущего пользователя могут "пережить" logout на этом - * же устройстве. + * Очищает все данные аутентификации (access, refresh, user). + * Кеш постов чатов (chatPostStore) НЕ очищаем: посты — публичные данные + * из ленты, а id чатов глобально уникальны, поэтому кеш не "протекает" + * между пользователями и сохраняет title/картинку чата после повторного входа. */ const logout = useCallback(() => { clearTokens(); - clearPostCache(); setUser(null); setToken(null); }, []); diff --git a/src/hooks/useContactSeller.js b/src/hooks/useContactSeller.js index e9bb74a..bf42019 100644 --- a/src/hooks/useContactSeller.js +++ b/src/hooks/useContactSeller.js @@ -28,6 +28,7 @@ export function useContactSeller(post) { if (!post) return; try { const chat = await createOrGetChat(post.userId, post.id, { + id: post.id, title: post.title, images: post.images, emoji: post.emoji, diff --git a/src/pages/ChatListPage.jsx b/src/pages/ChatListPage.jsx index 05992f7..6fc6842 100644 --- a/src/pages/ChatListPage.jsx +++ b/src/pages/ChatListPage.jsx @@ -15,7 +15,7 @@ export default function ChatListPage() { const navigate = useNavigate(); const { chats, loading, fetchChats } = useChats(); const { isAuthenticated } = useAuthContext(); - const { unreadMap } = useUnread(); + const { unreadMap, totalUnread } = useUnread(); const { deleteChat } = useDeleteChat(); const [deleteError, setDeleteError] = useState(""); @@ -25,6 +25,17 @@ export default function ChatListPage() { } }, [isAuthenticated, fetchChats]); + // При поступлении непрочитанного сообщения (WS) обновляем список, чтобы + // превью показывало новое сообщение и подсветку, не дожидаясь ручного обновления. + useEffect(() => { + if (!isAuthenticated) return; + const timer = setTimeout(() => fetchChats(), 500); + return () => clearTimeout(timer); + // totalUnread — числовое значение, поэтому изменение состава чатов с той же + // суммой не создаст цикл обновлений. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [totalUnread, isAuthenticated, fetchChats]); + const handleDeleteChat = useCallback(async (chatId) => { if (!window.confirm(t('chat.confirmDeleteChat'))) return; const result = await deleteChat(chatId); diff --git a/src/stores/chatPostStore.js b/src/stores/chatPostStore.js index a45ebf4..dec316e 100644 --- a/src/stores/chatPostStore.js +++ b/src/stores/chatPostStore.js @@ -1,7 +1,39 @@ +const STORAGE_KEY = "nakhodka:chatPosts"; + const postByChatId = new Map(); +/** Восстанавливает кеш постов из localStorage (выживает при перезагрузке страницы). */ +function loadFromStorage() { + try { + const raw = localStorage.getItem(STORAGE_KEY); + if (!raw) return; + const obj = JSON.parse(raw); + for (const [chatId, post] of Object.entries(obj)) { + postByChatId.set(chatId, post); + } + } catch (e) { + // Повреждённый/пустой storage игнорируем + } +} + +/** Пишет текущий кеш постов в localStorage. */ +function persist() { + try { + const obj = {}; + postByChatId.forEach((post, chatId) => { + obj[chatId] = post; + }); + localStorage.setItem(STORAGE_KEY, JSON.stringify(obj)); + } catch (e) { + // Недостаточно места / приватный режим — игнорируем + } +} + +loadFromStorage(); + export function setPostForChat(chatId, post) { postByChatId.set(chatId, post); + persist(); } export function getPostForChat(chatId) { @@ -14,14 +46,21 @@ export function cachePostsFromChats(chats) { postByChatId.set(chat.id, chat.post); } } + if (chats.length) persist(); } /** Удаляет запись при удалении конкретного чата. */ export function removePostForChat(chatId) { postByChatId.delete(chatId); + persist(); } /** Полностью очищает кеш — вызывать при logout(), чтобы не хранить данные предыдущей сессии. */ export function clearPostCache() { postByChatId.clear(); -} \ No newline at end of file + try { + localStorage.removeItem(STORAGE_KEY); + } catch (e) { + // ignore + } +} diff --git a/src/utils/chatDisplay.jsx b/src/utils/chatDisplay.jsx index d9ec00b..e18e302 100644 --- a/src/utils/chatDisplay.jsx +++ b/src/utils/chatDisplay.jsx @@ -24,11 +24,19 @@ export function sortMessages(messages) { }); } +/** Текст последнего сообщения. Бэкенд отдаёт lastMessage и строкой, и объектом — поддерживаем оба. */ +export function getLastMessageText(chat) { + const lm = chat?.lastMessage; + if (typeof lm === "string") return lm; + return lm?.content || ""; +} + export function getChatDisplayInfo(chat, post, unreadMap, t) { - const title = post?.title || chat.otherUser?.name || t('chat.user'); + const title = post?.title || chat.otherUserName || chat.otherUser?.name || t('chat.user'); const imgSrc = getImageUrl(post?.images?.[0]); const emoji = post?.emoji; const unreadCount = unreadMap[chat.id] ?? chat.unreadCount ?? chat.unreadMessages ?? 0; + const preview = getLastMessageText(chat) || t('chat.noMessages'); - return { title, imgSrc, emoji, unreadCount }; + return { title, imgSrc, emoji, unreadCount, preview }; } \ No newline at end of file