fixed image and name chat
This commit is contained in:
@@ -2,7 +2,7 @@ import { getChatDisplayInfo } from "../../utils/chatDisplay";
|
|||||||
import TrashIcon from "../icons/TrashIcon";
|
import TrashIcon from "../icons/TrashIcon";
|
||||||
|
|
||||||
export default function ChatListItem({ chat, post, unreadMap, t, onOpen, onDelete }) {
|
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 (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -13,13 +13,13 @@ export default function ChatListItem({ chat, post, unreadMap, t, onOpen, onDelet
|
|||||||
{imgSrc ? (
|
{imgSrc ? (
|
||||||
<img src={imgSrc} alt="" className="chat-list-avatar-img" />
|
<img src={imgSrc} alt="" className="chat-list-avatar-img" />
|
||||||
) : (
|
) : (
|
||||||
emoji || chat.otherUser?.name?.[0] || "?"
|
emoji || chat.otherUserName?.[0] || chat.otherUser?.name?.[0] || "?"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="chat-list-info">
|
<div className="chat-list-info">
|
||||||
<div className="chat-list-name">{title}</div>
|
<div className="chat-list-name">{title}</div>
|
||||||
<div className="chat-list-preview">
|
<div className="chat-list-preview">
|
||||||
{chat.lastMessage?.content || t('chat.noMessages')}
|
{preview}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{unreadCount > 0 && (
|
{unreadCount > 0 && (
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// src/context/AuthContext.jsx
|
// src/context/AuthContext.jsx
|
||||||
import { createContext, useContext, useState, useCallback } from "react";
|
import { createContext, useContext, useState, useCallback } from "react";
|
||||||
import { saveTokens, clearTokens, getAccessToken } from "../api/apiClient";
|
import { saveTokens, clearTokens, getAccessToken } from "../api/apiClient";
|
||||||
import { clearPostCache } from "../stores/chatPostStore";
|
|
||||||
|
|
||||||
const AuthContext = createContext(null);
|
const AuthContext = createContext(null);
|
||||||
|
|
||||||
@@ -35,14 +34,13 @@ export function AuthProvider({ children }) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Очищает все данные аутентификации (access, refresh, user) и модульные
|
* Очищает все данные аутентификации (access, refresh, user).
|
||||||
* кеши, привязанные к сессии пользователя (например, chatPostStore), —
|
* Кеш постов чатов (chatPostStore) НЕ очищаем: посты — публичные данные
|
||||||
* иначе данные предыдущего пользователя могут "пережить" logout на этом
|
* из ленты, а id чатов глобально уникальны, поэтому кеш не "протекает"
|
||||||
* же устройстве.
|
* между пользователями и сохраняет title/картинку чата после повторного входа.
|
||||||
*/
|
*/
|
||||||
const logout = useCallback(() => {
|
const logout = useCallback(() => {
|
||||||
clearTokens();
|
clearTokens();
|
||||||
clearPostCache();
|
|
||||||
setUser(null);
|
setUser(null);
|
||||||
setToken(null);
|
setToken(null);
|
||||||
}, []);
|
}, []);
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export function useContactSeller(post) {
|
|||||||
if (!post) return;
|
if (!post) return;
|
||||||
try {
|
try {
|
||||||
const chat = await createOrGetChat(post.userId, post.id, {
|
const chat = await createOrGetChat(post.userId, post.id, {
|
||||||
|
id: post.id,
|
||||||
title: post.title,
|
title: post.title,
|
||||||
images: post.images,
|
images: post.images,
|
||||||
emoji: post.emoji,
|
emoji: post.emoji,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default function ChatListPage() {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { chats, loading, fetchChats } = useChats();
|
const { chats, loading, fetchChats } = useChats();
|
||||||
const { isAuthenticated } = useAuthContext();
|
const { isAuthenticated } = useAuthContext();
|
||||||
const { unreadMap } = useUnread();
|
const { unreadMap, totalUnread } = useUnread();
|
||||||
const { deleteChat } = useDeleteChat();
|
const { deleteChat } = useDeleteChat();
|
||||||
const [deleteError, setDeleteError] = useState("");
|
const [deleteError, setDeleteError] = useState("");
|
||||||
|
|
||||||
@@ -25,6 +25,17 @@ export default function ChatListPage() {
|
|||||||
}
|
}
|
||||||
}, [isAuthenticated, fetchChats]);
|
}, [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) => {
|
const handleDeleteChat = useCallback(async (chatId) => {
|
||||||
if (!window.confirm(t('chat.confirmDeleteChat'))) return;
|
if (!window.confirm(t('chat.confirmDeleteChat'))) return;
|
||||||
const result = await deleteChat(chatId);
|
const result = await deleteChat(chatId);
|
||||||
|
|||||||
@@ -1,7 +1,39 @@
|
|||||||
|
const STORAGE_KEY = "nakhodka:chatPosts";
|
||||||
|
|
||||||
const postByChatId = new Map();
|
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) {
|
export function setPostForChat(chatId, post) {
|
||||||
postByChatId.set(chatId, post);
|
postByChatId.set(chatId, post);
|
||||||
|
persist();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPostForChat(chatId) {
|
export function getPostForChat(chatId) {
|
||||||
@@ -14,14 +46,21 @@ export function cachePostsFromChats(chats) {
|
|||||||
postByChatId.set(chat.id, chat.post);
|
postByChatId.set(chat.id, chat.post);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (chats.length) persist();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Удаляет запись при удалении конкретного чата. */
|
/** Удаляет запись при удалении конкретного чата. */
|
||||||
export function removePostForChat(chatId) {
|
export function removePostForChat(chatId) {
|
||||||
postByChatId.delete(chatId);
|
postByChatId.delete(chatId);
|
||||||
|
persist();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Полностью очищает кеш — вызывать при logout(), чтобы не хранить данные предыдущей сессии. */
|
/** Полностью очищает кеш — вызывать при logout(), чтобы не хранить данные предыдущей сессии. */
|
||||||
export function clearPostCache() {
|
export function clearPostCache() {
|
||||||
postByChatId.clear();
|
postByChatId.clear();
|
||||||
|
try {
|
||||||
|
localStorage.removeItem(STORAGE_KEY);
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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) {
|
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 imgSrc = getImageUrl(post?.images?.[0]);
|
||||||
const emoji = post?.emoji;
|
const emoji = post?.emoji;
|
||||||
const unreadCount = unreadMap[chat.id] ?? chat.unreadCount ?? chat.unreadMessages ?? 0;
|
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 };
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user