Skip to content

Commit

Permalink
refactoring messageToElements to include exact elements for each Mess…
Browse files Browse the repository at this point in the history
…ageBody types
  • Loading branch information
OvidijusParsiunas committed Nov 21, 2024
1 parent 4632e86 commit 3368027
Show file tree
Hide file tree
Showing 10 changed files with 74 additions and 26 deletions.
8 changes: 6 additions & 2 deletions component/src/types/messagesInternal.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import {MessageElements} from '../views/chat/messages/messages';
import {MessageFileType} from './messageFile';
import {MessageFile, MessageFileType} from './messageFile';
import {PropsRequired} from './utilityTypes';
import {MessageContent} from './messages';

export type MessageToElements = [MessageContentI, MessageElements[]][];
export type MessageBody = {text?: string; files?: MessageFile[]; html?: string};

export type MessageBodyElements = {text?: MessageElements; files?: MessageElements[]; html?: MessageElements};

export type MessageToElements = [MessageContentI, MessageBodyElements][];

export type MessageContentI = PropsRequired<MessageContent, 'role'>;

Expand Down
2 changes: 2 additions & 0 deletions component/src/views/chat/messages/fileMessageUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import {MessageElements} from './messages';

export class FileMessageUtils {
public static readonly DEFAULT_FILE_NAME = 'file';
public static readonly FILE_BUBBLE_CLASS = 'file-message';

// prettier-ignore
public static addMessage(
messages: MessagesBase, elements: MessageElements, styles: keyof MessageStyles, role: string, isTop: boolean) {
if (styles === 'loading') return;
messages.applyCustomStyles(elements, role, true, messages.messageStyles?.[styles]);
elements.bubbleElement.classList.add(FileMessageUtils.FILE_BUBBLE_CLASS);
if (!isTop) {
messages.elementRef.appendChild(elements.outerContainer);
messages.elementRef.scrollTop = messages.elementRef.scrollHeight;
Expand Down
10 changes: 7 additions & 3 deletions component/src/views/chat/messages/fileMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import {MessageUtils} from './messageUtils';
import {Messages} from './messages';

export class FileMessages {
private static readonly IMAGE_BUBBLE_CLASS = 'image-message';
private static readonly AUDIO_BUBBLE_CLASS = 'audio-message';
private static readonly ANY_FILE_BUBBLE_CLASS = 'any-file-message';

private static createImage(imageData: MessageFile, messagesContainerEl: HTMLElement, isTop: boolean) {
const imageElement = new Image();
imageElement.src = imageData.src as string;
Expand All @@ -19,7 +23,7 @@ export class FileMessages {
const image = FileMessages.createImage(imageData, messages.elementRef, isTop);
const elements = messages.createNewMessageElement('', role);
elements.bubbleElement.appendChild(image);
elements.bubbleElement.classList.add('image-message');
elements.bubbleElement.classList.add(FileMessages.IMAGE_BUBBLE_CLASS);
FileMessageUtils.addMessage(messages, elements, 'image', role, isTop);
}

Expand All @@ -41,7 +45,7 @@ export class FileMessages {
const audioElement = FileMessages.createAudioElement(audioData, role);
const elements = messages.createMessageElementsOnOrientation('', role, isTop);
elements.bubbleElement.appendChild(audioElement);
elements.bubbleElement.classList.add('audio-message');
elements.bubbleElement.classList.add(FileMessages.AUDIO_BUBBLE_CLASS);
FileMessageUtils.addMessage(messages, elements, 'audio', role, isTop);
}

Expand All @@ -64,7 +68,7 @@ export class FileMessages {
private static addNewAnyFileMessage(messages: Messages, data: MessageFile, role: string, isTop: boolean) {
const elements = messages.createMessageElementsOnOrientation('', role, isTop);
const anyFile = FileMessages.createAnyFile(data);
elements.bubbleElement.classList.add('any-file-message-bubble');
elements.bubbleElement.classList.add(FileMessages.ANY_FILE_BUBBLE_CLASS);
elements.bubbleElement.appendChild(anyFile);
FileMessageUtils.addMessage(messages, elements, 'file', role, isTop);
}
Expand Down
4 changes: 2 additions & 2 deletions component/src/views/chat/messages/history/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ export class History {
if (message) {
const messageContent = addAnyMessage({...message, sendUpdate: true}, true, true);
if (messageContent) {
const num = MessageUtils.getNumberOfElements(messageContent);
messageToElements.unshift([messageContent, messageElementRefs.slice(0, num)]);
const messageBody = MessageUtils.generateMessageBody(messageContent, messageElementRefs);
messageToElements.unshift([messageContent, messageBody]);
}
return messageContent;
} else {
Expand Down
8 changes: 6 additions & 2 deletions component/src/views/chat/messages/html/htmlMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,25 @@ import {MessageElements} from '../messages';
import {HTMLUtils} from './htmlUtils';

export class HTMLMessages {
public static readonly HTML_BUBBLE_CLASS = 'html-message';

private static addElement(messages: MessagesBase, outerElement: HTMLElement) {
messages.elementRef.appendChild(outerElement);
messages.elementRef.scrollTop = messages.elementRef.scrollHeight;
}

public static createElements(messages: MessagesBase, html: string, role: string, isTop: boolean) {
const messageElements = messages.createMessageElementsOnOrientation('', role, isTop);
messageElements.bubbleElement.classList.add('html-message');
messageElements.bubbleElement.classList.add(HTMLMessages.HTML_BUBBLE_CLASS);
messageElements.bubbleElement.innerHTML = html;
return messageElements;
}

// prettier-ignore
private static overwrite(messages: MessagesBase, html: string, role: string, messagesEls: MessageElements[]) {
const {messageToElements: msgToEls} = messages;
const overwrittenElements = MessageUtils.overwriteMessage(msgToEls, messagesEls, html, role, 'html', 'html-message');
const overwrittenElements = MessageUtils.overwriteMessage(
msgToEls, messagesEls, html, role, 'html', HTMLMessages.HTML_BUBBLE_CLASS);
if (overwrittenElements) {
overwrittenElements.bubbleElement.innerHTML = html;
HTMLUtils.apply(messages, overwrittenElements.outerContainer);
Expand Down
31 changes: 29 additions & 2 deletions component/src/views/chat/messages/messageUtils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import {MessageContentI, MessageToElements} from '../../../types/messagesInternal';
import {MessageBodyElements, MessageContentI, MessageToElements} from '../../../types/messagesInternal';
import {LoadingStyle} from '../../../utils/loading/loadingStyle';
import {MessageContent} from '../../../types/messages';
import {FileMessageUtils} from './fileMessageUtils';
import {HTMLMessages} from './html/htmlMessages';
import {Avatars} from '../../../types/avatars';
import {MessagesBase} from './messagesBase';
import {MessageElements} from './messages';
import {Names} from '../../../types/names';
import {Avatar} from './avatar';
Expand Down Expand Up @@ -133,14 +136,38 @@ export class MessageUtils {
messageEls.outerContainer.classList.add(...classes);
}

public static getNumberOfElements(messageContent: MessageContentI) {
private static getNumberOfElements(messageContent: MessageContentI) {
let length = 0;
if (messageContent.text !== undefined) length += 1;
if (messageContent.html !== undefined) length += 1;
if (messageContent.files) length += messageContent.files.length;
return length;
}

private static filterdMessageElements(elements: MessageElements[], className: string) {
return elements.filter((msgElements) => msgElements.bubbleElement.classList.contains(className));
}

private static findMessageElements(elements: MessageElements[], className: string) {
return elements.find((msgElements) => msgElements.bubbleElement.classList.contains(className));
}

private static generateMessageBodyElements(messageContent: MessageContentI, elements: MessageElements[]) {
const msgBodyEls: MessageBodyElements = {};
if (messageContent.text) msgBodyEls.text = MessageUtils.findMessageElements(elements, MessagesBase.TEXT_BUBBLE_CLASS);
if (messageContent.html) msgBodyEls.html = MessageUtils.findMessageElements(elements, HTMLMessages.HTML_BUBBLE_CLASS);
if (messageContent.files) {
msgBodyEls.files = MessageUtils.filterdMessageElements(elements, FileMessageUtils.FILE_BUBBLE_CLASS);
}
return msgBodyEls;
}

public static generateMessageBody(messageContent: MessageContentI, messageElementRefs: MessageElements[]) {
const numberOfMessageContentElement = MessageUtils.getNumberOfElements(messageContent);
const elements = messageElementRefs.slice(messageElementRefs.length - numberOfMessageContentElement);
return MessageUtils.generateMessageBodyElements(messageContent, elements);
}

public static classifyMessages(role: string, messageElementRefs: MessageElements[]) {
const currentRole = MessageUtils.buildRoleContainerClass(role);
messageElementRefs.forEach((messageEls, index) => {
Expand Down
2 changes: 1 addition & 1 deletion component/src/views/chat/messages/messages.css
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ pre {
float: right;
}

.any-file-message-bubble {
.any-file-message {
padding: 1px;
}

Expand Down
14 changes: 7 additions & 7 deletions component/src/views/chat/messages/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,8 @@ export class Messages extends MessagesBase {

private updateStateOnMessage(messageContent: MessageContentI, overwritten?: boolean, update = true, isHistory = false) {
if (!overwritten) {
const num = MessageUtils.getNumberOfElements(messageContent);
this.messageToElements.push([messageContent, this.messageElementRefs.slice(this.messageElementRefs.length - num)]);
const messageBody = MessageUtils.generateMessageBody(messageContent, this.messageElementRefs);
this.messageToElements.push([messageContent, messageBody]);
}
if (update) this.sendClientUpdate(messageContent, isHistory);
}
Expand Down Expand Up @@ -338,11 +338,11 @@ export class Messages extends MessagesBase {
});
this.messageElementRefs = retainedElements;
const retainedMessageToElements = this.messageToElements.filter((msgToEls) => {
if (msgToEls[0].text !== undefined || msgToEls[0].html !== undefined) {
// safe because streamed messages can't contain multiple props (text, html)
return msgToEls[1].find((els) => Messages.isActiveElement(els.bubbleElement.classList));
}
return false;
// safe because streamed messages can't contain multiple props (text, html)
return (
(msgToEls[1].text && Messages.isActiveElement(msgToEls[1].text.bubbleElement.classList)) ||
(msgToEls[1].html && Messages.isActiveElement(msgToEls[1].html.bubbleElement.classList))
);
});
this.messageToElements.splice(0, this.messageToElements.length, ...retainedMessageToElements);
if (isReset !== false) {
Expand Down
19 changes: 13 additions & 6 deletions component/src/views/chat/messages/messagesBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export class MessagesBase {
protected readonly _names?: Names;
private _remarkable: Remarkable;
private readonly _onMessage?: (message: MessageContentI, isHistory: boolean) => void;
public static readonly TEXT_BUBBLE_CLASS = 'text-message';

constructor(deepChat: DeepChat) {
this.elementRef = MessagesBase.createContainerElement();
Expand All @@ -42,7 +43,7 @@ export class MessagesBase {
this._onMessage = FireEvents.onMessage.bind(this, deepChat);
if (deepChat.htmlClassUtilities) this.htmlClassUtilities = deepChat.htmlClassUtilities;
setTimeout(() => {
this.submitUserMessage = deepChat.submitUserMessage; // wait for it to be available
this.submitUserMessage = deepChat.submitUserMessage; // wait for it to be available in input.ts
});
}

Expand All @@ -61,14 +62,16 @@ export class MessagesBase {
const messageElements = isTop
? this.createAndPrependNewMessageElement(text, role, isTop)
: this.createAndAppendNewMessageElement(text, role);
messageElements.bubbleElement.classList.add('text-message');
messageElements.bubbleElement.classList.add(MessagesBase.TEXT_BUBBLE_CLASS);
this.applyCustomStyles(messageElements, role, false);
MessageUtils.fillEmptyMessageElement(messageElements.bubbleElement, text);
return messageElements;
}

// prettier-ignore
private overwriteText(role: string, text: string, elementRefs: MessageElements[]) {
const elems = MessageUtils.overwriteMessage(this.messageToElements, elementRefs, text, role, 'text', 'text-message');
const elems = MessageUtils.overwriteMessage(
this.messageToElements, elementRefs, text, role, 'text', MessagesBase.TEXT_BUBBLE_CLASS);
if (elems) this.renderText(elems.bubbleElement, text);
return elems;
}
Expand Down Expand Up @@ -128,11 +131,16 @@ export class MessagesBase {
return MessagesBase.isLoadingMessage(elements) || HTMLDeepChatElements.isElementTemporary(elements);
}

public createMessageElements(text: string, role: string, isTop = false) {
private createElements(text: string, role: string) {
const messageElements = MessagesBase.createBaseElements(role);
const {outerContainer, innerContainer, bubbleElement} = messageElements;
outerContainer.appendChild(innerContainer);
this.addInnerContainerElements(bubbleElement, text, role);
return messageElements;
}

public createMessageElements(text: string, role: string, isTop = false) {
const messageElements = this.createElements(text, role);
MessageUtils.updateRefArr(this.messageElementRefs, messageElements, isTop);
MessageUtils.classifyMessages(role, this.messageElementRefs);
return messageElements;
Expand Down Expand Up @@ -218,8 +226,7 @@ export class MessagesBase {
protected refreshTextMessages() {
this._remarkable = RemarkableConfig.createNew();
this.messageToElements.forEach((msgToEls) => {
// important for text bubble to be first if multiple message content properties are used
if (msgToEls[0].text) this.renderText(msgToEls[1][0].bubbleElement, msgToEls[0].text);
if (msgToEls[1].text && msgToEls[0].text) this.renderText(msgToEls[1].text.bubbleElement, msgToEls[0].text);
});
}
}
2 changes: 1 addition & 1 deletion component/src/views/chat/messages/stream/messageStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export class MessageStream {
this._elements.bubbleElement.classList.add(MessageStream.MESSAGE_CLASS);
this._activeMessageRole = role;
this._message = {role: this._activeMessageRole, [streamType]: content};
this._messages.messageToElements.push([this._message, [this._elements]]);
this._messages.messageToElements.push([this._message, {[streamType]: this._elements}]);
}

private updateBasedOnType(content: string, expectedType: string, bubbleElement: HTMLElement, isOverwrite = false) {
Expand Down

0 comments on commit 3368027

Please sign in to comment.