UNPKG

1ff-chat-ui

Version:

chatbot to communicate with taught ai

193 lines (167 loc) 6 kB
import { UNSENT_MESSAGES_KEY } from './chat-ui'; import { rolesHTML } from './chat-widgets'; import { constructLink, replaceLinksWithAnchors, clearCarets, getTerm, removeUrl } from './helpers'; import { errorMessage, input, loadingDots, messages, resendButton } from './utils'; import { experimentsPrompt } from '../../src/lib/config/prompts-affected'; /** * Handles the start of the stream. * Hides loading dots, appends the current timestamp, and adds the assistant role element. * * @function onStreamStart * @memberof module:Chat * @returns {void} */ export function onStreamStart() { window.debugMode && console.log('stream-start'); loadingDots.hide(); const { element } = rolesHTML['assistant'](''); this.elements.messageIncrementor.appendChild(element); } /** * Handles the connect event. * It emits the 'chatHistory' event to request chat history for the user. * * @returns {void} */ export function onConnect() { window.debugMode && console.log(`Connected to ${this.url}, socket id: ${this.socket.id}`); this.socket.emit(this.events.chatHistory, { user_id: this.lastQuestionData.user_id, }); } /** * Handles the disconnect event. * * @returns {void} */ export function onDisconnect() { window.debugMode && console.log(`Disconnected from ${this.url}`); } /** * Handles the response from the server containing the chat history. * Prepends the initial assistant message to the history. * Updates the local storage with the updated history. * If the history is empty except for the initial message, displays loading dots and appends the initial message. * Otherwise, loads the existing messages from the history. * * @param {Object} res - The response object containing the chat history. * @returns {void} */ export function onChatHistory(res) { window.debugMode && console.log('onChatHistory: ', res); messages.clear(); errorMessage.hide(); loadingDots.hide(); this.refreshLocalStorageHistory(res.history); // when it is a fresh user without any history if (!res.history.length) { this.loadAssistantInitialMessage(); return; } // when it is an user with history load all its messages this.loadUserHistory(res.history); if (localStorage.getItem(UNSENT_MESSAGES_KEY)) { this.appendUnsentMessage(); } if (res.errors.length) { this.onError(); } } /** * Handles the data received in the stream. * Logs the received data in debug mode, refreshes the local storage history, * handles errors if present, processes the received text, and updates the last assistant message. * * @function onStreamData * @memberof module:Chat * @param {Object} data - The data received in the stream. * @param {Array} data.messages - The array of messages received. * @param {Array} data.errors - The array of errors received. * @returns {void} */ export function onStreamData(data) { window.debugMode && console.log('stream-data', data); const { messages, errors } = data; this.refreshLocalStorageHistory(messages); if (errors && errors.length) { this.onError(); return; } const lastMessageElement = this.getLastMessageElement('.assistant .js-assistant-message'); this.chunk = clearCarets(data.chunk); this.processTextInCaseOfSquareBrackets(); this.processTextInCaseOfCurlyBrackets(); this.processStringInCaseOfAngleBrackets(); !this.answersFromStream && (lastMessageElement.innerHTML += this.chunk); lastMessageElement.addClass('cursor'); } /** * Handles the end of the stream. * Performs necessary actions such as removing the last assistant message if it's empty, * adjusting CSS classes, * and showing/hiding the input based on the presence of answers. * * @function onStreamEnd * @memberof module:Chat * @returns {void} */ export function onStreamEnd() { window.debugMode && console.log('stream-end'); const lastMessageElement = this.getLastMessageElementConsistingMessage('.assistant'); const lastMessageTextContainer = lastMessageElement.querySelector('.js-assistant-message'); // in case stream ended with empty message, remove it from the DOM. lastMessageTextContainer.textContent === '' && lastMessageElement.remove(); lastMessageTextContainer.classList.remove('cursor'); this.hasAnswers = lastMessageElement.querySelector('.answers-container'); this.link = constructLink(lastMessageTextContainer.innerHTML); if (getTerm() === experimentsPrompt.finalPage) { lastMessageTextContainer.innerHTML = removeUrl(lastMessageTextContainer.innerHTML); } else { lastMessageTextContainer.innerHTML = replaceLinksWithAnchors(lastMessageTextContainer.innerHTML); } if (this.link) { this.setCtaButton(); return; } if (this.hasAnswers) { input.hide(this); return; } if ( !this.elements.ctaButton.classList.contains('hidden') || !this.elements.paymentButton.classList.contains('hidden') ) { return; } input.show(this); input.focus(this); } export function onStreamError(error) { window.debugMode && console.log('Stream error:', error); this.onError(); } /** * Emits a chat event to the socket server with the last question data. * If the socket is connected, it sends the data and adds loading dots to the message incrementor. * If the socket is disconnected, it hides the resend icon, triggers an error after a delay, and performs necessary UI updates. * * @returns {void} */ export function socketEmitChat(state) { resendButton.hideAll(); errorMessage.hide(); if (state.lastQuestionData.message) { if (state.socket.connected) { state.socket.emit(state.events.chat, state.lastQuestionData); window.debugMode && console.log('Emit chat: ', state.lastQuestionData); state.lastQuestionData.message = ''; localStorage.removeItem(UNSENT_MESSAGES_KEY); loadingDots.show(); } else { localStorage.setItem(UNSENT_MESSAGES_KEY, state.lastQuestionData.message); setTimeout(() => { state.onError(); }, 2000); } } }