let chatDiv; let qtdMsg = 0; let offsetMessage = 0; // Função executada quando o documento está pronto para inicializar chatDiv e configurar ouvintes de eventos $(document).ready(() => { chatDiv = document.querySelector('.body-message'); $('body').on('click', '.selectChat', async (e) => { const chatData = e.target; const chatId = chatData.getAttribute('data-id'); const chatType = chatData.getAttribute('data-type'); if ((chatType !== $(chatSelected).data('type'))) { await changeChat(chatId, chatType); } else { if (($(chatSelected).data('id') > 0 && chatId > 0) && ($(chatSelected).data('id') != chatId)) { changeChat(chatId, chatType); } } }) let chatSelected = document.querySelector('.chatSelected'); }); const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); // Função para mudar o chat com base no chatId e chatType async function changeChat(chatId, chatType) { const pathname = window.location.pathname; $('#is_loading').val('1'); $('#body-loading-message').removeClass('d-none').addClass('loading-move'); // Fecha a tela de anexo, se estiver aberta if (pathname.indexOf('/MoveTask/01/Chat/mensagens') !== -1 || pathname.indexOf('/Chat/mensagens') !== -1) { closeAnexo(); } // ✅ pequena pausa pro DOM atualizar await sleep(10); chatDiv = document.querySelector('.body-message'); offsetMessage = 0; const receiver = await receiverData(chatId, chatType, true); await Promise.all([ changeChatUser(receiver, chatType, chatId), changeChatGroupParticipants(chatType, chatId), saveChat(chatId, chatType), styleChatSelected(chatId, chatType), loadCustomerNotes(chatId, chatType), loadMessages(chatId, chatType), hiddenNewMensagemInChat(), ]); // pequena espera opcional pro DOM renderizar antes de verificar notificações setTimeout(async () => await existsNotification(), 300); offsetMessage = 20; $('#container-reply').remove(); $('.body-message').removeClass('padding-reply'); if (chatType == 'service') { // sempre oculta o body sem chat e exibe o body do chat $('.bodySemChat').addClass('d-none'); $('.bodyChat').removeClass('d-none'); // adicionar o botão de finalizar chat/atendimento addButtonFinishChatService(); if (SESSION_DATA.id_tipo_colaborador < USUARIO_CLIENTE) $('.bodyChat').addClass('blendDifference'); $('#btnGravarEnviarAudio').removeClass('d-flex').addClass('d-none'); if (SESSION_DATA.id_tipo_colaborador < USUARIO_CLIENTE) $('#btnMensagensProntas').removeClass('d-none'); } else { // adicionando a div de groupParticipants e o icon de pesquisar mensagens var html = '
'; html += ''; $('#actions-header-chat').html(html); $('#btnGravarEnviarAudio').removeClass('d-none').addClass('d-flex'); $('#btnMensagensProntas').addClass('d-none'); } } // Função para mudar o nome do chat async function changeChatUser(receiver, chatType, chatId) { const name = receiver.name[0].toUpperCase() + receiver.name.slice(1, receiver.name.length); $('#chatName').html(name); // placeholder chat $('.footer-write').html(''); $(".footer-write").attr("data-placeholder", `Envie uma mensagem para ${receiver.name}`); $('.footer-write').focus(); $('#user-icon-img').attr('src', receiver.picture).attr('onclick', "FileModal('" + receiver.picture + "')"); if ($(`#user-empresa-group-${chatId}`).length && chatType === 'user') { $('#empresasLista').html($(`#user-empresa-group-${chatId}`).html()) } else { $('#empresasLista').html(''); } } // Função para estilizar o chat selecionado function styleChatSelected(chatId, chatType) { $('.selectChat').removeClass('chatSelected'); if (chatType == 'user') $(`#user-${chatId}, #client-${chatId}`).addClass('chatSelected'); if (chatType == 'service') $(`#service-${chatId}`).addClass('chatSelected'); if (chatType == 'departament') $(`#departament-${chatId}`).addClass('chatSelected'); if (chatType == 'mural') $('#mural-1').addClass('chatSelected'); $('.body-footer').addClass('auto-pointer'); } // Função para rolar até o final do chat function defaultScroll(novaMensagem) { if (novaMensagem) hiddenNewMensagemInChat(); chatDiv.scrollTop = chatDiv.scrollHeight; } // Função para rolar até o final do chat se estiver perto do final function cautiousScroll(novaMensagem) { const scrollDifference = $('.message-data').last().offset()?.top - $('.body-message')[0]?.clientHeight; if (novaMensagem) hiddenNewMensagemInChat(); if (scrollDifference < 500) { chatDiv.scrollTop = chatDiv.scrollHeight; } } // Função para obter dados do receptor com base no receiverId e chatType async function receiverData(receiverId, chatType, calledByChangeChat = false) { let tableName; if (chatType == 'user') tableName = 'usuario'; else if (chatType == 'departament') tableName = 'departamento'; else if (chatType == 'bot') tableName = 'bot'; else if (chatType == 'service') tableName = 'service'; else tableName = 'mural'; // mural if (tableName == 'mural') { RECEIVER_DATA.id = null; RECEIVER_DATA.picture = DEFAULT_PICTURE; RECEIVER_DATA.name = "Mural"; return RECEIVER_DATA; } // service if (tableName == 'service') { const typeUserLogged = SESSION_DATA.id_tipo_colaborador; const data = await _GET('/App/Controller/Query/get.controller.php', { table_name: 'chat_atendimento', condition: ` AND id = ${receiverId}` }); const obj = JSON.parse(data)[0]; // se tiver em processo de transferência e for atendente, precisa bloquear a ação de enviar mensagem do chat if (calledByChangeChat && obj.id_atendente_destino && typeUserLogged < USUARIO_CLIENTE) { blockBodyFooterChat(); } if (!obj.id_atendente_destino && typeUserLogged < USUARIO_CLIENTE) { unblockBodyFooterChat(); } if (!obj.id_atendente && typeUserLogged >= USUARIO_CLIENTE) { RECEIVER_DATA.id = receiverId; RECEIVER_DATA.picture = DEFAULT_PICTURE_BOT; RECEIVER_DATA.name = "Aguardando atendente..."; RECEIVER_DATA.id_cliente = obj.id_cliente; RECEIVER_DATA.id_atendente = null; return RECEIVER_DATA; } else { const dataUser = await _GET('/App/Controller/Query/get.controller.php', { table_name: 'usuario', condition: ` AND id = ${(typeUserLogged >= USUARIO_CLIENTE) ? obj.id_atendente : obj.id_cliente}` }); const objUser = JSON.parse(dataUser)[0]; const nameUser = objUser.apelido || objUser.nome; const numProtocoloFormatado = `(#${formatarNumeroChamado(receiverId)})` RECEIVER_DATA.id = receiverId; RECEIVER_DATA.picture = DEFAULT_PICTURE; RECEIVER_DATA.name = `${typeUserLogged >= USUARIO_CLIENTE ? 'Atendente ' : ''} ${nameUser} ${typeUserLogged < USUARIO_CLIENTE ? numProtocoloFormatado : ''}`; RECEIVER_DATA.id_cliente = obj.id_cliente; RECEIVER_DATA.id_atendente = obj.id_atendente; return RECEIVER_DATA; } } // TaskBot if (tableName == 'bot') { RECEIVER_DATA.id = 0; RECEIVER_DATA.picture = DEFAULT_PICTURE_BOT; RECEIVER_DATA.name = "TaskBot "; return RECEIVER_DATA; } // usuario ou departamento _GET('/App/Controller/Query/get.controller.php', { table_name: tableName, condition: ` AND id = ${receiverId}` }).done(data => { const response = JSON.parse(data); RECEIVER_DATA.id = response[0].id; RECEIVER_DATA.name = response[0].apelido || response[0].nome; RECEIVER_DATA.picture = response[0].src_foto ? URL_ARQUIVOS + '/' + response[0].src_foto : DEFAULT_PICTURE; }) return RECEIVER_DATA; } function saveChatSearchMessage(chatId, chatType) { localStorage.setItem('openChatSearchMessage', JSON.stringify({ chatId, chatType })); } // Função para carregar mensagens com funcionalidade de rolagem function loadMessagesScroll(chatId, chatType, callback) { let pointerPositionScroll; const formData = new FormData(); if (chatType == 'user') formData.append('idDestinatario', RECEIVER_DATA.id); else if (chatType == 'departament') formData.append('idDepartamento', RECEIVER_DATA.id); else if (chatType == 'bot') formData.append('idDestinatario', SENDER_DATA.id); else if (chatType == 'service') formData.append('idChatAtendimento', RECEIVER_DATA.id); else formData.append('idRemetente', SENDER_DATA.id); if (chatType == 'bot') formData.append('idRemetente', '-1'); else formData.append('idRemetente', SENDER_DATA.id); formData.append('scroll', offsetMessage); fetch(`/${URL_BASE}App/Controller/Chat/MessageAPI.php`, { method: 'POST', body: formData }) .then(req => req.json()) .then(messages => { const messagesData = messages.message; const amountMessages = messages.amountMessage; if (messages.message.length == 0) pointerPositionScroll = false; else pointerPositionScroll = true; processMessages(messagesData, true, false) .then((result) => { createDateMessageV2(result); }) .catch((error) => { console.error("Error processing messages:", error); }); // notification amountMessages.forEach(message => { let chatType; if (message.remetente == '-1') chatType = 'bot'; else if (message.remetente && message.destinatario) chatType = 'user'; else if (message.remetente && message.departamento) chatType = 'departament'; else if (!message.destinatario && !message.departamento) chatType = 'mural'; const messageData = { chatType, sender: { id: message.remetente }, receiver: { id: message.destinatario ? message.destinatario : message.departamento } } }) }).finally(() => { offsetMessage += 20; callback(pointerPositionScroll); }); } // Loop assíncrono para processar mensagens async function processMessages(messagesData, createData = true, positionScroll = false) { let childType; if (createData) childType = 'prepend'; else childType = 'append'; let dateArray = []; for (const [key, message] of messagesData.entries()) { const dateSplit = message.data_envio.split(' '); if (!dateArray.includes(dateSplit[0])) dateArray.push(dateSplit[0]); let nextKey = key + 1; let nextMessage = messagesData[nextKey]; if ((!nextMessage) || (nextMessage.id_remetente !== message.id_remetente)) { message.printPicture = true; } else { message.printPicture = false; } let messageData; if (createData) { messageData = createMessageData(message); } else { messageData = message; } await fillMessages(messageData, childType); if (positionScroll) cautiousScroll(); } let object = { child_type: childType, date_array: dateArray, qtd_msg: messagesData.length } return object; } // Loop assincrono para processar as mensagens do contexto da mensagem pesquisada // o usaAppend foi add para quando a msg precisa ser tratada como prepend mas inserida como append async function processSearchMessages(messagesData, createData = true, positionScroll = false, container = '.body-message', usaAppend = false) { let childType = 'prepend'; // Sempre vai ser prepend, pois são msg já enviadas let dateArray = []; for (const [key, message] of messagesData.entries()) { const dateSplit = message.data_envio.split(' '); if (!dateArray.includes(dateSplit[0])) dateArray.push(dateSplit[0]); let nextKey = key + 1; let nextMessage = messagesData[nextKey]; if ((!nextMessage) || (nextMessage.id_remetente !== message.id_remetente)) { message.printPicture = true; } else { message.printPicture = false; } let messageData; if (createData) { messageData = createMessageData(message); } else { messageData = message; } await fillSearchMessages(messageData, childType, container, usaAppend, true); if (positionScroll) cautiousScroll(); } let object = { child_type: childType, date_array: dateArray, qtd_msg: messagesData.length } return object; } function loadMessages(chatId, chatType) { document.querySelector('.body-message').innerHTML = ''; const formData = new FormData(); if (chatType == 'user') formData.append('idDestinatario', RECEIVER_DATA.id); else if (chatType == 'departament') formData.append('idDepartamento', RECEIVER_DATA.id); // mensagens enviadas pelo chatbot else if (chatType == 'bot') formData.append('idDestinatario', SENDER_DATA.id); else if (chatType == 'service') formData.append('idChatAtendimento', RECEIVER_DATA.id); // mensagens normais else formData.append('idRemetente', SENDER_DATA.id); // remetente if (chatType == 'bot') formData.append('idRemetente', '-1'); else formData.append('idRemetente', SENDER_DATA.id); formData.append('scroll', offsetMessage); const formDataQtdMsgNaoLidas = new FormData(); formDataQtdMsgNaoLidas.append('chatID', chatId); formDataQtdMsgNaoLidas.append('chatType', chatType); // Verifica a quantidade de novas mensagens na conversa fetch(`/${URL_BASE}App/Controller/Chat/QtdMsgNaoLidas.php`, { method: 'POST', body: formDataQtdMsgNaoLidas }).then(req => req.json()).then(res => { const qtdMsgNaoLidas = res[0].qtd_msg; if (qtdMsgNaoLidas > 20) { formData.append('trazerTodasNaoLidas', 1); formData.append('limit', qtdMsgNaoLidas); offsetMessage = qtdMsgNaoLidas; } else { formData.append('limit', 20); } fetch(`/${URL_BASE}App/Controller/Chat/MessageAPI.php`, { method: 'POST', body: formData }).then(req => req.json()).then(messages => { const messagesData = messages.message; const amountMessages = messages.amountMessage; let primeiraMsgNaoLida = null; if (qtdMsgNaoLidas > 0) { primeiraMsgNaoLida = messagesData[qtdMsgNaoLidas - 1]; } const idPrimeiraMsgNaoLida = (!primeiraMsgNaoLida) ? 0 : primeiraMsgNaoLida.id; processMessages(messagesData, true, true, idPrimeiraMsgNaoLida).then((result) => { createDateMessageV2(result); setTimeout(() => { qtdMsg = result.qtd_msg; chatDiv.scrollTop = chatDiv.scrollHeight; $('.body-message').css({ 'opacity': '1' }); if (idPrimeiraMsgNaoLida > 0) { // Adicionar indicador visual de novas mensagens não lidas adicionarIndicadorNovasMensagens(idPrimeiraMsgNaoLida, qtdMsgNaoLidas); // levar o usuário ao topo da primeira msg não lida. scrollToNovasMensagens(); } }, 200); }).catch((error) => { console.error("Error processing messages:", error); }); // notification amountMessages.forEach(message => { let chatType; if (message.id_remetente == '-1') chatType = 'bot'; else if (message.id_remetente && message.id_destinatario) chatType = 'user'; else if (message.id_remetente && message.id_departamento) chatType = 'departament'; else if (!message.id_destinatario && !message.id_departamento) chatType = 'mural'; const messageData = { chatType, sender: { id: message.id_remetente }, receiver: { id: message.id_destinatario ? message.id_destinatario : message.id_departamento } } }) }).finally(() => { $('#body-loading-message').removeClass('loading-move'); $('#is_loading').val('0'); $('#current_chat').val(chatId); $('#chat_type').val(chatType); // workaround pras opções da ultima mensagem aparecer corretamente scrollWorkaround('.body-message'); messageViewed(chatId, chatType); }); }); } // Função para preencher mensagens function fillMessages(messageData, childType) { const { chatType, chatId } = JSON.parse(localStorage.getItem('openChat')); // mensagem usuário if (messageData.chatType == 'user' && chatType == 'user') { // mensagem enviada para mim mesmo if (messageData.sender.id == SESSION_DATA.id && messageData.receiver.id == SESSION_DATA.id && chatId == SESSION_DATA.id) { messageSender(messageData, childType, false); } // mensagem recebida por um usuário else if (messageData.receiver.id == SESSION_DATA.id && chatId == messageData.sender.id) messageReceiver(messageData, childType, false); // mensagem enviada por mim else if (messageData.sender.id == SESSION_DATA.id && chatId == messageData.receiver.id) messageSender(messageData, childType, false); } // mensagem departamento ou mural if ((messageData.chatType == 'departament' && chatType == 'departament') || (messageData.chatType == 'mural' && chatType == 'mural')) { if ((chatId == messageData.receiver.id) || (chatId == null)) { if (messageData.sender.id == SESSION_DATA.id) { messageSender(messageData, childType, false); } else { messageReceiver(messageData, childType, false); } } } if (messageData.chatType == 'service' && chatType == 'service') { if (messageData.chatServiceId == chatId || messageData.receiver.id == chatId) { if (messageData.sender.id == SESSION_DATA.id) { messageSender(messageData, childType, false); } else { if (!messageData.skip) { messageReceiver(messageData, childType, false); } } } else { return; } } if (messageData.chatType == 'bot' && chatType == 'bot') messageNeutral(messageData, childType); } // Função para preencher mensagens vindo da modal de pesquisa // o usaAppend foi add para quando a msg precisa ser tratada como prepend mas inserida como append // o hiddenMenuMsg foi criado para ocultar o menu de reações e ações das msg na modal de pesquisa de mensagem. function fillSearchMessages(messageData, childType, container = '.body-message', usaAppend = false, hiddenMenuMsg = false) { const { chatType, chatId } = JSON.parse(localStorage.getItem('openChatSearchMessage')); // mensagem usuário if (messageData.chatType == 'user' && chatType == 'user') { // mensagem enviada para mim mesmo if (messageData.sender.id == SESSION_DATA.id && messageData.receiver.id == SESSION_DATA.id && chatId == SESSION_DATA.id) { messageSender(messageData, childType, false, container, usaAppend, hiddenMenuMsg); } // mensagem recebida por um usuário else if (messageData.receiver.id == SESSION_DATA.id && chatId == messageData.sender.id) messageReceiver(messageData, childType, false, container, usaAppend, hiddenMenuMsg); // mensagem enviada por mim else if (messageData.sender.id == SESSION_DATA.id && chatId == messageData.receiver.id) messageSender(messageData, childType, false, container, usaAppend, hiddenMenuMsg); } if ((messageData.chatType == 'departament' && chatType == 'departament') || (messageData.chatType == 'mural' && chatType == 'mural')) { if ((chatId == messageData.receiver.id) || (chatId == null)) { if (messageData.sender.id == SESSION_DATA.id) { messageSender(messageData, childType, false, container, usaAppend, hiddenMenuMsg); } else { messageReceiver(messageData, childType, false, container, usaAppend, hiddenMenuMsg); } } } if (messageData.chatType == 'bot' && chatType == 'bot') messageNeutral(messageData, childType); } // Função para criar dados da mensagem function createMessageData(message) { const reactions = (message.reacoes_nomes != null) ? JSON.parse(message.reacoes_nomes) : {}; const date = message.data_envio.split(' '); const hours = date[1].split(':'); const hoursSend = `${hours[0]}:${hours[1]}`; let chatType; if (message.chatType == '' || message.chatType == undefined) { if (message.id_chat_atendimento != null) chatType = 'service'; else if (message.id_remetente && message.id_destinatario) chatType = 'user'; else if (message.id_remetente && message.id_departamento) chatType = 'departament'; else if (!message.id_destinatario && !message.id_departamento) chatType = 'mural'; } else { chatType = message.chatType; } const messageData = { user_exclusao: message.user_exclusao, data_exclusao: message.data_exclusao, data_edicao: message.data_edicao, chatType, chatServiceId: message.id_chat_atendimento, clientServiceId: message.id_cliente_chat, attendantServiceId: message.id_atendente_chat, internalComment: message.comentario_interno, sender: { id: message.id_remetente, name: message.nome_remetente ? message.nome_remetente : 'Bot Ares Sistemas', picture: message.foto_remetente ? `${URL_ARQUIVOS}/${message.foto_remetente}` : (chatType == 'service' && message.id_remetente == '-1') ? '/' + URL_BASE + 'App/View/Public/images/ares_bot.png' : null, }, receiver: { id: message.id_destinatario || message.id_departamento, id_departamento: message.id_departamento, name: message.nome_destinatario, picture: message.foto_destinatario ? `${URL_ARQUIVOS}/${message.foto_remetente}` : null }, dataReply: { messageId: message.id_msg_respondida, message: message.msg_respondida, id_remetente: message.id_remet_msg_respondida, name_remetente: message.remet_msg_respondida, id_destinatario: message.id_dest_msg_respondida, id_departamento: message.id_dep_msg_respondida, arquivos: message.arquivos_msg_respondida, caminho_arquivo: message.caminho_arquivo_msg_respondida ? message.caminho_arquivo_msg_respondida : '{}', nome_arquivo: message.nome_arquivo_msg_respondida }, filesId: message.arquivos ? message.arquivos : '{}', files: message.caminho_arquivo ? message.caminho_arquivo : '{}', thumbId: message.thumbnails ? message.thumbnails : '{}', thumb: message.caminho_thumb ? message.caminho_thumb : '{}', nome_arquivo: message.nome_arquivo, printPicture: message.printPicture, hour: hoursSend, date: date[0], message: message.mensagem, messageId: message.id, favorite: message.favoritos ? message.favoritos : [], viewed: message.visualizado, reactions: reactions, shareId: message.id_encaminhamento, }; return messageData; } // Função para rolagem customizada do chat function customChatScroll() { const currentScroll = document.querySelector('.body-message').scrollTop; const totalScroll = document.querySelector('.body-message').scrollHeight - document.querySelector('.body-message').clientHeight; //if(currentScroll + 100 > totalScroll) document.querySelector('.body-message').scrollTop = 152000; }