import { useState, useEffect, useCallback } from "react"; import { useAppState } from "@/pages/chat/state"; import { useDM, type DMUser } from "@/pages/chat/hooks/useDM"; import { API_BASE_URL } from "@/core/config"; import { getAuthHeaders } from "@/core/api/authApi"; import { fetchUserPublicKey } from "@/core/api/dmApi"; import type { Message } 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"; interface PublicChat { id: string; name: string; type: "public"; lastMessage?: Message; } interface DMConversation { id: number; username: string; display_name: string; profile_picture?: string; online?: boolean; type: "dm"; lastMessage?: string; unreadCount: number; publicKey?: string | null; } type ChatItem = PublicChat | DMConversation; export function UnifiedChatsList() { const { user, switchToPublicChat, switchToDM, chat } = useAppState(); const { dmUsers, isLoadingUsers, loadUsers } = useDM(); const [publicChats] = useState([ { id: "general", name: "Общий чат", type: "public" }, { id: "general2", name: "Общий чат 2", type: "public" } ]); const [lastMessages, setLastMessages] = useState>({}); const [allChats, setAllChats] = useState([]); // Load public chat last messages const loadLastMessages = useCallback(async () => { if (!user.authToken) return; try { const response = await fetch(`${API_BASE_URL}/get_messages`, { headers: getAuthHeaders(user.authToken) }); if (response.ok) { const data = await response.json(); if (data.messages && data.messages.length > 0) { const lastMessage = data.messages[data.messages.length - 1]; setLastMessages({ general: lastMessage, general2: lastMessage }); } } } catch (error) { console.error("Error loading last messages:", error); } }, [user.authToken]); // Load DM users when chats tab is active useEffect(() => { if (chat.activeTab === "chats") { loadUsers(); loadLastMessages(); } }, [chat.activeTab, loadUsers, loadLastMessages]); // Combine public chats and DMs into one list useEffect(() => { const publicChatItems: ChatItem[] = publicChats.map(chat => ({ ...chat, lastMessage: lastMessages[chat.id] })); const dmChatItems: ChatItem[] = dmUsers.map((user: DMUser) => ({ id: user.id, username: user.username, display_name: user.display_name, profile_picture: user.profile_picture, online: user.online, type: "dm" as const, lastMessage: user.lastMessage, unreadCount: user.unreadCount, publicKey: user.publicKey })); // Combine and sort by last message timestamp (DMs first, then public chats) const combined = [...dmChatItems, ...publicChatItems]; setAllChats(combined); }, [publicChats, lastMessages, dmUsers]); // WebSocket listener for public chat message updates useEffect(() => { if (!websocket) return; const handleWebSocketMessage = (e: MessageEvent) => { try { const msg = JSON.parse(e.data); if (msg.type === "newMessage") { const newMessage = msg.data as Message; // Update all public chats with the new message setLastMessages(prev => { const updated = { ...prev }; publicChats.forEach(chat => { updated[chat.id] = newMessage; }); return updated; }); } else if (msg.type === "messageEdited") { const editedMessage = msg.data as Message; // Update only if the edited message is the current last message setLastMessages(prev => { const updated = { ...prev }; publicChats.forEach(chat => { if (updated[chat.id]?.id === editedMessage.id) { updated[chat.id] = editedMessage; } }); return updated; }); } else if (msg.type === "messageDeleted") { const deletedMessageId = msg.data?.message_id; let needsReload = false; setLastMessages(prev => { const updated = { ...prev }; publicChats.forEach(chat => { if (updated[chat.id]?.id === deletedMessageId) { updated[chat.id] = undefined; needsReload = true; } }); return updated; }); if (needsReload) { loadLastMessages(); } } } catch (error) { console.error("Failed to handle WebSocket message in UnifiedChatsList:", error); } }; websocket.addEventListener("message", handleWebSocketMessage); return () => websocket.removeEventListener("message", handleWebSocketMessage); }, [publicChats, loadLastMessages]); // Subscribe to online status for all DM users useEffect(() => { const dmUsers = allChats.filter(chat => chat.type === "dm") as DMConversation[]; // Subscribe to all DM users dmUsers.forEach(dmUser => { onlineStatusManager.subscribe(dmUser.id); }); // Cleanup function to unsubscribe from all users return () => { dmUsers.forEach(dmUser => { onlineStatusManager.unsubscribe(dmUser.id); }); }; }, [allChats]); 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 maxContentLength = 50 - prefix.length; const content = lastMessage.content.length > maxContentLength ? lastMessage.content.substring(0, maxContentLength) + "..." : lastMessage.content; return prefix + content; } async function handlePublicChatClick(chatName: string) { await switchToPublicChat(chatName); } async function handleDMClick(dmConversation: DMConversation) { if (!dmConversation.publicKey) { const authToken = useAppState.getState().user.authToken; if (!authToken) return; const publicKey = await fetchUserPublicKey(dmConversation.id, authToken); if (publicKey) { dmConversation.publicKey = publicKey; } else { console.error("Failed to get public key for user:", dmConversation.id); return; } } await switchToDM({ userId: dmConversation.id, username: dmConversation.username, publicKey: dmConversation.publicKey, profilePicture: dmConversation.profile_picture, online: dmConversation.online || false }); } if (isLoadingUsers) { return ( ); } return ( {allChats.map((chat) => { if (chat.type === "public") { return ( handlePublicChatClick(chat.name)} style={{ cursor: "pointer" }} > {formatPublicChatMessage(chat.id) && ( {formatPublicChatMessage(chat.id)} )} {chat.name} ); } else { return ( handleDMClick(chat)} style={{ cursor: "pointer" }} > {chat.lastMessage || "Нет сообщений"}
{chat.display_name} { (e.target as HTMLImageElement).src = defaultAvatar; }} />
{chat.unreadCount > 0 && ( {chat.unreadCount} )}
); } })}
); }