diff --git a/plugins/plugins/core/components/ChatImage.js b/plugins/plugins/core/components/ChatImage.js new file mode 100644 index 00000000..a0f2abb0 --- /dev/null +++ b/plugins/plugins/core/components/ChatImage.js @@ -0,0 +1,289 @@ +import { LitElement, html, css } from 'lit'; +import { render } from 'lit/html.js'; +import { + use, + get, + translate, + translateUnsafeHTML, + registerTranslateConfig, +} from 'lit-translate'; +import axios from 'axios' +import { RequestQueueWithPromise } from '../../utils/queue'; + +const requestQueue = new RequestQueueWithPromise(5); + +export class ChatImage extends LitElement { + static get properties() { + return { + resource: { type: Object }, + isReady: { type: Boolean}, + status: {type: Object}, + setOpenDialogImage: { attribute: false} + }; + } + + static get styles() { + return css` + img { + max-width:45vh; + max-height:40vh; + border-radius: 5px; + cursor: pointer; + } + .smallLoading, + .smallLoading:after { + border-radius: 50%; + width: 2px; + height: 2px; + } + + .smallLoading { + border-width: 0.8em; + border-style: solid; + border-color: rgba(3, 169, 244, 0.2) rgba(3, 169, 244, 0.2) + rgba(3, 169, 244, 0.2) rgb(3, 169, 244); + font-size: 30px; + position: relative; + text-indent: -9999em; + transform: translateZ(0px); + animation: 1.1s linear 0s infinite normal none running loadingAnimation; + } + + .defaultSize { + width: 45vh; + height: 40vh; + } + + + + + @-webkit-keyframes loadingAnimation { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } + + @keyframes loadingAnimation { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } + `; + } + + constructor() { + super(); + this.resource = { + identifier: "", + name: "", + service: "" + } + this.status = { + status: '' + } + this.url = "" + this.isReady = false + this.nodeUrl = this.getNodeUrl() + this.myNode = this.getMyNode() + this.hasCalledWhenDownloaded = false + + this.observer = new IntersectionObserver(entries => { + for (const entry of entries) { + if (entry.isIntersecting && this.status.status !== 'READY') { + this._fetchImage(); + // Stop observing after the image has started loading + this.observer.unobserve(this); + } + } + }); + } + getNodeUrl(){ + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + return nodeUrl +} +getMyNode(){ + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + + return myNode +} + + getApiKey() { + const myNode = + window.parent.reduxStore.getState().app.nodeConfig.knownNodes[ + window.parent.reduxStore.getState().app.nodeConfig.node + ]; + let apiKey = myNode.apiKey; + return apiKey; + } + + async fetchResource() { + try { + // await qortalRequest({ + // action: 'GET_QDN_RESOURCE_PROPERTIES', + // name, + // service, + // identifier + // }) + await axios.get(`${this.nodeUrl}/arbitrary/resource/properties/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`) + + + } catch (error) {} + } + + async fetchVideoUrl() { + + this.fetchResource() + this.url = `${this.nodeUrl}/arbitrary/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?async=true&apiKey=${this.myNode.apiKey}` + + } + + async fetchStatus(){ + let isCalling = false + let percentLoaded = 0 + let timer = 24 + const response = await axios.get(`${this.nodeUrl}/arbitrary/resource/status/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`) + if(response && response.data && response.data.status === 'READY'){ + this.status = response.data + return + } + const intervalId = setInterval(async () => { + if (isCalling) return + isCalling = true + + const data = await requestQueue.enqueue(() => { + return axios.get(`${this.nodeUrl}/arbitrary/resource/status/${this.resource.service}/${this.resource.name}/${this.resource.identifier}?apiKey=${this.myNode.apiKey}`) + }); + const res = data.data + + isCalling = false + if (res.localChunkCount) { + if (res.percentLoaded) { + if ( + res.percentLoaded === percentLoaded && + res.percentLoaded !== 100 + ) { + timer = timer - 5 + } else { + timer = 24 + } + if (timer < 0) { + timer = 24 + isCalling = true + this.status = { + ...res, + status: 'REFETCHING' + } + + setTimeout(() => { + isCalling = false + this.fetchResource({ + name, + service, + identifier + }) + }, 25000) + return + } + percentLoaded = res.percentLoaded + } + + this.status = res + + } + + // check if progress is 100% and clear interval if true + if (res?.status === 'READY') { + clearInterval(intervalId) + this.status = res + this.isReady = true + } + }, 5000) // 1 second interval + } + + async _fetchImage() { + try { + this.fetchVideoUrl({ + name: this.resource.name, + service: this.resource.service, + identifier: this.resource.identifier + }) + this.fetchStatus() + } catch (error) { + + } + } + + firstUpdated(){ + this.observer.observe(this); + + } + + shouldUpdate(changedProperties) { + if (changedProperties.has('setOpenDialogImage') && changedProperties.size === 1) { + return false; + } + + return true + } + async updated(changedProperties) { + if (changedProperties && changedProperties.has('status')) { + if(this.hasCalledWhenDownloaded === false && this.status.status === 'DOWNLOADED'){ + this.fetchResource() + this.hasCalledWhenDownloaded = true + } + } + } + + render() { + + return html` +
+ ${ + this.status.status !== 'READY' + ? html` +
+
+

${`${Math.round(this.status.percentLoaded || 0 + ).toFixed(0)}% loaded`}

+
+ ` + : '' + } + ${this.status.status === 'READY' ? html` + this.setOpenDialogImage(true)} src=${this.url} /> + ` : ''} + +
+ ` + + + } +} + +customElements.define('chat-image', ChatImage); diff --git a/plugins/plugins/core/components/ChatPage.js b/plugins/plugins/core/components/ChatPage.js index 7f8a5f8d..8ccd94f0 100644 --- a/plugins/plugins/core/components/ChatPage.js +++ b/plugins/plugins/core/components/ChatPage.js @@ -3895,9 +3895,10 @@ class ChatPage extends LitElement { new Compressor(image, { quality: .6, maxWidth: 1200, + mimeType: 'image/webp', success(result) { const file = new File([result], "name", { - type: image.type + type: 'image/webp' }) compressedFile = file resolve() diff --git a/plugins/plugins/core/components/ChatScroller.js b/plugins/plugins/core/components/ChatScroller.js index 2c66f5ea..acc3faf7 100644 --- a/plugins/plugins/core/components/ChatScroller.js +++ b/plugins/plugins/core/components/ChatScroller.js @@ -1,1695 +1,2238 @@ -import { LitElement, html, css } from 'lit' -import { render } from 'lit/html.js' -import { repeat } from 'lit/directives/repeat.js' -import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate' -import { unsafeHTML } from 'lit/directives/unsafe-html.js' -import { chatStyles } from './ChatScroller-css.js' -import { Epml } from '../../../epml' -import { cropAddress } from "../../utils/cropAddress" -import { roundToNearestDecimal } from '../../utils/roundToNearestDecimal.js' -import { EmojiPicker } from 'emoji-picker-js' -import { generateHTML } from '@tiptap/core' -import isElectron from 'is-electron' -import localForage from 'localforage' +import { LitElement, html, css } from 'lit'; +import { render } from 'lit/html.js'; +import { repeat } from 'lit/directives/repeat.js'; +import { + use, + get, + translate, + translateUnsafeHTML, + registerTranslateConfig, +} from 'lit-translate'; +import { unsafeHTML } from 'lit/directives/unsafe-html.js'; +import { chatStyles } from './ChatScroller-css.js'; +import { Epml } from '../../../epml'; +import { cropAddress } from '../../utils/cropAddress'; +import { roundToNearestDecimal } from '../../utils/roundToNearestDecimal.js'; +import { EmojiPicker } from 'emoji-picker-js'; +import { generateHTML } from '@tiptap/core'; +import isElectron from 'is-electron'; +import localForage from 'localforage'; -import axios from 'axios' -import Highlight from '@tiptap/extension-highlight' -import ShortUniqueId from 'short-unique-id' -import StarterKit from '@tiptap/starter-kit' -import Underline from '@tiptap/extension-underline' +import axios from 'axios'; +import Highlight from '@tiptap/extension-highlight'; +import ShortUniqueId from 'short-unique-id'; +import StarterKit from '@tiptap/starter-kit'; +import Underline from '@tiptap/extension-underline'; -import './ChatModals.js' -import './LevelFounder.js' -import './NameMenu.js' -import './UserInfo/UserInfo.js' -import './WrapperModal' +import './ChatModals.js'; +import './LevelFounder.js'; +import './NameMenu.js'; +import './UserInfo/UserInfo.js'; +import './WrapperModal'; +import './ChatImage'; -import '@material/mwc-button' -import '@material/mwc-dialog' -import '@material/mwc-icon' -import '@vaadin/icon' -import '@vaadin/icons' -import '@vaadin/tooltip' -import { chatLimit, totalMsgCount } from './ChatPage.js' +import '@material/mwc-button'; +import '@material/mwc-dialog'; +import '@material/mwc-icon'; +import '@vaadin/icon'; +import '@vaadin/icons'; +import '@vaadin/tooltip'; +import { chatLimit, totalMsgCount } from './ChatPage.js'; -const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) +const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }); const chatLastSeen = localForage.createInstance({ - name: "chat-last-seen", -}) -let toggledMessage = {} + name: 'chat-last-seen', +}); +let toggledMessage = {}; -const uid = new ShortUniqueId() +const uid = new ShortUniqueId(); const getApiKey = () => { - const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] - let apiKey = myNode.apiKey - return apiKey -} - - + const myNode = + window.parent.reduxStore.getState().app.nodeConfig.knownNodes[ + window.parent.reduxStore.getState().app.nodeConfig.node + ]; + let apiKey = myNode.apiKey; + return apiKey; +}; const extractComponents = async (url) => { - if (!url.startsWith("qortal://")) { - return null - } + if (!url.startsWith('qortal://')) { + return null; + } - url = url.replace(/^(qortal\:\/\/)/, "") - if (url.includes("/")) { - let parts = url.split("/") - const service = parts[0].toUpperCase() - parts.shift() - const name = parts[0] - parts.shift() - let identifier + url = url.replace(/^(qortal\:\/\/)/, ''); + if (url.includes('/')) { + let parts = url.split('/'); + const service = parts[0].toUpperCase(); + parts.shift(); + const name = parts[0]; + parts.shift(); + let identifier; - if (parts.length > 0) { - identifier = parts[0] // Do not shift yet - // Check if a resource exists with this service, name and identifier combination - let responseObj = await parentEpml.request('apiCall', { - url: `/arbitrary/resource/status/${service}/${name}/${identifier}?apiKey=${getApiKey()}` - }) + if (parts.length > 0) { + identifier = parts[0]; // Do not shift yet + // Check if a resource exists with this service, name and identifier combination + let responseObj = await parentEpml.request('apiCall', { + url: `/arbitrary/resource/status/${service}/${name}/${identifier}?apiKey=${getApiKey()}`, + }); - if (responseObj.totalChunkCount > 0) { - // Identifier exists, so don't include it in the path - parts.shift() - } - else { - identifier = null - } - } + if (responseObj.totalChunkCount > 0) { + // Identifier exists, so don't include it in the path + parts.shift(); + } else { + identifier = null; + } + } - const path = parts.join("/") + const path = parts.join('/'); - const components = {} - components["service"] = service - components["name"] = name - components["identifier"] = identifier - components["path"] = path - return components - } + const components = {}; + components['service'] = service; + components['name'] = name; + components['identifier'] = identifier; + components['path'] = path; + return components; + } - return null -} + return null; +}; function processText(input) { - const linkRegex = /(qortal:\/\/\S+)/g + const linkRegex = /(qortal:\/\/\S+)/g; - function processNode(node) { - if (node.nodeType === Node.TEXT_NODE) { - const parts = node.textContent.split(linkRegex) + function processNode(node) { + if (node.nodeType === Node.TEXT_NODE) { + const parts = node.textContent.split(linkRegex); - if (parts.length > 1) { - const fragment = document.createDocumentFragment() + if (parts.length > 1) { + const fragment = document.createDocumentFragment(); - parts.forEach((part) => { - if (part.startsWith('qortal://')) { - const link = document.createElement('span') - // Store the URL in a data attribute - link.setAttribute('data-url', part) - link.textContent = part - link.style.color = 'var(--code-block-text-color)' - link.style.textDecoration = 'underline' - link.style.cursor = 'pointer' + parts.forEach((part) => { + if (part.startsWith('qortal://')) { + const link = document.createElement('span'); + // Store the URL in a data attribute + link.setAttribute('data-url', part); + link.textContent = part; + link.style.color = 'var(--code-block-text-color)'; + link.style.textDecoration = 'underline'; + link.style.cursor = 'pointer'; - link.addEventListener('click', async (e) => { - e.preventDefault() - try { - const res = await extractComponents(part) - if (!res) return - const { service, name, identifier, path } = res - let query = `?service=${service}` - if (name) { - query = query + `&name=${name}` - } - if (identifier) { - query = query + `&identifier=${identifier}` - } - if (path) { - query = query + `&path=${path}` - } - window.parent.reduxStore.dispatch(window.parent.reduxAction.setNewTab({ - url: `qdn/browser/index.html${query}`, - id: uid.rnd(), - myPlugObj: { - "url": "myapp", - "domain": "core", - "page": `qdn/browser/index.html${query}`, - "title": name, - "icon": service === 'WEBSITE' ? 'vaadin:desktop' : 'vaadin:external-browser', - "mwcicon": service === 'WEBSITE' ? 'desktop_mac' : 'open_in_browser', - "menus": [], - "parent": false - } - })) + link.addEventListener('click', async (e) => { + e.preventDefault(); + try { + const res = await extractComponents(part); + if (!res) return; + const { service, name, identifier, path } = res; + let query = `?service=${service}`; + if (name) { + query = query + `&name=${name}`; + } + if (identifier) { + query = query + `&identifier=${identifier}`; + } + if (path) { + query = query + `&path=${path}`; + } + window.parent.reduxStore.dispatch( + window.parent.reduxAction.setNewTab({ + url: `qdn/browser/index.html${query}`, + id: uid.rnd(), + myPlugObj: { + url: 'myapp', + domain: 'core', + page: `qdn/browser/index.html${query}`, + title: name, + icon: + service === 'WEBSITE' + ? 'vaadin:desktop' + : 'vaadin:external-browser', + mwcicon: + service === 'WEBSITE' + ? 'desktop_mac' + : 'open_in_browser', + menus: [], + parent: false, + }, + }) + ); + } catch (error) { + console.log({ error }); + } + }); - } catch (error) { - console.log({ error }) - } + fragment.appendChild(link); + } else { + const textNode = document.createTextNode(part); + fragment.appendChild(textNode); + } + }); - }) + node.replaceWith(fragment); + } + } else { + for (const childNode of Array.from(node.childNodes)) { + processNode(childNode); + } + } + } - fragment.appendChild(link) - } else { - const textNode = document.createTextNode(part) - fragment.appendChild(textNode) - } - }) + const wrapper = document.createElement('div'); + wrapper.innerHTML = input; - node.replaceWith(fragment) - } - } else { - for (const childNode of Array.from(node.childNodes)) { - processNode(childNode) - } - } - } + processNode(wrapper); - const wrapper = document.createElement('div') - wrapper.innerHTML = input - - processNode(wrapper) - - return wrapper + return wrapper; } const formatMessages = (messages) => { - const formattedMessages = messages.reduce((messageArray, message) => { - const currentMessage = message; - const lastGroupedMessage = messageArray[messageArray.length - 1]; + const formattedMessages = messages.reduce((messageArray, message) => { + const currentMessage = message; + const lastGroupedMessage = messageArray[messageArray.length - 1]; - currentMessage.firstMessageInChat = messageArray.length === 0; + currentMessage.firstMessageInChat = messageArray.length === 0; - let timestamp, sender, repliedToData; + 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; - } + 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; + 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 - }); - } + if (isSameGroup && lastGroupedMessage) { + lastGroupedMessage.messages.push(currentMessage); + } else { + messageArray.push({ + messages: [currentMessage], + ...currentMessage, + }); + } - return messageArray; - }, []); + return messageArray; + }, []); - return formattedMessages -} + return formattedMessages; +}; class ChatScroller extends LitElement { - static get properties() { - return { - theme: { type: String, reflect: true }, - getNewMessage: { attribute: false }, - getOldMessage: { attribute: false }, - getAfterMessages: { attribute: false }, - escapeHTML: { attribute: false }, - messages: { type: Object }, - hideMessages: { type: Array }, - setRepliedToMessageObj: { attribute: false }, - setEditedMessageObj: { attribute: false }, - sendMessage: { attribute: false }, - sendMessageForward: { attribute: false }, - showLastMessageRefScroller: { attribute: false }, - emojiPicker: { attribute: false }, - isLoadingMessages: { type: Boolean }, - setIsLoadingMessages: { attribute: false }, - chatId: { type: String }, - setForwardProperties: { attribute: false }, - setOpenPrivateMessage: { attribute: false }, - setOpenUserInfo: { attribute: false }, - setOpenTipUser: { attribute: false }, - setUserName: { attribute: false }, - setSelectedHead: { attribute: false }, - openTipUser: { type: Boolean }, - openUserInfo: { type: Boolean }, - userName: { type: String }, - selectedHead: { type: Object }, - goToRepliedMessage: { attribute: false }, - listSeenMessages: { type: Array }, - updateMessageHash: { type: Object }, - messagesToRender: { type: Array }, - oldMessages: { type: Array }, - clearUpdateMessageHashmap: { attribute: false}, - disableFetching: {type: Boolean}, - isLoadingBefore: {type: Boolean}, - isLoadingAfter: {type: Boolean}, - messageQueue: {type: Array}, - loggedInUserName: {type: String}, - loggedInUserAddress: {type: String} - } - } - - static get styles() { - return [chatStyles] - } - - constructor() { - super() - this.messages = { - messages: [], - type: '' - } - this.oldMessages = [] - this._upObserverhandler = this._upObserverhandler.bind(this) - this.newListMessages = this.newListMessages.bind(this) - this.newListMessagesUnreadMessages = this.newListMessagesUnreadMessages.bind(this) - this._downObserverHandler = this._downObserverHandler.bind(this) - this.isLastMessageBeforeUnread = this.isLastMessageBeforeUnread.bind(this) - this.replaceMessagesWithUpdate = this.replaceMessagesWithUpdate.bind(this) - this.__bottomObserverForFetchingMessagesHandler = this.__bottomObserverForFetchingMessagesHandler.bind(this) - this.myAddress = window.parent.reduxStore.getState().app.selectedAddress.address - this.hideMessages = JSON.parse(localStorage.getItem("MessageBlockedAddresses") || "[]") - this.openTipUser = false - this.openUserInfo = false - this.listSeenMessages = [] - this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' - this.messagesToRender = [] - this.disableFetching = false - this.isLoadingBefore = false - this.isLoadingAfter = false - this.disableAddingNewMessages = false - this.lastReadMessageTimestamp = null - this.messageQueue = [] - } - - addSeenMessage(val) { - this.listSeenMessages.push(val) - } - goToRepliedMessageFunc(val, val2){ - this.disableFetching = true - this.goToRepliedMessage(val, val2) - } - - shouldGroupWithLastMessage(newMessage, lastGroupedMessage) { - if (!lastGroupedMessage) return false; - - return Math.abs(lastGroupedMessage.timestamp - newMessage.timestamp) < 600000 && - lastGroupedMessage.sender === newMessage.sender && - !lastGroupedMessage.repliedToData; - } - - clearLoaders(){ - this.isLoadingBefore = false - this.isLoadingAfter = false - this.disableFetching = false - } - 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.clearLoaders() - this.requestUpdate(); - - } - - async newListMessages(newMessages, message) { - let data = [] - const copy = [...newMessages] - copy.forEach(newMessage => { - const lastGroupedMessage = data[data.length - 1]; - - if (this.shouldGroupWithLastMessage(newMessage, lastGroupedMessage)) { - lastGroupedMessage.messages.push(newMessage); - } else { - data.push({ - messages: [newMessage], - ...newMessage - }); - } - }); - - // const getCount = await parentEpml.request('apiCall', { - // type: 'api', - // 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` - // }) - this.messagesToRender = data - this.clearLoaders() - this.requestUpdate() - await this.updateComplete - - - - - - - - - } - - async newListMessagesUnreadMessages(newMessages, message, lastReadMessageTimestamp, count) { - - let data = []; - const copy = [...newMessages]; - - let dividerPlaced = false; // To ensure the divider is added only once - - // Start from the end of the list (newest messages) - for (let i = copy.length - 1; i >= 0; i--) { - let newMessage = copy[i]; - - // Initialize a property for the divider - newMessage.isDivider = false; - - // Check if this is the message before which the divider should be placed - if (!dividerPlaced && newMessage.timestamp <= lastReadMessageTimestamp) { - newMessage.isDivider = true; - dividerPlaced = true; // Ensure the divider is only added once - break; // Exit once the divider is placed - } - } - - copy.forEach((newMessage, groupIndex) => { - const lastGroupedMessage = data[data.length - 1]; - - if (this.shouldGroupWithLastMessage(newMessage, lastGroupedMessage)) { - lastGroupedMessage.messages.push(newMessage); - } else { - data.push({ - messages: [newMessage], - ...newMessage - }); - } - }); - if(count > 0){ - this.disableAddingNewMessages = true - } - this.messagesToRender = data; - this.clearLoaders(); - this.requestUpdate(); - await this.updateComplete; - const findElement = this.shadowRoot.getElementById('unread-divider-id') - if (findElement) { - findElement.scrollIntoView({ behavior: 'auto', block: 'center' }) - } - } - - - - - - async addNewMessages(newMessages, type) { - if(this.disableAddingNewMessages && type === 'newComingInAuto') return - let previousScrollTop; - let previousScrollHeight; - - const viewElement = this.shadowRoot.querySelector("#viewElement"); - previousScrollTop = viewElement.scrollTop; - previousScrollHeight = viewElement.scrollHeight; - - - const copy = type === 'initial' ? [] : [...this.messagesToRender] - - for (const newMessage of newMessages) { - const lastGroupedMessage = copy[copy.length - 1]; - - if (this.shouldGroupWithLastMessage(newMessage, lastGroupedMessage)) { - lastGroupedMessage.messages.push(newMessage); - } else { - copy.push({ - messages: [newMessage], - ...newMessage - }); - } - } - - - - // Ensure that the total number of individual messages doesn't exceed totalMsgCount - let totalMessagesCount = copy.reduce((acc, group) => acc + group.messages.length, 0); - while (totalMessagesCount > totalMsgCount && copy.length) { - if(newMessages.length < chatLimit && type !== 'newComingInAuto' && type !== 'initial'){ - this.disableAddingNewMessages = false - } - const firstGroup = copy[0]; - if (firstGroup.messages.length <= (totalMessagesCount - totalMsgCount)) { - // If removing the whole first group achieves the goal, remove it - totalMessagesCount -= firstGroup.messages.length; - copy.shift(); - } else { - // Otherwise, trim individual messages from the first group - const messagesToRemove = totalMessagesCount - totalMsgCount; - firstGroup.messages.splice(0, messagesToRemove); - totalMessagesCount = totalMsgCount; - } - } - this.messagesToRender = copy - this.requestUpdate(); - await this.updateComplete; - - if (type === 'initial') { - - viewElement.scrollTop = viewElement.scrollHeight - - - - } - - - this.clearLoaders() - } - - - async prependOldMessages(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); - } - previousMessage = message; - } - - // After processing all old messages, add the last group - if (currentMessageGroup) { - this.messagesToRender.unshift(currentMessageGroup); - } - - // Ensure that the total number of individual messages doesn't exceed totalMsgCount - let totalMessagesCount = this.messagesToRender.reduce((acc, group) => acc + group.messages.length, 0); - while (totalMessagesCount > totalMsgCount && this.messagesToRender.length) { - this.disableAddingNewMessages = true - const lastGroup = this.messagesToRender[this.messagesToRender.length - 1]; - if (lastGroup.messages.length <= (totalMessagesCount - totalMsgCount)) { - // If removing the whole last group achieves the goal, remove it - totalMessagesCount -= lastGroup.messages.length; - this.messagesToRender.pop(); - } else { - // Otherwise, trim individual messages from the last group - const messagesToRemove = totalMessagesCount - totalMsgCount; - lastGroup.messages.splice(-messagesToRemove, messagesToRemove); - totalMessagesCount = totalMsgCount; - } - } - this.clearLoaders() - this.requestUpdate(); - - } - - - async replaceMessagesWithUpdate(updatedMessages) { - - const viewElement = this.shadowRoot.querySelector("#viewElement"); - if (!viewElement) return; // Ensure the element exists - const isUserAtBottom = (viewElement.scrollTop + viewElement.clientHeight) === viewElement.scrollHeight; - - const previousScrollTop = viewElement.scrollTop; - const previousScrollHeight = viewElement.scrollHeight; - - // Using map to return a new array, rather than mutating the old one - const newMessagesToRender = this.messagesToRender.map(group => { - // For each message, return the updated message if it exists, otherwise return the original message - const updatedGroupMessages = group.messages.map(message => { - return updatedMessages[message.signature] ? {...message, ...updatedMessages[message.signature]} : message; - }); - - // Return a new group object with updated messages - return { - ...group, - messages: updatedGroupMessages - }; - }); - - this.messagesToRender = newMessagesToRender; - this.requestUpdate(); - await this.updateComplete; - - - if (isUserAtBottom) { - viewElement.scrollTop = viewElement.scrollHeight - viewElement.clientHeight; - } else { - // Adjust scroll position based on the difference in scroll heights - const newScrollHeight = viewElement.scrollHeight; - viewElement.scrollTop = viewElement.scrollTop + (newScrollHeight - viewElement.scrollHeight); - } - - - this.clearUpdateMessageHashmap(); - this.clearLoaders() - } - - - async replaceMessagesWithUpdateByArray(updatedMessagesArray) { - let previousScrollTop; - let previousScrollHeight; - - const viewElement = this.shadowRoot.querySelector("#viewElement"); - previousScrollTop = viewElement.scrollTop; - previousScrollHeight = viewElement.scrollHeight; - for (let group of this.messagesToRender) { - for (let i = 0; i < group.messages.length; i++) { - const update = updatedMessagesArray.find(updatedMessage => ((updatedMessage.chatReference === group.messages[i].signature) || (updatedMessage.chatReference === group.messages[i].originalSignature) || (updatedMessage.chatReference === group.messages[i].chatReference))); - if (update) { - Object.assign(group.messages[i], update); - } - } - } - this.requestUpdate(); - const newScrollHeight = viewElement.scrollHeight; - viewElement.scrollTop = previousScrollTop + (newScrollHeight - previousScrollHeight); - this.clearUpdateMessageHashmap() - this.clearLoaders() - } - - - - - async updated(changedProperties) { - if (changedProperties && changedProperties.has('messages')) { - if (this.messages.type === 'initial') { - - this.addNewMessages(this.messages.messages, 'initial') - - - - } else if (this.messages.type === 'initialLastSeen') { - this.newListMessagesUnreadMessages(this.messages.messages, 'initialLastSeen', this.messages.lastReadMessageTimestamp, this.messages.count) - - } - else if (this.messages.type === 'new') this.addNewMessages(this.messages.messages) - else if(this.messages.type === 'newComingInAuto') this.addNewMessages(this.messages.messages, 'newComingInAuto') - else if (this.messages.type === 'old') this.prependOldMessages(this.messages.messages) - else if (this.messages.type === 'inBetween') this.newListMessages(this.messages.messages, this.messages.signature) - else if (this.messages.type === 'update') this.replaceMessagesWithUpdateByArray(this.messages.messages) - - - } - if (changedProperties && changedProperties.has('updateMessageHash') && Object.keys(this.updateMessageHash).length > 0) { - this.replaceMessagesWithUpdate(this.updateMessageHash) - } - if (changedProperties && changedProperties.has('messageQueue') && Object.keys(this.messageQueue).length > 0) { - if(!this.disableAddingNewMessages){ - await new Promise((res)=> { - setTimeout(()=> { - res() - }, 200) - }) - const viewElement = this.shadowRoot.querySelector("#viewElement"); - viewElement.scrollTop = viewElement.scrollHeight + 200 - } - } - - - } - - isLastMessageBeforeUnread(message, formattedMessages) { - // if the message is the last one in the older messages list and its timestamp is before the user's last seen timestamp - if (message.timestamp < this.lastReadMessageTimestamp && formattedMessages.indexOf(message) === (formattedMessages.length - 21)) { - return true; - } - return false; - } - - - 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 - - - return html` - ${this.isLoadingBefore ? html` -
- -
- ` : ''} - - ` - } - - shouldUpdate(changedProperties) { - if (changedProperties.has('isLoadingMessages')) { - return true - } - if (changedProperties.has('chatId') && changedProperties.get('chatId')) { - return true - } - if (changedProperties.has('openTipUser')) { - return true - } - if (changedProperties.has('openUserInfo')) { - return true - } - if (changedProperties.has('userName')) { - return true - } - if(changedProperties.has('loggedInUserName')){ - return true - } - if (changedProperties.has('updateMessageHash')) { - return true - } - if(changedProperties.has('messagesToRender')){ - return true - } - if(changedProperties.has('isLoadingBefore')){ - return true - } - if(changedProperties.has('isLoadingAfter')){ - return true - } - if(changedProperties.has('messageQueue')){ - return true - } - // Only update element if prop1 changed. - return changedProperties.has('messages') - } - - async getUpdateComplete() { - await super.getUpdateComplete() - const marginElements = Array.from(this.shadowRoot.querySelectorAll('message-template')) - await Promise.all(marginElements.map(el => el.updateComplete)) - return true - } - - setToggledMessage(message) { - toggledMessage = message - } - - async firstUpdated() { - this.changeTheme() - window.addEventListener('storage', () => { - const checkTheme = localStorage.getItem('qortalTheme') - - if (checkTheme === 'dark') { - this.theme = 'dark' - } else { - this.theme = 'light' - } - document.querySelector('html').setAttribute('theme', this.theme) - }) - - this.emojiPicker.on('emoji', selection => { - this.sendMessage({ - type: 'reaction', - editedMessageObj: toggledMessage, - reaction: selection.emoji, - }) - }) - this.viewElement = this.shadowRoot.getElementById('viewElement') - this.upObserverElement = this.shadowRoot.getElementById('upObserver') - this.downObserverElement = this.shadowRoot.getElementById('downObserver') - this.bottomObserverForFetchingMessages = this.shadowRoot.getElementById('bottomObserverForFetchingMessages') - // Intialize Observers - this.upElementObserver() - this.downElementObserver() - this.bottomObserver() - - - this.clearConsole() - setInterval(() => { - this.clearConsole() - }, 60000) - } - - clearConsole() { - if (!isElectron()) { - } else { - console.clear() - window.parent.electronAPI.clearCache() - } - } - - changeTheme() { - const checkTheme = localStorage.getItem('qortalTheme') - if (checkTheme === 'dark') { - this.theme = 'dark' - } else { - this.theme = 'light' - } - document.querySelector('html').setAttribute('theme', this.theme) - } - - _getOldMessage(_scrollElement) { - this.getOldMessage(_scrollElement) - } - _getAfterMessages(_scrollElement) { - this.getAfterMessages(_scrollElement) - } - - - - _upObserverhandler(entries) { - if(!entries[0].target || !entries[0].target.nextElementSibling) return - if (entries[0].isIntersecting) { - if (this.disableFetching) { - return - } - this.disableFetching = true - this.isLoadingBefore = true - let _scrollElement = entries[0].target.nextElementSibling - this._getOldMessage(_scrollElement) - } - } - - _downObserverHandler(entries) { - if (!entries[0].isIntersecting) { - this.showLastMessageRefScroller(true) - } else { - this.showLastMessageRefScroller(false) - } - } - - __bottomObserverForFetchingMessagesHandler(entries) { - if (this.messagesToRender.length === 0 || this.disableFetching) { - return - } - if (!entries[0].isIntersecting || !entries[0].target || !entries[0].target.previousElementSibling) { - } else { - this.disableFetching = true - this.isLoadingAfter = true - let _scrollElement = entries[0].target.previousElementSibling - this._getAfterMessages(_scrollElement) - } - } - - upElementObserver() { - const options = { - root: this.viewElement, - rootMargin: '0px', - threshold: 1 - } - const observer = new IntersectionObserver(this._upObserverhandler, options) - observer.observe(this.upObserverElement) - } - - downElementObserver() { - const options = { - - } - // identify an element to observe - const elementToObserve = this.downObserverElement - // passing it a callback function - const observer = new IntersectionObserver(this._downObserverHandler, options) - // call `observe()` on that MutationObserver instance, - // passing it the element to observe, and the options object - observer.observe(elementToObserve) - } - bottomObserver() { - const options = { - - } - // identify an element to observe - const elementToObserve = this.bottomObserverForFetchingMessages - // passing it a callback function - const observer = new IntersectionObserver(this.__bottomObserverForFetchingMessagesHandler, options) - // call `observe()` on that MutationObserver instance, - // passing it the element to observe, and the options object - observer.observe(elementToObserve) - } + static get properties() { + return { + theme: { type: String, reflect: true }, + getNewMessage: { attribute: false }, + getOldMessage: { attribute: false }, + getAfterMessages: { attribute: false }, + escapeHTML: { attribute: false }, + messages: { type: Object }, + hideMessages: { type: Array }, + setRepliedToMessageObj: { attribute: false }, + setEditedMessageObj: { attribute: false }, + sendMessage: { attribute: false }, + sendMessageForward: { attribute: false }, + showLastMessageRefScroller: { attribute: false }, + emojiPicker: { attribute: false }, + isLoadingMessages: { type: Boolean }, + setIsLoadingMessages: { attribute: false }, + chatId: { type: String }, + setForwardProperties: { attribute: false }, + setOpenPrivateMessage: { attribute: false }, + setOpenUserInfo: { attribute: false }, + setOpenTipUser: { attribute: false }, + setUserName: { attribute: false }, + setSelectedHead: { attribute: false }, + openTipUser: { type: Boolean }, + openUserInfo: { type: Boolean }, + userName: { type: String }, + selectedHead: { type: Object }, + goToRepliedMessage: { attribute: false }, + listSeenMessages: { type: Array }, + updateMessageHash: { type: Object }, + messagesToRender: { type: Array }, + oldMessages: { type: Array }, + clearUpdateMessageHashmap: { attribute: false }, + disableFetching: { type: Boolean }, + isLoadingBefore: { type: Boolean }, + isLoadingAfter: { type: Boolean }, + messageQueue: { type: Array }, + loggedInUserName: { type: String }, + loggedInUserAddress: { type: String }, + }; + } + + static get styles() { + return [chatStyles]; + } + + constructor() { + super(); + this.messages = { + messages: [], + type: '', + }; + this.oldMessages = []; + this._upObserverhandler = this._upObserverhandler.bind(this); + this.newListMessages = this.newListMessages.bind(this); + this.newListMessagesUnreadMessages = + this.newListMessagesUnreadMessages.bind(this); + this._downObserverHandler = this._downObserverHandler.bind(this); + this.isLastMessageBeforeUnread = + this.isLastMessageBeforeUnread.bind(this); + this.replaceMessagesWithUpdate = + this.replaceMessagesWithUpdate.bind(this); + this.__bottomObserverForFetchingMessagesHandler = + this.__bottomObserverForFetchingMessagesHandler.bind(this); + this.myAddress = + window.parent.reduxStore.getState().app.selectedAddress.address; + this.hideMessages = JSON.parse( + localStorage.getItem('MessageBlockedAddresses') || '[]' + ); + this.openTipUser = false; + this.openUserInfo = false; + this.listSeenMessages = []; + this.theme = localStorage.getItem('qortalTheme') + ? localStorage.getItem('qortalTheme') + : 'light'; + this.messagesToRender = []; + this.disableFetching = false; + this.isLoadingBefore = false; + this.isLoadingAfter = false; + this.disableAddingNewMessages = false; + this.lastReadMessageTimestamp = null; + this.messageQueue = []; + } + + addSeenMessage(val) { + this.listSeenMessages.push(val); + } + goToRepliedMessageFunc(val, val2) { + this.disableFetching = true; + this.goToRepliedMessage(val, val2); + } + + shouldGroupWithLastMessage(newMessage, lastGroupedMessage) { + if (!lastGroupedMessage) return false; + + return ( + Math.abs(lastGroupedMessage.timestamp - newMessage.timestamp) < + 600000 && + lastGroupedMessage.sender === newMessage.sender && + !lastGroupedMessage.repliedToData + ); + } + + clearLoaders() { + this.isLoadingBefore = false; + this.isLoadingAfter = false; + this.disableFetching = false; + } + 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.clearLoaders(); + this.requestUpdate(); + } + + async newListMessages(newMessages, message) { + let data = []; + const copy = [...newMessages]; + copy.forEach((newMessage) => { + const lastGroupedMessage = data[data.length - 1]; + + if ( + this.shouldGroupWithLastMessage(newMessage, lastGroupedMessage) + ) { + lastGroupedMessage.messages.push(newMessage); + } else { + data.push({ + messages: [newMessage], + ...newMessage, + }); + } + }); + + // const getCount = await parentEpml.request('apiCall', { + // type: 'api', + // 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` + // }) + this.messagesToRender = data; + this.clearLoaders(); + this.requestUpdate(); + await this.updateComplete; + } + + async newListMessagesUnreadMessages( + newMessages, + message, + lastReadMessageTimestamp, + count + ) { + let data = []; + const copy = [...newMessages]; + + let dividerPlaced = false; // To ensure the divider is added only once + + // Start from the end of the list (newest messages) + for (let i = copy.length - 1; i >= 0; i--) { + let newMessage = copy[i]; + + // Initialize a property for the divider + newMessage.isDivider = false; + + // Check if this is the message before which the divider should be placed + if ( + !dividerPlaced && + newMessage.timestamp <= lastReadMessageTimestamp + ) { + newMessage.isDivider = true; + dividerPlaced = true; // Ensure the divider is only added once + break; // Exit once the divider is placed + } + } + + copy.forEach((newMessage, groupIndex) => { + const lastGroupedMessage = data[data.length - 1]; + + if ( + this.shouldGroupWithLastMessage(newMessage, lastGroupedMessage) + ) { + lastGroupedMessage.messages.push(newMessage); + } else { + data.push({ + messages: [newMessage], + ...newMessage, + }); + } + }); + if (count > 0) { + this.disableAddingNewMessages = true; + } + this.messagesToRender = data; + this.clearLoaders(); + this.requestUpdate(); + await this.updateComplete; + const findElement = this.shadowRoot.getElementById('unread-divider-id'); + if (findElement) { + findElement.scrollIntoView({ behavior: 'auto', block: 'center' }); + } + } + + async addNewMessages(newMessages, type) { + if (this.disableAddingNewMessages && type === 'newComingInAuto') return; + let previousScrollTop; + let previousScrollHeight; + + const viewElement = this.shadowRoot.querySelector('#viewElement'); + previousScrollTop = viewElement.scrollTop; + previousScrollHeight = viewElement.scrollHeight; + + const copy = type === 'initial' ? [] : [...this.messagesToRender]; + + for (const newMessage of newMessages) { + const lastGroupedMessage = copy[copy.length - 1]; + + if ( + this.shouldGroupWithLastMessage(newMessage, lastGroupedMessage) + ) { + lastGroupedMessage.messages.push(newMessage); + } else { + copy.push({ + messages: [newMessage], + ...newMessage, + }); + } + } + + // Ensure that the total number of individual messages doesn't exceed totalMsgCount + let totalMessagesCount = copy.reduce( + (acc, group) => acc + group.messages.length, + 0 + ); + while (totalMessagesCount > totalMsgCount && copy.length) { + if ( + newMessages.length < chatLimit && + type !== 'newComingInAuto' && + type !== 'initial' + ) { + this.disableAddingNewMessages = false; + } + const firstGroup = copy[0]; + if ( + firstGroup.messages.length <= + totalMessagesCount - totalMsgCount + ) { + // If removing the whole first group achieves the goal, remove it + totalMessagesCount -= firstGroup.messages.length; + copy.shift(); + } else { + // Otherwise, trim individual messages from the first group + const messagesToRemove = totalMessagesCount - totalMsgCount; + firstGroup.messages.splice(0, messagesToRemove); + totalMessagesCount = totalMsgCount; + } + } + this.messagesToRender = copy; + this.requestUpdate(); + await this.updateComplete; + + if (type === 'initial') { + viewElement.scrollTop = viewElement.scrollHeight; + } + + this.clearLoaders(); + } + + async prependOldMessages(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); + } + previousMessage = message; + } + + // After processing all old messages, add the last group + if (currentMessageGroup) { + this.messagesToRender.unshift(currentMessageGroup); + } + + // Ensure that the total number of individual messages doesn't exceed totalMsgCount + let totalMessagesCount = this.messagesToRender.reduce( + (acc, group) => acc + group.messages.length, + 0 + ); + while ( + totalMessagesCount > totalMsgCount && + this.messagesToRender.length + ) { + this.disableAddingNewMessages = true; + const lastGroup = + this.messagesToRender[this.messagesToRender.length - 1]; + if ( + lastGroup.messages.length <= + totalMessagesCount - totalMsgCount + ) { + // If removing the whole last group achieves the goal, remove it + totalMessagesCount -= lastGroup.messages.length; + this.messagesToRender.pop(); + } else { + // Otherwise, trim individual messages from the last group + const messagesToRemove = totalMessagesCount - totalMsgCount; + lastGroup.messages.splice(-messagesToRemove, messagesToRemove); + totalMessagesCount = totalMsgCount; + } + } + this.clearLoaders(); + this.requestUpdate(); + } + + async replaceMessagesWithUpdate(updatedMessages) { + const viewElement = this.shadowRoot.querySelector('#viewElement'); + if (!viewElement) return; // Ensure the element exists + const isUserAtBottom = + viewElement.scrollTop + viewElement.clientHeight === + viewElement.scrollHeight; + + const previousScrollTop = viewElement.scrollTop; + const previousScrollHeight = viewElement.scrollHeight; + + // Using map to return a new array, rather than mutating the old one + const newMessagesToRender = this.messagesToRender.map((group) => { + // For each message, return the updated message if it exists, otherwise return the original message + const updatedGroupMessages = group.messages.map((message) => { + return updatedMessages[message.signature] + ? { ...message, ...updatedMessages[message.signature] } + : message; + }); + + // Return a new group object with updated messages + return { + ...group, + messages: updatedGroupMessages, + }; + }); + + this.messagesToRender = newMessagesToRender; + this.requestUpdate(); + await this.updateComplete; + + if (isUserAtBottom) { + viewElement.scrollTop = + viewElement.scrollHeight - viewElement.clientHeight; + } else { + // Adjust scroll position based on the difference in scroll heights + const newScrollHeight = viewElement.scrollHeight; + viewElement.scrollTop = + viewElement.scrollTop + + (newScrollHeight - viewElement.scrollHeight); + } + + this.clearUpdateMessageHashmap(); + this.clearLoaders(); + } + + async replaceMessagesWithUpdateByArray(updatedMessagesArray) { + let previousScrollTop; + let previousScrollHeight; + + const viewElement = this.shadowRoot.querySelector('#viewElement'); + previousScrollTop = viewElement.scrollTop; + previousScrollHeight = viewElement.scrollHeight; + for (let group of this.messagesToRender) { + for (let i = 0; i < group.messages.length; i++) { + const update = updatedMessagesArray.find( + (updatedMessage) => + updatedMessage.chatReference === + group.messages[i].signature || + updatedMessage.chatReference === + group.messages[i].originalSignature || + updatedMessage.chatReference === + group.messages[i].chatReference + ); + if (update) { + Object.assign(group.messages[i], update); + } + } + } + this.requestUpdate(); + const newScrollHeight = viewElement.scrollHeight; + viewElement.scrollTop = + previousScrollTop + (newScrollHeight - previousScrollHeight); + this.clearUpdateMessageHashmap(); + this.clearLoaders(); + } + + async updated(changedProperties) { + if (changedProperties && changedProperties.has('messages')) { + if (this.messages.type === 'initial') { + this.addNewMessages(this.messages.messages, 'initial'); + } else if (this.messages.type === 'initialLastSeen') { + this.newListMessagesUnreadMessages( + this.messages.messages, + 'initialLastSeen', + this.messages.lastReadMessageTimestamp, + this.messages.count + ); + } else if (this.messages.type === 'new') + this.addNewMessages(this.messages.messages); + else if (this.messages.type === 'newComingInAuto') + this.addNewMessages(this.messages.messages, 'newComingInAuto'); + else if (this.messages.type === 'old') + this.prependOldMessages(this.messages.messages); + else if (this.messages.type === 'inBetween') + this.newListMessages( + this.messages.messages, + this.messages.signature + ); + else if (this.messages.type === 'update') + this.replaceMessagesWithUpdateByArray(this.messages.messages); + } + if ( + changedProperties && + changedProperties.has('updateMessageHash') && + Object.keys(this.updateMessageHash).length > 0 + ) { + this.replaceMessagesWithUpdate(this.updateMessageHash); + } + if ( + changedProperties && + changedProperties.has('messageQueue') && + Object.keys(this.messageQueue).length > 0 + ) { + if (!this.disableAddingNewMessages) { + await new Promise((res) => { + setTimeout(() => { + res(); + }, 200); + }); + const viewElement = + this.shadowRoot.querySelector('#viewElement'); + viewElement.scrollTop = viewElement.scrollHeight + 200; + } + } + } + + isLastMessageBeforeUnread(message, formattedMessages) { + // if the message is the last one in the older messages list and its timestamp is before the user's last seen timestamp + if ( + message.timestamp < this.lastReadMessageTimestamp && + formattedMessages.indexOf(message) === formattedMessages.length - 21 + ) { + return true; + } + return false; + } + + 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; + + return html` + ${this.isLoadingBefore + ? html` +
+ +
+ ` + : ''} + + `; + } + + shouldUpdate(changedProperties) { + if (changedProperties.has('isLoadingMessages')) { + return true; + } + if ( + changedProperties.has('chatId') && + changedProperties.get('chatId') + ) { + return true; + } + if (changedProperties.has('openTipUser')) { + return true; + } + if (changedProperties.has('openUserInfo')) { + return true; + } + if (changedProperties.has('userName')) { + return true; + } + if (changedProperties.has('loggedInUserName')) { + return true; + } + if (changedProperties.has('updateMessageHash')) { + return true; + } + if (changedProperties.has('messagesToRender')) { + return true; + } + if (changedProperties.has('isLoadingBefore')) { + return true; + } + if (changedProperties.has('isLoadingAfter')) { + return true; + } + if (changedProperties.has('messageQueue')) { + return true; + } + // Only update element if prop1 changed. + return changedProperties.has('messages'); + } + + async getUpdateComplete() { + await super.getUpdateComplete(); + const marginElements = Array.from( + this.shadowRoot.querySelectorAll('message-template') + ); + await Promise.all(marginElements.map((el) => el.updateComplete)); + return true; + } + + setToggledMessage(message) { + toggledMessage = message; + } + + async firstUpdated() { + this.changeTheme(); + window.addEventListener('storage', () => { + const checkTheme = localStorage.getItem('qortalTheme'); + + if (checkTheme === 'dark') { + this.theme = 'dark'; + } else { + this.theme = 'light'; + } + document.querySelector('html').setAttribute('theme', this.theme); + }); + + this.emojiPicker.on('emoji', (selection) => { + this.sendMessage({ + type: 'reaction', + editedMessageObj: toggledMessage, + reaction: selection.emoji, + }); + }); + this.viewElement = this.shadowRoot.getElementById('viewElement'); + this.upObserverElement = this.shadowRoot.getElementById('upObserver'); + this.downObserverElement = + this.shadowRoot.getElementById('downObserver'); + this.bottomObserverForFetchingMessages = this.shadowRoot.getElementById( + 'bottomObserverForFetchingMessages' + ); + // Intialize Observers + this.upElementObserver(); + this.downElementObserver(); + this.bottomObserver(); + + this.clearConsole(); + setInterval(() => { + this.clearConsole(); + }, 60000); + } + + clearConsole() { + if (!isElectron()) { + } else { + console.clear(); + window.parent.electronAPI.clearCache(); + } + } + + changeTheme() { + const checkTheme = localStorage.getItem('qortalTheme'); + if (checkTheme === 'dark') { + this.theme = 'dark'; + } else { + this.theme = 'light'; + } + document.querySelector('html').setAttribute('theme', this.theme); + } + + _getOldMessage(_scrollElement) { + this.getOldMessage(_scrollElement); + } + _getAfterMessages(_scrollElement) { + this.getAfterMessages(_scrollElement); + } + + _upObserverhandler(entries) { + if (!entries[0].target || !entries[0].target.nextElementSibling) return; + if (entries[0].isIntersecting) { + if (this.disableFetching) { + return; + } + this.disableFetching = true; + this.isLoadingBefore = true; + let _scrollElement = entries[0].target.nextElementSibling; + this._getOldMessage(_scrollElement); + } + } + + _downObserverHandler(entries) { + if (!entries[0].isIntersecting) { + this.showLastMessageRefScroller(true); + } else { + this.showLastMessageRefScroller(false); + } + } + + __bottomObserverForFetchingMessagesHandler(entries) { + if (this.messagesToRender.length === 0 || this.disableFetching) { + return; + } + if ( + !entries[0].isIntersecting || + !entries[0].target || + !entries[0].target.previousElementSibling + ) { + } else { + this.disableFetching = true; + this.isLoadingAfter = true; + let _scrollElement = entries[0].target.previousElementSibling; + this._getAfterMessages(_scrollElement); + } + } + + upElementObserver() { + const options = { + root: this.viewElement, + rootMargin: '0px', + threshold: 1, + }; + const observer = new IntersectionObserver( + this._upObserverhandler, + options + ); + observer.observe(this.upObserverElement); + } + + downElementObserver() { + const options = {}; + // identify an element to observe + const elementToObserve = this.downObserverElement; + // passing it a callback function + const observer = new IntersectionObserver( + this._downObserverHandler, + options + ); + // call `observe()` on that MutationObserver instance, + // passing it the element to observe, and the options object + observer.observe(elementToObserve); + } + bottomObserver() { + const options = {}; + // identify an element to observe + const elementToObserve = this.bottomObserverForFetchingMessages; + // passing it a callback function + const observer = new IntersectionObserver( + this.__bottomObserverForFetchingMessagesHandler, + options + ); + // call `observe()` on that MutationObserver instance, + // passing it the element to observe, and the options object + observer.observe(elementToObserve); + } } -window.customElements.define('chat-scroller', ChatScroller) - +window.customElements.define('chat-scroller', ChatScroller); class MessageTemplate extends LitElement { - static get properties() { - return { - messageObj: { type: Object }, - emojiPicker: { attribute: false }, - escapeHTML: { attribute: false }, - hideMessages: { type: Array }, - openDialogPrivateMessage: { type: Boolean }, - openDialogBlockUser: { type: Boolean }, - showBlockAddressIcon: { type: Boolean }, - setRepliedToMessageObj: { attribute: false }, - setEditedMessageObj: { attribute: false }, - sendMessage: { attribute: false }, - sendMessageForward: { attribute: false }, - openDialogImage: { type: Boolean }, - openDialogGif: { type: Boolean }, - openDeleteImage: { type: Boolean }, - openDeleteAttachment: { type: Boolean }, - isImageLoaded: { type: Boolean }, - isGifLoaded: { type: Boolean }, - isFirstMessage: { type: Boolean }, - isSingleMessageInGroup: { type: Boolean }, - isLastMessageInGroup: { type: Boolean }, - setToggledMessage: { attribute: false }, - setForwardProperties: { attribute: false }, - viewImage: { type: Boolean }, - setOpenPrivateMessage: { attribute: false }, - setOpenTipUser: { attribute: false }, - setOpenUserInfo: { attribute: false }, - setUserName: { attribute: false }, - openTipUser: { type: Boolean }, - goToRepliedMessage: { attribute: false }, - listSeenMessages: { type: Array }, - addSeenMessage: { attribute: false }, - chatId: { type: String }, - isInProgress: {type: Boolean}, - id: {type: String} - } + static get properties() { + return { + messageObj: { type: Object }, + emojiPicker: { attribute: false }, + escapeHTML: { attribute: false }, + hideMessages: { type: Array }, + openDialogPrivateMessage: { type: Boolean }, + openDialogBlockUser: { type: Boolean }, + showBlockAddressIcon: { type: Boolean }, + setRepliedToMessageObj: { attribute: false }, + setEditedMessageObj: { attribute: false }, + sendMessage: { attribute: false }, + sendMessageForward: { attribute: false }, + openDialogImage: { type: Boolean }, + openDialogGif: { type: Boolean }, + openDeleteImage: { type: Boolean }, + openDeleteAttachment: { type: Boolean }, + isImageLoaded: { type: Boolean }, + isGifLoaded: { type: Boolean }, + isFirstMessage: { type: Boolean }, + isSingleMessageInGroup: { type: Boolean }, + isLastMessageInGroup: { type: Boolean }, + setToggledMessage: { attribute: false }, + setForwardProperties: { attribute: false }, + viewImage: { type: Boolean }, + setOpenPrivateMessage: { attribute: false }, + setOpenTipUser: { attribute: false }, + setOpenUserInfo: { attribute: false }, + setUserName: { attribute: false }, + openTipUser: { type: Boolean }, + goToRepliedMessage: { attribute: false }, + listSeenMessages: { type: Array }, + addSeenMessage: { attribute: false }, + chatId: { type: String }, + isInProgress: { type: Boolean }, + id: { type: String }, + }; + } + + constructor() { + super(); + this.messageObj = {}; + this.openDialogPrivateMessage = false; + this.openDialogBlockUser = false; + this.showBlockAddressIcon = false; + this.myAddress = + window.parent.reduxStore.getState().app.selectedAddress.address; + this.imageFetches = 0; + this.gifFetches = 0; + this.openDialogImage = false; + this.openDialogGif = false; + this.isImageLoaded = false; + this.isGifLoaded = false; + this.isFirstMessage = false; + this.isSingleMessageInGroup = false; + this.isLastMessageInGroup = false; + this.viewImage = false; + this.isInProgress = false; + } + + static get styles() { + return [chatStyles]; + } + + // Open & Close Private Message Chat Modal + showPrivateMessageModal() { + this.openDialogPrivateMessage = true; + } + + hidePrivateMessageModal() { + this.openDialogPrivateMessage = false; + } + + // Open & Close Block User Chat Modal + showBlockUserModal() { + this.openDialogBlockUser = true; + } + + hideBlockUserModal() { + this.openDialogBlockUser = false; + } + + showBlockIconFunc(bool) { + if (bool) { + this.showBlockAddressIcon = true; + } else { + this.showBlockAddressIcon = false; + } + } + + async downloadAttachment(attachment) { + const myNode = + window.parent.reduxStore.getState().app.nodeConfig.knownNodes[ + window.parent.reduxStore.getState().app.nodeConfig.node + ]; + + const nodeUrl = + myNode.protocol + '://' + myNode.domain + ':' + myNode.port; + try { + axios + .get( + `${nodeUrl}/arbitrary/QCHAT_ATTACHMENT/${attachment.name}/${attachment.identifier}?apiKey=${myNode.apiKey}`, + { responseType: 'blob' } + ) + .then((response) => { + let filename = attachment.attachmentName; + let blob = new Blob([response.data], { + type: 'application/octet-stream', + }); + this.saveFileToDisk(blob, filename); + }); + } catch (error) { + console.error(error); + } + } + + async saveFileToDisk(blob, fileName) { + try { + const fileHandle = await self.showSaveFilePicker({ + suggestedName: fileName, + types: [ + { + description: 'File', + }, + ], + }); + const writeFile = async (fileHandle, contents) => { + const writable = await fileHandle.createWritable(); + await writable.write(contents); + await writable.close(); + }; + writeFile(fileHandle, blob).then(() => console.log('FILE SAVED')); + } catch (error) { + console.log(error); + } + } + + setOpenDialogImage(val){ + this.openDialogImage = val } - constructor() { - super() - this.messageObj = {} - this.openDialogPrivateMessage = false - this.openDialogBlockUser = false - this.showBlockAddressIcon = false - this.myAddress = window.parent.reduxStore.getState().app.selectedAddress.address - this.imageFetches = 0 - this.gifFetches = 0 - this.openDialogImage = false - this.openDialogGif = false - this.isImageLoaded = false - this.isGifLoaded = false - this.isFirstMessage = false - this.isSingleMessageInGroup = false - this.isLastMessageInGroup = false - this.viewImage = false - this.isInProgress = false - } + firstUpdated() { + const autoSeeChatList = + window.parent.reduxStore.getState().app.autoLoadImageChats; + if ( + autoSeeChatList.includes(this.chatId) || + this.listSeenMessages.includes(this.messageObj.signature) + ) { + this.viewImage = true; + } - static get styles() { - return [chatStyles] - } + const tooltips = this.shadowRoot.querySelectorAll('vaadin-tooltip'); + tooltips.forEach((tooltip) => { + const overlay = tooltip.shadowRoot.querySelector( + 'vaadin-tooltip-overlay' + ); + overlay.shadowRoot.getElementById('overlay').style.cssText = + 'background-color: transparent; box-shadow: rgb(50 50 93 / 25%) 0px 2px 5px -1px, rgb(0 0 0 / 30%) 0px 1px 3px -1px'; + overlay.shadowRoot.getElementById('content').style.cssText = + 'background-color: var(--reactions-tooltip-bg); color: var(--chat-bubble-msg-color); text-align: center; padding: 20px 10px; border-radius: 8px; font-family: Roboto, sans-serif; letter-spacing: 0.3px; font-weight: 300; font-size: 13.5px; transition: all 0.3s ease-in-out;'; + }); + this.clearConsole(); + setInterval(() => { + this.clearConsole(); + }, 60000); + } + + shouldUpdate(changedProperties) { + if (changedProperties.has('messageObj')) { + return true; + } + if (changedProperties.has('showBlockAddressIcon')) { + return true; + } + if (changedProperties.has('openDialogBlockUser')) { + return true; + } + if (changedProperties.has('viewImage')) { + return true; + } + if (changedProperties.has('isImageLoaded')) { + return true; + } + if (changedProperties.has('openDialogImage')) { + return true; + } + if (changedProperties.has('openDialogPrivateMessage')) { + return true; + } + if (changedProperties.has('openDialogGif')) { + return true; + } + if (changedProperties.has('isGifLoaded')) { + return true; + } + return false; + } + + clearConsole() { + if (!isElectron()) { + } else { + console.clear(); + window.parent.electronAPI.clearCache(); + } + } + + render() { + const hidemsg = this.hideMessages; + let message = ''; + let messageVersion2 = ''; + let messageVersion2WithLink = null; + let reactions = []; + let repliedToData = null; + let image = null; + let gif = null; + let isImageDeleted = false; + let isAttachmentDeleted = false; + let version = 0; + let isForwarded = false; + let isEdited = false; + let attachment = null; + try { + const parsedMessageObj = JSON.parse(this.messageObj.decodedMessage); + if (+parsedMessageObj.version > 1 && parsedMessageObj.messageText) { + messageVersion2 = generateHTML(parsedMessageObj.messageText, [ + StarterKit, + Underline, + Highlight, + // other extensions … + ]); + messageVersion2WithLink = processText(messageVersion2); + } + message = parsedMessageObj.messageText; + repliedToData = this.messageObj.repliedToData; + isImageDeleted = parsedMessageObj.isImageDeleted; + isAttachmentDeleted = parsedMessageObj.isAttachmentDeleted; + // reactions = parsedMessageObj.reactions || [] + version = parsedMessageObj.version; + isForwarded = parsedMessageObj.type === 'forward'; + isEdited = parsedMessageObj.isEdited && true; + if ( + parsedMessageObj.attachments && + Array.isArray(parsedMessageObj.attachments) && + parsedMessageObj.attachments.length > 0 + ) { + attachment = parsedMessageObj.attachments[0]; + } + if ( + parsedMessageObj.images && + Array.isArray(parsedMessageObj.images) && + parsedMessageObj.images.length > 0 + ) { + image = parsedMessageObj.images[0]; + } + if ( + parsedMessageObj.gifs && + Array.isArray(parsedMessageObj.gifs) && + parsedMessageObj.gifs.length > 0 + ) { + gif = parsedMessageObj.gifs[0]; + } + } catch (error) { + message = this.messageObj.decodedMessage; + } + let avatarImg = ''; + let imageHTML = ''; + let imageHTMLDialog = ''; + let imageUrl = ''; + let gifHTML = ''; + let gifHTMLDialog = ''; + let gifUrl = ''; + let nameMenu = ''; + let levelFounder = ''; + let hideit = hidemsg.includes(this.messageObj.sender); + let forwarded = ''; + let edited = ''; + + levelFounder = html``; + + if (this.messageObj.senderName) { + const myNode = + window.parent.reduxStore.getState().app.nodeConfig.knownNodes[ + window.parent.reduxStore.getState().app.nodeConfig.node + ]; + const nodeUrl = + myNode.protocol + '://' + myNode.domain + ':' + myNode.port; + const avatarUrl = `${nodeUrl}/arbitrary/THUMBNAIL/${this.messageObj.senderName}/qortal_avatar?async=true&apiKey=${myNode.apiKey}`; + avatarImg = html``; + } else { + avatarImg = html``; + } - // Open & Close Private Message Chat Modal - showPrivateMessageModal() { - this.openDialogPrivateMessage = true - } + const createGif = (gif) => { + const gifHTMLRes = new Image(); + gifHTMLRes.src = gif; + gifHTMLRes.style = + 'max-width:45vh; max-height:40vh; border-radius: 5px; cursor: pointer;'; + gifHTMLRes.onclick = () => { + this.openDialogGif = true; + }; + gifHTMLRes.onload = () => { + this.isGifLoaded = true; + }; + gifHTMLRes.onerror = () => { + if (this.gifFetches < 4) { + setTimeout(() => { + this.gifFetches = this.gifFetches + 1; + gifHTMLRes.src = gif; + }, 10000); + } else { + gifHTMLRes.src = '/img/chain.png'; + gifHTMLRes.style = + 'max-width:45vh; max-height:20vh; border-radius: 5px; filter: opacity(0.5);'; + gifHTMLRes.onclick = () => {}; + this.isGifLoaded = true; + } + }; + return gifHTMLRes; + }; - hidePrivateMessageModal() { - this.openDialogPrivateMessage = false - } + if (image) { + const myNode = + window.parent.reduxStore.getState().app.nodeConfig.knownNodes[ + window.parent.reduxStore.getState().app.nodeConfig.node + ]; + const nodeUrl = + myNode.protocol + '://' + myNode.domain + ':' + myNode.port; + imageUrl = `${nodeUrl}/arbitrary/${image.service}/${image.name}/${image.identifier}?async=true&apiKey=${myNode.apiKey}`; - // Open & Close Block User Chat Modal - showBlockUserModal() { - this.openDialogBlockUser = true - } + if (this.viewImage || this.myAddress === this.messageObj.sender) { + imageHTML = html` this.setOpenDialogImage(val)} + >`; + // imageHTML = createImage(imageUrl) + // imageHTMLDialog = createImage(imageUrl) + // imageHTMLDialog.style = "height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px;" + } + } - hideBlockUserModal() { - this.openDialogBlockUser = false - } + if (gif) { + const myNode = + window.parent.reduxStore.getState().app.nodeConfig.knownNodes[ + window.parent.reduxStore.getState().app.nodeConfig.node + ]; + const nodeUrl = + myNode.protocol + '://' + myNode.domain + ':' + myNode.port; + gifUrl = `${nodeUrl}/arbitrary/${gif.service}/${gif.name}/${gif.identifier}?filepath=${gif.filePath}&apiKey=${myNode.apiKey}`; + if (this.viewImage || this.myAddress === this.messageObj.sender) { + gifHTML = createGif(gifUrl); + gifHTMLDialog = createGif(gifUrl); + gifHTMLDialog.style = + 'height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px;'; + } + } - showBlockIconFunc(bool) { - if (bool) { - this.showBlockAddressIcon = true - } else { - this.showBlockAddressIcon = false - } - } + nameMenu = html` + + ${this.messageObj.senderName + ? this.messageObj.senderName + : cropAddress(this.messageObj.sender)} + + `; - async downloadAttachment(attachment) { + forwarded = html` + + ${translate('blockpage.bcchange17')} + + `; - const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + edited = html` + + ${translate('chatpage.cchange68')} + + `; - const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port - try { - axios.get(`${nodeUrl}/arbitrary/QCHAT_ATTACHMENT/${attachment.name}/${attachment.identifier}?apiKey=${myNode.apiKey}`, { responseType: 'blob' }) - .then(response => { - let filename = attachment.attachmentName - let blob = new Blob([response.data], { type: "application/octet-stream" }) - this.saveFileToDisk(blob, filename) - }) - } catch (error) { - console.error(error) - } - } + if (repliedToData) { + try { + const parsedMsg = JSON.parse(repliedToData.decodedMessage); + repliedToData.decodedMessage = parsedMsg; + } catch (error) {} + } - async saveFileToDisk(blob, fileName) { - try { - const fileHandle = await self.showSaveFilePicker({ - suggestedName: fileName, - types: [{ - description: "File", - }] - }) - const writeFile = async (fileHandle, contents) => { - const writable = await fileHandle.createWritable() - await writable.write(contents) - await writable.close() - } - writeFile(fileHandle, blob).then(() => console.log("FILE SAVED")) - } catch (error) { - console.log(error) - } - } + let repliedToMessageText = ''; + if ( + repliedToData && + repliedToData.decodedMessage && + repliedToData.decodedMessage.messageText + ) { + try { + repliedToMessageText = generateHTML( + repliedToData.decodedMessage.messageText, + [ + StarterKit, + Underline, + Highlight, + // other extensions … + ] + ); + } catch (error) {} + } - firstUpdated() { - const autoSeeChatList = window.parent.reduxStore.getState().app.autoLoadImageChats - if (autoSeeChatList.includes(this.chatId) || this.listSeenMessages.includes(this.messageObj.signature)) { - this.viewImage = true - } + let replacedMessage = ''; + if (message && +version < 2) { + const escapedMessage = this.escapeHTML(message); + if (escapedMessage) { + replacedMessage = escapedMessage.replace( + new RegExp('\r?\n', 'g'), + '
' + ); + } + } - const tooltips = this.shadowRoot.querySelectorAll('vaadin-tooltip') - tooltips.forEach(tooltip => { - const overlay = tooltip.shadowRoot.querySelector('vaadin-tooltip-overlay') - overlay.shadowRoot.getElementById("overlay").style.cssText = "background-color: transparent; box-shadow: rgb(50 50 93 / 25%) 0px 2px 5px -1px, rgb(0 0 0 / 30%) 0px 1px 3px -1px" - overlay.shadowRoot.getElementById('content').style.cssText = "background-color: var(--reactions-tooltip-bg); color: var(--chat-bubble-msg-color); text-align: center; padding: 20px 10px; border-radius: 8px; font-family: Roboto, sans-serif; letter-spacing: 0.3px; font-weight: 300; font-size: 13.5px; transition: all 0.3s ease-in-out;" - }) - this.clearConsole() - setInterval(() => { - this.clearConsole() - }, 60000) - } - - shouldUpdate(changedProperties){ - if (changedProperties.has('messageObj')) { - return true - } - if(changedProperties.has('showBlockAddressIcon')){ - return true - } - if(changedProperties.has('openDialogBlockUser')){ - return true - } - if(changedProperties.has('viewImage')){ - return true - } - if(changedProperties.has('isImageLoaded')){ - return true - } - if(changedProperties.has('openDialogImage')){ - return true - } - if(changedProperties.has('openDialogPrivateMessage')){ - return true - } - if(changedProperties.has('openDialogGif')){ - return true - } - if(changedProperties.has('isGifLoaded')){ - return true - } - return false - } - - clearConsole() { - if (!isElectron()) { - } else { - console.clear() - window.parent.electronAPI.clearCache() - } - } - - render() { - const hidemsg = this.hideMessages - let message = "" - let messageVersion2 = "" - let messageVersion2WithLink = null - let reactions = [] - let repliedToData = null - let image = null - let gif = null - let isImageDeleted = false - let isAttachmentDeleted = false - let version = 0 - let isForwarded = false - let isEdited = false - let attachment = null - try { - const parsedMessageObj = JSON.parse(this.messageObj.decodedMessage) - if (+parsedMessageObj.version > 1 && parsedMessageObj.messageText) { - messageVersion2 = generateHTML(parsedMessageObj.messageText, [ - StarterKit, - Underline, - Highlight - // other extensions … - ]) - messageVersion2WithLink = processText(messageVersion2) - - } - message = parsedMessageObj.messageText - repliedToData = this.messageObj.repliedToData - isImageDeleted = parsedMessageObj.isImageDeleted - isAttachmentDeleted = parsedMessageObj.isAttachmentDeleted - // reactions = parsedMessageObj.reactions || [] - version = parsedMessageObj.version - isForwarded = parsedMessageObj.type === 'forward' - isEdited = parsedMessageObj.isEdited && true - if (parsedMessageObj.attachments && Array.isArray(parsedMessageObj.attachments) && parsedMessageObj.attachments.length > 0) { - attachment = parsedMessageObj.attachments[0] - } - if (parsedMessageObj.images && Array.isArray(parsedMessageObj.images) && parsedMessageObj.images.length > 0) { - image = parsedMessageObj.images[0] - } - if (parsedMessageObj.gifs && Array.isArray(parsedMessageObj.gifs) && parsedMessageObj.gifs.length > 0) { - gif = parsedMessageObj.gifs[0] - } - } catch (error) { - message = this.messageObj.decodedMessage - } - let avatarImg = '' - let imageHTML = '' - let imageHTMLDialog = '' - let imageUrl = '' - let gifHTML = '' - let gifHTMLDialog = '' - let gifUrl = '' - let nameMenu = '' - let levelFounder = '' - let hideit = hidemsg.includes(this.messageObj.sender) - let forwarded = '' - let edited = '' - - levelFounder = html`` - - if (this.messageObj.senderName) { - const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] - const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port - const avatarUrl = `${nodeUrl}/arbitrary/THUMBNAIL/${this.messageObj.senderName}/qortal_avatar?async=true&apiKey=${myNode.apiKey}` - avatarImg = html`` - } else { - avatarImg = html`` - } - - const createImage = (imageUrl) => { - const imageHTMLRes = new Image() - imageHTMLRes.src = imageUrl - imageHTMLRes.style = "max-width:45vh; max-height:40vh; border-radius: 5px; cursor: pointer;" - imageHTMLRes.onclick = () => { - this.openDialogImage = true - } - imageHTMLRes.onload = () => { - this.isImageLoaded = true - } - imageHTMLRes.onerror = () => { - if (this.imageFetches < 4) { - setTimeout(() => { - this.imageFetches = this.imageFetches + 1 - imageHTMLRes.src = imageUrl - }, 10000) - } else { - setTimeout(() => { - this.imageFetches = this.imageFetches + 1 - imageHTMLRes.src = imageUrl - }, 15000) - } - } - return imageHTMLRes - } - - const createGif = (gif) => { - const gifHTMLRes = new Image() - gifHTMLRes.src = gif - gifHTMLRes.style = "max-width:45vh; max-height:40vh; border-radius: 5px; cursor: pointer;" - gifHTMLRes.onclick = () => { - this.openDialogGif = true - } - gifHTMLRes.onload = () => { - this.isGifLoaded = true - } - gifHTMLRes.onerror = () => { - if (this.gifFetches < 4) { - setTimeout(() => { - this.gifFetches = this.gifFetches + 1 - gifHTMLRes.src = gif - }, 10000) - } else { - gifHTMLRes.src = '/img/chain.png' - gifHTMLRes.style = "max-width:45vh; max-height:20vh; border-radius: 5px; filter: opacity(0.5);" - gifHTMLRes.onclick = () => { } - this.isGifLoaded = true - } - } - return gifHTMLRes - } - - if (image) { - const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] - const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port - imageUrl = `${nodeUrl}/arbitrary/${image.service}/${image.name}/${image.identifier}?async=true&apiKey=${myNode.apiKey}` - - if (this.viewImage || this.myAddress === this.messageObj.sender) { - imageHTML = createImage(imageUrl) - imageHTMLDialog = createImage(imageUrl) - imageHTMLDialog.style = "height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px;" - } - } - - if (gif) { - const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] - const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port - gifUrl = `${nodeUrl}/arbitrary/${gif.service}/${gif.name}/${gif.identifier}?filepath=${gif.filePath}&apiKey=${myNode.apiKey}` - if (this.viewImage || this.myAddress === this.messageObj.sender) { - gifHTML = createGif(gifUrl) - gifHTMLDialog = createGif(gifUrl) - gifHTMLDialog.style = "height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px;" - } - } - - nameMenu = html` - - ${this.messageObj.senderName ? this.messageObj.senderName : cropAddress(this.messageObj.sender)} - - ` - - forwarded = html` - - ${translate("blockpage.bcchange17")} - - ` - - edited = html` - - ${translate("chatpage.cchange68")} - - ` - - if (repliedToData) { - try { - const parsedMsg = JSON.parse(repliedToData.decodedMessage) - repliedToData.decodedMessage = parsedMsg - } catch (error) { - } - - } - - let repliedToMessageText = '' - if (repliedToData && repliedToData.decodedMessage && repliedToData.decodedMessage.messageText) { - try { - repliedToMessageText = generateHTML(repliedToData.decodedMessage.messageText, [ - StarterKit, - Underline, - Highlight - // other extensions … - ]) - } catch (error) { - - } - } - - let replacedMessage = '' - if (message && +version < 2) { - const escapedMessage = this.escapeHTML(message) - if (escapedMessage) { - replacedMessage = escapedMessage.replace(new RegExp('\r?\n', 'g'), '
') - } - - } - - return hideit ? html`
  • ` : html` + return hideit + ? html`
  • ` + : html`
  • -
    - ${(this.isSingleMessageInGroup === false || - (this.isSingleMessageInGroup === true && this.isLastMessageInGroup === true)) - ? ( - html` -
    { - if (this.myAddress === this.messageObj.sender) return - this.setOpenUserInfo(true) - this.setUserName(this.messageObj) - }} class="message-data-avatar"> - ${avatarImg} -
    - ` - ) : - html` -
    - `} + style="${ + this.isSingleMessageInGroup === true && + this.isLastMessageInGroup === false && + 'margin-bottom: 0' + }"> +
    + ${ + this.isSingleMessageInGroup === false || + (this.isSingleMessageInGroup === true && + this.isLastMessageInGroup === true) + ? html` +
    { + if ( + this.myAddress === + this.messageObj.sender + ) + return; + this.setOpenUserInfo(true); + this.setUserName( + this.messageObj + ); + }} + class="message-data-avatar" + > + ${avatarImg} +
    + ` + : html` +
    + ` + }
    + ${ + this.myAddress === this.messageObj.sender && + 'message-myBg' + } + ${ + ((this.isFirstMessage === true && + this.isSingleMessageInGroup === false) || + (this.isSingleMessageInGroup === true && + this.isLastMessageInGroup === true)) && + this.myAddress !== this.messageObj.sender + ? 'message-triangle' + : ((this.isFirstMessage === true && + this.isSingleMessageInGroup === + false) || + (this.isSingleMessageInGroup === + true && + this.isLastMessageInGroup === + true)) && + this.myAddress === this.messageObj.sender + ? 'message-myTriangle' + : null + }`}" + style="${ + this.isSingleMessageInGroup === true && + this.isLastMessageInGroup === false + ? 'margin-bottom: 0;' + : null + } + ${ + this.isFirstMessage === false && + this.isSingleMessageInGroup === true && + this.isLastMessageInGroup === false + ? 'border-radius: 8px 25px 25px 8px;' + : this.isFirstMessage === true && + this.isSingleMessageInGroup === true && + this.isLastMessageInGroup === false + ? 'border-radius: 27px 25px 25px 12px;' + : this.isFirstMessage === false && + this.isSingleMessageInGroup === true && + this.isLastMessageInGroup === true + ? 'border-radius: 10px 25px 25px 0;' + : this.isFirstMessage === true && + this.isSingleMessageInGroup === false && + this.isLastMessageInGroup === true + ? 'border-radius: 25px 25px 25px 0px;' + : null + }"> - ${repliedToData && html` -
    { - this.goToRepliedMessage(repliedToData, this.messageObj) - }}> -

    - ${repliedToData.senderName ? repliedToData.senderName : cropAddress(repliedToData.sender)} -

    -

    - ${version && version.toString() === '1' ? html` - ${repliedToData.decodedMessage.messageText} - ` : ''} - ${+version > 1 && repliedToMessageText ? html` - ${unsafeHTML(repliedToMessageText)} - ` - : ''} -

    -
    - `} - ${image && !isImageDeleted && !this.viewImage && this.myAddress !== this.messageObj.sender ? html` -
    { - this.viewImage = true - // this.addSeenMessage(this.messageObj.signature) - }} - class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : ''].join(' ')} - style=${this.isFirstMessage && "margin-top: 10px;"}> -
    - ${translate("chatpage.cchange40")} -
    -
    - ` : html``} - ${!this.isImageLoaded && image && this.viewImage ? html` -
    -
    -
    - - `: ''} - ${image && !isImageDeleted && (this.viewImage || this.myAddress === this.messageObj.sender) ? html` -
    - ${imageHTML} - ${this.myAddress === this.messageObj.sender ? html` - { - this.openDeleteImage = true - }} - class="image-delete-icon" icon="vaadin:close" slot="icon"> - ` : ''} - -
    - ` : image && isImageDeleted ? html` -

    ${translate("chatpage.cchange80")}

    - ` : html``} - ${gif && !this.viewImage && this.myAddress !== this.messageObj.sender ? html` -
    { - this.viewImage = true - }} - class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : ''].join(' ')} - style=${this.isFirstMessage && "margin-top: 10px;"}> -
    - ${translate("gifs.gchange25")} -
    -
    - ` : html``} - ${gif && (this.viewImage || this.myAddress === this.messageObj.sender) ? html` -
    - ${gifHTML} -
    - ` : html``} - ${attachment && !isAttachmentDeleted ? - html` -
    await this.downloadAttachment(attachment)} class="attachment-container"> -
    - attachment-icon -
    -
    -

    - ${attachment && attachment.attachmentName} -

    -

    - ${roundToNearestDecimal(attachment.attachmentSize)} mb -

    -
    - - - ${this.myAddress === this.messageObj.sender - ? html` - { - e.stopPropagation() - this.openDeleteAttachment = true - }} - class="image-delete-icon" icon="vaadin:close" slot="icon"> - - ` : html``} -
    - ` - : attachment && isAttachmentDeleted ? - html` -
    -
    -

    - ${translate("chatpage.cchange82")} -

    -
    -
    - ` - : html``} + ${ + repliedToData && + html` +
    { + this.goToRepliedMessage( + repliedToData, + this.messageObj + ); + }} + > +

    + ${repliedToData.senderName + ? repliedToData.senderName + : cropAddress( + repliedToData.sender + )} +

    +

    + ${version && + version.toString() === '1' + ? html` + ${repliedToData + .decodedMessage + .messageText} + ` + : ''} + ${+version > 1 && + repliedToMessageText + ? html` + ${unsafeHTML( + repliedToMessageText + )} + ` + : ''} +

    +
    + ` + } + ${ + image && + !isImageDeleted && + !this.viewImage && + this.myAddress !== + this.messageObj.sender + ? html` +
    { + this.viewImage = true; + // this.addSeenMessage(this.messageObj.signature) + }} + class=${[ + `image-container`, + !this.isImageLoaded + ? 'defaultSize' + : '', + ].join(' ')} + style=${this + .isFirstMessage && + 'margin-top: 10px;'} + > +
    + ${translate( + 'chatpage.cchange40' + )} +
    +
    + ` + : html`` + } + + ${ + image && + !isImageDeleted && + (this.viewImage || + this.myAddress === + this.messageObj.sender) + ? html` +
    + ${imageHTML} + ${this.myAddress === + this.messageObj.sender + ? html` + { + this.openDeleteImage = true; + }} + class="image-delete-icon" + icon="vaadin:close" + slot="icon" + > + ` + : ''} +
    + ` + : image && isImageDeleted + ? html` +

    + ${translate( + 'chatpage.cchange80' + )} +

    + ` + : html`` + } + ${ + gif && + !this.viewImage && + this.myAddress !== + this.messageObj.sender + ? html` +
    { + this.viewImage = true; + }} + class=${[ + `image-container`, + !this.isImageLoaded + ? 'defaultSize' + : '', + ].join(' ')} + style=${this + .isFirstMessage && + 'margin-top: 10px;'} + > +
    + ${translate( + 'gifs.gchange25' + )} +
    +
    + ` + : html`` + } + ${ + gif && + (this.viewImage || + this.myAddress === + this.messageObj.sender) + ? html` +
    + ${gifHTML} +
    + ` + : html`` + } + ${ + attachment && !isAttachmentDeleted + ? html` +
    + await this.downloadAttachment( + attachment + )} + class="attachment-container" + > +
    + attachment-icon +
    +
    +

    + ${attachment && + attachment.attachmentName} +

    +

    + ${roundToNearestDecimal( + attachment.attachmentSize + )} + mb +

    +
    + + + ${this.myAddress === + this.messageObj.sender + ? html` + { + e.stopPropagation(); + this.openDeleteAttachment = true; + }} + class="image-delete-icon" + icon="vaadin:close" + slot="icon" + > + + ` + : html``} +
    + ` + : attachment && isAttachmentDeleted + ? html` +
    +
    +

    + ${translate( + 'chatpage.cchange82' + )} +

    +
    +
    + ` + : html`` + }
    - ${+version > 1 ? messageVersion2WithLink ? html`${messageVersion2WithLink}` : html` - ${unsafeHTML(messageVersion2)} - ` : ''} + style=${ + image && + replacedMessage !== '' && + 'margin-top: 15px;' + }> + ${ + +version > 1 + ? messageVersion2WithLink + ? html`${messageVersion2WithLink}` + : html` + ${unsafeHTML( + messageVersion2 + )} + ` + : '' + } - ${version && version.toString() === '1' ? html` - ${unsafeHTML(this.emojiPicker.parse(replacedMessage))} - ` : ''} - ${version && version.toString() === '0' ? html` - ${unsafeHTML(this.emojiPicker.parse(replacedMessage))} - ` : ''} + ${ + version && + version.toString() === '1' + ? html` + ${unsafeHTML( + this.emojiPicker.parse( + replacedMessage + ) + )} + ` + : '' + } + ${ + version && + version.toString() === '0' + ? html` + ${unsafeHTML( + this.emojiPicker.parse( + replacedMessage + ) + )} + ` + : '' + }
    - ${isEdited ? - html` - - ${edited} - - ` - : '' - } - ${this.isInProgress ? html` -

    ${translate('chatpage.cchange91')}

    - ` : html` - - `} + style=${ + isEdited + ? 'justify-content: space-between;' + : 'justify-content: flex-end;' + } + class="${ + (this.isFirstMessage === + false && + this + .isSingleMessageInGroup === + true && + this + .isLastMessageInGroup === + true) || + (this.isFirstMessage === true && + this + .isSingleMessageInGroup === + false && + this + .isLastMessageInGroup === + true) + ? 'message-data-time' + : 'message-data-time-hidden' + }"> + ${ + isEdited + ? html` + + ${edited} + + ` + : '' + } + ${ + this.isInProgress + ? html` +

    + ${translate( + 'chatpage.cchange91' + )} +

    + ` + : html` + + ` + }
    - ${this.isInProgress ? '' : html` - this.showPrivateMessageModal()} - .showBlockUserModal=${() => this.showBlockUserModal()} - .showBlockIconFunc=${(props) => this.showBlockIconFunc(props)} - .showBlockAddressIcon=${this.showBlockAddressIcon} - .originalMessage=${{ ...this.messageObj, message }} - .setRepliedToMessageObj=${this.setRepliedToMessageObj} - .setEditedMessageObj=${this.setEditedMessageObj} - .myAddress=${this.myAddress} - @blur=${() => this.showBlockIconFunc(false)} - .sendMessage=${this.sendMessage} - .sendMessageForward=${this.sendMessageForward} - version=${version} - .emojiPicker=${this.emojiPicker} - .setToggledMessage=${this.setToggledMessage} - .setForwardProperties=${this.setForwardProperties} - ?firstMessageInChat=${this.messageObj.firstMessageInChat} - .setOpenPrivateMessage=${(val) => this.setOpenPrivateMessage(val)} - .setOpenTipUser=${(val) => this.setOpenTipUser(val)} - .setUserName=${(val) => this.setUserName(val)} - .gif=${!!gif} - > - - `} + ${ + this.isInProgress + ? '' + : html` + + this.showPrivateMessageModal()} + .showBlockUserModal=${() => + this.showBlockUserModal()} + .showBlockIconFunc=${(props) => + this.showBlockIconFunc( + props + )} + .showBlockAddressIcon=${this + .showBlockAddressIcon} + .originalMessage=${{ + ...this.messageObj, + message, + }} + .setRepliedToMessageObj=${this + .setRepliedToMessageObj} + .setEditedMessageObj=${this + .setEditedMessageObj} + .myAddress=${this.myAddress} + @blur=${() => + this.showBlockIconFunc( + false + )} + .sendMessage=${this.sendMessage} + .sendMessageForward=${this + .sendMessageForward} + version=${version} + .emojiPicker=${this.emojiPicker} + .setToggledMessage=${this + .setToggledMessage} + .setForwardProperties=${this + .setForwardProperties} + ?firstMessageInChat=${this + .messageObj + .firstMessageInChat} + .setOpenPrivateMessage=${( + val + ) => + this.setOpenPrivateMessage( + val + )} + .setOpenTipUser=${(val) => + this.setOpenTipUser(val)} + .setUserName=${(val) => + this.setUserName(val)} + .gif=${!!gif} + > + + ` + }
    -
    +
    ${reactions.map((reaction, index) => { - return html` - this.sendMessage({ - type: 'reaction', - editedMessageObj: this.messageObj, - reaction: reaction.type, - })} - id=${`reactions-${indexMessageTemplate}`} - class="reactions-bg"> - ${reaction.type} - ${reaction.qty} - 3 ? - ( - `${reaction.users[0].name - ? reaction.users[0].nameMessageTemplate - : cropAddress(reaction.users[0].address)}, - ${reaction.users[1].name - ? reaction.users[1].name - : cropAddress(reaction.users[1].address)}, - ${reaction.users[2].name - ? reaction.users[2].name - : cropAddress(reaction.users[2].address)} - ${get("chatpage.cchange71")} ${reaction.users.length - 3} ${get("chatpage.cchange72")}${(reaction.users.length - 3) > 1 ? html`${get("chatpage.cchange73")}` : ""} ${get("chatpage.cchange74")} ${reaction.type}` - ) : reaction.users.length === 3 ? - ( - `${reaction.users[0].name - ? reaction.users[0].name - : cropAddress(reaction.users[0].address)}, - ${reaction.users[1].name - ? reaction.users[1].name - : cropAddress(reaction.users[1].address)} - ${get("chatpage.cchange71")} - ${reaction.users[2].name - ? reaction.users[2].name - : cropAddress(reaction.users[2].address)} ${get("chatpage.cchange74")} ${reaction.type}` - ) : reaction.users.length === 2 ? - ( - `${reaction.users[0].name - ? reaction.users[0].name - : cropAddress(reaction.users[0].address)} - ${get("chatpage.cchange71")} - ${reaction.users[1].name - ? reaction.users[1].namMessageTemplatee - : cropAddress(reaction.users[1].address)} ${get("chatpage.cchange74")} ${reaction.type}` - ) : reaction.users.length === 1 ? - ( - `${reaction.users[0].name - ? reaction.users[0].name - : cropAddress(reaction.users[0].address)} ${get("chatpage.cchange74")} ${reaction.type}` - ) - : ""}> - - - ` - })} + return html` + + this.sendMessage({ + type: 'reaction', + editedMessageObj: + this.messageObj, + reaction: reaction.type, + })} + id=${`reactions-${indexMessageTemplate}`} + class="reactions-bg" + > + ${reaction.type} ${reaction.qty} + 3 + ? `${ + reaction.users[0] + .name + ? reaction + .users[0] + .nameMessageTemplate + : cropAddress( + reaction + .users[0] + .address + ) + }, + ${ + reaction.users[1].name + ? reaction.users[1].name + : cropAddress( + reaction.users[1] + .address + ) + }, + ${ + reaction.users[2].name + ? reaction.users[2].name + : cropAddress( + reaction.users[2] + .address + ) + } + ${get('chatpage.cchange71')} ${ + reaction.users + .length - 3 + } ${get( + 'chatpage.cchange72' + )}${ + reaction.users + .length - + 3 > + 1 + ? html`${get( + 'chatpage.cchange73' + )}` + : '' + } ${get( + 'chatpage.cchange74' + )} ${reaction.type}` + : reaction.users.length === + 3 + ? `${ + reaction.users[0] + .name + ? reaction + .users[0] + .name + : cropAddress( + reaction + .users[0] + .address + ) + }, + ${ + reaction.users[1].name + ? reaction.users[1].name + : cropAddress( + reaction.users[1] + .address + ) + } + ${get('chatpage.cchange71')} + ${ + reaction.users[2].name + ? reaction.users[2].name + : cropAddress( + reaction.users[2] + .address + ) + } ${get('chatpage.cchange74')} ${ + reaction.type + }` + : reaction.users.length === + 2 + ? `${ + reaction.users[0] + .name + ? reaction + .users[0] + .name + : cropAddress( + reaction + .users[0] + .address + ) + } + ${get('chatpage.cchange71')} + ${ + reaction.users[1].name + ? reaction.users[1] + .namMessageTemplatee + : cropAddress( + reaction.users[1] + .address + ) + } ${get('chatpage.cchange74')} ${ + reaction.type + }` + : reaction.users.length === + 1 + ? `${ + reaction.users[0] + .name + ? reaction + .users[0] + .name + : cropAddress( + reaction + .users[0] + .address + ) + } ${get( + 'chatpage.cchange74' + )} ${reaction.type}` + : ''} + > + + + `; + })}
    @@ -1698,7 +2241,11 @@ class MessageTemplate extends LitElement { this.hidePrivateMessageModal()} .hideBlockUserModal=${() => this.hideBlockUserModal()} toblockaddress=${this.messageObj.sender} @@ -1708,30 +2255,33 @@ class MessageTemplate extends LitElement { id="showDialogPublicKey" ?open=${this.openDialogImage} @closed=${() => { - this.openDialogImage = false - }}> + this.openDialogImage = false; + }}>
    - ${imageHTMLDialog} + ${this.openDialogImage ? html` + + ` : ''} +
    { -MessageTemplate - this.openDialogImage = false - }} + MessageTemplate; + this.openDialogImage = false; + }} > - ${translate("general.close")} + ${translate('general.close')} { - this.openDialogGif = false - }}>MessageTemplate + this.openDialogGif = false; + }}>MessageTemplate
    ${gifHTMLDialog} @@ -1741,34 +2291,35 @@ MessageTemplate dialogAction="cancel" class="red" @click=${() => { - - this.openDialogGif = false - }} + this.openDialogGif = false; + }} > - ${translate("general.close")} + ${translate('general.close')} MessageTemplate { - this.openDeleteImage = false - }}> + this.openDeleteImage = false; + }}>
    -

    ${translate("chatpage.cchange78")}

    +

    ${translate('chatpage.cchange78')}

    -