From e22ff8cba03f3fba50dceb8ae599183ce83da81b Mon Sep 17 00:00:00 2001 From: Justin Ferrari Date: Sun, 11 Sep 2022 12:12:28 -0500 Subject: [PATCH] Changed chat menu --- .gitignore | 3 +- .../plugins/core/components/ChatModals.js | 407 ++++++++++++ .../plugins/core/components/ChatPage.js | 64 +- .../plugins/core/components/ChatScroller.js | 621 +++++++++++++++--- .../core/components/ChatWelcomePage.js | 2 - .../plugins/core/components/LevelFounder.js | 2 - .../plugins/core/components/NameMenu.js | 2 - 7 files changed, 992 insertions(+), 109 deletions(-) create mode 100644 qortal-ui-plugins/plugins/core/components/ChatModals.js diff --git a/.gitignore b/.gitignore index 78df4048..6cf26473 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,8 @@ yarn.lock # Derived js files qortal-ui-plugins/plugins/core/**/*.js !*.src.js -qortal-ui-plugins/plugins/core/components/*.js -!*.js qortal-ui-core/src/redux/app/version.js +!qortal-ui-plugins/plugins/core/components/*.js # Node modules node_modules/ diff --git a/qortal-ui-plugins/plugins/core/components/ChatModals.js b/qortal-ui-plugins/plugins/core/components/ChatModals.js new file mode 100644 index 00000000..5b1a6712 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/components/ChatModals.js @@ -0,0 +1,407 @@ +import { LitElement, html, css} from 'lit-element'; +import { get, translate } from 'lit-translate'; +import { Epml } from '../../../epml'; +import snackbar from './snackbar.js' +import '@material/mwc-button'; +import '@material/mwc-dialog'; + +const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) + +class ChatModals extends LitElement { + static get properties() { + return { + openDialogPrivateMessage: {type: Boolean}, + openDialogBlockUser: {type: Boolean}, + isLoading: { type: Boolean }, + nametodialog: { type: String, attribute: true }, + hidePrivateMessageModal: {type: Function}, + hideBlockUserModal: {type: Function}, + toblockaddress: { type: String, attribute: true }, + chatBlockedAdresses: { type: Array }, + } + } + + constructor() { + super(); + this.isLoading = false; + this.hidePrivateMessageModal = () => {}; + this.hideBlockUserModal = () => {}; + this.chatBlockedAdresses = [] + } + + static get styles() { + return css` + .input { + width: 90%; + border: none; + display: inline-block; + font-size: 16px; + padding: 10px 20px; + border-radius: 5px; + resize: none; + background: #eee; + } + + .textarea { + width: 90%; + border: none; + display: inline-block; + font-size: 16px; + padding: 10px 20px; + border-radius: 5px; + height: 120px; + resize: none; + background: #eee; + } + + .close-button { + display:block; + --mdc-theme-primary: red; + } + ` + } + + firstUpdated() { + + const stopKeyEventPropagation = (e) => { + e.stopPropagation(); + return false; + } + + this.shadowRoot.getElementById('sendTo').addEventListener('keydown', stopKeyEventPropagation); + this.shadowRoot.getElementById('messageBox').addEventListener('keydown', stopKeyEventPropagation); + + parentEpml.ready().then(() => { + parentEpml.subscribe('selected_address', async selectedAddress => { + this.selectedAddress = {} + selectedAddress = JSON.parse(selectedAddress) + if (!selectedAddress || Object.entries(selectedAddress).length === 0) return + this.selectedAddress = selectedAddress + }) + parentEpml.request('apiCall', { + url: `/addresses/balance/${window.parent.reduxStore.getState().app.selectedAddress.address}` + }).then(res => { + this.balance = res + }) + }) + parentEpml.imReady() + + } + + // Send Private Message + + _sendMessage() { + this.isLoading = true + + const recipient = this.shadowRoot.getElementById('sendTo').value + const messageBox = this.shadowRoot.getElementById('messageBox') + const messageText = messageBox.value + + if (recipient.length === 0) { + this.isLoading = false + } else if (messageText.length === 0) { + this.isLoading = false + } else { + this.sendMessage() + } + } + + async sendMessage() { + this.isLoading = true + + const _recipient = this.shadowRoot.getElementById('sendTo').value + const messageBox = this.shadowRoot.getElementById('messageBox') + const messageText = messageBox.value + let recipient + + const validateName = async (receiverName) => { + let myRes + let myNameRes = await parentEpml.request('apiCall', { + type: 'api', + url: `/names/${receiverName}` + }) + + if (myNameRes.error === 401) { + myRes = false + } else { + myRes = myNameRes + } + + return myRes + } + + const myNameRes = await validateName(_recipient) + if (!myNameRes) { + + recipient = _recipient + } else { + + recipient = myNameRes.owner + } + + let _reference = new Uint8Array(64); + window.crypto.getRandomValues(_reference); + + let sendTimestamp = Date.now() + + let reference = window.parent.Base58.encode(_reference) + + const getAddressPublicKey = async () => { + let isEncrypted + let _publicKey + + let addressPublicKey = await parentEpml.request('apiCall', { + type: 'api', + url: `/addresses/publickey/${recipient}` + }) + + + if (addressPublicKey.error === 102) { + _publicKey = false + // Do something here... + let err1string = get('welcomepage.wcchange7') + parentEpml.request('showSnackBar', `${err1string}`) + this.isLoading = false + } else if (addressPublicKey !== false) { + isEncrypted = 1 + _publicKey = addressPublicKey + sendMessageRequest(isEncrypted, _publicKey) + } else { + isEncrypted = 0 + _publicKey = this.selectedAddress.address + sendMessageRequest(isEncrypted, _publicKey) + } + }; + + const sendMessageRequest = async (isEncrypted, _publicKey) => { + + let chatResponse = await parentEpml.request('chat', { + type: 18, + nonce: this.selectedAddress.nonce, + params: { + timestamp: sendTimestamp, + recipient: recipient, + recipientPublicKey: _publicKey, + message: messageText, + lastReference: reference, + proofOfWorkNonce: 0, + isEncrypted: isEncrypted, + isText: 1 + } + }) + _computePow(chatResponse) + } + + const _computePow = async (chatBytes) => { + + const _chatBytesArray = Object.keys(chatBytes).map(function (key) { return chatBytes[key]; }); + const chatBytesArray = new Uint8Array(_chatBytesArray) + const chatBytesHash = new window.parent.Sha256().process(chatBytesArray).finish().result + const hashPtr = window.parent.sbrk(32, window.parent.heap); + const hashAry = new Uint8Array(window.parent.memory.buffer, hashPtr, 32); + hashAry.set(chatBytesHash); + + const difficulty = this.balance === 0 ? 12 : 8; + + const workBufferLength = 8 * 1024 * 1024; + const workBufferPtr = window.parent.sbrk(workBufferLength, window.parent.heap); + + let nonce = window.parent.computePow(hashPtr, workBufferPtr, workBufferLength, difficulty) + + let _response = await parentEpml.request('sign_chat', { + nonce: this.selectedAddress.nonce, + chatBytesArray: chatBytesArray, + chatNonce: nonce + }) + getSendChatResponse(_response) + } + + const getSendChatResponse = (response) => { + + if (response === true) { + messageBox.value = '' + let err2string = get('welcomepage.wcchange8') + parentEpml.request('showSnackBar', `${err2string}`) + this.isLoading = false + this.shadowRoot.querySelector('#startPmDialog').close() + } else if (response.error) { + parentEpml.request('showSnackBar', response.message) + this.isLoading = false + this.shadowRoot.querySelector('#startPmDialog').close() + } else { + let err3string = get('welcomepage.wcchange9') + parentEpml.request('showSnackBar', `${err3string}`) + this.isLoading = false + this.shadowRoot.querySelector('#startPmDialog').close() + } + + } + getAddressPublicKey() + } + + _textArea(e) { + if (e.keyCode === 13 && !e.shiftKey) this._sendMessage() + } + + getApiKey() { + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; + let apiKey = myNode.apiKey; + return apiKey; + } + + getChatBlockedList() { + 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 blockedAddressesUrl = `${nodeUrl}/lists/blockedAddresses?apiKey=${this.getApiKey()}` + const err3string = 'No regitered name' + + localStorage.removeItem("ChatBlockedAddresses") + + var obj = []; + + fetch(blockedAddressesUrl).then(response => { + return response.json() + }).then(data => { + return data.map(item => { + const noName = { + name: err3string, + owner: item + } + fetch(`${nodeUrl}/names/address/${item}?limit=0&reverse=true`).then(res => { + return res.json() + }).then(jsonRes => { + if(jsonRes.length) { + jsonRes.map (item => { + obj.push(item) + }) + } else { + obj.push(noName) + } + localStorage.setItem("ChatBlockedAddresses", JSON.stringify(obj)) + }) + }) + }) + } + + relMessages() { + setTimeout(() => { + window.location.href = window.location.href.split( '#' )[0] + }, 500) + } + + async getChatBlockedAdresses() { + const chatBlockedAdresses = await parentEpml.request('apiCall', { + url: `/lists/blockedAddresses?apiKey=${this.getApiKey()}` + }) + this.chatBlockedAdresses = chatBlockedAdresses + } + + + // Chat Block Address + + async chatBlockAddress() { + let address = this.toblockaddress + + let items = [ + address + ] + + let addressJsonString = JSON.stringify({ "items": items }) + + let ret = await parentEpml.request('apiCall', { + url: `/lists/blockedAddresses?apiKey=${this.getApiKey()}`, + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: `${addressJsonString}` + }) + + if (ret === true) { + this.chatBlockedAdresses = this.chatBlockedAdresses.filter(item => item != address) + this.chatBlockedAdresses.push(address) + this.getChatBlockedList() + this.hideBlockUserModal() + let err1string = get("blockpage.bcchange2") + snackbar.add({ + labelText: `${err1string}`, + dismiss: true + }) + this.relMessages() + } else { + this.hideBlockUserModal() + let err2string = get("blockpage.bcchange2") + snackbar.add({ + labelText: `${err2string}`, + dismiss: true + }) + } + console.log({ret}) + return ret + } + + render() { + return html` + this.hidePrivateMessageModal()} + > +
+

${translate('welcomepage.wcchange2')}

+
+
+

${translate('welcomepage.wcchange3')}

+ +

+ +

+ ${translate('welcomepage.wcchange6')} + + + ${translate('general.close')} + +
+ this.hideBlockUserModal()} + > +
+

${translate('blockpage.bcchange5')}

+
+

${translate('blockpage.bcchange6')}


+

${this.nametodialog}

+
+ + ${translate('general.yes')} + + + ${translate('general.no')} + +
+ `; + } +} + +customElements.define('chat-modals', ChatModals); \ No newline at end of file diff --git a/qortal-ui-plugins/plugins/core/components/ChatPage.js b/qortal-ui-plugins/plugins/core/components/ChatPage.js index fe93e0f3..297e0bbf 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatPage.js +++ b/qortal-ui-plugins/plugins/core/components/ChatPage.js @@ -4,7 +4,7 @@ import { Epml } from '../../../epml.js' import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate' registerTranslateConfig({ - loader: lang => fetch(`/language/${lang}.json`).then(res => res.json()) + loader: lang => fetch(`/language/${lang}.json`).then(res => res.json()) }) import { escape, unescape } from 'html-escaper'; @@ -44,7 +44,8 @@ class ChatPage extends LitElement { isPasteMenuOpen: { type: Boolean }, showNewMesssageBar: { attribute: false }, hideNewMesssageBar: { attribute: false }, - chatEditorPlaceholder: { type: String } + chatEditorPlaceholder: { type: String }, + messagesRendered: { type: Array }, } } @@ -133,6 +134,7 @@ class ChatPage extends LitElement { this.isUserDown = false this.isPasteMenuOpen = false this.chatEditorPlaceholder = this.renderPlaceholder() + this.messagesRendered = [] } render() { @@ -153,7 +155,7 @@ class ChatPage extends LitElement { firstUpdated() { // TODO: Load and fetch messages from localstorage (maybe save messages to localstorage...) - this.changeLanguage(); + // this.changeLanguage(); this.emojiPickerHandler = this.shadowRoot.querySelector('.emoji-button'); this.mirrorChatInput = this.shadowRoot.getElementById('messageBox'); this.chatMessageInput = this.shadowRoot.getElementById('_chatEditorDOM'); @@ -169,7 +171,7 @@ class ChatPage extends LitElement { } else { return this.chatEditor.focus(); } - }; + } }); // Init EmojiPicker @@ -279,19 +281,32 @@ class ChatPage extends LitElement { } renderChatScroller(initialMessages) { - return html` ` + return html` ` } - getOldMessage(scrollElement) { + async getUpdateComplete() { + await super.getUpdateComplete(); + const marginElements = Array.from(this.shadowRoot.querySelectorAll('chat-scroller')); + await Promise.all(marginElements.map(el => el.updateComplete)); + return true; + } + + async getOldMessage(scrollElement) { if (this._messages.length <= 15 && this._messages.length >= 1) { // 15 is the default number of messages... let __msg = [...this._messages] this._messages = [] + this.messagesRendered = [...__msg, ...this.messagesRendered] + await this.getUpdateComplete(); + scrollElement.scrollIntoView({ behavior: 'auto', block: 'center' }); return { oldMessages: __msg, scrollElement: scrollElement } } else if (this._messages.length > 15) { + this.messagesRendered = [...this._messages.splice(this._messages.length - 15), ...this.messagesRendered] + await this.getUpdateComplete(); + scrollElement.scrollIntoView({ behavior: 'auto', block: 'center' }); return { oldMessages: this._messages.splice(this._messages.length - 15), scrollElement: scrollElement } } else { @@ -299,8 +314,8 @@ class ChatPage extends LitElement { } } - processMessages(messages, isInitial) { - + async processMessages(messages, isInitial) { + console.log({ messages }) if (isInitial) { this.messages = messages.map((eachMessage) => { @@ -327,7 +342,19 @@ class ChatPage extends LitElement { // TODO: Determine number of initial messages by screen height... this._messages.length <= 15 ? adjustMessages() : this._initialMessages = this._messages.splice(this._messages.length - 15); + + this.messagesRendered = this._initialMessages + + // try { + // const viewElement = this.shadowRoot.querySelector('chat-scroller') + // console.log({viewElement}) + // // viewElement.scrollTop = this.viewElement.scrollHeight + 50 + // } catch (error) { + // console.error(error) + // } + + this.isLoadingMessages = false setTimeout(() => this.downElementObserver(), 500) } else { @@ -354,6 +381,7 @@ class ChatPage extends LitElement { }) this.newMessages = this.newMessages.concat(_newMessages) + } } @@ -387,7 +415,7 @@ class ChatPage extends LitElement { nameMenu = `` } - if ( hideit === true ) { + if (hideit === true) { return `
  • ` @@ -406,7 +434,7 @@ class ChatPage extends LitElement { } } - renderNewMessage(newMessage) { + async renderNewMessage(newMessage) { const viewElement = this.shadowRoot.querySelector('chat-scroller').shadowRoot.getElementById('viewElement'); const downObserver = this.shadowRoot.querySelector('chat-scroller').shadowRoot.getElementById('downObserver'); @@ -416,16 +444,22 @@ class ChatPage extends LitElement { if (newMessage.sender === this.selectedAddress.address) { - viewElement.insertBefore(li, downObserver); + this.messagesRendered = [...this.messagesRendered, newMessage] + await this.getUpdateComplete(); + viewElement.scrollTop = viewElement.scrollHeight; } else if (this.isUserDown) { // Append the message and scroll to the bottom if user is down the page - viewElement.insertBefore(li, downObserver); + this.messagesRendered = [...this.messagesRendered, newMessage] + await this.getUpdateComplete(); + viewElement.scrollTop = viewElement.scrollHeight; } else { - viewElement.insertBefore(li, downObserver); + this.messagesRendered = [...this.messagesRendered, newMessage] + await this.getUpdateComplete(); + this.showNewMesssageBar(); } } @@ -843,7 +877,7 @@ class ChatPage extends LitElement { vertical-align: bottom; } `; - editor.content.head.appendChild(editor.styles); + editor.content.head.appendChild(editor.styles); }; ChatEditor.prototype.enable = function () { @@ -1030,7 +1064,7 @@ class ChatPage extends LitElement { function doInit() { return new ChatEditor(); - }; + } return doInit(); }; diff --git a/qortal-ui-plugins/plugins/core/components/ChatScroller.js b/qortal-ui-plugins/plugins/core/components/ChatScroller.js index b48ea99b..bf7be1a3 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatScroller.js +++ b/qortal-ui-plugins/plugins/core/components/ChatScroller.js @@ -1,16 +1,18 @@ -import { LitElement, html, css } from 'lit' -import { render } from 'lit/html.js' -import { Epml } from '../../../epml.js' - -import './LevelFounder.js' -import './NameMenu.js' - -import '@material/mwc-button' -import '@material/mwc-dialog' -import '@material/mwc-icon' +import { LitElement, html, css } from 'lit'; +import { render } from 'lit/html.js'; +import { repeat } from 'lit/directives/repeat.js'; +import { translate, get } from 'lit-translate'; +import { Epml } from "../../../epml"; +import './LevelFounder.js'; +import './NameMenu.js'; +import './ChatModals.js'; +import '@vaadin/icons'; +import '@vaadin/icon'; +import '@material/mwc-button'; +import '@material/mwc-dialog'; +import '@material/mwc-icon'; const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) - class ChatScroller extends LitElement { static get properties() { return { @@ -63,8 +65,29 @@ class ChatScroller extends LitElement { padding: 20px; } + .last-message-ref { + position: fixed; + font-size: 20px; + right: 40px; + bottom: 100px; + width: 50; + height: 50; + z-index: 5; + opacity: 0; + color: black; + background-color: white; + border-radius: 50%; + transition: all 0.1s ease-in-out; + } + + .last-message-ref:hover { + cursor: pointer; + transform: scale(1.1); + } + .chat-list { overflow-y: auto; + overflow-x: hidden; height: 92vh; box-sizing: border-box; } @@ -77,7 +100,6 @@ class ChatScroller extends LitElement { .message-data-name { color: var(--black); - cursor: pointer; } .message-data-time { @@ -190,7 +212,7 @@ class ChatScroller extends LitElement { super() this.messages = [] this._upObserverhandler = this._upObserverhandler.bind(this) - this.isLoading = false + this._downObserverHandler = this._downObserverHandler.bind(this) this.myAddress = window.parent.reduxStore.getState().app.selectedAddress.address this.hideMessages = JSON.parse(localStorage.getItem("MessageBlockedAddresses") || "[]") } @@ -200,103 +222,61 @@ class ChatScroller extends LitElement { return html` ` } - firstUpdated() { + async firstUpdated() { this.viewElement = this.shadowRoot.getElementById('viewElement') this.upObserverElement = this.shadowRoot.getElementById('upObserver') this.downObserverElement = this.shadowRoot.getElementById('downObserver') - this.renderChatMessages(this.initialMessages) + // Intialize Observers this.upElementObserver() - + this.downElementObserver() + await this.updateComplete this.viewElement.scrollTop = this.viewElement.scrollHeight + 50 } - chatMessageTemplate(messageObj) { - const hidemsg = this.hideMessages - - let avatarImg = '' - let nameMenu = '' - let levelFounder = '' - let hideit = hidemsg.includes(messageObj.sender) - - levelFounder = `` - - if (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/${messageObj.senderName}/qortal_avatar?async=true&apiKey=${myNode.apiKey}` - avatarImg = `` - } - - if (messageObj.sender === this.myAddress) { - nameMenu = `${messageObj.senderName ? messageObj.senderName : messageObj.sender}` - } else { - nameMenu = `` - } - - if ( hideit === true ) { - return ` -
  • - ` - } else { - return ` -
  • -
    - ${nameMenu} - ${levelFounder} - -
    -
    ${avatarImg}
    -
    ${this.emojiPicker.parse(this.escapeHTML(messageObj.decodedMessage))}
    -
  • - ` - } - } - - renderChatMessages(messages) { - messages.forEach(message => { - const li = document.createElement('li'); - li.innerHTML = this.chatMessageTemplate(message); - li.id = message.signature; - this.downObserverElement.before(li); - }); - } - - renderOldMessages(listOfOldMessages) { - let { oldMessages, scrollElement } = listOfOldMessages; - - let _oldMessages = oldMessages.reverse(); - _oldMessages.forEach(oldMessage => { - const li = document.createElement('li'); - li.innerHTML = this.chatMessageTemplate(oldMessage); - li.id = oldMessage.signature; - this.upObserverElement.after(li); - scrollElement.scrollIntoView({ behavior: 'auto', block: 'center' }); - }); - } - _getOldMessage(_scrollElement) { - let listOfOldMessages = this.getOldMessage(_scrollElement) + this.getOldMessage(_scrollElement) - if (listOfOldMessages) { - this.renderOldMessages(listOfOldMessages) - } + } _upObserverhandler(entries) { if (entries[0].isIntersecting) { let _scrollElement = entries[0].target.nextElementSibling - + console.log({ _scrollElement }) this._getOldMessage(_scrollElement) } } + _downObserverHandler(entries) { + + if (!entries[0].isIntersecting) { + this.shadowRoot.querySelector(".last-message-ref").style.opacity = '1' + } else { + this.shadowRoot.querySelector(".last-message-ref").style.opacity = '0' + } + } + upElementObserver() { const options = { root: this.viewElement, @@ -307,6 +287,475 @@ class ChatScroller extends LitElement { const observer = new IntersectionObserver(this._upObserverhandler, options) observer.observe(this.upObserverElement) } + + downElementObserver() { + const options = { + root: this.viewElement, + rootMargin: '0px', + threshold: 1 + }; + // 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); + + } + } window.customElements.define('chat-scroller', ChatScroller) + + +class MessageTemplate extends LitElement { + static get properties() { + return { + messageObj: { type: Object }, + hideMessages: { type: Array }, + openDialogPrivateMessage: {type: Boolean}, + openDialogBlockUser: {type: Boolean}, + showBlockAddressIcon: { type: Boolean }, + }; + } + + constructor() { + super(); + this.messageObj = {} + this.openDialogPrivateMessage = false + this.openDialogBlockUser = false + this.showBlockAddressIcon = false + this.myAddress = window.parent.reduxStore.getState().app.selectedAddress.address + } + + static get styles() { + return css` + html { + --scrollbarBG: #a1a1a1; + --thumbBG: #6a6c75; + } + + *::-webkit-scrollbar { + width: 11px; + } + + * { + scrollbar-width: thin; + scrollbar-color: var(--thumbBG) var(--scrollbarBG); + --mdc-theme-primary: rgb(3, 169, 244); + --mdc-theme-secondary: var(--mdc-theme-primary); + } + + *::-webkit-scrollbar-track { + background: var(--scrollbarBG); + } + + *::-webkit-scrollbar-thumb { + background-color: var(--thumbBG); + border-radius: 6px; + border: 3px solid var(--scrollbarBG); + } + + a { + color: var(--black); + text-decoration: none; + } + + ul { + list-style: none; + margin: 0; + padding: 20px; + } + + .chat-list { + overflow-y: auto; + overflow-x: hidden; + height: 92vh; + box-sizing: border-box; + } + + .message-data { + width: 92%; + margin-bottom: 15px; + margin-left: 50px; + } + + .message-data-name { + color: var(--black); + } + + .message-data-time { + color: #a8aab1; + font-size: 13px; + padding-left: 6px; + padding-bottom: 4px; + } + + .message-data-level { + color: #03a9f4; + font-size: 13px; + padding-left: 8px; + padding-bottom: 4px; + } + + .message-container { + position: relative; + } + + .message { + color: black; + padding: 12px 10px; + line-height: 19px; + white-space: pre-line; + word-wrap: break-word; + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; + font-size: 16px; + border-radius: 7px; + margin-bottom: 20px; + width: 90%; + position: relative; + } + + .message:after { + bottom: 100%; + left: 93%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + white-space: pre-line; + word-wrap: break-word; + pointer-events: none; + border-bottom-color: #ddd; + border-width: 10px; + margin-left: -10px; + } + + .message-parent:hover .chat-hover { + display: block; + } + + .message-parent:hover .message{ + filter:brightness(0.90); + } + + .chat-hover { + display: none; + position: absolute; + top: -32px; + left: 86%; + } + + .emoji { + width: 1.7em; + height: 1.5em; + margin-bottom: -2px; + vertical-align: bottom; + object-fit: contain; + } + + .my-message { + background: #d1d1d1; + border: 2px solid #eeeeee; + } + + .my-message:after { + border-bottom-color: #d1d1d1; + left: 7%; + } + + .other-message { + background: #f1f1f1; + border: 2px solid #dedede; + } + + .other-message:after { + border-bottom-color: #f1f1f1; + left: 7%; + } + + .align-left { + text-align: left; + } + + .align-right { + text-align: right; + } + + .float-left { + float: left; + } + + .float-right { + float: right; + } + + .clearfix:after { + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; + } + + img { + border-radius: 25%; + } + ` + } + + // 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) { + this.shadowRoot.querySelector(".chat-hover").focus({ preventScroll: true }) + if(bool) { + this.showBlockAddressIcon = true; + } else { + this.showBlockAddressIcon = false; + } + } + + render() { + console.log(this.showBlockAddressIcon) + const hidemsg = this.hideMessages + + let avatarImg = '' + let nameMenu = '' + let levelFounder = '' + let hideit = hidemsg.includes(this.messageObj.sender) + + 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`` + } + + if (this.messageObj.sender === this.myAddress) { + nameMenu = html`${this.messageObj.senderName ? this.messageObj.senderName : this.messageObj.sender}` + } else { + nameMenu = html`${this.messageObj.senderName ? this.messageObj.senderName : this.messageObj.sender}` + } + + return hideit ? html`
  • ` : html` +
  • +
    + ${nameMenu} + ${levelFounder} + +
    +
    ${avatarImg}
    +
    +
    ${this.emojiPicker.parse(this.escapeHTML(this.messageObj.decodedMessage))}
    + this.showPrivateMessageModal()} + .showBlockUserModal=${() => this.showBlockUserModal()} + .showBlockIconFunc=${(props) => this.showBlockIconFunc(props)} + .showBlockAddressIcon=${this.showBlockAddressIcon} + @blur=${() => this.showBlockIconFunc(false)} + > + +
    +
  • + this.hidePrivateMessageModal()} + .hideBlockUserModal=${() => this.hideBlockUserModal()} + toblockaddress=${this.messageObj.sender} + > + + `; + } +} + +window.customElements.define('message-template', MessageTemplate); + +class ChatMenu extends LitElement { + static get properties() { + return { + menuItems: { type: Array }, + selectedAddress: { type: Object }, + showPrivateMessageModal: {type: Function}, + showBlockUserModal: {type: Function}, + toblockaddress: { type: String, attribute: true }, + showBlockIconFunc: {type: Function}, + showBlockAddressIcon: {type: Boolean} + }; + } + + constructor() { + super(); + this.selectedAddress = window.parent.reduxStore.getState().app.selectedAddress.address; + this.showPrivateMessageModal = () => {}; + this.showBlockUserModal = () => {}; + } + + static get styles() { + return css` + .container { + display: flex; + flex-direction: row; + align-items: center; + gap: 5px; + background-color: white; + border: 1px solid #dad9d9; + border-radius: 5px; + height:100%; + width: 100px; + position: relative; + } + + .menu-icon { + width: 100%; + padding: 5px; + display: flex; + align-items: center; + font-size: 13px; + } + + .menu-icon:hover { + background-color: #dad9d9; + transition: all 0.1s ease-in-out; + cursor: pointer; + } + + .tooltip { + position: relative; + } + + .tooltip:before { + content: attr(data-text); + position: absolute; + top: -47px; + left: 50%; + transform: translateX(-50%); + width: 90px; + padding: 10px; + border-radius: 10px; + background:#fff; + color: #000; + text-align: center; + box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px; + font-size: 12px; + z-index: 5; + display: none; + } + + .tooltip:hover:before { + display: block; + } + + .tooltip:after { + content: ""; + position: absolute; + margin-top: -7px; + top: -7px; + border: 10px solid #fff; + border-color: white transparent transparent transparent; + z-index: 5; + display: none; + } + + .tooltip:hover:before, .tooltip:hover:after { + display: block; + } + + .block-user-container { + display: block; + position: absolute; + left: -48px; + } + + .block-user { + justify-content: space-between; + border: 1px solid rgb(218, 217, 217); + border-radius: 5px; + background-color: white; + width: 100%; + height: 32px; + padding: 3px 8px; + box-shadow: rgba(77, 77, 82, 0.2) 0px 7px 29px 0px; + } + + ` + } + + // Copy address to clipboard + + async copyToClipboard(text) { + try { + let copyString1 = get("walletpage.wchange4") + await navigator.clipboard.writeText(text) + parentEpml.request('showSnackBar', `${copyString1}`) + } catch (err) { + let copyString2 = get("walletpage.wchange39") + parentEpml.request('showSnackBar', `${copyString2}`) + console.error('Copy to clipboard error:', err) + } + } + + render() { + + return html` +
    + + + + ${this.showBlockAddressIcon ? html` +
    + +
    + ` : html` +
    + `} +
    + ` + } +} + +window.customElements.define('chat-menu', ChatMenu); + diff --git a/qortal-ui-plugins/plugins/core/components/ChatWelcomePage.js b/qortal-ui-plugins/plugins/core/components/ChatWelcomePage.js index 8bf3725d..1d9ea9bc 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatWelcomePage.js +++ b/qortal-ui-plugins/plugins/core/components/ChatWelcomePage.js @@ -245,9 +245,7 @@ class ChatWelcomePage extends LitElement { } firstUpdated() { - this.changeTheme() - this.changeLanguage() const stopKeyEventPropagation = (e) => { e.stopPropagation(); diff --git a/qortal-ui-plugins/plugins/core/components/LevelFounder.js b/qortal-ui-plugins/plugins/core/components/LevelFounder.js index 0bfbfd62..3f70ef68 100644 --- a/qortal-ui-plugins/plugins/core/components/LevelFounder.js +++ b/qortal-ui-plugins/plugins/core/components/LevelFounder.js @@ -95,8 +95,6 @@ class LevelFounder extends LitElement { } firstUpdated() { - - this.changeLanguage() this.checkAddressInfo() window.addEventListener('storage', () => { diff --git a/qortal-ui-plugins/plugins/core/components/NameMenu.js b/qortal-ui-plugins/plugins/core/components/NameMenu.js index 79ae1826..77472b71 100644 --- a/qortal-ui-plugins/plugins/core/components/NameMenu.js +++ b/qortal-ui-plugins/plugins/core/components/NameMenu.js @@ -246,8 +246,6 @@ class NameMenu extends LitElement { } firstUpdated() { - - this.changeLanguage() this.getChatBlockedAdresses() setInterval(() => {