const usernameInput = document.querySelector('#username'); const roomInput = document.querySelector('#room'); const connectBtn = document.querySelector('#connectBtn'); const disconnectBtn = document.querySelector('#disconnectBtn'); const clearBtn = document.querySelector('#clearBtn'); const messageForm = document.querySelector('#messageForm'); const messageInput = document.querySelector('#messageInput'); const sendBtn = document.querySelector('#sendBtn'); const messages = document.querySelector('#messages'); const statusPill = document.querySelector('#connectionStatus'); const usersList = document.querySelector('#usersList'); const roomTitle = document.querySelector('#roomTitle'); const roomSubtitle = document.querySelector('#roomSubtitle'); let socket = null; let currentUser = ''; let currentRoom = ''; // Persist the last used identity locally so refreshing the page is convenient. usernameInput.value = localStorage.getItem('chat_username') || ''; roomInput.value = localStorage.getItem('chat_room') || 'عمومی'; function setConnectedState(isConnected) { connectBtn.disabled = isConnected; disconnectBtn.disabled = !isConnected; messageInput.disabled = !isConnected; sendBtn.disabled = !isConnected; usernameInput.disabled = isConnected; roomInput.disabled = isConnected; statusPill.textContent = isConnected ? 'متصل' : 'قطع'; statusPill.classList.toggle('connected', isConnected); } function showEmptyState() { messages.innerHTML = '
هنوز پیامی وجود ندارد.
وارد یک اتاق شوید و پیام بفرستید.
'; } function clearEmptyState() { const emptyState = messages.querySelector('.empty-state'); if (emptyState) emptyState.remove(); } function safeName(value, fallback) { return value.trim() || fallback; } function formatTime(timestamp) { if (!timestamp) return ''; const date = new Date(timestamp); if (Number.isNaN(date.getTime())) return ''; // The server stores UTC timestamps; the browser formats them for the user. return date.toLocaleTimeString('fa-IR', { hour: '2-digit', minute: '2-digit' }); } function addMessage(payload) { clearEmptyState(); const card = document.createElement('article'); card.className = 'message'; if (payload.type === 'system') card.classList.add('system'); if (payload.client_id === currentUser && payload.type === 'message') card.classList.add('mine'); const meta = document.createElement('div'); meta.className = 'message-meta'; const sender = document.createElement('span'); sender.textContent = payload.client_id || 'ناشناس'; const time = document.createElement('span'); time.textContent = formatTime(payload.timestamp); meta.append(sender, time); const content = document.createElement('div'); content.className = 'message-content'; content.textContent = payload.content || ''; card.append(meta, content); messages.appendChild(card); messages.scrollTop = messages.scrollHeight; } function updateUsers(users = []) { usersList.innerHTML = ''; if (!users.length) { const li = document.createElement('li'); li.className = 'muted'; li.textContent = 'کاربری آنلاین نیست'; usersList.appendChild(li); return; } users.forEach((user) => { const li = document.createElement('li'); li.textContent = user; usersList.appendChild(li); }); } function connect() { currentUser = safeName(usernameInput.value, 'ناشناس'); currentRoom = safeName(roomInput.value, 'عمومی'); localStorage.setItem('chat_username', currentUser); localStorage.setItem('chat_room', currentRoom); const wsProtocol = window.location.protocol === 'https:' ? 'wss' : 'ws'; const wsUrl = `${wsProtocol}://${window.location.host}/ws/${encodeURIComponent(currentRoom)}/${encodeURIComponent(currentUser)}`; // Opening this object starts the HTTP Upgrade handshake automatically. socket = new WebSocket(wsUrl); socket.addEventListener('open', () => { setConnectedState(true); roomTitle.textContent = `اتاق: ${currentRoom}`; roomSubtitle.textContent = `شما با نام ${currentUser} گفتگو می‌کنید.`; messageInput.focus(); }); socket.addEventListener('message', (event) => { const payload = JSON.parse(event.data); // Presence messages update the sidebar; chat/system messages go to the feed. if (payload.type === 'presence') { updateUsers(payload.users || []); return; } addMessage(payload); }); socket.addEventListener('close', () => { setConnectedState(false); updateUsers([]); roomSubtitle.textContent = 'ارتباط بسته شد.'; socket = null; }); socket.addEventListener('error', () => { addMessage({ type: 'system', client_id: 'سیستم', content: 'خطا در اتصال. مطمئن شوید سرور برنامه در حال اجرا است.', timestamp: new Date().toISOString(), }); }); } function disconnect() { if (socket) socket.close(); } connectBtn.addEventListener('click', connect); disconnectBtn.addEventListener('click', disconnect); clearBtn.addEventListener('click', showEmptyState); messageForm.addEventListener('submit', (event) => { event.preventDefault(); const content = messageInput.value.trim(); if (!content || !socket || socket.readyState !== WebSocket.OPEN) return; // The server accepts JSON messages and broadcasts the content to the room. socket.send(JSON.stringify({ content })); messageInput.value = ''; messageInput.focus(); }); showEmptyState(); setConnectedState(false);