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