Files

214 lines
7.5 KiB
JavaScript

/**
* App Chat
*/
'use strict';
document.addEventListener('DOMContentLoaded', () => {
// DOM Elements
const elements = {
chatContactsBody: document.querySelector('.app-chat-contacts .sidebar-body'),
chatHistoryBody: document.querySelector('.chat-history-body'),
chatSidebarLeftBody: document.querySelector('.app-chat-sidebar-left .sidebar-body'),
chatSidebarRightBody: document.querySelector('.app-chat-sidebar-right .sidebar-body'),
chatUserStatus: [...document.querySelectorAll(".form-check-input[name='chat-user-status']")],
chatSidebarLeftUserAbout: document.getElementById('chat-sidebar-left-user-about'),
formSendMessage: document.querySelector('.form-send-message'),
messageInput: document.querySelector('.message-input'),
searchInput: document.querySelector('.chat-search-input'),
chatContactListItems: [...document.querySelectorAll('.chat-contact-list-item:not(.chat-contact-list-item-title)')],
textareaInfo: document.getElementById('textarea-maxlength-info'),
conversationButton: document.getElementById('app-chat-conversation-btn'),
chatHistoryHeader: document.querySelector(".chat-history-header [data-target='#app-chat-contacts']"),
speechToText: $('.speech-to-text'),
appChatConversation: document.getElementById('app-chat-conversation'),
appChatHistory: document.getElementById('app-chat-history')
};
const userStatusClasses = {
active: 'avatar-online',
offline: 'avatar-offline',
away: 'avatar-away',
busy: 'avatar-busy'
};
/**
* Initialize PerfectScrollbar on provided elements.
* @param {HTMLElement[]} elements - List of elements to initialize.
*/
const initPerfectScrollbar = elements => {
elements.forEach(el => {
if (el) {
new PerfectScrollbar(el, {
wheelPropagation: false,
suppressScrollX: true
});
}
});
};
/**
* Scroll chat history to the bottom.
*/
const scrollToBottom = () => elements.chatHistoryBody?.scrollTo(0, elements.chatHistoryBody.scrollHeight);
/**
* Update user status avatar classes.
* @param {string} status - Status key from userStatusClasses.
*/
const updateUserStatus = status => {
const leftSidebarAvatar = document.querySelector('.chat-sidebar-left-user .avatar');
const contactsAvatar = document.querySelector('.app-chat-contacts .avatar');
[leftSidebarAvatar, contactsAvatar].forEach(avatar => {
if (avatar) avatar.className = avatar.className.replace(/avatar-\w+/, userStatusClasses[status]);
});
};
// Handle textarea max length count.
function handleMaxLengthCount(inputElement, infoElement, maxLength) {
const currentLength = inputElement.value.length;
const remaining = maxLength - currentLength;
infoElement.className = 'maxLength label-success';
if (remaining >= 0) {
infoElement.textContent = `${currentLength}/${maxLength}`;
}
if (remaining <= 0) {
infoElement.textContent = `${currentLength}/${maxLength}`;
infoElement.classList.remove('label-success');
infoElement.classList.add('label-danger');
}
}
/**
* Switch to chat conversation view.
*/
const switchToChatConversation = () => {
elements.appChatConversation.classList.replace('d-flex', 'd-none');
elements.appChatHistory.classList.replace('d-none', 'd-block');
};
/**
* Filter chat contacts by search input.
* @param {string} selector - CSS selector for chat/contact list items.
* @param {string} searchValue - Search input value.
* @param {string} placeholderSelector - Selector for placeholder element.
*/
const filterChatContacts = (selector, searchValue, placeholderSelector) => {
const items = document.querySelectorAll(`${selector}:not(.chat-contact-list-item-title)`);
let visibleCount = 0;
items.forEach(item => {
const isVisible = item.textContent.toLowerCase().includes(searchValue);
item.classList.toggle('d-flex', isVisible);
item.classList.toggle('d-none', !isVisible);
if (isVisible) visibleCount++;
});
document.querySelector(placeholderSelector)?.classList.toggle('d-none', visibleCount > 0);
};
/**
* Initialize speech-to-text functionality.
*/
const initSpeechToText = () => {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SpeechRecognition || elements.speechToText.length === 0) return;
const recognition = new SpeechRecognition();
let listening = false;
elements.speechToText.on('click', function () {
if (!listening) recognition.start();
recognition.onspeechstart = () => (listening = true);
recognition.onresult = event => {
$(this).closest('.form-send-message').find('.message-input').val(event.results[0][0].transcript);
};
recognition.onspeechend = () => (listening = false);
recognition.onerror = () => (listening = false);
});
};
// Initialize PerfectScrollbar
initPerfectScrollbar([
elements.chatContactsBody,
elements.chatHistoryBody,
elements.chatSidebarLeftBody,
elements.chatSidebarRightBody
]);
// Scroll to the bottom of the chat history
scrollToBottom();
// Attach user status change event
elements.chatUserStatus.forEach(statusInput => {
statusInput.addEventListener('click', () => updateUserStatus(statusInput.value));
});
// Handle max length for textarea
const maxLength = parseInt(elements.chatSidebarLeftUserAbout.getAttribute('maxlength'), 10);
handleMaxLengthCount(elements.chatSidebarLeftUserAbout, elements.textareaInfo, maxLength);
elements.chatSidebarLeftUserAbout.addEventListener('input', () => {
handleMaxLengthCount(elements.chatSidebarLeftUserAbout, elements.textareaInfo, maxLength);
});
// Attach chat conversation switch event
elements.conversationButton?.addEventListener('click', switchToChatConversation);
// Attach chat contact selection event
elements.chatContactListItems.forEach(item => {
item.addEventListener('click', () => {
elements.chatContactListItems.forEach(contact => contact.classList.remove('active'));
item.classList.add('active');
switchToChatConversation();
});
});
// Attach chat search filter event
elements.searchInput?.addEventListener(
'keyup',
debounce(e => {
const searchValue = e.target.value.toLowerCase();
filterChatContacts('#chat-list li', searchValue, '.chat-list-item-0');
filterChatContacts('#contact-list li', searchValue, '.contact-list-item-0');
}, 300)
);
// Attach message send event
elements.formSendMessage?.addEventListener('submit', e => {
e.preventDefault();
const message = elements.messageInput.value.trim();
if (message) {
const messageDiv = document.createElement('div');
messageDiv.className = 'chat-message-text mt-2';
messageDiv.innerHTML = `<p class="mb-0 text-break">${message}</p>`;
document.querySelector('li:last-child .chat-message-wrapper')?.appendChild(messageDiv);
elements.messageInput.value = '';
scrollToBottom();
}
});
// Fix overlay issue for chat sidebar
elements.chatHistoryHeader?.addEventListener('click', () => {
document.querySelector('.app-chat-sidebar-left .close-sidebar')?.removeAttribute('data-overlay');
});
// Initialize speech-to-text
initSpeechToText();
});
/**
* Debounce utility function.
* @param {Function} func - Function to debounce.
* @param {number} wait - Delay in milliseconds.
*/
function debounce(func, wait) {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}