Files
internet-engineering-project/app/static/app.js

175 lines
5.5 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 = '';
// 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 = '<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 '';
// 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);