import { useState, useEffect } from 'react'; import { Search, Edit, Settings, Shield, Wifi, WifiOff, MessageCircle } from 'lucide-react'; import { useStore } from '../store'; import { useNavigate } from 'react-router-dom'; import { wsClient } from '../api/ws'; import api from '../api/client'; import Avatar from '../components/Avatar'; import ChatListItem from '../components/ChatListItem'; import MessageList from '../components/MessageList'; import ChatHeader from '../components/ChatHeader'; import ChatInfoPanel from '../components/ChatInfoPanel'; import NewChatModal from '../components/NewChatModal'; import AdminPanel from '../components/admin/AdminPanel'; import ProfileModal from '../components/ProfileModal'; import { usePushNotifications } from '../hooks/usePushNotifications'; import { Chat } from '../types'; export default function MainLayout() { const { user, chats, activeChat, setActiveChat, connected, onlineUsers, updateChat, setChats } = useStore(); const navigate = useNavigate(); const [search, setSearch] = useState(''); const [searchUsers, setSearchUsers] = useState([]); const [showNew, setShowNew] = useState(false); const [showAdmin, setShowAdmin] = useState(false); const [showInfo, setShowInfo] = useState(false); const [showProfile, setShowProfile] = useState(false); const [mobileChatOpen, setMobileChatOpen] = useState(false); const [fullChat, setFullChat] = useState(null); usePushNotifications(!!user); // Handle URL param ?chat=xxx useEffect(() => { const params = new URLSearchParams(location.search); const chatId = params.get('chat'); if (chatId) { const chat = chats.find(c => c.id === chatId); if (chat) openChat(chat); } }, [chats]); // Handle SW messages useEffect(() => { if (!('serviceWorker' in navigator)) return; const handler = (event: MessageEvent) => { if (event.data?.type === 'open_chat') { const chat = chats.find(c => c.id === event.data.chatId); if (chat) openChat(chat); } }; navigator.serviceWorker.addEventListener('message', handler); return () => navigator.serviceWorker.removeEventListener('message', handler); }, [chats]); async function openChat(chat: Chat) { // Load full chat info try { const { data } = await api.get(`/api/chats/${chat.id}`); const merged = { ...chat, ...data }; setActiveChat(merged); setFullChat(merged); updateChat({ id: chat.id, unreadCount: 0 }); wsClient.send('read_messages', { chatId: chat.id }); } catch { setActiveChat(chat); setFullChat(chat); } setMobileChatOpen(true); setShowInfo(false); } // Global search: fetch users when query changes useEffect(() => { if (search.trim().length < 1) { setSearchUsers([]); return; } const timer = setTimeout(async () => { try { const { data } = await api.get('/api/users'); const q = search.toLowerCase(); setSearchUsers(data.filter((u: any) => (u.displayName || '').toLowerCase().includes(q) || (u.username || '').toLowerCase().includes(q) || (u.phone || '').includes(q) )); } catch {} }, 200); return () => clearTimeout(timer); }, [search]); async function openOrCreatePrivate(userId: string) { try { const { data: chatData } = await api.post(`/api/chats/private/${userId}`); setSearch(''); // Load full chat detail directly by ID — don't rely on list lookup const { data: fullChatData } = await api.get(`/api/chats/${chatData.id}`); // Also refresh the sidebar chat list api.get('/api/chats').then(r => setChats(r.data)).catch(() => {}); // Build chat object to open const chatToOpen = { ...fullChatData, id: chatData.id }; setActiveChat(chatToOpen); setFullChat(chatToOpen); setMobileChatOpen(true); setShowInfo(false); } catch (e) { console.error('openOrCreatePrivate:', e); } } const filtered = chats.filter(c => c.title?.toLowerCase().includes(search.toLowerCase()) ); const canSend = (() => { if (!activeChat) return false; if (activeChat.type === 'private') return true; if (activeChat.type === 'group') return activeChat.canSendMessages !== false; if (activeChat.type === 'channel') return activeChat.myRole === 'owner' || activeChat.myRole === 'admin' || !!user?.isAdmin; return false; })(); return (
{/* Sidebar */}
{/* Sidebar Header */}
setShowProfile(true)} className="cursor-pointer" />
{user?.displayName}
{connected ? <> Online : <> Offline }
{user?.isAdmin && ( )}
{/* Search */}
setSearch(e.target.value)} placeholder="Поиск чатов..." className="flex-1 bg-transparent text-sm outline-none text-gray-700 placeholder-gray-400" />
{/* Chat List */}
{filtered.map(chat => ( { openChat(chat); setSearch(''); }} /> ))} {/* Global user search results */} {search && searchUsers.length > 0 && ( <> {filtered.length > 0 &&
Пользователи
} {searchUsers.map(u => ( ))} )} {search && filtered.length === 0 && searchUsers.length === 0 && (
Ничего не найдено
)} {!search && filtered.length === 0 && (
Нет чатов. Создайте новый!
)}
{/* Main Chat Area */}
{activeChat && fullChat ? ( <>
{ setMobileChatOpen(false); setActiveChat(null); }} onRefresh={() => openChat(activeChat)} onShowInfo={() => setShowInfo(!showInfo)} />
{/* Info Panel */} {showInfo && ( setShowInfo(false)} onRefresh={() => openChat(activeChat)} /> )}
) : (

JaniChat

Выберите чат или создайте новый

)}
{/* Modals */} {showNew && setShowNew(false)} onOpen={async (id) => { try { const { data: allChats } = await api.get('/api/chats'); setChats(allChats); const found = allChats.find((c: any) => c.id === id); if (found) openChat(found); } catch {} }} />} {showAdmin && setShowAdmin(false)} />} {showProfile && setShowProfile(false)} />}
); }