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.bcchange7")}
+
+
+
+
+
${translate("blockpage.bcchange5")}
+
+ ${translate("blockpage.bcchange6")}
+ ${this.nametodialog}
+
+ this.chatBlockAddress()}"
+ class="block"
+ >
+ ${translate("general.yes")}
+
+ this.myMenu()}"
+ class="block red"
+ >
+ ${translate("general.no")}
+
+
+
+
+
${translate("welcomepage.wcchange2")}
+
+
+ ${translate("welcomepage.wcchange3")}
+
+
+
+
+ ${translate("welcomepage.wcchange6")}
+ this.myMenu()}"
+ class="block red"
+ >
+ ${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)