diff --git a/plugins/plugins/core/components/ChatPage.js b/plugins/plugins/core/components/ChatPage.js index b3d65f65..e14fe629 100644 --- a/plugins/plugins/core/components/ChatPage.js +++ b/plugins/plugins/core/components/ChatPage.js @@ -55,7 +55,7 @@ const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) export const queue = new RequestQueue(); - +export const chatLimit = 40 class ChatPage extends LitElement { static get properties() { return { @@ -81,7 +81,7 @@ class ChatPage extends LitElement { hideNewMessageBar: { attribute: false }, setOpenPrivateMessage: { attribute: false }, chatEditorPlaceholder: { type: String }, - messagesRendered: { type: Array }, + messagesRendered: { type: Object }, repliedToMessageObj: { type: Object }, editedMessageObj: { type: Object }, iframeHeight: { type: Number }, @@ -120,7 +120,8 @@ class ChatPage extends LitElement { gifsLoading: { type: Boolean }, goToRepliedMessage: { attribute: false }, isLoadingGoToRepliedMessage: { type: Object }, - updateMessageHash: { type: Object} + updateMessageHash: { type: Object}, + oldMessages: {type: Array} } } @@ -1309,7 +1310,10 @@ class ChatPage extends LitElement { this.isUserDown = false this.isPasteMenuOpen = false this.chatEditorPlaceholder = "" - this.messagesRendered = [] + this.messagesRendered = { + messages: [], + type: '' + } this.repliedToMessageObj = null this.editedMessageObj = null this.iframeHeight = 42 @@ -1360,6 +1364,7 @@ class ChatPage extends LitElement { this.updateMessageHash = {} this.addToUpdateMessageHashmap = this.addToUpdateMessageHashmap.bind(this) this.getAfterMessages = this.getAfterMessages.bind(this) + this.oldMessages = [] } setOpenGifModal(value) { @@ -2510,6 +2515,7 @@ class ChatPage extends LitElement { this.goToRepliedMessage(val, val2)} - .getOldMessageAfter=${(val) => this.getOldMessageAfter(val)} .updateMessageHash=${this.updateMessageHash} > @@ -2691,7 +2696,7 @@ class ChatPage extends LitElement { if (this.isReceipient) { const getInitialMessages = await parentEpml.request('apiCall', { type: 'api', - url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${this._chatId}&limit=20&reverse=true&before=${scrollElement.messageObj.timestamp}&haschatreference=false&encoding=BASE64` + url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${this._chatId}&limit=${chatLimit}&reverse=true&before=${scrollElement.messageObj.timestamp}&haschatreference=false&encoding=BASE64` }) let decodeMsgs = [] await new Promise((res, rej) => { @@ -2718,22 +2723,26 @@ class ChatPage extends LitElement { addToUpdateMessageHashmap: this.addToUpdateMessageHashmap })); - let list = [...decodeMsgs, ...this.messagesRendered.slice(0,80)] + let list = [...decodeMsgs] - await new Promise((res, rej) => { + // await new Promise((res, rej) => { - this.webWorkerSortMessages.postMessage({list}); + // this.webWorkerSortMessages.postMessage({list}); - this.webWorkerSortMessages.onmessage = e => { - console.log('e',e) + // this.webWorkerSortMessages.onmessage = e => { + // console.log('e',e) - list = e.data - res() + // list = e.data + // res() - } - }) + // } + // }) - this.messagesRendered = list + this.messagesRendered = { + messages: list, + type: 'old', + el: scrollElement + } this.isLoadingOldMessages = false await this.getUpdateComplete() @@ -2747,7 +2756,7 @@ class ChatPage extends LitElement { } else { const getInitialMessages = await parentEpml.request('apiCall', { type: 'api', - url: `/chat/messages?txGroupId=${Number(this._chatId)}&limit=20&reverse=true&before=${scrollElement.messageObj.timestamp}&haschatreference=false&encoding=BASE64` + url: `/chat/messages?txGroupId=${Number(this._chatId)}&limit=${chatLimit}&reverse=true&before=${scrollElement.messageObj.timestamp}&haschatreference=false&encoding=BASE64` }) let decodeMsgs = [] @@ -2777,23 +2786,27 @@ class ChatPage extends LitElement { _publicKey: this._publicKey, addToUpdateMessageHashmap: this.addToUpdateMessageHashmap })); - let list = [...decodeMsgs, ...this.messagesRendered.slice(0,80)] + let list = [...decodeMsgs] - await new Promise((res, rej) => { + // await new Promise((res, rej) => { - this.webWorkerSortMessages.postMessage({list}); + // this.webWorkerSortMessages.postMessage({list}); - this.webWorkerSortMessages.onmessage = e => { - console.log('e',e) + // this.webWorkerSortMessages.onmessage = e => { + // console.log('e',e) - list = e.data - res() + // list = e.data + // res() - } - }) + // } + // }) - this.messagesRendered = list - this.isLoadingOldMessages = false + this.messagesRendered = { + messages: list, + type: 'old', + el: scrollElement + } + // this.isLoadingOldMessages = false await this.getUpdateComplete() const marginElements = Array.from(this.shadowRoot.querySelector('chat-scroller').shadowRoot.querySelectorAll('message-template')) const findElement = marginElements.find((item) => item.messageObj.signature === scrollElement.messageObj.signature) @@ -2804,7 +2817,6 @@ class ChatPage extends LitElement { } } async getAfterMessages(scrollElement) { - const firstMsg = this.messagesRendered.at(-1) const timestamp = scrollElement.messageObj.timestamp if (this.isReceipient) { @@ -2839,7 +2851,7 @@ class ChatPage extends LitElement { addToUpdateMessageHashmap: this.addToUpdateMessageHashmap })); - let list = [...this.messagesRendered.slice(-80), ...decodeMsgs] + let list = [ ...decodeMsgs] await new Promise((res, rej) => { @@ -2854,7 +2866,11 @@ class ChatPage extends LitElement { } }) - this.messagesRendered = list + this.messagesRendered = { + messages: list, + type: 'new' + } + this.isLoadingOldMessages = false await this.getUpdateComplete() @@ -2898,7 +2914,7 @@ class ChatPage extends LitElement { - let list = [...this.messagesRendered.slice(-80), ...decodeMsgs] + let list = [...decodeMsgs] await new Promise((res, rej) => { @@ -2913,7 +2929,10 @@ class ChatPage extends LitElement { } }) - this.messagesRendered = list + this.messagesRendered = { + messages: list, + type: 'new' + } this.isLoadingOldMessages = false @@ -2949,127 +2968,7 @@ const originalScrollHeight = viewElement.scrollHeight; viewElement.scrollTop = originalScrollTop + heightDifference; } - async getOldMessageAfter(scrollElement) { - if (this.isReceipient) { - const getInitialMessages = await parentEpml.request('apiCall', { - type: 'api', - url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${this._chatId}&limit=20&reverse=true&afer=${scrollElement.messageObj.timestamp}&haschatreference=false&encoding=BASE64` - }) - - let decodeMsgs = [] - await new Promise((res, rej) => { - this.webWorkerDecodeMessages.postMessage({messages: getInitialMessages, isReceipient: this.isReceipient, _publicKey: this._publicKey, privateKey: window.parent.reduxStore.getState().app.selectedAddress.keyPair.privateKey }); - - this.webWorkerDecodeMessages.onmessage = e => { - decodeMsgs = e.data - res() - - } - this.webWorkerDecodeMessages.onerror = e => { - console.log('e',e) - rej() - - } - }) - - - queue.push(() => replaceMessagesEdited({ - decodedMessages: decodeMsgs, - parentEpml, - isReceipient: this.isReceipient, - decodeMessageFunc: this.decodeMessage, - _publicKey: this._publicKey, - addToUpdateMessageHashmap: this.addToUpdateMessageHashmap - })); - - - let list = [...this.messagesRendered.slice(-80), ...decodeMsgs] - - await new Promise((res, rej) => { - - this.webWorkerSortMessages.postMessage({list}); - - this.webWorkerSortMessages.onmessage = e => { - console.log('e',e) - - list = e.data - res() - - } - }) - - this.messagesRendered = list - - - this.isLoadingOldMessages = false - await this.getUpdateComplete() - const marginElements = Array.from(this.shadowRoot.querySelector('chat-scroller').shadowRoot.querySelectorAll('message-template')) - - const findElement = marginElements.find((item) => item.messageObj.signature === scrollElement.messageObj.signature) - - if (findElement) { - findElement.scrollIntoView({ behavior: 'auto', block: 'center' }) - } - } else { - const getInitialMessages = await parentEpml.request('apiCall', { - type: 'api', - url: `/chat/messages?txGroupId=${Number(this._chatId)}&limit=20&reverse=true&after=${scrollElement.messageObj.timestamp}&haschatreference=false&encoding=BASE64` - }) - - let decodeMsgs = [] - await new Promise((res, rej) => { - this.webWorkerDecodeMessages.postMessage({messages: getInitialMessages, isReceipient: this.isReceipient, _publicKey: this._publicKey, privateKey: window.parent.reduxStore.getState().app.selectedAddress.keyPair.privateKey }); - - this.webWorkerDecodeMessages.onmessage = e => { - decodeMsgs = e.data - res() - - } - this.webWorkerDecodeMessages.onerror = e => { - console.log('e',e) - rej() - - } - }) - - - queue.push(() => replaceMessagesEdited({ - decodedMessages: decodeMsgs, - parentEpml, - isReceipient: this.isReceipient, - decodeMessageFunc: this.decodeMessage, - _publicKey: this._publicKey, - addToUpdateMessageHashmap: this.addToUpdateMessageHashmap - })); - - - let list = [...this.messagesRendered.slice(-80), ...decodeMsgs] - - await new Promise((res, rej) => { - - this.webWorkerSortMessages.postMessage({list}); - - this.webWorkerSortMessages.onmessage = e => { - console.log('e',e) - - list = e.data - res() - - } - }) - - this.messagesRendered = list - - this.isLoadingOldMessages = false - await this.getUpdateComplete() - const marginElements = Array.from(this.shadowRoot.querySelector('chat-scroller').shadowRoot.querySelectorAll('message-template')) - const findElement = marginElements.find((item) => item.messageObj.signature === scrollElement.messageObj.signature) - - if (findElement) { - findElement.scrollIntoView({ behavior: 'auto', block: 'center' }) - } - } - } + async processMessages(messages, isInitial) { const isReceipient = this.chatId.includes('direct') @@ -3133,7 +3032,10 @@ viewElement.scrollTop = originalScrollTop + heightDifference; // TODO: Determine number of initial messages by screen height... // this.messagesRendered = this._messages - this.messagesRendered = this._messages + this.messagesRendered = { + messages: this._messages, + type: 'initial' + } this.isLoadingMessages = false setTimeout(() => this.downElementObserver(), 500) @@ -3337,13 +3239,13 @@ viewElement.scrollTop = originalScrollTop + heightDifference; const lastMessage = cachedData[cachedData.length - 1] const newMessages = await parentEpml.request('apiCall', { type: 'api', - url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}&limit=20&reverse=true&after=${lastMessage.timestamp}&haschatreference=false&encoding=BASE64` + url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}&limit=${chatLimit}&reverse=true&after=${lastMessage.timestamp}&haschatreference=false&encoding=BASE64` }) - getInitialMessages = [...cachedData, ...newMessages].slice(-20) + getInitialMessages = [...newMessages] } else { getInitialMessages = await parentEpml.request('apiCall', { type: 'api', - url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}&limit=20&reverse=true&haschatreference=false&encoding=BASE64` + url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}&limit=${chatLimit}&reverse=true&haschatreference=false&encoding=BASE64` }) } @@ -3437,15 +3339,16 @@ viewElement.scrollTop = originalScrollTop + heightDifference; const newMessages = await parentEpml.request('apiCall', { type: 'api', - url: `/chat/messages?txGroupId=${groupId}&limit=20&reverse=true&after=${lastMessage.timestamp}&haschatreference=false&encoding=BASE64` + url: `/chat/messages?txGroupId=${groupId}&limit=${chatLimit}&reverse=true&after=${lastMessage.timestamp}&haschatreference=false&encoding=BASE64` }) - getInitialMessages = [...cachedData, ...newMessages].slice(-20) - } else { + getInitialMessages = [...newMessages] + }else { getInitialMessages = await parentEpml.request('apiCall', { type: 'api', - url: `/chat/messages?txGroupId=${groupId}&limit=20&reverse=true&haschatreference=false&encoding=BASE64` + url: `/chat/messages?txGroupId=${groupId}&limit=${chatLimit}&reverse=true&haschatreference=false&encoding=BASE64` }) - } + + } this.processMessages(getInitialMessages, true) diff --git a/plugins/plugins/core/components/ChatScroller.js b/plugins/plugins/core/components/ChatScroller.js index a3afe95d..9ef56e2e 100644 --- a/plugins/plugins/core/components/ChatScroller.js +++ b/plugins/plugins/core/components/ChatScroller.js @@ -166,15 +166,53 @@ function processText(input) { return wrapper } +const formatMessages = (messages) => { + const formattedMessages = messages.reduce((messageArray, message) => { + const currentMessage = message; + const lastGroupedMessage = messageArray[messageArray.length - 1]; + + currentMessage.firstMessageInChat = messageArray.length === 0; + + let timestamp, sender, repliedToData; + + if (lastGroupedMessage) { + timestamp = lastGroupedMessage.timestamp; + sender = lastGroupedMessage.sender; + repliedToData = lastGroupedMessage.repliedToData; + } else { + timestamp = currentMessage.timestamp; + sender = currentMessage.sender; + repliedToData = currentMessage.repliedToData; + } + + const isSameGroup = Math.abs(timestamp - currentMessage.timestamp) < 600000 && + sender === currentMessage.sender && + !repliedToData; + + if (isSameGroup && lastGroupedMessage) { + lastGroupedMessage.messages.push(currentMessage); + } else { + messageArray.push({ + messages: [currentMessage], + ...currentMessage + }); + } + + return messageArray; + }, []); + + return formattedMessages +} + class ChatScroller extends LitElement { static get properties() { return { theme: { type: String, reflect: true }, getNewMessage: { attribute: false }, getOldMessage: { attribute: false }, - getAfterMessages: {attribute: false}, + getAfterMessages: { attribute: false }, escapeHTML: { attribute: false }, - messages: { type: Array }, + messages: { type: Object }, hideMessages: { type: Array }, setRepliedToMessageObj: { attribute: false }, setEditedMessageObj: { attribute: false }, @@ -196,9 +234,10 @@ class ChatScroller extends LitElement { userName: { type: String }, selectedHead: { type: Object }, goToRepliedMessage: { attribute: false }, - getOldMessageAfter: { attribute: false }, listSeenMessages: { type: Array }, - updateMessageHash: {type: Object} + updateMessageHash: { type: Object }, + messagesToRender: { type: Array }, + oldMessages: { type: Array } } } @@ -208,7 +247,11 @@ class ChatScroller extends LitElement { constructor() { super() - this.messages = [] + this.messages = { + messages: [], + type: '' + } + this.oldMessages = [] this._upObserverhandler = this._upObserverhandler.bind(this) this._downObserverHandler = this._downObserverHandler.bind(this) this.__bottomObserverForFetchingMessagesHandler = this.__bottomObserverForFetchingMessagesHandler.bind(this) @@ -218,52 +261,176 @@ class ChatScroller extends LitElement { this.openUserInfo = false this.listSeenMessages = [] this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' + this.messagesToRender = [] } addSeenMessage(val) { this.listSeenMessages.push(val) } - render() { - let formattedMessages = this.messages.reduce((messageArray, message, index) => { - if(this.updateMessageHash[message.signature]){ - - message = this.updateMessageHash[message.signature] - } - const lastGroupedMessage = messageArray[messageArray.length - 1] - let timestamp - let sender - let repliedToData - let firstMessageInChat + shouldGroupWithLastMessage(newMessage, lastGroupedMessage) { + if (!lastGroupedMessage) return false; - if (index === 0) { - firstMessageInChat = true + return Math.abs(lastGroupedMessage.timestamp - newMessage.timestamp) < 600000 && + lastGroupedMessage.sender === newMessage.sender && + !lastGroupedMessage.repliedToData; + } + addNewMessage(newMessage) { + const lastGroupedMessage = this.messagesToRender[this.messagesToRender.length - 1]; + + if (this.shouldGroupWithLastMessage(newMessage, lastGroupedMessage)) { + lastGroupedMessage.messages.push(newMessage); + } else { + this.messagesToRender.push({ + messages: [newMessage], + ...newMessage + }); + } + + this.requestUpdate(); + } + + async addNewMessages(newMessages, type) { + console.log('sup') + newMessages.forEach(newMessage => { + const lastGroupedMessage = this.messagesToRender[this.messagesToRender.length - 1]; + + if (this.shouldGroupWithLastMessage(newMessage, lastGroupedMessage)) { + lastGroupedMessage.messages.push(newMessage); } else { - firstMessageInChat = false + this.messagesToRender.push({ + messages: [newMessage], + ...newMessage + }); } + }); - message = { ...message, firstMessageInChat } + this.requestUpdate(); + if(type === 'initial'){ + await this.getUpdateComplete(); + setTimeout(() => { + this.viewElement.scrollTop = this.viewElement.scrollHeight + 50; + this.setIsLoadingMessages(false) + }, 50) + } + - if (lastGroupedMessage) { - timestamp = lastGroupedMessage.timestamp - sender = lastGroupedMessage.sender - repliedToData = lastGroupedMessage.repliedToData - } - const isSameGroup = Math.abs(timestamp - message.timestamp) < 600000 && sender === message.sender && !repliedToData + } - if (isSameGroup) { - messageArray[messageArray.length - 1].messages = [ - ...(messageArray[messageArray.length - 1].messages || []), - message - ] - } else { - messageArray.push({ + async prependOldMessages(oldMessages) { + + console.log('2', { oldMessages }) + if (!this.messagesToRender) this.messagesToRender = []; // Ensure it's initialized + + let currentMessageGroup = null; + let previousMessage = null; + + for (const message of oldMessages) { + if (!previousMessage || !this.shouldGroupWithLastMessage(message, previousMessage)) { + // If no previous message, or if the current message shouldn't be grouped with the previous, + // push the current group to the front of the formatted messages (since these are older messages) + if (currentMessageGroup) { + this.messagesToRender.unshift(currentMessageGroup); + } + currentMessageGroup = { + id: message.signature, messages: [message], ...message - }) + }; + } else { + // Add to the current group + currentMessageGroup.messages.push(message); } - return messageArray - }, []) + previousMessage = message; + } + + // After processing all old messages, add the last group + if (currentMessageGroup) { + this.messagesToRender.unshift(currentMessageGroup); + } + + this.requestUpdate(); + this.setIsLoadingMessages(false) + // await this.getUpdateComplete(); + // setTimeout(()=> { + // this.viewElement.scrollTop = this.viewElement.scrollHeight + 50; + // this.setIsLoadingMessages(false) + // },50) + + + + } + + async replaceMessagesWithUpdate(updatedMessages) { + for (let group of this.messagesToRender) { + for (let i = 0; i < group.messages.length; i++) { + if (updatedMessages[group.messages[i].signature]) { + Object.assign(group.messages[i], updatedMessages[group.messages[i].signature]); + } + } + } + this.requestUpdate(); + } + + + + async updated(changedProperties) { + if (changedProperties && changedProperties.has('messages')) { + console.log('this.messages', this.messages) + if (this.messages.type === 'initial') { + this.addNewMessages(this.messages.messages, 'initial') + + + + } else if (this.messages.type === 'new') this.addNewMessages(this.messages.messages) + else if (this.messages.type === 'old') this.prependOldMessages(this.messages.messages) + + + } + if (changedProperties && changedProperties.has('updateMessageHash')) { + this.replaceMessagesWithUpdate(this.updateMessageHash) + } + + } + + render() { + // let formattedMessages = this.messages.reduce((messageArray, message) => { + // const currentMessage = this.updateMessageHash[message.signature] || message; + // const lastGroupedMessage = messageArray[messageArray.length - 1]; + + // currentMessage.firstMessageInChat = messageArray.length === 0; + + // let timestamp, sender, repliedToData; + + // if (lastGroupedMessage) { + // timestamp = lastGroupedMessage.timestamp; + // sender = lastGroupedMessage.sender; + // repliedToData = lastGroupedMessage.repliedToData; + // } else { + // timestamp = currentMessage.timestamp; + // sender = currentMessage.sender; + // repliedToData = currentMessage.repliedToData; + // } + + // const isSameGroup = Math.abs(timestamp - currentMessage.timestamp) < 600000 && + // sender === currentMessage.sender && + // !repliedToData; + + // if (isSameGroup && lastGroupedMessage) { + // lastGroupedMessage.messages.push(currentMessage); + // } else { + // messageArray.push({ + // messages: [currentMessage], + // ...currentMessage + // }); + // } + + // return messageArray; + // }, []); + + let formattedMessages = this.messagesToRender + + console.log('this.messagesToRender', this.messagesToRender) return html` ${this.isLoadingMessages ? html` @@ -273,12 +440,15 @@ class ChatScroller extends LitElement { ` : ''}