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')}
+
+ this.hidePrivateMessageModal()}'
+ class='close-button'
+ >
+ ${translate('general.close')}
+
+
+ this.hideBlockUserModal()}
+ >
+
+
${translate('blockpage.bcchange5')}
+
+ ${translate('blockpage.bcchange6')}
+ ${this.nametodialog}
+
+ this.chatBlockAddress()}'
+ class='block'
+ >
+ ${translate('general.yes')}
+
+ this.hideBlockUserModal()}'
+ class='close-button'
+ >
+ ${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`
-
+
+ ${repeat(
+ this.messages,
+ (message) => message.reference,
+ (message) => html``
+ )}
+
+
+ {
+ console.log("yo500")
+ this.shadowRoot.getElementById('downObserver').scrollIntoView({
+ behavior: 'smooth',
+ })
+ }}>
+
`
}
- 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(() => {