import { useState, useEffect, useCallback, useMemo } from "react"; import { useUserStore } from "@/state/user"; import { useChatStore } from "@/state/chat"; import { useDM, type DMUser } from "@/pages/chat/hooks/useDM"; import api from "@/core/api"; import { StatusBadge } from "@/core/components/StatusBadge"; import type { Message, VerificationStatus } from "@/core/types"; import { websocket } from "@/core/websocket"; import { onlineStatusManager } from "@/core/onlineStatusManager"; import { OnlineIndicator } from "@/pages/chat/ui/right/OnlineIndicator"; import defaultAvatar from "@/images/default-avatar.png"; import { MaterialBadge, MaterialCircularProgress, MaterialIcon, MaterialList, MaterialListItem } from "@/utils/material"; import { displayNameForUser, isRedactedPeer } from "@/core/userDisplay"; import { DeletedUserAvatar } from "@/core/DeletedUserAvatar"; import styles from "@/pages/chat/css/left-panel.module.scss"; interface PublicChat { id: string; name: string; type: "public"; lastMessage?: Message; } interface DMConversation { id: number; userId: number; username: string; display_name: string; profile_picture?: string; online?: boolean; type: "dm"; lastMessage?: string; unreadCount: number; publicKey?: string | null; verified?: boolean; verification_status?: VerificationStatus; } type ChatItem = PublicChat | DMConversation; const PUBLIC_CHAT: PublicChat = { id: "general", name: "Общий чат", type: "public" }; export function UnifiedChatsList() { const { user } = useUserStore(); const { switchToPublicChat, switchToDM, activeTab } = useChatStore(); const { dmUsers, isLoadingUsers, loadUsers } = useDM(); const [lastMessages, setLastMessages] = useState>({}); const loadLastMessages = useCallback(async () => { if (!user.authToken) return; try { const { messages } = await api.chats.general.fetchMessages(user.authToken, 1); if (messages?.length > 0) { const lastMessage = messages[messages.length - 1]; setLastMessages({ general: lastMessage }); } } catch (error) { console.error("Error loading last messages:", error); } }, [user.authToken]); useEffect(() => { if (activeTab === "chats") { loadUsers(); loadLastMessages(); } }, [activeTab, loadUsers, loadLastMessages]); const allChats = useMemo(() => { return [ ...dmUsers.map((user: DMUser) => ({ ...user, userId: user.id, display_name: displayNameForUser({ ...user, id: user.id }), type: "dm" as const })), { ...PUBLIC_CHAT, lastMessage: lastMessages[PUBLIC_CHAT.id] } ]; }, [lastMessages, dmUsers]); useEffect(() => { if (!websocket) return; function handleWebSocketMessage(e: MessageEvent) { try { const msg = JSON.parse(e.data); if (msg.type === "newMessage") { const newMessage = msg.data as Message; setLastMessages(prev => ({ ...prev, [PUBLIC_CHAT.id]: newMessage })); } else if (msg.type === "messageEdited") { const editedMessage = msg.data as Message; setLastMessages(prev => { if (prev[PUBLIC_CHAT.id]?.id === editedMessage.id) { return { ...prev, [PUBLIC_CHAT.id]: editedMessage }; } return prev; }); } else if (msg.type === "messageDeleted") { const deletedMessageId = msg.data?.message_id; setLastMessages(prev => { if (prev[PUBLIC_CHAT.id]?.id === deletedMessageId) { loadLastMessages(); return { ...prev, [PUBLIC_CHAT.id]: undefined }; } return prev; }); } } catch (error) { console.error("Failed to handle WebSocket message in UnifiedChatsList:", error); } }; websocket.addEventListener("message", handleWebSocketMessage); return () => websocket.removeEventListener("message", handleWebSocketMessage); }, [loadLastMessages]); useEffect(() => { dmUsers.forEach(dmUser => { onlineStatusManager.subscribe(dmUser.id); }); return () => { dmUsers.forEach(dmUser => { onlineStatusManager.unsubscribe(dmUser.id); }); }; }, [dmUsers]); function formatPublicChatMessage(chatId: string): string { const lastMessage = lastMessages[chatId]; if (!lastMessage) return ""; const isCurrentUser = lastMessage.user_id === user.currentUser?.id; const prefix = isCurrentUser ? "Вы: " : `${lastMessage.username}: `; const maxLength = 50 - prefix.length; const content = lastMessage.content.length > maxLength ? lastMessage.content.substring(0, maxLength) + "..." : lastMessage.content; return prefix + content; }; async function handleDMClick(dmConversation: DMConversation) { if (!dmConversation.publicKey) { const authToken = useUserStore.getState().user.authToken; if (!authToken) return; const publicKey = await api.chats.dm.fetchUserPublicKey(dmConversation.id, authToken); if (!publicKey) { console.error("Failed to get public key for user:", dmConversation.id); return; } dmConversation.publicKey = publicKey; } await switchToDM({ userId: dmConversation.id, username: dmConversation.username, publicKey: dmConversation.publicKey, profilePicture: dmConversation.profile_picture, online: dmConversation.online || false }); }; if (isLoadingUsers) { return ; } if (user.isSuspended) { return ( ); } return ( {allChats.map((chat) => { if (chat.type === "public") { const formattedMessage = formatPublicChatMessage(chat.id); return ( switchToPublicChat(chat.name)} style={{ cursor: "pointer" }} > {formattedMessage && ( {formattedMessage} )} {chat.name} ); } const isDeletedDm = isRedactedPeer(chat); const displayName = displayNameForUser({ ...chat, id: chat.id }); return ( handleDMClick(chat)} style={{ cursor: "pointer" }} >
{displayName} {!isDeletedDm && ( )}
{chat.lastMessage || "Нет сообщений"}
{isDeletedDm ? ( ) : ( {displayName} { e.target.src = defaultAvatar; }} /> )} {!isDeletedDm && }
{chat.unreadCount > 0 && ( {chat.unreadCount} )}
); })}
); }