From 6b546893623434455ee1d7c21939627eeb39b618 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Tue, 3 May 2022 14:52:17 +0200 Subject: [PATCH] New Menu --- .../plugins/core/components/NameMenu.js | 622 ++++++++++++++++++ 1 file changed, 622 insertions(+) create mode 100644 qortal-ui-plugins/plugins/core/components/NameMenu.js diff --git a/qortal-ui-plugins/plugins/core/components/NameMenu.js b/qortal-ui-plugins/plugins/core/components/NameMenu.js new file mode 100644 index 00000000..910dd7d4 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/components/NameMenu.js @@ -0,0 +1,622 @@ +import { LitElement, html, css } from 'lit' +import { render } from 'lit/html.js' +import { Epml } from '../../../epml.js' +import snackbar from './snackbar.js' +import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate' + +registerTranslateConfig({ + loader: lang => fetch(`/language/${lang}.json`).then(res => res.json()) +}) + +import '@material/mwc-snackbar' +import '@material/mwc-button' +import '@material/mwc-dialog' +import '@material/mwc-icon' +import '@polymer/paper-tooltip/paper-tooltip.js' +import '@polymer/paper-spinner/paper-spinner-lite.js' + +const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) + +class NameMenu extends LitElement { + static get properties() { + return { + toblockaddress: { type: String, attribute: true }, + nametodialog: { type: String, attribute: true }, + chatBlockedAdresses: { type: Array }, + selectedAddress: { type: Object }, + config: { type: Object }, + myAddress: { type: Object, reflect: true }, + messages: { type: Array }, + btnDisable: { type: Boolean }, + isLoading: { type: Boolean }, + balance: { type: Number } + } + } + + static get styles() { + return css` + * { + --mdc-theme-primary: rgb(3, 169, 244); + --mdc-theme-secondary: var(--mdc-theme-primary); + --mdc-dialog-content-ink-color: var(--black); + --mdc-theme-surface: var(--white); + --mdc-theme-text-primary-on-background: var(--black); + --paper-input-container-focus-color: var(--mdc-theme-primary); + --lumo-primary-text-color: rgb(0, 167, 245); + --lumo-primary-color-50pct: rgba(0, 167, 245, 0.5); + --lumo-primary-color-10pct: rgba(0, 167, 245, 0.1); + --lumo-primary-color: hsl(199, 100%, 48%); + --lumo-base-color: var(--white); + --lumo-body-text-color: var(--black); + } + + a { + background-color: transparent; + color: var(--black); + text-decoration: none; + display: inline; + position: relative; + } + + a:hover { + background-color: transparent; + color: var(--black); + text-decoration: none; + display: inline; + position: relative; + } + + a:after { + content: ''; + position: absolute; + width: 100%; + transform: scaleX(0); + height: 2px; + bottom: 0; + left: 0; + background-color: #03a9f4; + transform-origin: bottom right; + transition: transform 0.25s ease-out; + } + + a:hover:after { + transform: scaleX(1); + transform-origin: bottom left; + } + + .block { + } + + .red { + --mdc-theme-primary: red; + } + + h2 { + margin:0; + } + + h2, h3, h4, h5 { + color:# var(--black); + font-weight: 400; + } + + .custom { + --paper-tooltip-background: #03a9f4; + --paper-tooltip-text-color: #fff; + } + + .dropdown { + position: relative; + display: inline; + } + + .dropdown a:hover { + background-color: transparent; + } + + .dropdown-content { + display: none; + position: absolute; + bottom: 25px; + left: 10px; + background-color: var(--white); + min-width: 200px; + overflow: auto; + border: 1px solid transparent; + border-radius: 10px; + box-shadow: var(--qchatshadow); + z-index: 1; + } + + .dropdown-content span { + color: var(--nav-text-color); + text-align: center; + padding-top: 12px; + display: block; + } + + .dropdown-content a { + color: var(--nav-text-color); + padding: 12px 12px; + text-decoration: none; + display: block; + } + + .dropdown-content a:hover { + background-color: var(--nav-color-hover); + } + + .showList { + display: block; + } + + .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; + } + ` + } + + constructor() { + super() + this.chatBlockedAdresses = [] + this.selectedAddress = window.parent.reduxStore.getState().app.selectedAddress.address + this.myAddress = {} + this.balance = 1 + this.messages = [] + this.btnDisable = false + this.isLoading = false + } + + render() { + return html` + + +
+

${translate("blockpage.bcchange5")}

+
+

${translate("blockpage.bcchange6")}


+

${this.nametodialog}

+
+ + ${translate("general.yes")} + + + ${translate("general.no")} + +
+ +
+

${translate("welcomepage.wcchange2")}

+
+
+

${translate("welcomepage.wcchange3")}

+ +

+ +

+ ${translate("welcomepage.wcchange6")} + + ${translate("general.close")} + +
+ ` + } + + firstUpdated() { + + this.changeLanguage() + this.getChatBlockedAdresses() + + setInterval(() => { + this.getChatBlockedAdresses(); + }, 60000) + + window.addEventListener('storage', () => { + const checkLanguage = localStorage.getItem('qortalLanguage') + use(checkLanguage) + }) + + window.onclick = function(event) { + if (!event.target.matches('.block')) { + var dropdowns = document.getElementsByClassName('dropdown-content'); + var i; + for (i = 0; i < dropdowns.length; i++) { + var openDropdown = dropdowns[i]; + if (openDropdown.classList.contains('showList')) { + openDropdown.classList.remove('showList'); + } + } + } + } + + const stopKeyEventPropagation = (e) => { + e.stopPropagation(); + return false; + } + + this.shadowRoot.getElementById('sendTo').addEventListener('keydown', stopKeyEventPropagation); + this.shadowRoot.getElementById('messageBox').addEventListener('keydown', stopKeyEventPropagation); + + const getDataFromURL = () => { + let tempUrl = document.location.href + let splitedUrl = decodeURI(tempUrl).split('?') + let urlData = splitedUrl[1] + if (urlData !== undefined) { + this.chatId = urlData + } + } + + let configLoaded = false + + 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() + } + + changeLanguage() { + const checkLanguage = localStorage.getItem('qortalLanguage') + + if (checkLanguage === null || checkLanguage.length === 0) { + localStorage.setItem('qortalLanguage', 'us') + use('us') + } else { + use(checkLanguage) + } + } + + myMenu() { + this.shadowRoot.getElementById('myDropdown').classList.toggle('showList') + this.shadowRoot.querySelector('#blockNameDialog').close() + this.shadowRoot.querySelector('#startPmDialog').close() + } + + closeMenu() { + this.shadowRoot.getElementById('myDropdown').classList.toggle('showList') + var dropdowns = document.getElementsByClassName('dropdown-content'); + var i; + for (i = 0; i < dropdowns.length; i++) { + var openDropdown = dropdowns[i]; + if (openDropdown.classList.contains('showList')) { + openDropdown.classList.remove('showList'); + } + } + } + + async copyToClipboard(text) { + try { + let copyString1 = get("walletpage.wchange4") + await navigator.clipboard.writeText(text) + parentEpml.request('showSnackBar', `${copyString1}`) + this.closeMenu() + } catch (err) { + let copyString2 = get("walletpage.wchange39") + parentEpml.request('showSnackBar', `${copyString2}`) + console.error('Copy to clipboard error:', err) + this.closeMenu() + } + } + + async getChatBlockedAdresses() { + const chatBlockedAdresses = await parentEpml.request('apiCall', { + url: `/lists/blockedAddresses?apiKey=${this.getApiKey()}` + }) + this.chatBlockedAdresses = chatBlockedAdresses + } + + 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.closeMenu() + this.shadowRoot.querySelector('#blockNameDialog').close() + let err1string = get("blockpage.bcchange2") + snackbar.add({ + labelText: `${err1string}`, + dismiss: true + }) + } else { + this.closeMenu() + this.shadowRoot.querySelector('#blockNameDialog').close() + let err2string = get("blockpage.bcchange2") + snackbar.add({ + labelText: `${err2string}`, + dismiss: true + }) + } + return ret + } + + 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)) + }) + }) + }) + } + + _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(e) { + 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.closeMenu() + } else if (response.error) { + parentEpml.request('showSnackBar', response.message) + this.isLoading = false + this.closeMenu() + } else { + let err3string = get("welcomepage.wcchange9") + parentEpml.request('showSnackBar', `${err3string}`) + this.isLoading = false + this.closeMenu() + } + + } + getAddressPublicKey() + } + + _textMenu(event) { + const getSelectedText = () => { + var text = ""; + if (typeof window.getSelection != "undefined") { + text = window.getSelection().toString(); + } else if (typeof this.shadowRoot.selection != "undefined" && this.shadowRoot.selection.type == "Text") { + text = this.shadowRoot.selection.createRange().text; + } + return text; + } + + const checkSelectedTextAndShowMenu = () => { + let selectedText = getSelectedText(); + if (selectedText && typeof selectedText === 'string') { + let _eve = { pageX: event.pageX, pageY: event.pageY, clientX: event.clientX, clientY: event.clientY } + let textMenuObject = { selectedText: selectedText, eventObject: _eve, isFrame: true } + parentEpml.request('openCopyTextMenu', textMenuObject) + } + } + checkSelectedTextAndShowMenu() + } + + _textArea(e) { + if (e.keyCode === 13 && !e.shiftKey) this._sendMessage() + } + + isEmptyArray(arr) { + if (!arr) { return true } + return arr.length === 0 + } + + getApiKey() { + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; + let apiKey = myNode.apiKey; + return apiKey; + } +} + +window.customElements.define('name-menu', NameMenu)