(() => { let ws = null let expectingMessage = false const currentUserEl = document.getElementById('username') const loginBtn = document.getElementById('login-btn') const messageLog = document.getElementById('message-log') const publishForm = document.getElementById('publish-form') const recipientSelect = document.getElementById('recipient-select') const messageInput = document.getElementById('message-input') const onlineList = document.getElementById('online-list') // Auto-fill username from URL parameter const urlParams = new URLSearchParams(window.location.search) const urlUser = urlParams.get('user') if (urlUser) { currentUserEl.value = urlUser } // Login button handler loginBtn.addEventListener('click', () => { const username = currentUserEl.value.trim() if (!username) { appendLog('Please enter a username', true) return } // Connect to WebSocket with username connect(username) // Show publish form, hide login loginBtn.style.display = 'none' currentUserEl.style.display = 'none' publishForm.style.display = 'flex' messageInput.disabled = false messageInput.focus() appendLog(`Joined as ${username}`) }) function connect(username) { const encodedUser = encodeURIComponent(username) ws = new WebSocket(`ws://${location.host}/ws/subscribe?user=${encodedUser}`) ws.addEventListener('open', () => { console.info('WebSocket connected') refreshUsers() // Refresh users every 5 seconds setInterval(refreshUsers, 5000) }) ws.addEventListener('message', (ev) => { if (typeof ev.data !== 'string') { console.error('unexpected message type', typeof ev.data) return } const p = appendLog(ev.data) if (expectingMessage) { p.scrollIntoView() expectingMessage = false } }) ws.addEventListener('close', (ev) => { appendLog(`Disconnected (code: ${ev.code})`, true) if (ev.code !== 1001) { appendLog('Reconnecting...', true) setTimeout(() => connect(username), 2000) } }) ws.addEventListener('error', (err) => { console.error('WebSocket error:', err) }) } function refreshUsers() { fetch('/ws/users') .then(r => r.json()) .then(users => { onlineList.textContent = users.join(', ') || 'none' // Update recipient dropdown recipientSelect.innerHTML = '' if (users.length > 0) { users.forEach(user => { const opt = document.createElement('option') opt.value = user opt.textContent = user recipientSelect.appendChild(opt) }) } }) .catch(err => { console.error('Failed to refresh users:', err) }) } function appendLog(text, error = false) { const p = document.createElement('p') const time = new Date().toLocaleTimeString() p.innerText = `[${time}] ${text}` if (error) { p.style.color = '#ff6b6b' } messageLog.append(p) return p } // Submit message publishForm.onsubmit = async (ev) => { ev.preventDefault() const recipient = recipientSelect.value.trim() const msg = messageInput.value.trim() if (!recipient || !msg) { appendLog('Please select a recipient and enter a message', true) return } messageInput.value = '' expectingMessage = true try { const payload = JSON.stringify({ to: recipient, message: msg }) const resp = await fetch('/ws/publish', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: payload, }) if (resp.status !== 202) { const text = await resp.text() throw new Error(`Failed: ${text}`) } } catch (err) { appendLog(`Send failed: ${err.message}`, true) } } appendLog('Enter your username to join the chat') })()