import { useEffect } from "react"; import { ChatList } from "../components/ChatList"; import { ChatInfoPanel } from "../components/ChatInfoPanel"; import { MessageComposer } from "../components/MessageComposer"; import { MessageList } from "../components/MessageList"; import { getNotifications, type NotificationItem } from "../api/notifications"; import { searchMessages } from "../api/chats"; import type { Message } from "../chat/types"; import { useRealtime } from "../hooks/useRealtime"; import { useAuthStore } from "../store/authStore"; import { useChatStore } from "../store/chatStore"; import { useState } from "react"; export function ChatsPage() { const me = useAuthStore((s) => s.me); const logout = useAuthStore((s) => s.logout); const loadChats = useChatStore((s) => s.loadChats); const activeChatId = useChatStore((s) => s.activeChatId); const chats = useChatStore((s) => s.chats); const setActiveChatId = useChatStore((s) => s.setActiveChatId); const setFocusedMessage = useChatStore((s) => s.setFocusedMessage); const loadMessages = useChatStore((s) => s.loadMessages); const activeChat = chats.find((chat) => chat.id === activeChatId); const isReadOnlyChannel = Boolean(activeChat && activeChat.type === "channel" && activeChat.my_role === "member"); const [infoOpen, setInfoOpen] = useState(false); const [searchOpen, setSearchOpen] = useState(false); const [searchQuery, setSearchQuery] = useState(""); const [searchLoading, setSearchLoading] = useState(false); const [searchResults, setSearchResults] = useState([]); const [notificationsOpen, setNotificationsOpen] = useState(false); const [notifications, setNotifications] = useState([]); const [loadingNotifications, setLoadingNotifications] = useState(false); useRealtime(); useEffect(() => { void loadChats(); }, [loadChats]); useEffect(() => { if (activeChatId) { void loadMessages(activeChatId); } }, [activeChatId, loadMessages]); useEffect(() => { if (!searchOpen) { return; } const term = searchQuery.trim(); if (term.length < 2) { setSearchResults([]); setSearchLoading(false); return; } let cancelled = false; setSearchLoading(true); void (async () => { try { const found = await searchMessages(term, activeChatId ?? undefined); if (!cancelled) { setSearchResults(found); } } catch { if (!cancelled) { setSearchResults([]); } } finally { if (!cancelled) { setSearchLoading(false); } } })(); return () => { cancelled = true; }; }, [searchOpen, searchQuery, activeChatId]); useEffect(() => { if (!notificationsOpen) { return; } let cancelled = false; setLoadingNotifications(true); void (async () => { try { const items = await getNotifications(30); if (!cancelled) { setNotifications(items); } } catch { if (!cancelled) { setNotifications([]); } } finally { if (!cancelled) { setLoadingNotifications(false); } } })(); return () => { cancelled = true; }; }, [notificationsOpen]); return (
{activeChatId ? ( ) : null}

{activeChat?.display_title || activeChat?.title || me?.name || `@${me?.username}`}

{activeChat ? headerMetaLabel(activeChat) : "Select a chat"}

{activeChatId && !isReadOnlyChannel ? ( ) : activeChatId && isReadOnlyChannel ? (
Только администраторы могут публиковать сообщения в этом канале
) : (
Выберите чат, чтобы начать переписку
)}
setInfoOpen(false)} /> {searchOpen ? (
setSearchOpen(false)}>
e.stopPropagation()}>

Search messages

setSearchQuery(e.target.value)} /> {searchLoading ?

Searching...

: null} {!searchLoading && searchQuery.trim().length >= 2 && searchResults.length === 0 ? (

Nothing found

) : null}
{searchResults.map((message) => { const chatMeta = chats.find((chat) => chat.id === message.chat_id); const chatLabel = chatMeta?.public_id ?? String(message.chat_id); return ( ); })}
) : null} {notificationsOpen ? (
setNotificationsOpen(false)}>
e.stopPropagation()}>

Notifications

{loadingNotifications ?

Loading...

: null} {!loadingNotifications && notifications.length === 0 ?

No notifications

: null}
{notifications.map((item) => (

{item.event_type}

{new Date(item.created_at).toLocaleString()}

))}
) : null}
); } function headerMetaLabel(chat: { type: "private" | "group" | "channel"; is_saved?: boolean; counterpart_is_online?: boolean | null; counterpart_last_seen_at?: string | null; members_count?: number | null; online_count?: number | null; subscribers_count?: number | null; }): string { if (chat.is_saved) { return "Saved Messages"; } if (chat.type === "private") { if (chat.counterpart_is_online) { return "online"; } if (chat.counterpart_last_seen_at) { return `last seen ${formatLastSeen(chat.counterpart_last_seen_at)}`; } return "offline"; } if (chat.type === "group") { const members = chat.members_count ?? 0; const online = chat.online_count ?? 0; return `${members} members, ${online} online`; } const subscribers = chat.subscribers_count ?? chat.members_count ?? 0; return `${subscribers} subscribers`; } function formatLastSeen(value: string): string { const date = new Date(value); if (Number.isNaN(date.getTime())) { return "recently"; } return date.toLocaleString(undefined, { day: "2-digit", month: "short", hour: "2-digit", minute: "2-digit" }); }