UNPKG

@plteam/chat-ui

Version:

CUI Kit is a free and open-source library for creating AI assistant chat interfaces, built with React, Material UI, and TypeScript

121 lines (119 loc) 4.85 kB
import { ObservableReactValue } from '../utils/observers/ObservableReactValue'; import { arrayLast } from '../utils/arrayUtils/arrayLast'; import { isDefined } from '../utils/isDefined'; import { ThreadBranchMapManager } from './ThreadBranchMapManager'; const rootMessageHash = 'rootMessage'; export class ThreadMessages { allMessages = new ObservableReactValue([]); currentMessages = new ObservableReactValue([]); internalMessageTransformer; getCurrentBranchFn; branchMapManager = new ThreadBranchMapManager(); get allMessagesArray() { return this.allMessages.value; } // Remember which branch we stopped at, so that upon re-entering the thread, reopens it _lastMessage; _callbackInitiated = false; init = (enableBranches) => { if (this._callbackInitiated) return; const map = this._createNewMap(this.allMessagesArray); this._updateBranch(map, this._lastMessage, enableBranches); // reaction to adding to the array of all messages to update the thread this.allMessages.subscribe(() => { const map = this._createNewMap(this.allMessagesArray); this._updateBranch(map, arrayLast(this.allMessagesArray), enableBranches); }); }; changeBranchesStatus = (enableBranches) => { const map = this._createNewMap(this.allMessagesArray); this._updateBranch(map, this._lastMessage, enableBranches); }; handleChangeBranch = (message) => { const parentId = message.parentId ?? rootMessageHash; const map = this._createNewMap(this.allMessagesArray); map.set(parentId, { messages: [message] }); // console.log(message, messagesParentMap); this._updateBranch(map, message, true); }; _updateBranch = (map, startFrom, enableBranches) => { if (enableBranches !== true) { this.currentMessages.setValue(this.allMessagesArray); return; } if (!startFrom) startFrom = this._lastMessage; let newBranch = []; if (this.getCurrentBranchFn) { newBranch = this.getCurrentBranchFn(startFrom ? this.internalMessageTransformer(startFrom) : undefined) .map(v => this.allMessagesArray.find(m => m.id === v.id)) .filter(isDefined); } else { const rootMessages = startFrom ? [startFrom] : map.get(rootMessageHash)?.messages ?? []; const branches = []; for (const rootMessage of rootMessages) { branches.push(this._getBranchRecursive(rootMessage, map)); } const topItems = branches.map(b => arrayLast(b)) .filter(isDefined); const topItem = arrayLast(topItems); /*console.log( branches.length, sortByDesc(branches[0]?.slice() ?? [], 'time').map((v) => ({ text: v.text, pid: v.parentId, id: v.id, time: v.time - 1734000000, })) ); console.log(topItem, createTree(topItem, thread.messages));*/ newBranch = topItem?.parentId ? this._createTree(topItem, this.allMessagesArray) : this.allMessagesArray; } this.currentMessages.setValue(newBranch); this._lastMessage = arrayLast(newBranch); return this._lastMessage; }; _getBranchRecursive = (parent, map) => { const branch = [parent]; const parentMap = map.get(parent.id); if (parentMap) { // все ради цикла :) const darkness = parentMap.messages; for (const child of darkness) { branch.push(...this._getBranchRecursive(child, map)); } } return branch; }; _createTree = (lastItem, messages) => { const branch = []; let nextItem = lastItem; while (nextItem) { branch.push(nextItem); if (nextItem.parentId) { nextItem = messages.find(m => m.id === nextItem.parentId); } else { nextItem = undefined; } } return branch.reverse(); }; _createNewMap = (messages) => { // not needed, since we don't use the map if getCurrentBranchFn provided if (this.getCurrentBranchFn) return new Map(); return this.branchMapManager.createMap(messages); }; push = (...newMessages) => { this.allMessages.setValue([ ...this.allMessages.value, ...newMessages, ]); }; get length() { return this.allMessages.value.length; } }