169 lines
5.1 KiB
JavaScript
169 lines
5.1 KiB
JavaScript
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 = '';
|
|
|
|
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 = '<div class="empty-state">هنوز پیامی وجود ندارد.<br />وارد یک اتاق شوید و پیام بفرستید.</div>';
|
|
}
|
|
|
|
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 '';
|
|
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)}`;
|
|
|
|
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);
|
|
|
|
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;
|
|
|
|
socket.send(JSON.stringify({ content }));
|
|
messageInput.value = '';
|
|
messageInput.focus();
|
|
});
|
|
|
|
showEmptyState();
|
|
setConnectedState(false);
|