Reduce api calls and log spam

This commit is contained in:
AlphaX-Projects 2023-07-24 18:47:58 +02:00
parent 360e82e621
commit b086d2ddc1
3 changed files with 905 additions and 971 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,57 +1,66 @@
import { LitElement, html, css } from 'lit'; import { LitElement, html, css } from 'lit'
import { render } from 'lit/html.js'; import { render } from 'lit/html.js'
import { repeat } from 'lit/directives/repeat.js'; import { repeat } from 'lit/directives/repeat.js'
import { translate, get } from 'lit-translate'; import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate'
import { unsafeHTML } from 'lit/directives/unsafe-html.js'; import { unsafeHTML } from 'lit/directives/unsafe-html.js'
import { chatStyles } from './ChatScroller-css.js' import { chatStyles } from './ChatScroller-css.js'
import { Epml } from "../../../epml"; import { Epml } from '../../../epml'
import { cropAddress } from "../../utils/cropAddress"; import { cropAddress } from "../../utils/cropAddress"
import { roundToNearestDecimal } from '../../utils/roundToNearestDecimal.js'; import { roundToNearestDecimal } from '../../utils/roundToNearestDecimal.js'
import './LevelFounder.js'; import { EmojiPicker } from 'emoji-picker-js'
import './NameMenu.js'; import { generateHTML } from '@tiptap/core'
import './ChatModals.js';
import './WrapperModal'; import axios from 'axios'
import "./UserInfo/UserInfo";
import '@vaadin/icons';
import '@vaadin/icon';
import '@vaadin/tooltip';
import '@material/mwc-button';
import '@material/mwc-dialog';
import '@material/mwc-icon';
import { EmojiPicker } from 'emoji-picker-js';
import { generateHTML } from '@tiptap/core';
import axios from "axios";
import StarterKit from '@tiptap/starter-kit'
import Underline from '@tiptap/extension-underline';
import Highlight from '@tiptap/extension-highlight' import Highlight from '@tiptap/extension-highlight'
import ShortUniqueId from 'short-unique-id'; 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 '@material/mwc-button'
import '@material/mwc-dialog'
import '@material/mwc-icon'
import '@vaadin/icon'
import '@vaadin/icons'
import '@vaadin/tooltip'
registerTranslateConfig({
loader: lang => fetch(`/language/${lang}.json`).then(res => res.json())
})
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
let toggledMessage = {} let toggledMessage = {}
const uid = new ShortUniqueId() const uid = new ShortUniqueId()
const getApiKey = () => { const getApiKey = () => {
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
let apiKey = myNode.apiKey; let apiKey = myNode.apiKey
return apiKey; return apiKey
} }
const extractComponents = async (url) => { const extractComponents = async (url) => {
if (!url.startsWith("qortal://")) { if (!url.startsWith("qortal://")) {
return null; return null
} }
url = url.replace(/^(qortal\:\/\/)/, ""); url = url.replace(/^(qortal\:\/\/)/, "")
if (url.includes("/")) { if (url.includes("/")) {
let parts = url.split("/"); let parts = url.split("/")
const service = parts[0].toUpperCase(); const service = parts[0].toUpperCase()
parts.shift(); parts.shift()
const name = parts[0]; const name = parts[0]
parts.shift(); parts.shift()
let identifier; let identifier
if (parts.length > 0) { if (parts.length > 0) {
identifier = parts[0]; // Do not shift yet identifier = parts[0] // Do not shift yet
// Check if a resource exists with this service, name and identifier combination // Check if a resource exists with this service, name and identifier combination
let responseObj = await parentEpml.request('apiCall', { let responseObj = await parentEpml.request('apiCall', {
url: `/arbitrary/resource/status/${service}/${name}/${identifier}?apiKey=${getApiKey()}` url: `/arbitrary/resource/status/${service}/${name}/${identifier}?apiKey=${getApiKey()}`
@ -59,48 +68,48 @@ const extractComponents = async (url) => {
if (responseObj.totalChunkCount > 0) { if (responseObj.totalChunkCount > 0) {
// Identifier exists, so don't include it in the path // Identifier exists, so don't include it in the path
parts.shift(); parts.shift()
} }
else { else {
identifier = null; identifier = null
} }
} }
const path = parts.join("/"); const path = parts.join("/")
const components = {}; const components = {}
components["service"] = service; components["service"] = service
components["name"] = name; components["name"] = name
components["identifier"] = identifier; components["identifier"] = identifier
components["path"] = path; components["path"] = path
return components; return components
} }
return null; return null
} }
function processText(input) { function processText(input) {
const linkRegex = /(qortal:\/\/\S+)/g; const linkRegex = /(qortal:\/\/\S+)/g
function processNode(node) { function processNode(node) {
if (node.nodeType === Node.TEXT_NODE) { if (node.nodeType === Node.TEXT_NODE) {
const parts = node.textContent.split(linkRegex); const parts = node.textContent.split(linkRegex)
if (parts.length > 1) { if (parts.length > 1) {
const fragment = document.createDocumentFragment(); const fragment = document.createDocumentFragment()
parts.forEach((part) => { parts.forEach((part) => {
if (part.startsWith('qortal://')) { if (part.startsWith('qortal://')) {
const link = document.createElement('span'); const link = document.createElement('span')
// Store the URL in a data attribute // Store the URL in a data attribute
link.setAttribute('data-url', part); link.setAttribute('data-url', part)
link.textContent = part; link.textContent = part
link.style.color = 'var(--nav-text-color)'; link.style.color = 'var(--nav-text-color)'
link.style.textDecoration = 'underline'; link.style.textDecoration = 'underline'
link.style.cursor = 'pointer' link.style.cursor = 'pointer'
link.addEventListener('click', async (e) => { link.addEventListener('click', async (e) => {
e.preventDefault(); e.preventDefault()
try { try {
const res = await extractComponents(part) const res = await extractComponents(part)
if (!res) return if (!res) return
@ -134,31 +143,32 @@ function processText(input) {
console.log({ error }) console.log({ error })
} }
}); })
fragment.appendChild(link); fragment.appendChild(link)
} else { } else {
const textNode = document.createTextNode(part); const textNode = document.createTextNode(part)
fragment.appendChild(textNode); fragment.appendChild(textNode)
} }
}); })
node.replaceWith(fragment); node.replaceWith(fragment)
} }
} else { } else {
for (const childNode of Array.from(node.childNodes)) { for (const childNode of Array.from(node.childNodes)) {
processNode(childNode); processNode(childNode)
} }
} }
} }
const wrapper = document.createElement('div'); const wrapper = document.createElement('div')
wrapper.innerHTML = input; wrapper.innerHTML = input
processNode(wrapper); processNode(wrapper)
return wrapper return wrapper
} }
class ChatScroller extends LitElement { class ChatScroller extends LitElement {
static get properties() { static get properties() {
return { return {
@ -193,7 +203,7 @@ class ChatScroller extends LitElement {
} }
static get styles() { static get styles() {
return [chatStyles]; return [chatStyles]
} }
constructor() { constructor() {
@ -203,8 +213,8 @@ class ChatScroller extends LitElement {
this._downObserverHandler = this._downObserverHandler.bind(this) this._downObserverHandler = this._downObserverHandler.bind(this)
this.myAddress = window.parent.reduxStore.getState().app.selectedAddress.address this.myAddress = window.parent.reduxStore.getState().app.selectedAddress.address
this.hideMessages = JSON.parse(localStorage.getItem("MessageBlockedAddresses") || "[]") this.hideMessages = JSON.parse(localStorage.getItem("MessageBlockedAddresses") || "[]")
this.openTipUser = false; this.openTipUser = false
this.openUserInfo = false; this.openUserInfo = false
this.listSeenMessages = [] this.listSeenMessages = []
} }
@ -214,39 +224,39 @@ class ChatScroller extends LitElement {
render() { render() {
let formattedMessages = this.messages.reduce((messageArray, message, index) => { let formattedMessages = this.messages.reduce((messageArray, message, index) => {
const lastGroupedMessage = messageArray[messageArray.length - 1]; const lastGroupedMessage = messageArray[messageArray.length - 1]
let timestamp; let timestamp
let sender; let sender
let repliedToData; let repliedToData
let firstMessageInChat; let firstMessageInChat
if (index === 0) { if (index === 0) {
firstMessageInChat = true; firstMessageInChat = true
} else { } else {
firstMessageInChat = false; firstMessageInChat = false
} }
message = { ...message, firstMessageInChat } message = { ...message, firstMessageInChat }
if (lastGroupedMessage) { if (lastGroupedMessage) {
timestamp = lastGroupedMessage.timestamp; timestamp = lastGroupedMessage.timestamp
sender = lastGroupedMessage.sender; sender = lastGroupedMessage.sender
repliedToData = lastGroupedMessage.repliedToData; repliedToData = lastGroupedMessage.repliedToData
} }
const isSameGroup = Math.abs(timestamp - message.timestamp) < 600000 && sender === message.sender && !repliedToData; const isSameGroup = Math.abs(timestamp - message.timestamp) < 600000 && sender === message.sender && !repliedToData
if (isSameGroup) { if (isSameGroup) {
messageArray[messageArray.length - 1].messages = [ messageArray[messageArray.length - 1].messages = [
...(messageArray[messageArray.length - 1].messages || []), ...(messageArray[messageArray.length - 1].messages || []),
message message
]; ]
} else { } else {
messageArray.push({ messageArray.push({
messages: [message], messages: [message],
...message ...message
}); })
} }
return messageArray; return messageArray
}, []) }, [])
@ -312,18 +322,18 @@ class ChatScroller extends LitElement {
return true return true
} }
// Only update element if prop1 changed. // Only update element if prop1 changed.
return changedProperties.has('messages'); return changedProperties.has('messages')
} }
async getUpdateComplete() { async getUpdateComplete() {
await super.getUpdateComplete(); await super.getUpdateComplete()
const marginElements = Array.from(this.shadowRoot.querySelectorAll('message-template')); const marginElements = Array.from(this.shadowRoot.querySelectorAll('message-template'))
await Promise.all(marginElements.map(el => el.updateComplete)); await Promise.all(marginElements.map(el => el.updateComplete))
return true; return true
} }
setToggledMessage(message) { setToggledMessage(message) {
toggledMessage = message; toggledMessage = message
} }
async firstUpdated() { async firstUpdated() {
@ -334,15 +344,15 @@ class ChatScroller extends LitElement {
editedMessageObj: toggledMessage, editedMessageObj: toggledMessage,
reaction: selection.emoji, reaction: selection.emoji,
}) })
}); })
this.viewElement = this.shadowRoot.getElementById('viewElement'); this.viewElement = this.shadowRoot.getElementById('viewElement')
this.upObserverElement = this.shadowRoot.getElementById('upObserver'); this.upObserverElement = this.shadowRoot.getElementById('upObserver')
this.downObserverElement = this.shadowRoot.getElementById('downObserver'); this.downObserverElement = this.shadowRoot.getElementById('downObserver')
// Intialize Observers // Intialize Observers
this.upElementObserver(); this.upElementObserver()
this.downElementObserver(); this.downElementObserver()
await this.getUpdateComplete(); await this.getUpdateComplete()
this.viewElement.scrollTop = this.viewElement.scrollHeight + 50; this.viewElement.scrollTop = this.viewElement.scrollHeight + 50
} }
_getOldMessage(_scrollElement) { _getOldMessage(_scrollElement) {
@ -358,19 +368,19 @@ class ChatScroller extends LitElement {
if (this.messages.length < 20) { if (this.messages.length < 20) {
return return
} }
this.setIsLoadingMessages(true); this.setIsLoadingMessages(true)
let _scrollElement = entries[0].target.nextElementSibling; let _scrollElement = entries[0].target.nextElementSibling
this._getOldMessage(_scrollElement); this._getOldMessage(_scrollElement)
} }
} }
_downObserverHandler(entries) { _downObserverHandler(entries) {
if (!entries[0].isIntersecting) { if (!entries[0].isIntersecting) {
let _scrollElement = entries[0].target.previousElementSibling; let _scrollElement = entries[0].target.previousElementSibling
// this._getOldMessageAfter(_scrollElement); // this._getOldMessageAfter(_scrollElement)
this.showLastMessageRefScroller(true); this.showLastMessageRefScroller(true)
} else { } else {
this.showLastMessageRefScroller(false); this.showLastMessageRefScroller(false)
} }
} }
@ -379,9 +389,9 @@ class ChatScroller extends LitElement {
root: this.viewElement, root: this.viewElement,
rootMargin: '0px', rootMargin: '0px',
threshold: 1 threshold: 1
}; }
const observer = new IntersectionObserver(this._upObserverhandler, options); const observer = new IntersectionObserver(this._upObserverhandler, options)
observer.observe(this.upObserverElement); observer.observe(this.upObserverElement)
} }
downElementObserver() { downElementObserver() {
@ -391,12 +401,12 @@ class ChatScroller extends LitElement {
threshold: 1 threshold: 1
} }
// identify an element to observe // identify an element to observe
const elementToObserve = this.downObserverElement; const elementToObserve = this.downObserverElement
// passing it a callback function // passing it a callback function
const observer = new IntersectionObserver(this._downObserverHandler, options); const observer = new IntersectionObserver(this._downObserverHandler, options)
// call `observe()` on that MutationObserver instance, // call `observe()` on that MutationObserver instance,
// passing it the element to observe, and the options object // passing it the element to observe, and the options object
observer.observe(elementToObserve); observer.observe(elementToObserve)
} }
} }
@ -442,7 +452,7 @@ class MessageTemplate extends LitElement {
} }
constructor() { constructor() {
super(); super()
this.messageObj = {} this.messageObj = {}
this.openDialogPrivateMessage = false this.openDialogPrivateMessage = false
this.openDialogBlockUser = false this.openDialogBlockUser = false
@ -461,7 +471,7 @@ class MessageTemplate extends LitElement {
} }
static get styles() { static get styles() {
return [chatStyles]; return [chatStyles]
} }
@ -485,26 +495,26 @@ class MessageTemplate extends LitElement {
showBlockIconFunc(bool) { showBlockIconFunc(bool) {
if (bool) { if (bool) {
this.showBlockAddressIcon = true; this.showBlockAddressIcon = true
} else { } else {
this.showBlockAddressIcon = false; this.showBlockAddressIcon = false
} }
} }
async downloadAttachment(attachment) { async downloadAttachment(attachment) {
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; 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 nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
try { try {
axios.get(`${nodeUrl}/arbitrary/QCHAT_ATTACHMENT/${attachment.name}/${attachment.identifier}?apiKey=${myNode.apiKey}`, { responseType: 'blob' }) axios.get(`${nodeUrl}/arbitrary/QCHAT_ATTACHMENT/${attachment.name}/${attachment.identifier}?apiKey=${myNode.apiKey}`, { responseType: 'blob' })
.then(response => { .then(response => {
let filename = attachment.attachmentName; let filename = attachment.attachmentName
let blob = new Blob([response.data], { type: "application/octet-stream" }); let blob = new Blob([response.data], { type: "application/octet-stream" })
this.saveFileToDisk(blob, filename); this.saveFileToDisk(blob, filename)
}) })
} catch (error) { } catch (error) {
console.error(error); console.error(error)
} }
} }
@ -533,31 +543,31 @@ class MessageTemplate extends LitElement {
this.viewImage = true this.viewImage = true
} }
const tooltips = this.shadowRoot.querySelectorAll('vaadin-tooltip'); const tooltips = this.shadowRoot.querySelectorAll('vaadin-tooltip')
tooltips.forEach(tooltip => { tooltips.forEach(tooltip => {
const overlay = tooltip.shadowRoot.querySelector('vaadin-tooltip-overlay'); 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("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;"; 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;"
}); })
} }
render() { render() {
const hidemsg = this.hideMessages; const hidemsg = this.hideMessages
let message = ""; let message = ""
let messageVersion2 = "" let messageVersion2 = ""
let messageVersion2WithLink = null let messageVersion2WithLink = null
let reactions = []; let reactions = []
let repliedToData = null; let repliedToData = null
let image = null; let image = null
let gif = null; let gif = null
let isImageDeleted = false; let isImageDeleted = false
let isAttachmentDeleted = false; let isAttachmentDeleted = false
let version = 0; let version = 0
let isForwarded = false let isForwarded = false
let isEdited = false let isEdited = false
let attachment = null; let attachment = null
try { try {
const parsedMessageObj = JSON.parse(this.messageObj.decodedMessage); const parsedMessageObj = JSON.parse(this.messageObj.decodedMessage)
if (+parsedMessageObj.version > 1 && parsedMessageObj.messageText) { if (+parsedMessageObj.version > 1 && parsedMessageObj.messageText) {
messageVersion2 = generateHTML(parsedMessageObj.messageText, [ messageVersion2 = generateHTML(parsedMessageObj.messageText, [
StarterKit, StarterKit,
@ -568,121 +578,122 @@ class MessageTemplate extends LitElement {
messageVersion2WithLink = processText(messageVersion2) messageVersion2WithLink = processText(messageVersion2)
} }
message = parsedMessageObj.messageText; message = parsedMessageObj.messageText
repliedToData = this.messageObj.repliedToData; repliedToData = this.messageObj.repliedToData
isImageDeleted = parsedMessageObj.isImageDeleted; isImageDeleted = parsedMessageObj.isImageDeleted
isAttachmentDeleted = parsedMessageObj.isAttachmentDeleted; isAttachmentDeleted = parsedMessageObj.isAttachmentDeleted
// reactions = parsedMessageObj.reactions || []; // reactions = parsedMessageObj.reactions || []
version = parsedMessageObj.version; version = parsedMessageObj.version
isForwarded = parsedMessageObj.type === 'forward'; isForwarded = parsedMessageObj.type === 'forward'
isEdited = parsedMessageObj.isEdited && true; isEdited = parsedMessageObj.isEdited && true
if (parsedMessageObj.attachments && Array.isArray(parsedMessageObj.attachments) && parsedMessageObj.attachments.length > 0) { if (parsedMessageObj.attachments && Array.isArray(parsedMessageObj.attachments) && parsedMessageObj.attachments.length > 0) {
attachment = parsedMessageObj.attachments[0]; attachment = parsedMessageObj.attachments[0]
} }
if (parsedMessageObj.images && Array.isArray(parsedMessageObj.images) && parsedMessageObj.images.length > 0) { if (parsedMessageObj.images && Array.isArray(parsedMessageObj.images) && parsedMessageObj.images.length > 0) {
image = parsedMessageObj.images[0]; image = parsedMessageObj.images[0]
} }
if (parsedMessageObj.gifs && Array.isArray(parsedMessageObj.gifs) && parsedMessageObj.gifs.length > 0) { if (parsedMessageObj.gifs && Array.isArray(parsedMessageObj.gifs) && parsedMessageObj.gifs.length > 0) {
gif = parsedMessageObj.gifs[0]; gif = parsedMessageObj.gifs[0]
} }
} catch (error) { } catch (error) {
message = this.messageObj.decodedMessage; message = this.messageObj.decodedMessage
} }
let avatarImg = ''; let avatarImg = ''
let imageHTML = ''; let imageHTML = ''
let imageHTMLDialog = ''; let imageHTMLDialog = ''
let imageUrl = ''; let imageUrl = ''
let gifHTML = ''; let gifHTML = ''
let gifHTMLDialog = ''; let gifHTMLDialog = ''
let gifUrl = ''; let gifUrl = ''
let nameMenu = ''; let nameMenu = ''
let levelFounder = ''; let levelFounder = ''
let hideit = hidemsg.includes(this.messageObj.sender); let hideit = hidemsg.includes(this.messageObj.sender)
let forwarded = '' let forwarded = ''
let edited = '' let edited = ''
levelFounder = html`<level-founder checkleveladdress="${this.messageObj.sender}"></level-founder>`; levelFounder = html`<level-founder checkleveladdress="${this.messageObj.sender}"></level-founder>`
if (this.messageObj.senderName) { if (this.messageObj.senderName) {
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; 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 nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
const avatarUrl = `${nodeUrl}/arbitrary/THUMBNAIL/${this.messageObj.senderName}/qortal_avatar?async=true&apiKey=${myNode.apiKey}`; const avatarUrl = `${nodeUrl}/arbitrary/THUMBNAIL/${this.messageObj.senderName}/qortal_avatar?async=true&apiKey=${myNode.apiKey}`
avatarImg = html`<img src="${avatarUrl}" style="max-width:100%; max-height:100%;" onerror="this.onerror=null; this.src='/img/incognito.png';" />`; avatarImg = html`<img src="${avatarUrl}" style="max-width:100%; max-height:100%;" onerror="this.onerror=null; this.src='/img/incognito.png';" />`
} else { } else {
avatarImg = html`<img src='/img/incognito.png' style="max-width:100%; max-height:100%;" onerror="this.onerror=null;" />` avatarImg = html`<img src='/img/incognito.png' style="max-width:100%; max-height:100%;" onerror="this.onerror=null;" />`
} }
const createImage = (imageUrl) => { const createImage = (imageUrl) => {
const imageHTMLRes = new Image(); const imageHTMLRes = new Image()
imageHTMLRes.src = imageUrl; imageHTMLRes.src = imageUrl
imageHTMLRes.style = "max-width:45vh; max-height:40vh; border-radius: 5px; cursor: pointer"; imageHTMLRes.style = "max-width:45vh; max-height:40vh; border-radius: 5px; cursor: pointer;"
imageHTMLRes.onclick = () => { imageHTMLRes.onclick = () => {
this.openDialogImage = true; this.openDialogImage = true
} }
imageHTMLRes.onload = () => { imageHTMLRes.onload = () => {
this.isImageLoaded = true; this.isImageLoaded = true
} }
imageHTMLRes.onerror = () => { imageHTMLRes.onerror = () => {
if (this.imageFetches < 4) { if (this.imageFetches < 4) {
setTimeout(() => { setTimeout(() => {
this.imageFetches = this.imageFetches + 1; this.imageFetches = this.imageFetches + 1
imageHTMLRes.src = imageUrl; imageHTMLRes.src = imageUrl
}, 2000); }, 10000)
} else { } else {
setTimeout(() => { setTimeout(() => {
this.imageFetches = this.imageFetches + 1; this.imageFetches = this.imageFetches + 1
imageHTMLRes.src = imageUrl; imageHTMLRes.src = imageUrl
}, 6000); }, 15000)
} }
}; }
return imageHTMLRes; return imageHTMLRes
} }
const createGif = (gif) => { const createGif = (gif) => {
const gifHTMLRes = new Image(); const gifHTMLRes = new Image()
gifHTMLRes.src = gif; gifHTMLRes.src = gif
gifHTMLRes.style = "max-width:45vh; max-height:40vh; border-radius: 5px; cursor: pointer"; gifHTMLRes.style = "max-width:45vh; max-height:40vh; border-radius: 5px; cursor: pointer;"
gifHTMLRes.onclick = () => { gifHTMLRes.onclick = () => {
this.openDialogGif = true; this.openDialogGif = true
} }
gifHTMLRes.onload = () => { gifHTMLRes.onload = () => {
this.isGifLoaded = true; this.isGifLoaded = true
} }
gifHTMLRes.onerror = () => { gifHTMLRes.onerror = () => {
if (this.gifFetches < 4) { if (this.gifFetches < 4) {
setTimeout(() => { setTimeout(() => {
this.gifFetches = this.gifFetches + 1; this.gifFetches = this.gifFetches + 1
gifHTMLRes.src = gif; gifHTMLRes.src = gif
}, 500); }, 10000)
} else { } else {
gifHTMLRes.src = '/img/chain.png'; gifHTMLRes.src = '/img/chain.png'
gifHTMLRes.style = "max-width:45vh; max-height:20vh; border-radius: 5px; filter: opacity(0.5)"; gifHTMLRes.style = "max-width:45vh; max-height:20vh; border-radius: 5px; filter: opacity(0.5);"
gifHTMLRes.onclick = () => { } gifHTMLRes.onclick = () => { }
this.isGifLoaded = true this.isGifLoaded = true
} }
}; }
return gifHTMLRes; return gifHTMLRes
} }
if (image) { if (image) {
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; 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 nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
imageUrl = `${nodeUrl}/arbitrary/${image.service}/${image.name}/${image.identifier}?async=true&apiKey=${myNode.apiKey}`; imageUrl = `${nodeUrl}/arbitrary/${image.service}/${image.name}/${image.identifier}?async=true&apiKey=${myNode.apiKey}`
if (this.viewImage || this.myAddress === this.messageObj.sender) { if (this.viewImage || this.myAddress === this.messageObj.sender) {
imageHTML = createImage(imageUrl); imageHTML = createImage(imageUrl)
imageHTMLDialog = createImage(imageUrl) imageHTMLDialog = createImage(imageUrl)
imageHTMLDialog.style = "height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px"; imageHTMLDialog.style = "height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px;"
} }
} }
if (gif) { if (gif) {
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; 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 nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
gifUrl = `${nodeUrl}/arbitrary/${gif.service}/${gif.name}/${gif.identifier}?filepath=${gif.filePath}&apiKey=${myNode.apiKey}`; gifUrl = `${nodeUrl}/arbitrary/${gif.service}/${gif.name}/${gif.identifier}?filepath=${gif.filePath}&apiKey=${myNode.apiKey}`
if (this.viewImage || this.myAddress === this.messageObj.sender) { if (this.viewImage || this.myAddress === this.messageObj.sender) {
gifHTML = createGif(gifUrl); gifHTML = createGif(gifUrl)
gifHTMLDialog = createGif(gifUrl) gifHTMLDialog = createGif(gifUrl)
gifHTMLDialog.style = "height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px"; gifHTMLDialog.style = "height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px;"
} }
} }
@ -690,27 +701,29 @@ class MessageTemplate extends LitElement {
<span class="${this.messageObj.sender === this.myAddress && 'message-data-my-name'}"> <span class="${this.messageObj.sender === this.myAddress && 'message-data-my-name'}">
${this.messageObj.senderName ? this.messageObj.senderName : cropAddress(this.messageObj.sender)} ${this.messageObj.senderName ? this.messageObj.senderName : cropAddress(this.messageObj.sender)}
</span> </span>
`; `
forwarded = html` forwarded = html`
<span class="${this.messageObj.sender === this.myAddress && 'message-data-forward'}"> <span class="${this.messageObj.sender === this.myAddress && 'message-data-forward'}">
${translate("blockpage.bcchange17")} ${translate("blockpage.bcchange17")}
</span> </span>
`; `
edited = html` edited = html`
<span class="edited-message-style"> <span class="edited-message-style">
${translate("chatpage.cchange68")} ${translate("chatpage.cchange68")}
</span> </span>
`; `
if (repliedToData) { if (repliedToData) {
try { try {
const parsedMsg = JSON.parse(repliedToData.decodedMessage); const parsedMsg = JSON.parse(repliedToData.decodedMessage)
repliedToData.decodedMessage = parsedMsg; repliedToData.decodedMessage = parsedMsg
} catch (error) { } catch (error) {
} }
} }
let repliedToMessageText = '' let repliedToMessageText = ''
if (repliedToData && repliedToData.decodedMessage && repliedToData.decodedMessage.messageText) { if (repliedToData && repliedToData.decodedMessage && repliedToData.decodedMessage.messageText) {
try { try {
@ -724,16 +737,16 @@ class MessageTemplate extends LitElement {
} }
} }
let replacedMessage = '' let replacedMessage = ''
if (message && +version < 2) { if (message && +version < 2) {
const escapedMessage = this.escapeHTML(message) const escapedMessage = this.escapeHTML(message)
if (escapedMessage) { if (escapedMessage) {
replacedMessage = escapedMessage.replace(new RegExp('\r?\n', 'g'), '<br />'); replacedMessage = escapedMessage.replace(new RegExp('\r?\n', 'g'), '<br />')
} }
} }
return hideit ? html`<li class="clearfix"></li>` : html` return hideit ? html`<li class="clearfix"></li>` : html`
<li <li
class="clearfix message-parent" class="clearfix message-parent"
@ -753,9 +766,9 @@ class MessageTemplate extends LitElement {
<div <div
style=${this.myAddress === this.messageObj.sender ? "cursor: auto;" : "cursor: pointer;"} style=${this.myAddress === this.messageObj.sender ? "cursor: auto;" : "cursor: pointer;"}
@click=${() => { @click=${() => {
if (this.myAddress === this.messageObj.sender) return; if (this.myAddress === this.messageObj.sender) return
this.setOpenUserInfo(true); this.setOpenUserInfo(true)
this.setUserName(this.messageObj); this.setUserName(this.messageObj)
}} class="message-data-avatar"> }} class="message-data-avatar">
${avatarImg} ${avatarImg}
</div> </div>
@ -789,9 +802,9 @@ class MessageTemplate extends LitElement {
<span <span
style=${this.myAddress === this.messageObj.sender ? "cursor: auto;" : "cursor: pointer;"} style=${this.myAddress === this.messageObj.sender ? "cursor: auto;" : "cursor: pointer;"}
@click=${() => { @click=${() => {
if (this.myAddress === this.messageObj.sender) return; if (this.myAddress === this.messageObj.sender) return
this.setOpenUserInfo(true); this.setOpenUserInfo(true)
this.setUserName(this.messageObj); this.setUserName(this.messageObj)
}} }}
class="message-data-name"> class="message-data-name">
${nameMenu} ${nameMenu}
@ -844,13 +857,13 @@ class MessageTemplate extends LitElement {
}} }}
class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : ''].join(' ')} class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : ''].join(' ')}
style=${this.isFirstMessage && "margin-top: 10px;"}> style=${this.isFirstMessage && "margin-top: 10px;"}>
<div style="display:flex;width:100%;height:100%;justify-content:center;align-items:center;cursor:pointer;color:var(--black)"> <div style="display:flex;width:100%;height:100%;justify-content:center;align-items:center;cursor:pointer;color:var(--black);">
${translate("chatpage.cchange40")} ${translate("chatpage.cchange40")}
</div> </div>
</div> </div>
` : html``} ` : html``}
${!this.isImageLoaded && image && this.viewImage ? html` ${!this.isImageLoaded && image && this.viewImage ? html`
<div style="display:flex;width:100%;height:100%;justify-content:center;align-items:center;position:absolute"> <div style="display:flex;width:100%;height:100%;justify-content:center;align-items:center;position:absolute;">
<div class=${`smallLoading`}></div> <div class=${`smallLoading`}></div>
</div> </div>
@ -863,7 +876,7 @@ class MessageTemplate extends LitElement {
${this.myAddress === this.messageObj.sender ? html` ${this.myAddress === this.messageObj.sender ? html`
<vaadin-icon <vaadin-icon
@click=${() => { @click=${() => {
this.openDeleteImage = true; this.openDeleteImage = true
}} }}
class="image-delete-icon" icon="vaadin:close" slot="icon"></vaadin-icon> class="image-delete-icon" icon="vaadin:close" slot="icon"></vaadin-icon>
` : ''} ` : ''}
@ -879,7 +892,7 @@ class MessageTemplate extends LitElement {
}} }}
class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : ''].join(' ')} class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : ''].join(' ')}
style=${this.isFirstMessage && "margin-top: 10px;"}> style=${this.isFirstMessage && "margin-top: 10px;"}>
<div style="display:flex;width:100%;height:100%;justify-content:center;align-items:center;cursor:pointer;color:var(--black)"> <div style="display:flex;width:100%;height:100%;justify-content:center;align-items:center;cursor:pointer;color:var(--black);">
${translate("gifs.gchange25")} ${translate("gifs.gchange25")}
</div> </div>
</div> </div>
@ -917,8 +930,8 @@ class MessageTemplate extends LitElement {
? html` ? html`
<vaadin-icon <vaadin-icon
@click=${(e) => { @click=${(e) => {
e.stopPropagation(); e.stopPropagation()
this.openDeleteAttachment = true; this.openDeleteAttachment = true
}} }}
class="image-delete-icon" icon="vaadin:close" slot="icon"> class="image-delete-icon" icon="vaadin:close" slot="icon">
</vaadin-icon> </vaadin-icon>
@ -1128,7 +1141,7 @@ class MessageTemplate extends LitElement {
hideActions hideActions
?open=${this.openDeleteImage} ?open=${this.openDeleteImage}
@closed=${() => { @closed=${() => {
this.openDeleteImage = false; this.openDeleteImage = false
}}> }}>
<div class="delete-image-msg"> <div class="delete-image-msg">
<p>${translate("chatpage.cchange78")}</p> <p>${translate("chatpage.cchange78")}</p>
@ -1153,7 +1166,7 @@ class MessageTemplate extends LitElement {
hideActions hideActions
?open=${this.openDeleteAttachment} ?open=${this.openDeleteAttachment}
@closed=${() => { @closed=${() => {
this.openDeleteAttachment = false; this.openDeleteAttachment = false
}}> }}>
<div class="delete-image-msg"> <div class="delete-image-msg">
<p>${translate("chatpage.cchange79")}</p> <p>${translate("chatpage.cchange79")}</p>
@ -1182,7 +1195,7 @@ class MessageTemplate extends LitElement {
} }
} }
window.customElements.define('message-template', MessageTemplate); window.customElements.define('message-template', MessageTemplate)
class ChatMenu extends LitElement { class ChatMenu extends LitElement {
static get properties() { static get properties() {
@ -1212,13 +1225,13 @@ class ChatMenu extends LitElement {
} }
constructor() { constructor() {
super(); super()
this.showPrivateMessageModal = () => { }; this.showPrivateMessageModal = () => { }
this.showBlockUserModal = () => { }; this.showBlockUserModal = () => { }
} }
static get styles() { static get styles() {
return [chatStyles]; return [chatStyles]
} }
// Copy address to clipboard // Copy address to clipboard
@ -1246,7 +1259,7 @@ class ChatMenu extends LitElement {
key: '' key: ''
} }
try { try {
parsedMessageObj = JSON.parse(this.originalMessage.decodedMessage); parsedMessageObj = JSON.parse(this.originalMessage.decodedMessage)
} catch (error) { } catch (error) {
parsedMessageObj = {} parsedMessageObj = {}
@ -1335,7 +1348,7 @@ class ChatMenu extends LitElement {
this.versionErrorSnack() this.versionErrorSnack()
return return
} }
this.setRepliedToMessageObj({ ...this.originalMessage, version: this.version }); this.setRepliedToMessageObj({ ...this.originalMessage, version: this.version })
}}"> }}">
<vaadin-icon icon="vaadin:reply" slot="icon"></vaadin-icon> <vaadin-icon icon="vaadin:reply" slot="icon"></vaadin-icon>
</div> </div>
@ -1351,7 +1364,7 @@ class ChatMenu extends LitElement {
this.versionErrorSnack() this.versionErrorSnack()
return return
} }
this.setEditedMessageObj(this.originalMessage); this.setEditedMessageObj(this.originalMessage)
}}> }}>
<vaadin-icon icon="vaadin:pencil" slot="icon"></vaadin-icon> <vaadin-icon icon="vaadin:pencil" slot="icon"></vaadin-icon>
</div> </div>
@ -1363,9 +1376,9 @@ class ChatMenu extends LitElement {
class=${`menu-icon ${!this.firstMessageInChat ? "tooltip" : ""}`} class=${`menu-icon ${!this.firstMessageInChat ? "tooltip" : ""}`}
data-text="${translate("blockpage.bcchange18")}" data-text="${translate("blockpage.bcchange18")}"
@click=${(e) => { @click=${(e) => {
e.preventDefault(); e.preventDefault()
this.setUserName(this.originalMessage); this.setUserName(this.originalMessage)
this.setOpenTipUser(true); this.setOpenTipUser(true)
}}> }}>
<vaadin-icon icon="vaadin:dollar" slot="icon"></vaadin-icon> <vaadin-icon icon="vaadin:dollar" slot="icon"></vaadin-icon>
</div> </div>

View File

@ -1,119 +1,114 @@
import { LitElement, html } from 'lit'; import { LitElement, html } from 'lit'
import { render } from 'lit/html.js'; import { render } from 'lit/html.js'
import { translate } from 'lit-translate'; import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate'
import { userInfoStyles } from './UserInfo-css.js'; import { userInfoStyles } from './UserInfo-css.js'
import { Epml } from '../../../../epml'; import { Epml } from '../../../../epml'
import '@vaadin/button'; import { cropAddress } from '../../../utils/cropAddress.js'
import '@polymer/paper-progress/paper-progress.js';
import { cropAddress } from '../../../utils/cropAddress.js'; import '@polymer/paper-progress/paper-progress.js'
import '@vaadin/button'
registerTranslateConfig({
loader: lang => fetch(`/language/${lang}.json`).then(res => res.json())
})
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
export class UserInfo extends LitElement { export class UserInfo extends LitElement {
static get properties() { static get properties() {
return { return {
setOpenUserInfo: { attribute: false }, setOpenUserInfo: { attribute: false },
setOpenTipUser: { attribute: false }, setOpenTipUser: { attribute: false },
setOpenPrivateMessage: { attribute: false }, setOpenPrivateMessage: { attribute: false },
userName: { type: String }, userName: { type: String },
selectedHead: { type: Object }, selectedHead: { type: Object },
isImageLoaded: { type: Boolean } isImageLoaded: { type: Boolean }
} }
} }
constructor() { constructor() {
super() super()
this.isImageLoaded = false this.isImageLoaded = false
this.selectedHead = {} this.selectedHead = {}
this.imageFetches = 0 this.imageFetches = 0
}
static styles = [userInfoStyles]
createImage(imageUrl) {
const imageHTMLRes = new Image()
imageHTMLRes.src = imageUrl
imageHTMLRes.classList.add("user-info-avatar")
imageHTMLRes.onload = () => {
this.isImageLoaded = true
}
imageHTMLRes.onerror = () => {
if (this.imageFetches < 4) {
setTimeout(() => {
this.imageFetches = this.imageFetches + 1
imageHTMLRes.src = imageUrl;
}, 10000)
} else {
this.isImageLoaded = false
}
}
return imageHTMLRes
}
render() {
let avatarImg = ""
if (this.selectedHead && this.selectedHead.name) {
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.selectedHead.name}/qortal_avatar?async=true&apiKey=${myNode.apiKey}`
avatarImg = this.createImage(avatarUrl)
}
return html`
<div style="position: relative;">
<vaadin-icon
class="close-icon"
icon="vaadin:close-big"
slot="icon"
@click=${() => {
this.setOpenUserInfo(false)
}}
>
</vaadin-icon>
${this.isImageLoaded ? html`<div class="avatar-container">${avatarImg}</div>` : html``}
${!this.isImageLoaded && this.selectedHead && this.selectedHead.name ? html`
<div class="avatar-container">
<div class="user-info-no-avatar">
${this.selectedHead.name.charAt(0)}
</div>
</div>
` : ""}
${!this.isImageLoaded && this.selectedHead && !this.selectedHead.name ? html`
<div class="avatar-container">
<img src="/img/incognito.png" alt="avatar" />
</div>`
: ""}
<div class="user-info-header">
${this.selectedHead && this.selectedHead.name ? this.selectedHead.name : this.selectedHead ? cropAddress(this.selectedHead.address) : null}
</div>
<div class="send-message-button" @click="${() => {
this.setOpenPrivateMessage({
name: this.userName,
open: true
})
this.setOpenUserInfo(false)
}}">
${translate("chatpage.cchange58")}
</div>
<div style="margin-top: 5px;" class="send-message-button" @click=${() => {
this.setOpenTipUser(true)
this.setOpenUserInfo(false)
}}>
${translate("chatpage.cchange59")}
</div>
</div>
`
}
} }
static styles = [userInfoStyles] customElements.define('user-info', UserInfo)
createImage(imageUrl) {
const imageHTMLRes = new Image();
imageHTMLRes.src = imageUrl;
imageHTMLRes.classList.add("user-info-avatar");
imageHTMLRes.onload = () => {
this.isImageLoaded = true;
}
imageHTMLRes.onerror = () => {
if (this.imageFetches < 4) {
setTimeout(() => {
this.imageFetches = this.imageFetches + 1;
imageHTMLRes.src = imageUrl;
}, 500);
} else {
this.isImageLoaded = false
}
};
return imageHTMLRes;
}
render() {
let avatarImg = "";
if (this.selectedHead && this.selectedHead.name) {
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.selectedHead.name}/qortal_avatar?async=true&apiKey=${myNode.apiKey}`;
avatarImg = this.createImage(avatarUrl);
}
return html`
<div style=${"position: relative;"}>
<vaadin-icon
class="close-icon"
icon="vaadin:close-big"
slot="icon"
@click=${() => {
this.setOpenUserInfo(false)
}}>
</vaadin-icon>
${this.isImageLoaded ?
html`
<div class="avatar-container">
${avatarImg}
</div>` :
html``}
${!this.isImageLoaded && this.selectedHead && this.selectedHead.name ?
html`
<div class="avatar-container">
<div class="user-info-no-avatar">
${this.selectedHead.name.charAt(0)}
</div>
</div>
`
: ""}
${!this.isImageLoaded && this.selectedHead && !this.selectedHead.name ?
html`
<div class="avatar-container">
<img src="/img/incognito.png" alt="avatar" />
</div>`
: ""}
<div class="user-info-header">
${this.selectedHead && this.selectedHead.name ? this.selectedHead.name : this.selectedHead ? cropAddress(this.selectedHead.address) : null}
</div>
<div
class="send-message-button"
@click="${() => {
this.setOpenPrivateMessage({
name: this.userName,
open: true
})
this.setOpenUserInfo(false);
}
}">
${translate("chatpage.cchange58")}
</div>
<div
style=${"margin-top: 5px;"}
class="send-message-button"
@click=${() => {
this.setOpenTipUser(true);
this.setOpenUserInfo(false);
}}>
${translate("chatpage.cchange59")}
</div>
</div>
`
}
}
customElements.define('user-info', UserInfo);