Skip to content

Commit

Permalink
ability to add file messages via the changeMessage method
Browse files Browse the repository at this point in the history
  • Loading branch information
OvidijusParsiunas committed Dec 5, 2024
1 parent f9b2869 commit 7bf7ced
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 34 deletions.
10 changes: 8 additions & 2 deletions component/src/views/chat/messages/fileMessageUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@ export class FileMessageUtils {
public static readonly FILE_BUBBLE_CLASS = 'file-message';

// prettier-ignore
public static addMessage(
messages: MessagesBase, elements: MessageElements, styles: keyof MessageStyles, role: string, isTop: boolean) {
public static setElementProps(
messages: MessagesBase, elements: MessageElements, styles: keyof MessageStyles, role: string) {
if (styles === 'loading') return;
messages.applyCustomStyles(elements, role, true, messages.messageStyles?.[styles]);
elements.bubbleElement.classList.add(FileMessageUtils.FILE_BUBBLE_CLASS);
}

// prettier-ignore
public static addMessage(
messages: MessagesBase, elements: MessageElements, styles: keyof MessageStyles, role: string, isTop: boolean) {
FileMessageUtils.setElementProps(messages, elements, styles, role);
if (!isTop) {
messages.elementRef.appendChild(elements.outerContainer);
messages.elementRef.scrollTop = messages.elementRef.scrollHeight;
Expand Down
46 changes: 27 additions & 19 deletions component/src/views/chat/messages/fileMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,30 @@ import {MessageFile, MessageFiles} from '../../../types/messageFile';
import {SVGIconUtils} from '../../../utils/svg/svgIconUtils';
import {FILE_ICON_STRING} from '../../../icons/fileIcon';
import {Browser} from '../../../utils/browser/browser';
import {MessageStyles} from '../../../types/messages';
import {FileMessageUtils} from './fileMessageUtils';
import {MessageUtils} from './messageUtils';
import {Messages} from './messages';
import {MessagesBase} from './messagesBase';

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) {
private static createImage(imageData: MessageFile, messagesContainerEl: HTMLElement, isTop: boolean, scroll = true) {
const imageElement = new Image();
imageElement.src = imageData.src as string;
if (!isTop) FileMessageUtils.scrollDownOnImageLoad(imageElement.src, messagesContainerEl);
if (!isTop && scroll) FileMessageUtils.scrollDownOnImageLoad(imageElement.src, messagesContainerEl);
return FileMessageUtils.processContent('image', imageElement, imageElement.src, imageData.name);
}

// WORK - image still does not scroll down when loaded
private static async addNewImageMessage(messages: Messages, imageData: MessageFile, role: string, isTop: boolean) {
const image = FileMessages.createImage(imageData, messages.elementRef, isTop);
const elements = messages.createNewMessageElement('', role);
private static createImageMessage(msg: MessagesBase, imageD: MessageFile, role: string, isTop: boolean, scroll = true) {
const image = FileMessages.createImage(imageD, msg.elementRef, isTop, scroll);
const elements = msg.createNewMessageElement('', role);
elements.bubbleElement.appendChild(image);
elements.bubbleElement.classList.add(FileMessages.IMAGE_BUBBLE_CLASS);
FileMessageUtils.addMessage(messages, elements, 'image', role, isTop);
return {type: 'image', elements};
}

private static createAudioElement(audioData: MessageFile, role: string) {
Expand All @@ -41,12 +42,12 @@ export class FileMessages {
return audioElement;
}

private static addNewAudioMessage(messages: Messages, audioData: MessageFile, role: string, isTop: boolean) {
private static createNewAudioMessage(messages: MessagesBase, audioData: MessageFile, role: string, isTop: boolean) {
const audioElement = FileMessages.createAudioElement(audioData, role);
const elements = messages.createMessageElementsOnOrientation('', role, isTop);
elements.bubbleElement.appendChild(audioElement);
elements.bubbleElement.classList.add(FileMessages.AUDIO_BUBBLE_CLASS);
FileMessageUtils.addMessage(messages, elements, 'audio', role, isTop);
return {type: 'audio', elements};
}

private static createAnyFile(imageData: MessageFile) {
Expand All @@ -65,25 +66,32 @@ export class FileMessages {
return FileMessageUtils.processContent('any', contents, imageData.src, fileNameElement.textContent);
}

private static addNewAnyFileMessage(messages: Messages, data: MessageFile, role: string, isTop: boolean) {
private static createNewAnyFileMessage(messages: MessagesBase, data: MessageFile, role: string, isTop: boolean) {
const elements = messages.createMessageElementsOnOrientation('', role, isTop);
const anyFile = FileMessages.createAnyFile(data);
elements.bubbleElement.classList.add(FileMessages.ANY_FILE_BUBBLE_CLASS);
elements.bubbleElement.appendChild(anyFile);
FileMessageUtils.addMessage(messages, elements, 'file', role, isTop);
return {type: 'file', elements};
}

// no overwrite previous message logic as it is complex to track which files are to be overwritten
public static addMessages(messages: Messages, files: MessageFiles, role: string, isTop: boolean) {
files.forEach((fileData) => {
public static createMessages(msg: MessagesBase, files: MessageFiles, role: string, isTop = false, scroll = true) {
return files.map((fileData) => {
if (fileData.ref) fileData = FileMessageUtils.removeFileRef(fileData);
if (FileMessageUtils.isAudioFile(fileData)) {
FileMessages.addNewAudioMessage(messages, fileData, role, isTop);
} else if (FileMessageUtils.isImageFile(fileData)) {
FileMessages.addNewImageMessage(messages, fileData, role, isTop);
} else {
FileMessages.addNewAnyFileMessage(messages, fileData, role, isTop);
return FileMessages.createNewAudioMessage(msg, fileData, role, isTop);
}
if (FileMessageUtils.isImageFile(fileData)) {
return FileMessages.createImageMessage(msg, fileData, role, isTop, scroll);
}
return FileMessages.createNewAnyFileMessage(msg, fileData, role, isTop);
});
}

// no overwrite previous message logic as it is complex to track which files are to be overwritten
public static addMessages(messages: MessagesBase, files: MessageFiles, role: string, isTop: boolean) {
const typeToElements = FileMessages.createMessages(messages, files, role, isTop);
typeToElements.forEach(({type, elements}) => {
FileMessageUtils.addMessage(messages, elements, type as keyof MessageStyles, role, isTop);
});
}
}
54 changes: 41 additions & 13 deletions component/src/views/chat/messages/messageUtils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import {MessageBody, MessageBodyElements, MessageContentI, MessageToElements} from '../../../types/messagesInternal';
import {MessageContent, MessageStyles} from '../../../types/messages';
import {LoadingStyle} from '../../../utils/loading/loadingStyle';
import {MessageContent} from '../../../types/messages';
import {MessageFile} from '../../../types/messageFile';
import {FileMessageUtils} from './fileMessageUtils';
import {HTMLMessages} from './html/htmlMessages';
import {Avatars} from '../../../types/avatars';
import {MessagesBase} from './messagesBase';
import {FileMessages} from './fileMessages';
import {MessageElements} from './messages';
import {Names} from '../../../types/names';
import {Avatar} from './avatar';
Expand Down Expand Up @@ -196,6 +198,15 @@ export class MessageUtils {
});
}

private static removeTextHTMLMessage(msg: MessagesBase, messageToEls: MessageToElements[0], type: 'text' | 'html') {
const elemsToRemove = messageToEls[1][type];
const removalElsIndex = msg.messageElementRefs.findIndex((messageElements) => messageElements === elemsToRemove);
msg.messageElementRefs.splice(removalElsIndex, 1);
elemsToRemove?.outerContainer.remove();
delete messageToEls[0][type];
delete messageToEls[1][type];
}

private static changeHTMLMessage(msg: MessagesBase, messageToEls: MessageToElements[0], newHTML: string) {
if (messageToEls[1].html) {
HTMLMessages.overwriteElements(msg, newHTML, messageToEls[1].html);
Expand All @@ -211,21 +222,32 @@ export class MessageUtils {
messageToEls[0].html = newHTML;
}

private static removeTextMessage(msg: MessagesBase, messageToEls: MessageToElements[0]) {
const elemsToRemove = messageToEls[1].text;
const removalElsIndex = msg.messageElementRefs.findIndex((messageElements) => messageElements === elemsToRemove);
msg.messageElementRefs.splice(removalElsIndex, 1);
elemsToRemove?.outerContainer.remove();
delete messageToEls[0].text;
delete messageToEls[1].text;
private static changeFileMessages(msg: MessagesBase, messageToEls: MessageToElements[0], newFiles: MessageFile[]) {
const role = messageToEls[0].role;
const typeToElements = FileMessages.createMessages(msg, newFiles, role);
const beforeElement =
messageToEls[1].html?.outerContainer ||
messageToEls[1].files?.[messageToEls[1].files?.length - 1].outerContainer?.nextSibling ||
messageToEls[1].text?.outerContainer?.nextSibling;
typeToElements.forEach(({type, elements}) => {
FileMessageUtils.setElementProps(msg, elements, type as keyof MessageStyles, role);
msg.elementRef.insertBefore(elements.outerContainer, beforeElement as Node);
});
if (messageToEls[1].files) {
// remove the existing ones
} else {
// const nextMsgElsIndex = msg.messageElementRefs.findIndex((messageElements) => messageElements === nextElements);
}
messageToEls[1].files = typeToElements.map(({elements}) => elements);
messageToEls[0].files = newFiles;
}

private static changeTextMessage(msg: MessagesBase, messageToEls: MessageToElements[0], newText: string) {
if (messageToEls[1].text) {
msg.renderText(messageToEls[1].text.bubbleElement, newText);
} else {
const messageElements = msg.createElements(newText, messageToEls[0].role);
const nextElements = (messageToEls[1].html || messageToEls[1].files?.[0]) as MessageElements;
const nextElements = (messageToEls[1].files?.[0] || messageToEls[1].html) as MessageElements;
msg.elementRef.insertBefore(messageElements.outerContainer, nextElements.outerContainer);
const nextMsgElsIndex = msg.messageElementRefs.findIndex((messageElements) => messageElements === nextElements);
msg.messageElementRefs.splice(nextMsgElsIndex, 0, messageElements);
Expand All @@ -238,13 +260,19 @@ export class MessageUtils {
if (messageToEls) {
if (messageBody.text) {
MessageUtils.changeTextMessage(msg, messageToEls, messageBody.text);
} else if (messageToEls[1].text) {
MessageUtils.removeTextMessage(msg, messageToEls);
}
if (messageBody.html) {
MessageUtils.changeHTMLMessage(msg, messageToEls, messageBody.html);
} else if (messageToEls[1].text) {
// MessageUtils.removeTextMessage(msg, messageToEls);
}
if (messageBody.files) {
MessageUtils.changeFileMessages(msg, messageToEls, messageBody.files);
}
// Important to remove after elements are changed as existing element indexes are used
if (!messageBody.text && messageToEls[1].text) {
MessageUtils.removeTextHTMLMessage(msg, messageToEls, 'text');
}
if (!messageBody.html && messageToEls[1].html) {
MessageUtils.removeTextHTMLMessage(msg, messageToEls, 'html');
}
} else {
console.error('Message index not found. Please use the `getMessages` method to find the correct index');
Expand Down

0 comments on commit 7bf7ced

Please sign in to comment.