Files

114 lines
3.3 KiB
JavaScript

var ws = null;
var userTypingActive = false;
function setAppHeight() {
var height = window.visualViewport ? window.visualViewport.height : window.innerHeight;
document.documentElement.style.setProperty('--app-height', height + 'px');
}
setAppHeight();
window.addEventListener('resize', setAppHeight);
if (window.visualViewport) {
window.visualViewport.addEventListener('resize', setAppHeight);
window.visualViewport.addEventListener('scroll', setAppHeight);
}
function scrollMessages() {
var el = document.getElementById('messages');
if (el) el.scrollTop = el.scrollHeight;
}
function showSupporterTyping() {
var indicator = document.getElementById('peer-typing');
if (!indicator) return;
indicator.classList.add('visible');
scrollMessages();
}
function hideSupporterTyping() {
var indicator = document.getElementById('peer-typing');
if (indicator) indicator.classList.remove('visible');
}
function sendUserTypingState(state) {
if (!ws || ws.readyState !== WebSocket.OPEN || !window.__sessionId) return;
ws.send('typing:user:' + state + ':' + window.__sessionId);
}
function startUserTyping() {
if (userTypingActive) return;
userTypingActive = true;
sendUserTypingState('start');
}
function stopUserTyping() {
if (!userTypingActive) return;
sendUserTypingState('stop');
userTypingActive = false;
}
function handleUserTypingInput() {
var input = document.getElementById('chat-input');
if (input && input.value.trim()) startUserTyping();
else stopUserTyping();
}
document.addEventListener('htmx:afterSwap', function (e) {
if (e.detail.target.id === 'messages') scrollMessages();
});
document.addEventListener('htmx:beforeRequest', function (e) {
if (e.detail.elt && e.detail.elt.id === 'chat-form') stopUserTyping();
});
function resetChat() {
stopUserTyping();
var oldWs = ws;
ws = null;
window.__sessionId = null;
userTypingActive = false;
if (oldWs) oldWs.close();
location.reload();
}
function initChat(sessionId) {
window.__sessionId = sessionId;
document.getElementById('chat-session-id').textContent = 'Session #' + sessionId.slice(0, 8);
var wsProtocol = location.protocol === 'https:' ? 'wss' : 'ws';
ws = new WebSocket(wsProtocol + '://' + location.host + '/ws/user/' + sessionId);
ws.onmessage = function (message) {
if (message.data === '__reset__') {
resetChat();
return;
}
if (message.data.startsWith('typing:supporter:start:')) {
showSupporterTyping();
return;
}
if (message.data.startsWith('typing:supporter:stop:')) {
hideSupporterTyping();
return;
}
if (message.data.startsWith('typing:user:')) return;
hideSupporterTyping();
htmx.trigger('#messages', 'refresh');
};
ws.onopen = function () {
htmx.trigger('#messages', 'refresh');
};
ws.onerror = function () {
console.warn('WebSocket error; polling will continue.');
};
var chatInput = document.getElementById('chat-input');
if (chatInput) {
chatInput.addEventListener('focus', function () { setTimeout(setAppHeight, 80); });
chatInput.addEventListener('blur', function () { setTimeout(setAppHeight, 80); });
}
setAppHeight();
}