From 950817b8776f947ae8fd10a5f145e9a205c6c811 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 2 Sep 2023 22:02:31 -0700 Subject: [PATCH] fetch updates in bg --- plugins/plugins/core/components/ChatPage.js | 168 ++++++++++------ .../plugins/core/components/ChatScroller.js | 10 +- .../plugins/core/components/LevelFounder.js | 1 - .../core/messaging/q-chat/q-chat.src.js | 2 + plugins/plugins/utils/queue.js | 34 ++++ .../plugins/utils/replace-messages-edited.js | 188 ++++++++++++++++-- 6 files changed, 330 insertions(+), 73 deletions(-) create mode 100644 plugins/plugins/utils/queue.js diff --git a/plugins/plugins/core/components/ChatPage.js b/plugins/plugins/core/components/ChatPage.js index 23286834..d0dd3a78 100644 --- a/plugins/plugins/core/components/ChatPage.js +++ b/plugins/plugins/core/components/ChatPage.js @@ -43,6 +43,7 @@ import '@material/mwc-dialog' import '@material/mwc-icon' import '@polymer/paper-dialog/paper-dialog.js' import '@polymer/paper-spinner/paper-spinner-lite.js' +import { RequestQueue } from '../../utils/queue.js' const chatLastSeen = localForage.createInstance({ name: "chat-last-seen", @@ -50,6 +51,9 @@ const chatLastSeen = localForage.createInstance({ const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) +export const queue = new RequestQueue(); + + class ChatPage extends LitElement { static get properties() { return { @@ -113,7 +117,8 @@ class ChatPage extends LitElement { openGifModal: { type: Boolean }, gifsLoading: { type: Boolean }, goToRepliedMessage: { attribute: false }, - isLoadingGoToRepliedMessage: { type: Object } + isLoadingGoToRepliedMessage: { type: Object }, + updateMessageHash: { type: Object} } } @@ -1348,6 +1353,8 @@ class ChatPage extends LitElement { offsetHeight: 0 } this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' + this.updateMessageHash = {} + this.addToUpdateMessageHashmap = this.addToUpdateMessageHashmap.bind(this) } setOpenGifModal(value) { @@ -2502,6 +2509,7 @@ class ChatPage extends LitElement { .selectedHead=${this.selectedHead} .goToRepliedMessage=${(val, val2) => this.goToRepliedMessage(val, val2)} .getOldMessageAfter=${(val) => this.getOldMessageAfter(val)} + .updateMessageHash=${this.updateMessageHash} > ` @@ -2548,13 +2556,15 @@ class ChatPage extends LitElement { return this.decodeMessage(eachMessage) }) - // const replacedMessages = await replaceMessagesEdited({ - // decodedMessages: decodeMsgs, - // parentEpml, - // isReceipient: this.isReceipient, - // decodeMessageFunc: this.decodeMessage, - // _publicKey: this._publicKey - // }) + + queue.push(() => replaceMessagesEdited({ + decodedMessages: decodeMsgs, + parentEpml, + isReceipient: this.isReceipient, + decodeMessageFunc: this.decodeMessage, + _publicKey: this._publicKey, + addToUpdateMessageHashmap: this.addToUpdateMessageHashmap + })); this.messagesRendered = [...decodeMsgs, ...this.messagesRendered].sort(function (a, b) { return a.timestamp @@ -2578,13 +2588,15 @@ class ChatPage extends LitElement { return this.decodeMessage(eachMessage) }) - // const replacedMessages = await replaceMessagesEdited({ - // decodedMessages: decodeMsgs, - // parentEpml, - // isReceipient: this.isReceipient, - // decodeMessageFunc: this.decodeMessage, - // _publicKey: this._publicKey - // }) + queue.push(() => replaceMessagesEdited({ + decodedMessages: decodeMsgs, + parentEpml, + isReceipient: this.isReceipient, + decodeMessageFunc: this.decodeMessage, + _publicKey: this._publicKey, + addToUpdateMessageHashmap: this.addToUpdateMessageHashmap + })); + this.messagesRendered = [...decodeMsgs, ...this.messagesRendered].sort(function (a, b) { return a.timestamp @@ -2612,13 +2624,15 @@ class ChatPage extends LitElement { return this.decodeMessage(eachMessage) }) - // const replacedMessages = await replaceMessagesEdited({ - // decodedMessages: decodeMsgs, - // parentEpml, - // isReceipient: this.isReceipient, - // decodeMessageFunc: this.decodeMessage, - // _publicKey: this._publicKey - // }) + + queue.push(() => replaceMessagesEdited({ + decodedMessages: decodeMsgs, + parentEpml, + isReceipient: this.isReceipient, + decodeMessageFunc: this.decodeMessage, + _publicKey: this._publicKey, + addToUpdateMessageHashmap: this.addToUpdateMessageHashmap + })); this.messagesRendered = [...decodeMsgs, ...this.messagesRendered].sort(function (a, b) { return a.timestamp @@ -2644,13 +2658,15 @@ class ChatPage extends LitElement { return this.decodeMessage(eachMessage) }) - // const replacedMessages = await replaceMessagesEdited({ - // decodedMessages: decodeMsgs, - // parentEpml, - // isReceipient: this.isReceipient, - // decodeMessageFunc: this.decodeMessage, - // _publicKey: this._publicKey - // }) + + queue.push(() => replaceMessagesEdited({ + decodedMessages: decodeMsgs, + parentEpml, + isReceipient: this.isReceipient, + decodeMessageFunc: this.decodeMessage, + _publicKey: this._publicKey, + addToUpdateMessageHashmap: this.addToUpdateMessageHashmap + })); this.messagesRendered = [...decodeMsgs, ...this.messagesRendered].sort(function (a, b) { return a.timestamp @@ -2668,6 +2684,27 @@ class ChatPage extends LitElement { } } + async addToUpdateMessageHashmap(array){ + console.log({array}) + const viewElement = this.shadowRoot.querySelector('chat-scroller').shadowRoot.getElementById('viewElement') + const originalScrollTop = viewElement.scrollTop; +const originalScrollHeight = viewElement.scrollHeight; + + const newObj = {} + + array.forEach((item)=> { + const signature = item.originalSignature || item.signature + newObj[signature] = item + }) + this.updateMessageHash = { + ...this.updateMessageHash, + ...newObj + } + await this.getUpdateComplete() + const heightDifference = viewElement.scrollHeight - originalScrollHeight; +viewElement.scrollTop = originalScrollTop + heightDifference; + } + async getOldMessageAfter(scrollElement) { if (this.isReceipient) { const getInitialMessages = await parentEpml.request('apiCall', { @@ -2679,13 +2716,15 @@ class ChatPage extends LitElement { return this.decodeMessage(eachMessage) }) - // const replacedMessages = await replaceMessagesEdited({ - // decodedMessages: decodeMsgs, - // parentEpml, - // isReceipient: this.isReceipient, - // decodeMessageFunc: this.decodeMessage, - // _publicKey: this._publicKey - // }) + + queue.push(() => replaceMessagesEdited({ + decodedMessages: decodeMsgs, + parentEpml, + isReceipient: this.isReceipient, + decodeMessageFunc: this.decodeMessage, + _publicKey: this._publicKey, + addToUpdateMessageHashmap: this.addToUpdateMessageHashmap + })); this.messagesRendered = [...this.messagesRendered, ...decodeMsgs].sort(function (a, b) { return a.timestamp @@ -2711,13 +2750,15 @@ class ChatPage extends LitElement { return this.decodeMessage(eachMessage) }) - // const replacedMessages = await replaceMessagesEdited({ - // decodedMessages: decodeMsgs, - // parentEpml, - // isReceipient: this.isReceipient, - // decodeMessageFunc: this.decodeMessage, - // _publicKey: this._publicKey - // }) + + queue.push(() => replaceMessagesEdited({ + decodedMessages: decodeMsgs, + parentEpml, + isReceipient: this.isReceipient, + decodeMessageFunc: this.decodeMessage, + _publicKey: this._publicKey, + addToUpdateMessageHashmap: this.addToUpdateMessageHashmap + })); this.messagesRendered = [...this.messagesRendered, ...decodeMsgs].sort(function (a, b) { return a.timestamp @@ -2751,13 +2792,21 @@ class ChatPage extends LitElement { }) if (isInitial) { this.chatEditorPlaceholder = await this.renderPlaceholder() - // const replacedMessages = await replaceMessagesEdited({ - // decodedMessages: decodedMessages, - // parentEpml, - // isReceipient: isReceipient, - // decodeMessageFunc: this.decodeMessage, - // _publicKey: this._publicKey - // }) + + + try { + queue.push(() => replaceMessagesEdited({ + decodedMessages: decodedMessages, + parentEpml, + isReceipient: isReceipient, + decodeMessageFunc: this.decodeMessage, + _publicKey: this._publicKey, + addToUpdateMessageHashmap: this.addToUpdateMessageHashmap + })); + } catch (error) { + console.log({error}) + } + this._messages = decodedMessages.sort(function (a, b) { return a.timestamp @@ -2771,14 +2820,17 @@ class ChatPage extends LitElement { setTimeout(() => this.downElementObserver(), 500) } else { - // const replacedMessages = await replaceMessagesEdited({ - // decodedMessages: decodedMessages, - // parentEpml, - // isReceipient: isReceipient, - // decodeMessageFunc: this.decodeMessage, - // _publicKey: this._publicKey, - // isNotInitial: true - // }) + + + queue.push(() => replaceMessagesEdited({ + decodedMessages: decodedMessages, + parentEpml, + isReceipient: isReceipient, + decodeMessageFunc: this.decodeMessage, + _publicKey: this._publicKey, + isNotInitial: true, + addToUpdateMessageHashmap: this.addToUpdateMessageHashmap + })); const renderEachMessage = decodedMessages.map(async (msg) => { await this.renderNewMessage(msg) diff --git a/plugins/plugins/core/components/ChatScroller.js b/plugins/plugins/core/components/ChatScroller.js index f5ee24de..01b4b92e 100644 --- a/plugins/plugins/core/components/ChatScroller.js +++ b/plugins/plugins/core/components/ChatScroller.js @@ -196,7 +196,8 @@ class ChatScroller extends LitElement { selectedHead: { type: Object }, goToRepliedMessage: { attribute: false }, getOldMessageAfter: { attribute: false }, - listSeenMessages: { type: Array } + listSeenMessages: { type: Array }, + updateMessageHash: {type: Object} } } @@ -223,6 +224,10 @@ class ChatScroller extends LitElement { 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 @@ -319,6 +324,9 @@ class ChatScroller extends LitElement { if (changedProperties.has('userName')) { return true } + if (changedProperties.has('updateMessageHash')) { + return true + } // Only update element if prop1 changed. return changedProperties.has('messages') } diff --git a/plugins/plugins/core/components/LevelFounder.js b/plugins/plugins/core/components/LevelFounder.js index e2e4c1eb..3c8224e9 100644 --- a/plugins/plugins/core/components/LevelFounder.js +++ b/plugins/plugins/core/components/LevelFounder.js @@ -101,7 +101,6 @@ class LevelFounder extends LitElement { } firstUpdated() { - console.log('levelFounder') parentEpml.ready().then(() => { parentEpml.subscribe('selected_address', async selectedAddress => { diff --git a/plugins/plugins/core/messaging/q-chat/q-chat.src.js b/plugins/plugins/core/messaging/q-chat/q-chat.src.js index 1f60e079..7ef7d494 100644 --- a/plugins/plugins/core/messaging/q-chat/q-chat.src.js +++ b/plugins/plugins/core/messaging/q-chat/q-chat.src.js @@ -465,6 +465,7 @@ class Chat extends LitElement { url: `/addresses/balance/${window.parent.reduxStore.getState().app.selectedAddress.address}` }).then(res => { this.balance = res + this.requestUpdate() }) }) parentEpml.imReady() @@ -834,6 +835,7 @@ class Chat extends LitElement { chatId=${this.activeChatHeadUrl} .setOpenPrivateMessage=${(val) => this.setOpenPrivateMessage(val)} .setActiveChatHeadUrl=${(val)=> this.setActiveChatHeadUrl(val)} + balance=${this.balance} > ` diff --git a/plugins/plugins/utils/queue.js b/plugins/plugins/utils/queue.js new file mode 100644 index 00000000..d6130f8c --- /dev/null +++ b/plugins/plugins/utils/queue.js @@ -0,0 +1,34 @@ +export class RequestQueue { + constructor(maxConcurrent = 5) { + this.queue = []; + this.maxConcurrent = maxConcurrent; + this.currentConcurrent = 0; + } + + push(request) { + return new Promise((resolve, reject) => { + this.queue.push({ + request, + resolve, + reject, + }); + this.checkQueue(); + }); + } + + checkQueue() { + if (this.queue.length === 0 || this.currentConcurrent >= this.maxConcurrent) return; + + const { request, resolve, reject } = this.queue.shift(); + this.currentConcurrent++; + + request() + .then(resolve) + .catch(reject) + .finally(() => { + this.currentConcurrent--; + this.checkQueue(); + }); + } +} + diff --git a/plugins/plugins/utils/replace-messages-edited.js b/plugins/plugins/utils/replace-messages-edited.js index ad815fe8..d27364d4 100644 --- a/plugins/plugins/utils/replace-messages-edited.js +++ b/plugins/plugins/utils/replace-messages-edited.js @@ -1,11 +1,161 @@ +// export const replaceMessagesEdited = async ({ +// decodedMessages, +// parentEpml, +// isReceipient, +// decodeMessageFunc, +// _publicKey, +// addToUpdateMessageHashmap +// }) => { +// const findNewMessages = decodedMessages.map(async (msg) => { +// let msgItem = null +// try { +// let msgQuery = `&involving=${msg.recipient}&involving=${msg.sender}` +// if (!isReceipient) { +// msgQuery = `&txGroupId=${msg.txGroupId}` +// } +// const response = await parentEpml.request("apiCall", { +// type: "api", +// url: `/chat/messages?chatreference=${msg.signature}&reverse=true${msgQuery}&limit=1&sender=${msg.sender}&encoding=BASE64`, +// }) + +// if (response && Array.isArray(response) && response.length !== 0) { +// let responseItem = { ...response[0] } +// const decodeResponseItem = decodeMessageFunc(responseItem, isReceipient, _publicKey) +// delete decodeResponseItem.timestamp + +// msgItem = { +// ...msg, +// ...decodeResponseItem, +// senderName: msg.senderName, +// sender: msg.sender, +// editedTimestamp: response[0].timestamp, +// originalSignature: msg.signature +// } +// } +// } catch (error) { +// } + +// return msgItem +// }) +// const updateMessages = await Promise.all(findNewMessages) +// const filterOutNull = updateMessages.filter((item)=> item !== 'null' && item !== null) + +// const findNewMessages2 = filterOutNull.map(async (msg) => { +// let parsedMessageObj = msg +// try { +// parsedMessageObj = JSON.parse(msg.decodedMessage) +// } catch (error) { +// return msg +// } +// let msgItem = msg +// try { +// let msgQuery = `&involving=${msg.recipient}&involving=${msg.sender}` +// if (!isReceipient) { +// msgQuery = `&txGroupId=${msg.txGroupId}` +// } +// if (parsedMessageObj.repliedTo) { +// let originalReply +// if(+parsedMessageObj.version > 2){ +// originalReply = await parentEpml.request("apiCall", { +// type: "api", +// url: `/chat/message/${parsedMessageObj.repliedTo}?encoding=BASE64`, +// }) +// } +// if(+parsedMessageObj.version < 3){ +// originalReply = await parentEpml.request("apiCall", { +// type: "api", +// url: `/chat/messages?reference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}&encoding=BASE64`, +// }) +// } + + + + +// const originalReplyMessage = originalReply.timestamp ? originalReply : originalReply.length !== 0 ? originalReply[0] : null + +// const response = await parentEpml.request("apiCall", { +// type: "api", +// url: `/chat/messages?chatreference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}&limit=1&sender=${originalReplyMessage.sender}&encoding=BASE64`, +// }) + +// if ( +// originalReplyMessage && +// response && +// Array.isArray(response) && +// response.length !== 0 +// ) { +// const decodeOriginalReply = decodeMessageFunc(originalReplyMessage, isReceipient, _publicKey) + +// const decodeUpdatedReply = decodeMessageFunc(response[0], isReceipient, _publicKey) +// const formattedRepliedToData = { +// ...decodeUpdatedReply, +// senderName: decodeOriginalReply.senderName, +// sender: decodeOriginalReply.sender, +// } +// msgItem = { +// ...msg, +// repliedToData: formattedRepliedToData, +// } +// } else { + + +// if ( +// originalReplyMessage +// ) { + +// msgItem = { +// ...msg, +// repliedToData: decodeMessageFunc(originalReplyMessage, isReceipient, _publicKey), +// } +// } +// } +// } +// } catch (error) { +// } + +// return msgItem +// }) +// const updateMessages2 = await Promise.all(findNewMessages2) +// console.log({updateMessages2}) +// updateMessages2.forEach((item)=> { +// addToUpdateMessageHashmap(item.originalSignature, item) +// }) +// return updateMessages2 +// } + + export const replaceMessagesEdited = async ({ - decodedMessages, - parentEpml, + decodedMessages, + parentEpml, isReceipient, decodeMessageFunc, - _publicKey + _publicKey, + addToUpdateMessageHashmap }) => { - const findNewMessages = decodedMessages.map(async (msg) => { + const MAX_CONCURRENT_REQUESTS = 5; // Maximum number of concurrent requests + + const executeWithConcurrencyLimit = async (array, asyncFn) => { + const results = []; + const concurrencyPool = []; + + for (const item of array) { + const promise = asyncFn(item); + concurrencyPool.push(promise); + + if (concurrencyPool.length >= MAX_CONCURRENT_REQUESTS) { + results.push(...await Promise.all(concurrencyPool)); + concurrencyPool.length = 0; // Clear the concurrency pool + } + } + + if (concurrencyPool.length > 0) { + results.push(...await Promise.all(concurrencyPool)); + } + + return results; + }; + + const findNewMessages = async (msg) => { let msgItem = msg try { let msgQuery = `&involving=${msg.recipient}&involving=${msg.sender}` @@ -28,15 +178,16 @@ export const replaceMessagesEdited = async ({ senderName: msg.senderName, sender: msg.sender, editedTimestamp: response[0].timestamp, + originalSignature: msg.signature } } } catch (error) { } return msgItem - }) - const updateMessages = await Promise.all(findNewMessages) - const findNewMessages2 = updateMessages.map(async (msg) => { + }; + + const findNewMessages2 = async (msg) => { let parsedMessageObj = msg try { parsedMessageObj = JSON.parse(msg.decodedMessage) @@ -74,6 +225,7 @@ export const replaceMessagesEdited = async ({ url: `/chat/messages?chatreference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}&limit=1&sender=${originalReplyMessage.sender}&encoding=BASE64`, }) + if ( originalReplyMessage && response && @@ -98,7 +250,6 @@ export const replaceMessagesEdited = async ({ if ( originalReplyMessage ) { - msgItem = { ...msg, repliedToData: decodeMessageFunc(originalReplyMessage, isReceipient, _publicKey), @@ -109,9 +260,20 @@ export const replaceMessagesEdited = async ({ } catch (error) { } - return msgItem - }) - const updateMessages2 = await Promise.all(findNewMessages2) - return updateMessages2 -} + return msgItem + }; + + + + const sortedMessages = decodedMessages.sort((a, b) => b.timestamp - a.timestamp); + + // Execute the functions with concurrency limit + const updateMessages = await executeWithConcurrencyLimit(sortedMessages, findNewMessages); + const updateMessages2 = await executeWithConcurrencyLimit(updateMessages.filter(item => item !== 'null' && item !== null), findNewMessages2); + + addToUpdateMessageHashmap(updateMessages2) + + + return updateMessages2; +};