forked from Qortal/qortal-ui
Update to QDN
This commit is contained in:
parent
afa3c07401
commit
369b64928d
@ -91,10 +91,30 @@ const generateForPlugins = () => {
|
|||||||
in: "plugins/core/group-management/group-management.src.js",
|
in: "plugins/core/group-management/group-management.src.js",
|
||||||
out: "plugins/core/group-management/group-management.js",
|
out: "plugins/core/group-management/group-management.js",
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// in: 'plugins/core/group-management/group-transaction/group-transaction.src.js',
|
||||||
|
// out: 'plugins/core/group-management/group-transaction/group-transaction.js'
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
in: "plugins/core/name-registration/name-registration.src.js",
|
in: "plugins/core/name-registration/name-registration.src.js",
|
||||||
out: "plugins/core/name-registration/name-registration.js",
|
out: "plugins/core/name-registration/name-registration.js",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
in: "plugins/core/qdn/websites.src.js",
|
||||||
|
out: "plugins/core/qdn/websites.js",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: "plugins/core/qdn/publish/publish.src.js",
|
||||||
|
out: "plugins/core/qdn/publish/publish.js",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: "plugins/core/qdn/browser/browser.src.js",
|
||||||
|
out: "plugins/core/qdn/browser/browser.js",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: "plugins/core/qdn/data-management/data-management.src.js",
|
||||||
|
out: "plugins/core/qdn/data-management/data-management.js",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
in: "plugins/core/messaging/messaging.src.js",
|
in: "plugins/core/messaging/messaging.src.js",
|
||||||
out: "plugins/core/messaging/messaging.js",
|
out: "plugins/core/messaging/messaging.js",
|
||||||
@ -114,7 +134,7 @@ const generateForPlugins = () => {
|
|||||||
{
|
{
|
||||||
in: "plugins/core/puzzles/puzzles.src.js",
|
in: "plugins/core/puzzles/puzzles.src.js",
|
||||||
out: "plugins/core/puzzles/puzzles.js",
|
out: "plugins/core/puzzles/puzzles.js",
|
||||||
}
|
},
|
||||||
].map((file) => {
|
].map((file) => {
|
||||||
return generateRollupConfig(
|
return generateRollupConfig(
|
||||||
path.join(__dirname, file.in),
|
path.join(__dirname, file.in),
|
||||||
|
@ -11,18 +11,15 @@
|
|||||||
"main": "default-plugins.js",
|
"main": "default-plugins.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Qortal/qortal-ui.git",
|
"url": "https://github.com/Qortal/UI.git",
|
||||||
"directory": "qortal-ui-plugins"
|
"directory": "qortal-ui-plugins"
|
||||||
},
|
},
|
||||||
"author": "QORTAL <admin@qortal.org>",
|
"author": "QORTAL <admin@qortal.org>",
|
||||||
"license": {
|
"license": "GPL-3.0",
|
||||||
"type": "GPL-3.0",
|
|
||||||
"url" : "https://opensource.org/licenses/GPL-3.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@material/mwc-list": "^0.18.0",
|
"@material/mwc-list": "^0.18.0",
|
||||||
"@material/mwc-select": "^0.18.0",
|
"@material/mwc-select": "^0.18.0",
|
||||||
"emoji-picker-js": "https://github.com/lotw7277/emoji-picker-js"
|
"emoji-picker-js": "https://github.com/Qortal/emoji-picker-js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.16.0",
|
"@babel/core": "^7.16.0",
|
||||||
|
@ -80,6 +80,9 @@ class ChatPage extends LitElement {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
max-height: 40px;
|
max-height: 40px;
|
||||||
}
|
}
|
||||||
|
.float-left {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,6 +222,13 @@ class ChatPage extends LitElement {
|
|||||||
* @property sender and other info..
|
* @property sender and other info..
|
||||||
*/
|
*/
|
||||||
chatMessageTemplate(messageObj) {
|
chatMessageTemplate(messageObj) {
|
||||||
|
let avatarImg = '';
|
||||||
|
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?apiKey=${myNode.apiKey}`;
|
||||||
|
avatarImg = `<img src="${avatarUrl}" style="max-width:100%; max-height:100%;" onerror="this.onerror=null;this.style.display='none';" />`;
|
||||||
|
}
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<li class="clearfix">
|
<li class="clearfix">
|
||||||
@ -226,7 +236,8 @@ class ChatPage extends LitElement {
|
|||||||
<span class="message-data-name">${messageObj.senderName ? messageObj.senderName : messageObj.sender}</span>
|
<span class="message-data-name">${messageObj.senderName ? messageObj.senderName : messageObj.sender}</span>
|
||||||
<span class="message-data-time"><message-time timestamp=${messageObj.timestamp}></message-time></span>
|
<span class="message-data-time"><message-time timestamp=${messageObj.timestamp}></message-time></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="message ${messageObj.sender === this.selectedAddress.address ? "my-message float-right" : "other-message"}">${this.emojiPicker.parse(escape(messageObj.decodedMessage))}</div>
|
<div class="message ${messageObj.sender === this.selectedAddress.address ? "my-message float-right" : "other-message float-left"}">${this.emojiPicker.parse(escape(messageObj.decodedMessage))}</div>
|
||||||
|
<div class="message-data-avatar" style="width:40px; ${messageObj.sender === this.selectedAddress.address ? "float:right;" : "float:left;"} margin:3px;">${avatarImg}</div>
|
||||||
</li>
|
</li>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
@ -123,6 +123,10 @@ class ChatScroller extends LitElement {
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.float-left {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
.float-right {
|
.float-right {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
@ -159,6 +163,13 @@ class ChatScroller extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
chatMessageTemplate(messageObj) {
|
chatMessageTemplate(messageObj) {
|
||||||
|
let avatarImg = '';
|
||||||
|
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?apiKey=${myNode.apiKey}`;
|
||||||
|
avatarImg = `<img src="${avatarUrl}" style="max-width:100%; max-height:100%;" onerror="this.onerror=null;this.style.display='none';" />`;
|
||||||
|
}
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<li class="clearfix">
|
<li class="clearfix">
|
||||||
@ -166,7 +177,8 @@ class ChatScroller extends LitElement {
|
|||||||
<span class="message-data-name">${messageObj.senderName ? messageObj.senderName : messageObj.sender}</span>
|
<span class="message-data-name">${messageObj.senderName ? messageObj.senderName : messageObj.sender}</span>
|
||||||
<span class="message-data-time"><message-time timestamp=${messageObj.timestamp}></message-time></span>
|
<span class="message-data-time"><message-time timestamp=${messageObj.timestamp}></message-time></span>
|
||||||
</div>
|
</div>
|
||||||
<div id="messageContent" class="message ${messageObj.sender === this.myAddress ? "my-message float-right" : "other-message"}">${this.emojiPicker.parse(this.escapeHTML(messageObj.decodedMessage))}</div>
|
<div id="messageContent" class="message ${messageObj.sender === this.myAddress ? "my-message float-right" : "other-message float-left"}">${this.emojiPicker.parse(this.escapeHTML(messageObj.decodedMessage))}</div>
|
||||||
|
<div class="message-data-avatar" style="width:40px; ${messageObj.sender === this.myAddress ? "float:right;" : "float:left;"} margin:3px;">${avatarImg}</div>
|
||||||
</li>
|
</li>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,24 @@ parentEpml.ready().then(() => {
|
|||||||
menus: [],
|
menus: [],
|
||||||
parent: false
|
parent: false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
url: 'websites',
|
||||||
|
domain: 'core',
|
||||||
|
page: 'qdn/index.html',
|
||||||
|
title: 'Websites',
|
||||||
|
icon: 'computer',
|
||||||
|
menus: [],
|
||||||
|
parent: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url: 'data-management',
|
||||||
|
domain: 'core',
|
||||||
|
page: 'qdn/data-management/index.html',
|
||||||
|
title: 'Data Management',
|
||||||
|
icon: 'dns',
|
||||||
|
menus: [],
|
||||||
|
parent: false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
url: 'q-chat',
|
url: 'q-chat',
|
||||||
domain: 'core',
|
domain: 'core',
|
||||||
@ -83,7 +101,7 @@ parentEpml.ready().then(() => {
|
|||||||
menus: [],
|
menus: [],
|
||||||
parent: false
|
parent: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
url: 'puzzles',
|
url: 'puzzles',
|
||||||
domain: 'core',
|
domain: 'core',
|
||||||
page: 'puzzles/index.html',
|
page: 'puzzles/index.html',
|
||||||
@ -91,7 +109,7 @@ parentEpml.ready().then(() => {
|
|||||||
icon: 'extension',
|
icon: 'extension',
|
||||||
menus: [],
|
menus: [],
|
||||||
parent: false
|
parent: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const registerPlugins = (pluginInfo) => {
|
const registerPlugins = (pluginInfo) => {
|
||||||
|
@ -1,45 +1,45 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<link rel="stylesheet" href="/font/material-icons.css">
|
<link rel="stylesheet" href="/font/material-icons.css">
|
||||||
<style>
|
<style>
|
||||||
html {
|
html {
|
||||||
--scrollbarBG: #a1a1a1;
|
--scrollbarBG: #a1a1a1;
|
||||||
--thumbBG: #6a6c75;
|
--thumbBG: #6a6c75;
|
||||||
}
|
}
|
||||||
|
|
||||||
*::-webkit-scrollbar {
|
*::-webkit-scrollbar {
|
||||||
width: 11px;
|
width: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: var(--thumbBG) var(--scrollbarBG);
|
scrollbar-color: var(--thumbBG) var(--scrollbarBG);
|
||||||
}
|
}
|
||||||
|
|
||||||
*::-webkit-scrollbar-track {
|
*::-webkit-scrollbar-track {
|
||||||
background: var(--scrollbarBG);
|
background: var(--scrollbarBG);
|
||||||
}
|
}
|
||||||
|
|
||||||
*::-webkit-scrollbar-thumb {
|
*::-webkit-scrollbar-thumb {
|
||||||
background-color: var(--thumbBG);
|
background-color: var(--thumbBG);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
border: 3px solid var(--scrollbarBG);
|
border: 3px solid var(--scrollbarBG);
|
||||||
}
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: "Roboto", sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<minting-info></minting-info>
|
<minting-info></minting-info>
|
||||||
<script src="minting-info.js"></script>
|
<script src="minting-info.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -3,6 +3,7 @@
|
|||||||
// import '@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js'
|
// import '@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js'
|
||||||
|
|
||||||
import { LitElement, html, css } from 'lit-element'
|
import { LitElement, html, css } from 'lit-element'
|
||||||
|
import { render } from 'lit-html'
|
||||||
import { Epml } from '../../../epml.js'
|
import { Epml } from '../../../epml.js'
|
||||||
|
|
||||||
import '@material/mwc-icon'
|
import '@material/mwc-icon'
|
||||||
@ -81,9 +82,15 @@ class NameRegistration extends LitElement {
|
|||||||
|
|
||||||
<div class="divCard">
|
<div class="divCard">
|
||||||
<h3 style="margin: 0; margin-bottom: 1em; text-align: center;">Registered Names</h3>
|
<h3 style="margin: 0; margin-bottom: 1em; text-align: center;">Registered Names</h3>
|
||||||
<vaadin-grid id="namesGrid" style="height:auto;" ?hidden="${this.isEmptyArray(this.names)}" aria-label="Peers" .items="${this.names}" height-by-rows>
|
<vaadin-grid id="namesGrid" style="height:auto;" ?hidden="${this.isEmptyArray(this.names)}" aria-label="Names" .items="${this.names}" height-by-rows>
|
||||||
|
<vaadin-grid-column width="5rem" flex-grow="0" header="Avatar" .renderer=${(root, column, data) => {
|
||||||
|
render(html`${this.renderAvatar(data.item)}`, root)
|
||||||
|
}}></vaadin-grid-column>
|
||||||
<vaadin-grid-column path="name"></vaadin-grid-column>
|
<vaadin-grid-column path="name"></vaadin-grid-column>
|
||||||
<vaadin-grid-column path="owner"></vaadin-grid-column>
|
<vaadin-grid-column path="owner"></vaadin-grid-column>
|
||||||
|
<vaadin-grid-column width="12rem" flex-grow="0" header="Action" .renderer=${(root, column, data) => {
|
||||||
|
render(html`${this.renderAvatarButton(data.item)}`, root)
|
||||||
|
}}></vaadin-grid-column>
|
||||||
</vaadin-grid>
|
</vaadin-grid>
|
||||||
${this.isEmptyArray(this.names) ? html`
|
${this.isEmptyArray(this.names) ? html`
|
||||||
No names registered by this account!
|
No names registered by this account!
|
||||||
@ -131,6 +138,23 @@ class NameRegistration extends LitElement {
|
|||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderAvatar(nameObj) {
|
||||||
|
let name = nameObj.name
|
||||||
|
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
|
||||||
|
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
|
const url = `${nodeUrl}/arbitrary/THUMBNAIL/${name}/qortal_avatar?apiKey=${this.getApiKey()}`;
|
||||||
|
return html`<img src="${url}" style="width:100%;" onerror="this.onerror=null;this.style.display='none';">`
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAvatarButton(nameObj) {
|
||||||
|
return html`<mwc-button @click=${() => this.uploadAvatar(nameObj)}><mwc-icon>perm_identity</mwc-icon> Set Avatar</mwc-button>`
|
||||||
|
}
|
||||||
|
|
||||||
|
async uploadAvatar(nameObj) {
|
||||||
|
let name = nameObj.name
|
||||||
|
window.location.href = `../qdn/publish/index.html?service=THUMBNAIL&identifier=qortal_avatar&name=${name}&uploadType=file&category=Avatar&showName=false&showService=false&showIdentifier=false`
|
||||||
|
}
|
||||||
|
|
||||||
// getNamesGrid() {
|
// getNamesGrid() {
|
||||||
|
|
||||||
// const myGrid = this.shadowRoot.querySelector('#namesGrid')
|
// const myGrid = this.shadowRoot.querySelector('#namesGrid')
|
||||||
@ -207,6 +231,12 @@ class NameRegistration extends LitElement {
|
|||||||
parentEpml.imReady()
|
parentEpml.imReady()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getApiKey() {
|
||||||
|
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
|
||||||
|
let apiKey = myNode.apiKey;
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
clearSelection() {
|
clearSelection() {
|
||||||
|
|
||||||
window.getSelection().removeAllRanges()
|
window.getSelection().removeAllRanges()
|
||||||
|
@ -274,7 +274,7 @@ class NodeManagement extends LitElement {
|
|||||||
forceSyncPeer (peerAddress, rowIndex) {
|
forceSyncPeer (peerAddress, rowIndex) {
|
||||||
parentEpml
|
parentEpml
|
||||||
.request("apiCall", {
|
.request("apiCall", {
|
||||||
url: `/admin/forcesync`,
|
url: `/admin/forcesync?apiKey=${this.getApiKey()}`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: peerAddress,
|
body: peerAddress,
|
||||||
})
|
})
|
||||||
@ -286,7 +286,7 @@ class NodeManagement extends LitElement {
|
|||||||
removePeer(peerAddress, rowIndex) {
|
removePeer(peerAddress, rowIndex) {
|
||||||
parentEpml
|
parentEpml
|
||||||
.request("apiCall", {
|
.request("apiCall", {
|
||||||
url: `/peers`,
|
url: `/peers?apiKey=${this.getApiKey()}`,
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
body: peerAddress,
|
body: peerAddress,
|
||||||
})
|
})
|
||||||
@ -307,7 +307,7 @@ class NodeManagement extends LitElement {
|
|||||||
|
|
||||||
parentEpml
|
parentEpml
|
||||||
.request("apiCall", {
|
.request("apiCall", {
|
||||||
url: `/peers`,
|
url: `/peers?apiKey=${this.getApiKey()}`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: addPeerAddress,
|
body: addPeerAddress,
|
||||||
})
|
})
|
||||||
@ -327,7 +327,7 @@ class NodeManagement extends LitElement {
|
|||||||
|
|
||||||
parentEpml
|
parentEpml
|
||||||
.request("apiCall", {
|
.request("apiCall", {
|
||||||
url: `/admin/mintingaccounts`,
|
url: `/admin/mintingaccounts?apiKey=${this.getApiKey()}`,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: this.addMintingAccountKey,
|
body: this.addMintingAccountKey,
|
||||||
})
|
})
|
||||||
@ -384,7 +384,7 @@ class NodeManagement extends LitElement {
|
|||||||
this.removeMintingAccountLoading = true;
|
this.removeMintingAccountLoading = true;
|
||||||
|
|
||||||
parentEpml.request("apiCall", {
|
parentEpml.request("apiCall", {
|
||||||
url: `/admin/mintingaccounts`,
|
url: `/admin/mintingaccounts?apiKey=${this.getApiKey()}`,
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
body: publicKey,
|
body: publicKey,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
@ -492,6 +492,12 @@ class NodeManagement extends LitElement {
|
|||||||
parentEpml.imReady();
|
parentEpml.imReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getApiKey() {
|
||||||
|
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
|
||||||
|
let apiKey = myNode.apiKey;
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
clearSelection() {
|
clearSelection() {
|
||||||
window.getSelection().removeAllRanges()
|
window.getSelection().removeAllRanges()
|
||||||
window.parent.getSelection().removeAllRanges()
|
window.parent.getSelection().removeAllRanges()
|
||||||
|
475
qortal-ui-plugins/plugins/core/qdn/browser/browser.src.js
Normal file
475
qortal-ui-plugins/plugins/core/qdn/browser/browser.src.js
Normal file
@ -0,0 +1,475 @@
|
|||||||
|
import { LitElement, html, css } from 'lit-element'
|
||||||
|
import { render } from 'lit-html'
|
||||||
|
import { Epml } from '../../../../epml'
|
||||||
|
|
||||||
|
import '@material/mwc-button'
|
||||||
|
import '@material/mwc-textfield'
|
||||||
|
import '@material/mwc-select'
|
||||||
|
import '@material/mwc-list/mwc-list-item.js'
|
||||||
|
import '@material/mwc-slider'
|
||||||
|
import '@polymer/paper-progress/paper-progress.js'
|
||||||
|
|
||||||
|
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
||||||
|
|
||||||
|
class WebBrowser extends LitElement {
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
url: { type: String },
|
||||||
|
name: { type: String },
|
||||||
|
service: { type: String },
|
||||||
|
identifier: { type: String },
|
||||||
|
followedNames: { type: Array },
|
||||||
|
blockedNames: { type: Array },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get observers() {
|
||||||
|
return ['_kmxKeyUp(amount)']
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
* {
|
||||||
|
--mdc-theme-primary: rgb(3, 169, 244);
|
||||||
|
--mdc-theme-secondary: var(--mdc-theme-primary);
|
||||||
|
--paper-input-container-focus-color: var(--mdc-theme-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#websitesWrapper paper-button {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
#websitesWrapper .buttons {
|
||||||
|
/* --paper-button-ink-color: var(--paper-green-500);
|
||||||
|
color: var(--paper-green-500); */
|
||||||
|
width: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-bar {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 100px;
|
||||||
|
background-color: white;
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-bar-button mwc-icon {
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iframe-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 36px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border-top: 1px solid #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iframe-container iframe {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: none;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text] {
|
||||||
|
margin: 0;
|
||||||
|
padding: 2px 0 0 20px;
|
||||||
|
border: 0;
|
||||||
|
height: 34px;
|
||||||
|
font-size: 16px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
paper-progress {
|
||||||
|
--paper-progress-active-color: var(--mdc-theme-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<div id="websitesWrapper" style="width:auto; padding:10px; background: #fff;">
|
||||||
|
<div class="layout horizontal center">
|
||||||
|
<div class="address-bar">
|
||||||
|
<mwc-button @click=${() => this.goBack()} title="Back" class="address-bar-button"><mwc-icon>arrow_back_ios</mwc-icon></mwc-button>
|
||||||
|
<mwc-button @click=${() => this.goForward()} title="Forward" class="address-bar-button"><mwc-icon>arrow_forward_ios</mwc-icon></mwc-button>
|
||||||
|
<mwc-button @click=${() => this.refresh()} title="Reload" class="address-bar-button"><mwc-icon>refresh</mwc-icon></mwc-button>
|
||||||
|
<mwc-button @click=${() => this.goBackToList()} title="Back to list" class="address-bar-button"><mwc-icon>home</mwc-icon></mwc-button>
|
||||||
|
<input disabled style="width:550px;" id="address" type="text" value="qortal://${this.service.toLowerCase()}/${this.name}"></input>
|
||||||
|
<mwc-button @click=${() => this.delete()} title="Delete ${this.service} ${this.name} from node" class="address-bar-button float-right"><mwc-icon>delete</mwc-icon></mwc-button>
|
||||||
|
${this.renderBlockUnblockButton()}
|
||||||
|
${this.renderFollowUnfollowButton()}
|
||||||
|
</div>
|
||||||
|
<div class="iframe-container">
|
||||||
|
<iframe id="browser-iframe" src="${this.url}" sandbox="allow-scripts allow-forms">
|
||||||
|
Your browser doesn't support iframes
|
||||||
|
</iframe>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
renderFollowUnfollowButton() {
|
||||||
|
// Only show the follow/unfollow button if we have permission to modify the list on this node
|
||||||
|
if (this.followedNames == null || !Array.isArray(this.followedNames)) {
|
||||||
|
return html``
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.followedNames.indexOf(this.name) === -1) {
|
||||||
|
// render follow button
|
||||||
|
return html`<mwc-button @click=${() => this.follow()} title="Follow ${this.name}" class="address-bar-button float-right"><mwc-icon>add_to_queue</mwc-icon></mwc-button>`
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// render unfollow button
|
||||||
|
return html`<mwc-button @click=${() => this.unfollow()} title="Unfollow ${this.name}" class="address-bar-button float-right"><mwc-icon>remove_from_queue</mwc-icon></mwc-button>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBlockUnblockButton() {
|
||||||
|
// Only show the block/unblock button if we have permission to modify the list on this node
|
||||||
|
if (this.blockedNames == null || !Array.isArray(this.blockedNames)) {
|
||||||
|
return html``
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.blockedNames.indexOf(this.name) === -1) {
|
||||||
|
// render block button
|
||||||
|
return html`<mwc-button @click=${() => this.block()} title="Block ${this.name}" class="address-bar-button float-right"><mwc-icon>block</mwc-icon></mwc-button>`
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// render unblock button
|
||||||
|
return html`<mwc-button @click=${() => this.unblock()} title="Unblock ${this.name}" class="address-bar-button float-right"><mwc-icon>radio_button_unchecked</mwc-icon></mwc-button>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
|
||||||
|
goBack() {
|
||||||
|
window.history.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
goForward() {
|
||||||
|
window.history.forward();
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
goBackToList() {
|
||||||
|
window.location="../index.html";
|
||||||
|
}
|
||||||
|
|
||||||
|
follow() {
|
||||||
|
this.followName(this.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
unfollow() {
|
||||||
|
this.unfollowName(this.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
block() {
|
||||||
|
this.blockName(this.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
unblock() {
|
||||||
|
this.unblockName(this.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete() {
|
||||||
|
this.deleteCurrentResource();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async followName(name) {
|
||||||
|
let items = [
|
||||||
|
name
|
||||||
|
]
|
||||||
|
let namesJsonString = JSON.stringify({"items": items})
|
||||||
|
|
||||||
|
let ret = await parentEpml.request('apiCall', {
|
||||||
|
url: `/lists/followedNames?apiKey=${this.getApiKey()}`,
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: `${namesJsonString}`
|
||||||
|
})
|
||||||
|
|
||||||
|
if (ret === true) {
|
||||||
|
// Successfully followed - add to local list
|
||||||
|
// Remove it first by filtering the list - doing it this way ensures the UI updates
|
||||||
|
// immediately, as apposed to only adding if it doesn't already exist
|
||||||
|
this.followedNames = this.followedNames.filter(item => item != name);
|
||||||
|
this.followedNames.push(name)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parentEpml.request('showSnackBar', 'Error occurred when trying to follow this registered name. Please try again')
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
async unfollowName(name) {
|
||||||
|
let items = [
|
||||||
|
name
|
||||||
|
]
|
||||||
|
let namesJsonString = JSON.stringify({"items": items})
|
||||||
|
|
||||||
|
let ret = await parentEpml.request('apiCall', {
|
||||||
|
url: `/lists/followedNames?apiKey=${this.getApiKey()}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: `${namesJsonString}`
|
||||||
|
})
|
||||||
|
|
||||||
|
if (ret === true) {
|
||||||
|
// Successfully unfollowed - remove from local list
|
||||||
|
this.followedNames = this.followedNames.filter(item => item != name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parentEpml.request('showSnackBar', 'Error occurred when trying to unfollow this registered name. Please try again')
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
async blockName(name) {
|
||||||
|
let items = [
|
||||||
|
name
|
||||||
|
]
|
||||||
|
let namesJsonString = JSON.stringify({"items": items})
|
||||||
|
|
||||||
|
let ret = await parentEpml.request('apiCall', {
|
||||||
|
url: `/lists/blockedNames?apiKey=${this.getApiKey()}`,
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: `${namesJsonString}`
|
||||||
|
})
|
||||||
|
|
||||||
|
if (ret === true) {
|
||||||
|
// Successfully blocked - add to local list
|
||||||
|
// Remove it first by filtering the list - doing it this way ensures the UI updates
|
||||||
|
// immediately, as apposed to only adding if it doesn't already exist
|
||||||
|
this.blockedNames = this.blockedNames.filter(item => item != name);
|
||||||
|
this.blockedNames.push(name)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parentEpml.request('showSnackBar', 'Error occurred when trying to block this registered name. Please try again')
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
async unblockName(name) {
|
||||||
|
let items = [
|
||||||
|
name
|
||||||
|
]
|
||||||
|
let namesJsonString = JSON.stringify({"items": items})
|
||||||
|
|
||||||
|
let ret = await parentEpml.request('apiCall', {
|
||||||
|
url: `/lists/blockedNames?apiKey=${this.getApiKey()}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: `${namesJsonString}`
|
||||||
|
})
|
||||||
|
|
||||||
|
if (ret === true) {
|
||||||
|
// Successfully unblocked - remove from local list
|
||||||
|
this.blockedNames = this.blockedNames.filter(item => item != name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parentEpml.request('showSnackBar', 'Error occurred when trying to unblock this registered name. Please try again')
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteCurrentResource() {
|
||||||
|
if (this.followedNames.indexOf(this.name) != -1) {
|
||||||
|
// Following name - so deleting won't work
|
||||||
|
parentEpml.request('showSnackBar', "Can't delete data from followed names. Please unfollow first.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let identifier = this.identifier == null ? "default" : resource.identifier;
|
||||||
|
|
||||||
|
let ret = await parentEpml.request('apiCall', {
|
||||||
|
url: `/arbitrary/resource/${this.service}/${this.name}/${identifier}?apiKey=${this.getApiKey()}`,
|
||||||
|
method: 'DELETE'
|
||||||
|
})
|
||||||
|
|
||||||
|
if (ret === true) {
|
||||||
|
this.goBackToList();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parentEpml.request('showSnackBar', 'Error occurred when trying to delete this resource. Please try again')
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Helper Functions (Re-Used in Most part of the UI )
|
||||||
|
|
||||||
|
textColor(color) {
|
||||||
|
return color == 'light' ? 'rgba(255,255,255,0.7)' : 'rgba(0,0,0,0.87)'
|
||||||
|
}
|
||||||
|
|
||||||
|
_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()
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
this.url = 'about:blank'
|
||||||
|
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
this.name = urlParams.get('name');
|
||||||
|
this.service = urlParams.get('service');
|
||||||
|
this.identifier = null; // FUTURE: add support for identifiers
|
||||||
|
|
||||||
|
this.followedNames = []
|
||||||
|
this.blockedNames = []
|
||||||
|
|
||||||
|
|
||||||
|
const getFollowedNames = async () => {
|
||||||
|
// this.followedNames = []
|
||||||
|
|
||||||
|
let followedNames = await parentEpml.request('apiCall', {
|
||||||
|
url: `/lists/followedNames?apiKey=${this.getApiKey()}`
|
||||||
|
})
|
||||||
|
|
||||||
|
this.followedNames = followedNames
|
||||||
|
setTimeout(getFollowedNames, this.config.user.nodeSettings.pingInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getBlockedNames = async () => {
|
||||||
|
// this.blockedNames = []
|
||||||
|
|
||||||
|
let blockedNames = await parentEpml.request('apiCall', {
|
||||||
|
url: `/lists/blockedNames?apiKey=${this.getApiKey()}`
|
||||||
|
})
|
||||||
|
|
||||||
|
this.blockedNames = blockedNames
|
||||||
|
setTimeout(getBlockedNames, this.config.user.nodeSettings.pingInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
const render = () => {
|
||||||
|
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
|
||||||
|
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
|
||||||
|
this.url = `${nodeUrl}/render/${this.service}/${this.name}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const authorizeAndRender = () => {
|
||||||
|
parentEpml.request('apiCall', {
|
||||||
|
url: `/render/authorize/${this.name}?apiKey=${this.getApiKey()}`,
|
||||||
|
method: "POST"
|
||||||
|
}).then(res => {
|
||||||
|
console.log(res)
|
||||||
|
if (res.error) {
|
||||||
|
// Authorization problem - API key incorrect?
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
render()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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.subscribe('config', c => {
|
||||||
|
this.config = JSON.parse(c)
|
||||||
|
if (!configLoaded) {
|
||||||
|
authorizeAndRender()
|
||||||
|
setTimeout(getFollowedNames, 1)
|
||||||
|
setTimeout(getBlockedNames, 1)
|
||||||
|
configLoaded = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
parentEpml.subscribe('copy_menu_switch', async value => {
|
||||||
|
|
||||||
|
if (value === 'false' && window.getSelection().toString().length !== 0) {
|
||||||
|
|
||||||
|
this.clearSelection()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
|
||||||
|
window.addEventListener('contextmenu', (event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
this._textMenu(event)
|
||||||
|
})
|
||||||
|
|
||||||
|
window.addEventListener('click', () => {
|
||||||
|
parentEpml.request('closeCopyTextMenu', null)
|
||||||
|
})
|
||||||
|
|
||||||
|
window.onkeyup = (e) => {
|
||||||
|
if (e.keyCode === 27) {
|
||||||
|
parentEpml.request('closeCopyTextMenu', null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getApiKey() {
|
||||||
|
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
|
||||||
|
let apiKey = myNode.apiKey;
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearSelection() {
|
||||||
|
window.getSelection().removeAllRanges()
|
||||||
|
window.parent.getSelection().removeAllRanges()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.customElements.define('web-browser', WebBrowser)
|
44
qortal-ui-plugins/plugins/core/qdn/browser/index.html
Normal file
44
qortal-ui-plugins/plugins/core/qdn/browser/index.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="/font/material-icons.css">
|
||||||
|
<style>
|
||||||
|
html {
|
||||||
|
--scrollbarBG: #a1a1a1;
|
||||||
|
--thumbBG: #6a6c75;
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar {
|
||||||
|
width: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: var(--thumbBG) var(--scrollbarBG);
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar-track {
|
||||||
|
background: var(--scrollbarBG);
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar-thumb {
|
||||||
|
background-color: var(--thumbBG);
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 3px solid var(--scrollbarBG);
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<web-browser></web-browser>
|
||||||
|
<script src="browser.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -0,0 +1,434 @@
|
|||||||
|
import { LitElement, html, css } from 'lit-element'
|
||||||
|
import { render } from 'lit-html'
|
||||||
|
import { Epml } from '../../../../epml'
|
||||||
|
|
||||||
|
import '@material/mwc-icon'
|
||||||
|
import '@material/mwc-button'
|
||||||
|
|
||||||
|
import '@vaadin/vaadin-grid/vaadin-grid.js'
|
||||||
|
import '@vaadin/vaadin-grid/theme/material/all-imports.js'
|
||||||
|
|
||||||
|
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
||||||
|
|
||||||
|
class DataManagement extends LitElement {
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
loading: { type: Boolean },
|
||||||
|
resources: { type: Array },
|
||||||
|
blockedNames: { type: Array },
|
||||||
|
followedNames: { type: Array },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get observers() {
|
||||||
|
return ['_kmxKeyUp(amount)']
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
* {
|
||||||
|
--mdc-theme-primary: rgb(3, 169, 244);
|
||||||
|
--paper-input-container-focus-color: var(--mdc-theme-primary);
|
||||||
|
}
|
||||||
|
#websites-list-page {
|
||||||
|
background: #fff;
|
||||||
|
padding: 12px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divCard {
|
||||||
|
border: 1px solid #eee;
|
||||||
|
padding: 1em;
|
||||||
|
/** box-shadow: 0 1px 1px 0 rgba(0,0,0,0.14), 0 2px 1px -1px rgba(0,0,0,0.12), 0 1px 2px 0 rgba(0,0,0,0.20); **/
|
||||||
|
box-shadow: 0 .3px 1px 0 rgba(0,0,0,0.14), 0 1px 1px -1px rgba(0,0,0,0.12), 0 1px 2px 0 rgba(0,0,0,0.20);
|
||||||
|
margin-bottom: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2, h3, h4, h5 {
|
||||||
|
color:#333;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.visitSite {
|
||||||
|
color: #000;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: hidden !important;
|
||||||
|
visibility: none !important;
|
||||||
|
}
|
||||||
|
.details {
|
||||||
|
display: flex;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
font-size: 18px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
padding: 13px 20px;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #555;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
font-weight:600;
|
||||||
|
font-size:12px;
|
||||||
|
line-height: 32px;
|
||||||
|
opacity: 0.66;
|
||||||
|
}
|
||||||
|
.itemList {
|
||||||
|
padding:0;
|
||||||
|
}
|
||||||
|
.default-identifier {
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<div id="websites-list-page">
|
||||||
|
<div style="min-height:48px; display: flex; padding-bottom: 6px; margin: 2px;">
|
||||||
|
<h2 style="margin: 0; flex: 1; padding-top: .1em; display: inline;">Data Management</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="divCard">
|
||||||
|
<h3 style="margin: 0; margin-bottom: 1em; text-align: center;">Data hosted by this node</h3>
|
||||||
|
<vaadin-grid id="resourcesGrid" style="height:auto;" ?hidden="${this.isEmptyArray(this.resources)}" aria-label="Websites" .items="${this.resources}" height-by-rows>
|
||||||
|
<!--<vaadin-grid-column path="name"></vaadin-grid-column>-->
|
||||||
|
<vaadin-grid-column header="Registered Name" .renderer=${(root, column, data) => {
|
||||||
|
render(html`${this.renderName(data.item)}`, root)
|
||||||
|
}}></vaadin-grid-column>
|
||||||
|
<vaadin-grid-column header="Service" .renderer=${(root, column, data) => {
|
||||||
|
render(html`${this.renderService(data.item)}`, root)
|
||||||
|
}}></vaadin-grid-column>
|
||||||
|
<vaadin-grid-column header="Identifier" .renderer=${(root, column, data) => {
|
||||||
|
render(html`${this.renderIdentifier(data.item)}`, root)
|
||||||
|
}}></vaadin-grid-column>
|
||||||
|
<vaadin-grid-column width="10rem" flex-grow="0" header="" .renderer=${(root, column, data) => {
|
||||||
|
render(html`${this.renderDeleteButton(data.item)}`, root);
|
||||||
|
}}></vaadin-grid-column>
|
||||||
|
<vaadin-grid-column width="10rem" flex-grow="0" header="" .renderer=${(root, column, data) => {
|
||||||
|
render(html`${this.renderBlockUnblockButton(data.item)}`, root);
|
||||||
|
}}></vaadin-grid-column>
|
||||||
|
</vaadin-grid>
|
||||||
|
${this.renderDefaultText()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
goBack() {
|
||||||
|
window.history.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderDefaultText() {
|
||||||
|
if (this.resources == null || !Array.isArray(this.resources)) {
|
||||||
|
return html`<br />Couldn't fetch hosted data list from node`
|
||||||
|
}
|
||||||
|
if (this.isEmptyArray(this.resources)) {
|
||||||
|
return html`<br />This node isn't hosting any data`;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
renderName(resource) {
|
||||||
|
let name = resource.name
|
||||||
|
return html`${name}`
|
||||||
|
}
|
||||||
|
|
||||||
|
renderService(resource) {
|
||||||
|
let service = resource.service
|
||||||
|
return html`${service}`
|
||||||
|
}
|
||||||
|
|
||||||
|
renderIdentifier(resource) {
|
||||||
|
return resource.identifier == null ? html`<span class="default-identifier">default</span>` : html`${resource.identifier}`
|
||||||
|
}
|
||||||
|
|
||||||
|
renderDeleteButton(resource) {
|
||||||
|
let name = resource.name
|
||||||
|
|
||||||
|
// Only show the block/unblock button if we have permission to modify the list on this node
|
||||||
|
// We can use the blocked names list for this, as it won't be a valid array if we have no access
|
||||||
|
if (this.blockedNames == null || !Array.isArray(this.blockedNames)) {
|
||||||
|
return html``
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to check if we are following this name, as if we are, there is no point in deleting anything
|
||||||
|
// as it will be re-fetched immediately. In these cases we should show an UNFOLLOW button.
|
||||||
|
if (this.followedNames.indexOf(name) != -1) {
|
||||||
|
// render unfollow button
|
||||||
|
return html`<mwc-button @click=${() => this.unfollowName(resource)}><mwc-icon>remove_from_queue</mwc-icon> Unfollow</mwc-button>`
|
||||||
|
}
|
||||||
|
|
||||||
|
// render delete button
|
||||||
|
return html`<mwc-button @click=${() => this.deleteResource(resource)} onclick="this.blur();"><mwc-icon>delete</mwc-icon> Delete</mwc-button>`
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBlockUnblockButton(resource) {
|
||||||
|
let name = resource.name
|
||||||
|
|
||||||
|
// Only show the block/unblock button if we have permission to modify the list on this node
|
||||||
|
if (this.blockedNames == null || !Array.isArray(this.blockedNames)) {
|
||||||
|
return html``
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.blockedNames.indexOf(name) === -1) {
|
||||||
|
// render block button
|
||||||
|
return html`<mwc-button @click=${() => this.blockName(resource)}><mwc-icon>block</mwc-icon> Block</mwc-button>`
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// render unblock button
|
||||||
|
return html`<mwc-button @click=${() => this.unblockName(resource)}><mwc-icon>radio_button_unchecked</mwc-icon> Unblock</mwc-button>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async blockName(resource) {
|
||||||
|
let name = resource.name
|
||||||
|
let items = [
|
||||||
|
name
|
||||||
|
]
|
||||||
|
let namesJsonString = JSON.stringify({"items": items})
|
||||||
|
|
||||||
|
let ret = await parentEpml.request('apiCall', {
|
||||||
|
url: `/lists/blockedNames?apiKey=${this.getApiKey()}`,
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: `${namesJsonString}`
|
||||||
|
})
|
||||||
|
|
||||||
|
if (ret === true) {
|
||||||
|
// Successfully blocked - add to local list
|
||||||
|
// Remove it first by filtering the list - doing it this way ensures the UI updates
|
||||||
|
// immediately, as apposed to only adding if it doesn't already exist
|
||||||
|
this.blockedNames = this.blockedNames.filter(item => item != name);
|
||||||
|
this.blockedNames.push(name)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parentEpml.request('showSnackBar', 'Error occurred when trying to block this registered name. Please try again')
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
async unfollowName(websiteObj) {
|
||||||
|
let name = websiteObj.name
|
||||||
|
let items = [
|
||||||
|
name
|
||||||
|
]
|
||||||
|
let namesJsonString = JSON.stringify({"items": items})
|
||||||
|
|
||||||
|
let ret = await parentEpml.request('apiCall', {
|
||||||
|
url: `/lists/followedNames?apiKey=${this.getApiKey()}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: `${namesJsonString}`
|
||||||
|
})
|
||||||
|
|
||||||
|
if (ret === true) {
|
||||||
|
// Successfully unfollowed - remove from local list
|
||||||
|
this.followedNames = this.followedNames.filter(item => item != name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parentEpml.request('showSnackBar', 'Error occurred when trying to unfollow this registered name. Please try again')
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
async unblockName(resource) {
|
||||||
|
let name = resource.name
|
||||||
|
let items = [
|
||||||
|
name
|
||||||
|
]
|
||||||
|
let namesJsonString = JSON.stringify({"items": items})
|
||||||
|
|
||||||
|
let ret = await parentEpml.request('apiCall', {
|
||||||
|
url: `/lists/blockedNames?apiKey=${this.getApiKey()}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: `${namesJsonString}`
|
||||||
|
})
|
||||||
|
|
||||||
|
if (ret === true) {
|
||||||
|
// Successfully unblocked - remove from local list
|
||||||
|
this.blockedNames = this.blockedNames.filter(item => item != name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parentEpml.request('showSnackBar', 'Error occurred when trying to unblock this registered name. Please try again')
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteResource(resource) {
|
||||||
|
let identifier = resource.identifier == null ? "default" : resource.identifier;
|
||||||
|
|
||||||
|
let ret = await parentEpml.request('apiCall', {
|
||||||
|
url: `/arbitrary/resource/${resource.service}/${resource.name}/${identifier}?apiKey=${this.getApiKey()}`,
|
||||||
|
method: 'DELETE'
|
||||||
|
})
|
||||||
|
|
||||||
|
if (ret === true) {
|
||||||
|
// Successfully deleted - so refresh the page
|
||||||
|
this.getArbitraryResources();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parentEpml.request('showSnackBar', 'Error occurred when trying to delete this resource. Please try again')
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Helper Functions (Re-Used in Most part of the UI )
|
||||||
|
|
||||||
|
textColor(color) {
|
||||||
|
return color == 'light' ? 'rgba(255,255,255,0.7)' : 'rgba(0,0,0,0.87)'
|
||||||
|
}
|
||||||
|
|
||||||
|
_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()
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
this.selectedAddress = {}
|
||||||
|
this.resources = []
|
||||||
|
this.blockedNames = []
|
||||||
|
this.followedNames = []
|
||||||
|
this.isLoading = false
|
||||||
|
}
|
||||||
|
|
||||||
|
getArbitraryResources = async () => {
|
||||||
|
// this.resources = []
|
||||||
|
|
||||||
|
let resources = await parentEpml.request('apiCall', {
|
||||||
|
url: `/arbitrary/hosted/resources?apiKey=${this.getApiKey()}`
|
||||||
|
})
|
||||||
|
|
||||||
|
this.resources = resources
|
||||||
|
}
|
||||||
|
|
||||||
|
getBlockedNames = async () => {
|
||||||
|
// this.blockedNames = []
|
||||||
|
|
||||||
|
let blockedNames = await parentEpml.request('apiCall', {
|
||||||
|
url: `/lists/blockedNames?apiKey=${this.getApiKey()}`
|
||||||
|
})
|
||||||
|
|
||||||
|
this.blockedNames = blockedNames
|
||||||
|
}
|
||||||
|
|
||||||
|
getFollowedNames = async () => {
|
||||||
|
// this.followedNames = []
|
||||||
|
|
||||||
|
let followedNames = await parentEpml.request('apiCall', {
|
||||||
|
url: `/lists/followedNames?apiKey=${this.getApiKey()}`
|
||||||
|
})
|
||||||
|
|
||||||
|
this.followedNames = followedNames
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
|
||||||
|
window.addEventListener('contextmenu', (event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
this._textMenu(event)
|
||||||
|
})
|
||||||
|
|
||||||
|
window.addEventListener('click', () => {
|
||||||
|
parentEpml.request('closeCopyTextMenu', null)
|
||||||
|
})
|
||||||
|
|
||||||
|
window.onkeyup = (e) => {
|
||||||
|
if (e.keyCode === 27) {
|
||||||
|
parentEpml.request('closeCopyTextMenu', null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.subscribe('config', c => {
|
||||||
|
this.config = JSON.parse(c)
|
||||||
|
if (!configLoaded) {
|
||||||
|
setTimeout(this.getArbitraryResources, 1)
|
||||||
|
setTimeout(this.getFollowedNames, 1)
|
||||||
|
setTimeout(this.getBlockedNames, 1)
|
||||||
|
setInterval(this.getArbitraryResources, 30*1000)
|
||||||
|
setInterval(this.getFollowedNames, 30*1000)
|
||||||
|
setInterval(this.getBlockedNames, 30*1000)
|
||||||
|
configLoaded = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
parentEpml.subscribe('copy_menu_switch', async value => {
|
||||||
|
|
||||||
|
if (value === 'false' && window.getSelection().toString().length !== 0) {
|
||||||
|
|
||||||
|
this.clearSelection()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
parentEpml.imReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
getApiKey() {
|
||||||
|
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
|
||||||
|
let apiKey = myNode.apiKey;
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearSelection() {
|
||||||
|
window.getSelection().removeAllRanges()
|
||||||
|
window.parent.getSelection().removeAllRanges()
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmptyArray(arr) {
|
||||||
|
if (!arr) { return true }
|
||||||
|
return arr.length === 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.customElements.define('data-management', DataManagement)
|
@ -0,0 +1,45 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="/font/material-icons.css">
|
||||||
|
<style>
|
||||||
|
html {
|
||||||
|
--scrollbarBG: #a1a1a1;
|
||||||
|
--thumbBG: #6a6c75;
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar {
|
||||||
|
width: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: var(--thumbBG) var(--scrollbarBG);
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar-track {
|
||||||
|
background: var(--scrollbarBG);
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar-thumb {
|
||||||
|
background-color: var(--thumbBG);
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 3px solid var(--scrollbarBG);
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<data-management></data-management>
|
||||||
|
<script src="data-management.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
46
qortal-ui-plugins/plugins/core/qdn/index.html
Normal file
46
qortal-ui-plugins/plugins/core/qdn/index.html
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="/font/material-icons.css">
|
||||||
|
<style>
|
||||||
|
html {
|
||||||
|
--scrollbarBG: #a1a1a1;
|
||||||
|
--thumbBG: #6a6c75;
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar {
|
||||||
|
width: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: var(--thumbBG) var(--scrollbarBG);
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar-track {
|
||||||
|
background: var(--scrollbarBG);
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar-thumb {
|
||||||
|
background-color: var(--thumbBG);
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 3px solid var(--scrollbarBG);
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<websites-list></websites-list>
|
||||||
|
|
||||||
|
<script src="websites.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
44
qortal-ui-plugins/plugins/core/qdn/publish/index.html
Normal file
44
qortal-ui-plugins/plugins/core/qdn/publish/index.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="/font/material-icons.css">
|
||||||
|
<style>
|
||||||
|
html {
|
||||||
|
--scrollbarBG: #a1a1a1;
|
||||||
|
--thumbBG: #6a6c75;
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar {
|
||||||
|
width: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: var(--thumbBG) var(--scrollbarBG);
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar-track {
|
||||||
|
background: var(--scrollbarBG);
|
||||||
|
}
|
||||||
|
|
||||||
|
*::-webkit-scrollbar-thumb {
|
||||||
|
background-color: var(--thumbBG);
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 3px solid var(--scrollbarBG);
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: "Roboto", sans-serif;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<publish-data></publish-data>
|
||||||
|
<script src="publish.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
518
qortal-ui-plugins/plugins/core/qdn/publish/publish.src.js
Normal file
518
qortal-ui-plugins/plugins/core/qdn/publish/publish.src.js
Normal file
@ -0,0 +1,518 @@
|
|||||||
|
import { LitElement, html, css } from 'lit-element'
|
||||||
|
import { render } from 'lit-html'
|
||||||
|
import { Epml } from '../../../../epml'
|
||||||
|
|
||||||
|
import '@material/mwc-button'
|
||||||
|
import '@material/mwc-textfield'
|
||||||
|
import '@material/mwc-select'
|
||||||
|
import '@material/mwc-list/mwc-list-item.js'
|
||||||
|
import '@material/mwc-slider'
|
||||||
|
import '@polymer/paper-progress/paper-progress.js'
|
||||||
|
|
||||||
|
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
||||||
|
|
||||||
|
class PublishData extends LitElement {
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
name: { type: String },
|
||||||
|
service: { type: String },
|
||||||
|
identifier: { type: String },
|
||||||
|
category: { type: String },
|
||||||
|
uploadType: { type: String },
|
||||||
|
showName: { type: Boolean },
|
||||||
|
showService: { type: Boolean },
|
||||||
|
showIdentifier: { type: Boolean },
|
||||||
|
serviceLowercase: { type: String },
|
||||||
|
names: { type: Array },
|
||||||
|
registeredName: { type: String },
|
||||||
|
selectedName: { type: String },
|
||||||
|
path: { type: String },
|
||||||
|
portForwardingEnabled: { type: Boolean },
|
||||||
|
//selectedAddress: { type: Object },
|
||||||
|
|
||||||
|
amount: { type: Number },
|
||||||
|
generalMessage: { type: String },
|
||||||
|
successMessage: { type: String },
|
||||||
|
errorMessage: { type: String },
|
||||||
|
loading: { type: Boolean },
|
||||||
|
btnDisable: { type: Boolean },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get observers() {
|
||||||
|
return ['_kmxKeyUp(amount)']
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
* {
|
||||||
|
--mdc-theme-primary: rgb(3, 169, 244);
|
||||||
|
--mdc-theme-secondary: var(--mdc-theme-primary);
|
||||||
|
--paper-input-container-focus-color: var(--mdc-theme-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#publishWrapper paper-button {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
#publishWrapper .buttons {
|
||||||
|
/* --paper-button-ink-color: var(--paper-green-500);
|
||||||
|
color: var(--paper-green-500); */
|
||||||
|
width: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
mwc-textfield {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
paper-progress {
|
||||||
|
--paper-progress-active-color: var(--mdc-theme-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-text {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-bar {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 100px;
|
||||||
|
background-color: white;
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-bar-button mwc-icon {
|
||||||
|
width: 30px;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<div id="publishWrapper" style="width:auto; padding:10px; background: #fff; height:100vh;">
|
||||||
|
<div class="layout horizontal center" style=" padding:12px 15px;">
|
||||||
|
<div class="address-bar">
|
||||||
|
<mwc-button @click=${() => this.goBack()} class="address-bar-button"><mwc-icon>arrow_back_ios</mwc-icon> Back</mwc-button>
|
||||||
|
</div>
|
||||||
|
<paper-card style="width:100%; max-width:740px;">
|
||||||
|
<div style="background-color: ${this.selectedAddress.color}; margin:0; margin-top:20px; color: ${this.textColor(this.selectedAddress.textColor)};">
|
||||||
|
<h3 style="margin:0; padding:8px 0; text-transform:capitalize;">Publish / Update ${this.category}</h3>
|
||||||
|
<p style="font-style:italic; font-size:14px;" ?hidden="${this.portForwardingEnabled}">Note: it is recommended that you set up port forwarding before hosting data, so that it can more easily accessed by peers on the network.</p>
|
||||||
|
</div>
|
||||||
|
</paper-card>
|
||||||
|
<!-- TODO: adapt this dropdown to list all names on the account. Right now it's hardcoded to a single name -->
|
||||||
|
<p style="display: ${this.showName ? 'block' : 'none'}">
|
||||||
|
<mwc-select id="registeredName" label="Select Name" index="0" @selected=${(e) => this.selectName(e)} style="min-width: 130px; max-width:100%; width:100%;">
|
||||||
|
<mwc-list-item value="${this.registeredName}">${this.registeredName}</mwc-list-item>
|
||||||
|
</mwc-select>
|
||||||
|
</p>
|
||||||
|
${this.renderUploadField()}
|
||||||
|
<p style="display: ${this.showService ? 'block' : 'none'}">
|
||||||
|
<mwc-textfield style="width:100%;" label="Service" id="service" type="text" value="${this.service}"></mwc-textfield>
|
||||||
|
</p>
|
||||||
|
<p style="display: ${this.showIdentifier ? 'block' : 'none'}">
|
||||||
|
<mwc-textfield style="width:100%;" label="Identifier" id="identifier" type="text" value="${this.identifier != null ? this.identifier : ''}"></mwc-textfield>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p style="break-word;">${this.generalMessage}</p>
|
||||||
|
<p style="color:red">${this.errorMessage}</p>
|
||||||
|
<p style="color:green;word-break: break-word;">${this.successMessage}</p>
|
||||||
|
|
||||||
|
${this.loading ? html` <paper-progress indeterminate style="width:100%; margin:4px;"></paper-progress> ` : ''}
|
||||||
|
|
||||||
|
<div class="buttons">
|
||||||
|
<div>
|
||||||
|
<mwc-button ?disabled=${this.btnDisable} style="width:100%;" raised icon="send" @click=${(e) => this.doPublish(e)}>Publish </mwc-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
|
||||||
|
goBack() {
|
||||||
|
window.history.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
renderUploadField() {
|
||||||
|
if (this.uploadType === "file") {
|
||||||
|
return html`<p>
|
||||||
|
<input style="width:100%;" id="file" type="file" />
|
||||||
|
</p>`;
|
||||||
|
}
|
||||||
|
else if (this.uploadType === "zip") {
|
||||||
|
return html`<p>
|
||||||
|
<span class="upload-text">Select zip file containing static content:</span><br />
|
||||||
|
<input style="width:100%;" id="file" type="file" accept=".zip" />
|
||||||
|
</p>`;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return html`<p>
|
||||||
|
<mwc-textfield style="width:100%;" label="Local path to static files" id="path" type="text" value="${this.path}"></mwc-textfield>
|
||||||
|
</p>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
doPublish(e) {
|
||||||
|
let registeredName = this.shadowRoot.getElementById('registeredName').value
|
||||||
|
let service = this.shadowRoot.getElementById('service').value
|
||||||
|
let identifier = this.shadowRoot.getElementById('identifier').value
|
||||||
|
|
||||||
|
// If name is hidden, use the value passed in via the name parameter
|
||||||
|
if (!this.showName) {
|
||||||
|
registeredName = this.name
|
||||||
|
}
|
||||||
|
|
||||||
|
let file;
|
||||||
|
let path;
|
||||||
|
|
||||||
|
if (this.uploadType === "file" || this.uploadType === "zip") {
|
||||||
|
file = this.shadowRoot.getElementById('file').files[0]
|
||||||
|
}
|
||||||
|
else if (this.uploadType === "path") {
|
||||||
|
path = this.shadowRoot.getElementById('path').value
|
||||||
|
}
|
||||||
|
|
||||||
|
this.generalMessage = ''
|
||||||
|
this.successMessage = ''
|
||||||
|
this.errorMessage = ''
|
||||||
|
|
||||||
|
if (registeredName === '') {
|
||||||
|
this.showName = true
|
||||||
|
parentEpml.request('showSnackBar', 'Please select a registered name to publish data for')
|
||||||
|
}
|
||||||
|
else if (this.uploadType === "file" && file == null) {
|
||||||
|
parentEpml.request('showSnackBar', 'Please select a file to host')
|
||||||
|
}
|
||||||
|
else if (this.uploadType === "zip" && file == null) {
|
||||||
|
parentEpml.request('showSnackBar', 'Please select a zip file to host')
|
||||||
|
}
|
||||||
|
else if (this.uploadType === "path" && path === '') {
|
||||||
|
parentEpml.request('showSnackBar', 'Please enter the directory path containing the static content')
|
||||||
|
}
|
||||||
|
else if (service === '') {
|
||||||
|
parentEpml.request('showSnackBar', 'Please enter a service name')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.publishData(registeredName, path, file, service, identifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async publishData(registeredName, path, file, service, identifier) {
|
||||||
|
this.loading = true
|
||||||
|
this.btnDisable = true
|
||||||
|
|
||||||
|
const validateName = async (receiverName) => {
|
||||||
|
let nameRes = await parentEpml.request('apiCall', {
|
||||||
|
type: 'api',
|
||||||
|
url: `/names/${receiverName}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
return nameRes
|
||||||
|
}
|
||||||
|
|
||||||
|
const showError = async (errorMessage) => {
|
||||||
|
this.loading = false
|
||||||
|
this.btnDisable = false
|
||||||
|
this.generalMessage = ''
|
||||||
|
this.successMessage = ''
|
||||||
|
console.error(errorMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
const validate = async () => {
|
||||||
|
let validNameRes = await validateName(registeredName)
|
||||||
|
if (validNameRes.error) {
|
||||||
|
this.errorMessage = "Error: " + validNameRes.message
|
||||||
|
showError(this.errorMessage)
|
||||||
|
throw new Error(this.errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.generalMessage = "Processing data... this can take some time...";
|
||||||
|
|
||||||
|
let transactionBytes = await uploadData(registeredName, path, file)
|
||||||
|
if (transactionBytes.error) {
|
||||||
|
this.errorMessage = "Error: " + transactionBytes.message
|
||||||
|
showError(this.errorMessage)
|
||||||
|
throw new Error(this.errorMessage);
|
||||||
|
}
|
||||||
|
else if (transactionBytes.includes("Error 500 Internal Server Error")) {
|
||||||
|
this.errorMessage = "Internal Server Error when publishing data"
|
||||||
|
showError(this.errorMessage)
|
||||||
|
throw new Error(this.errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.generalMessage = "Computing proof of work... this can take some time...";
|
||||||
|
|
||||||
|
let signAndProcessRes = await signAndProcess(transactionBytes)
|
||||||
|
if (signAndProcessRes.error) {
|
||||||
|
this.errorMessage = "Error: " + signAndProcessRes.message
|
||||||
|
showError(this.errorMessage)
|
||||||
|
throw new Error(this.errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.btnDisable = false
|
||||||
|
this.loading = false
|
||||||
|
this.errorMessage = ''
|
||||||
|
this.generalMessage = ''
|
||||||
|
this.successMessage = 'Transaction successful!'
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadData = async (registeredName, path, file) => {
|
||||||
|
let postBody = path
|
||||||
|
let urlSuffix = ""
|
||||||
|
if (file != null) {
|
||||||
|
|
||||||
|
// If we're sending zipped data, make sure to use the /zip version of the POST /arbitrary/* API
|
||||||
|
if (this.uploadType === "zip") {
|
||||||
|
urlSuffix = "/zip"
|
||||||
|
}
|
||||||
|
// If we're sending file data, use the /base64 version of the POST /arbitrary/* API
|
||||||
|
else if (this.uploadType === "file") {
|
||||||
|
urlSuffix = "/base64"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base64 encode the file to work around compatibility issues between javascript and java byte arrays
|
||||||
|
let fileBuffer = new Uint8Array(await file.arrayBuffer())
|
||||||
|
postBody = Buffer.from(fileBuffer).toString('base64');
|
||||||
|
}
|
||||||
|
|
||||||
|
let uploadDataUrl = `/arbitrary/${this.service}/${registeredName}${urlSuffix}`
|
||||||
|
if (identifier != null && identifier.trim().length > 0) {
|
||||||
|
uploadDataUrl = `/arbitrary/${service}/${registeredName}/${this.identifier}${urlSuffix}`
|
||||||
|
}
|
||||||
|
|
||||||
|
let uploadDataRes = await parentEpml.request('apiCall', {
|
||||||
|
type: 'api',
|
||||||
|
method: 'POST',
|
||||||
|
url: `${uploadDataUrl}?apiKey=${this.getApiKey()}`,
|
||||||
|
body: `${postBody}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
return uploadDataRes
|
||||||
|
}
|
||||||
|
|
||||||
|
const convertBytesForSigning = async (transactionBytesBase58) => {
|
||||||
|
let convertedBytes = await parentEpml.request('apiCall', {
|
||||||
|
type: 'api',
|
||||||
|
method: 'POST',
|
||||||
|
url: `/transactions/convert`,
|
||||||
|
body: `${transactionBytesBase58}`,
|
||||||
|
})
|
||||||
|
|
||||||
|
return convertedBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
const signAndProcess = async (transactionBytesBase58) => {
|
||||||
|
let convertedBytesBase58 = await convertBytesForSigning(transactionBytesBase58)
|
||||||
|
if (convertedBytesBase58.error) {
|
||||||
|
this.errorMessage = "Error: " + convertedBytesBase58.message
|
||||||
|
showError(this.errorMessage)
|
||||||
|
throw new Error(this.errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
const convertedBytes = window.parent.Base58.decode(convertedBytesBase58);
|
||||||
|
const _convertedBytesArray = Object.keys(convertedBytes).map(function (key) { return convertedBytes[key]; });
|
||||||
|
const convertedBytesArray = new Uint8Array(_convertedBytesArray)
|
||||||
|
const convertedBytesHash = new window.parent.Sha256().process(convertedBytesArray).finish().result
|
||||||
|
|
||||||
|
const hashPtr = window.parent.sbrk(32, window.parent.heap);
|
||||||
|
const hashAry = new Uint8Array(window.parent.memory.buffer, hashPtr, 32);
|
||||||
|
hashAry.set(convertedBytesHash);
|
||||||
|
|
||||||
|
const difficulty = 14;
|
||||||
|
const workBufferLength = 8 * 1024 * 1024;
|
||||||
|
const workBufferPtr = window.parent.sbrk(workBufferLength, window.parent.heap);
|
||||||
|
|
||||||
|
this.errorMessage = '';
|
||||||
|
this.successMessage = '';
|
||||||
|
let nonce = window.parent.computePow(hashPtr, workBufferPtr, workBufferLength, difficulty)
|
||||||
|
|
||||||
|
let response = await parentEpml.request('sign_arbitrary', {
|
||||||
|
nonce: this.selectedAddress.nonce,
|
||||||
|
arbitraryBytesBase58: transactionBytesBase58,
|
||||||
|
arbitraryBytesForSigningBase58: convertedBytesBase58,
|
||||||
|
arbitraryNonce: nonce
|
||||||
|
})
|
||||||
|
|
||||||
|
let myResponse = { error: '' }
|
||||||
|
if (response === false) {
|
||||||
|
myResponse.error = "Unable to sign and process transaction"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
myResponse = response
|
||||||
|
}
|
||||||
|
|
||||||
|
return myResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Helper Functions (Re-Used in Most part of the UI )
|
||||||
|
|
||||||
|
textColor(color) {
|
||||||
|
return color == 'light' ? 'rgba(255,255,255,0.7)' : 'rgba(0,0,0,0.87)'
|
||||||
|
}
|
||||||
|
|
||||||
|
_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()
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.showName = false;
|
||||||
|
this.showService = false
|
||||||
|
this.showIdentifier = false
|
||||||
|
|
||||||
|
const urlParams = new URLSearchParams(window.location.search)
|
||||||
|
this.name = urlParams.get('name')
|
||||||
|
this.service = urlParams.get('service')
|
||||||
|
this.identifier = urlParams.get('identifier')
|
||||||
|
this.category = urlParams.get('category')
|
||||||
|
this.uploadType = urlParams.get('uploadType') !== "null" ? urlParams.get('uploadType') : "file"
|
||||||
|
|
||||||
|
if (urlParams.get('showName') === "true") {
|
||||||
|
this.showName = true
|
||||||
|
}
|
||||||
|
if (urlParams.get('showService') === "true") {
|
||||||
|
this.showService = true
|
||||||
|
}
|
||||||
|
if (urlParams.get('showIdentifier') === "true") {
|
||||||
|
this.showIdentifier = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.identifier != null) {
|
||||||
|
if (this.identifier === "null" || this.identifier.trim().length == 0) {
|
||||||
|
this.identifier = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.portForwardingEnabled = true // Default to true so the message doesn't appear and disappear quickly
|
||||||
|
this.names = []
|
||||||
|
this.registeredName = ''
|
||||||
|
this.selectedName = 'invalid'
|
||||||
|
this.path = ''
|
||||||
|
//this.selectedAddress = {}
|
||||||
|
this.successMessage = ''
|
||||||
|
this.generalMessage = ''
|
||||||
|
this.errorMessage = ''
|
||||||
|
this.loading = false
|
||||||
|
this.btnDisable = false
|
||||||
|
|
||||||
|
const fetchNames = () => {
|
||||||
|
parentEpml.request('apiCall', {
|
||||||
|
url: `/names/address/${this.selectedAddress.address}?limit=0&reverse=true`
|
||||||
|
}).then(res => {
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.names = res
|
||||||
|
this.registeredName = res[0].name;
|
||||||
|
}, 1)
|
||||||
|
})
|
||||||
|
setTimeout(fetchNames, this.config.user.nodeSettings.pingInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchPeersSummary = () => {
|
||||||
|
parentEpml.request('apiCall', {
|
||||||
|
url: `/peers/summary`
|
||||||
|
}).then(res => {
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.portForwardingEnabled = (res.inboundConnections != null && res.inboundConnections > 0);
|
||||||
|
}, 1)
|
||||||
|
})
|
||||||
|
setTimeout(fetchNames, this.config.user.nodeSettings.pingInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
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.subscribe('config', c => {
|
||||||
|
if (!configLoaded) {
|
||||||
|
setTimeout(fetchNames, 1)
|
||||||
|
setTimeout(fetchPeersSummary, 1)
|
||||||
|
configLoaded = true
|
||||||
|
}
|
||||||
|
this.config = JSON.parse(c)
|
||||||
|
})
|
||||||
|
parentEpml.subscribe('copy_menu_switch', async value => {
|
||||||
|
|
||||||
|
if (value === 'false' && window.getSelection().toString().length !== 0) {
|
||||||
|
|
||||||
|
this.clearSelection()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
|
||||||
|
window.addEventListener('contextmenu', (event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
this._textMenu(event)
|
||||||
|
})
|
||||||
|
|
||||||
|
window.addEventListener('click', () => {
|
||||||
|
parentEpml.request('closeCopyTextMenu', null)
|
||||||
|
})
|
||||||
|
|
||||||
|
window.onkeyup = (e) => {
|
||||||
|
if (e.keyCode === 27) {
|
||||||
|
parentEpml.request('closeCopyTextMenu', null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectName(e) {
|
||||||
|
const name = this.shadowRoot.getElementById('registeredName').innerHTML
|
||||||
|
this.selectedName = name
|
||||||
|
}
|
||||||
|
|
||||||
|
getApiKey() {
|
||||||
|
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
|
||||||
|
let apiKey = myNode.apiKey;
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearSelection() {
|
||||||
|
window.getSelection().removeAllRanges()
|
||||||
|
window.parent.getSelection().removeAllRanges()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.customElements.define('publish-data', PublishData)
|
487
qortal-ui-plugins/plugins/core/qdn/websites.src.js
Normal file
487
qortal-ui-plugins/plugins/core/qdn/websites.src.js
Normal file
@ -0,0 +1,487 @@
|
|||||||
|
|
||||||
|
import { LitElement, html, css } from 'lit-element'
|
||||||
|
import { render } from 'lit-html'
|
||||||
|
import { Epml } from '../../../epml.js'
|
||||||
|
|
||||||
|
import '@material/mwc-icon'
|
||||||
|
import '@material/mwc-button'
|
||||||
|
|
||||||
|
import '@vaadin/vaadin-grid/vaadin-grid.js'
|
||||||
|
import '@vaadin/vaadin-grid/theme/material/all-imports.js'
|
||||||
|
|
||||||
|
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
|
||||||
|
|
||||||
|
class Websites extends LitElement {
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
service: { type: String },
|
||||||
|
identifier: { type: String },
|
||||||
|
loading: { type: Boolean },
|
||||||
|
resources: { type: Array },
|
||||||
|
followedNames: { type: Array },
|
||||||
|
blockedNames: { type: Array },
|
||||||
|
relayMode: { type: Boolean },
|
||||||
|
selectedAddress: { type: Object },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
* {
|
||||||
|
--mdc-theme-primary: rgb(3, 169, 244);
|
||||||
|
--paper-input-container-focus-color: var(--mdc-theme-primary);
|
||||||
|
}
|
||||||
|
#websites-list-page {
|
||||||
|
background: #fff;
|
||||||
|
padding: 12px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divCard {
|
||||||
|
border: 1px solid #eee;
|
||||||
|
padding: 1em;
|
||||||
|
/** box-shadow: 0 1px 1px 0 rgba(0,0,0,0.14), 0 2px 1px -1px rgba(0,0,0,0.12), 0 1px 2px 0 rgba(0,0,0,0.20); **/
|
||||||
|
box-shadow: 0 .3px 1px 0 rgba(0,0,0,0.14), 0 1px 1px -1px rgba(0,0,0,0.12), 0 1px 2px 0 rgba(0,0,0,0.20);
|
||||||
|
margin-bottom: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2, h3, h4, h5 {
|
||||||
|
color:#333;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.visitSite {
|
||||||
|
color: #000;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: hidden !important;
|
||||||
|
visibility: none !important;
|
||||||
|
}
|
||||||
|
.details {
|
||||||
|
display: flex;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
font-size: 14px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
padding: 13px 20px;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #555;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
font-weight:600;
|
||||||
|
font-size:12px;
|
||||||
|
line-height: 32px;
|
||||||
|
opacity: 0.66;
|
||||||
|
}
|
||||||
|
.itemList {
|
||||||
|
padding:0;
|
||||||
|
}
|
||||||
|
/* .itemList > * {
|
||||||
|
padding-left:24px;
|
||||||
|
padding-right:24px;
|
||||||
|
} */
|
||||||
|
.relay-mode-notice {
|
||||||
|
margin:auto;
|
||||||
|
text-align:center;
|
||||||
|
word-break:normal;
|
||||||
|
font-size:14px;
|
||||||
|
line-height:20px;
|
||||||
|
color:rgb(100,100,100);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
this.service = "WEBSITE"
|
||||||
|
this.identifier = null
|
||||||
|
this.selectedAddress = {}
|
||||||
|
this.resources = []
|
||||||
|
this.followedNames = []
|
||||||
|
this.blockedNames = []
|
||||||
|
this.relayMode = null
|
||||||
|
this.isLoading = false
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<div id="websites-list-page">
|
||||||
|
<div style="min-height:48px; display: flex; padding-bottom: 6px; margin: 2px;">
|
||||||
|
<h2 style="margin: 0; flex: 1; padding-top: .1em; display: inline;">Browse Websites</h2>
|
||||||
|
${this.renderPublishButton()}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="divCard">
|
||||||
|
<h3 style="margin: 0; margin-bottom: 1em; text-align: center;">Websites</h3>
|
||||||
|
<vaadin-grid id="resourcesGrid" style="height:auto;" ?hidden="${this.isEmptyArray(this.resources)}" aria-label="Websites" .items="${this.resources}" height-by-rows>
|
||||||
|
<!--<vaadin-grid-column path="name"></vaadin-grid-column>-->
|
||||||
|
<vaadin-grid-column header="Name" .renderer=${(root, column, data) => {
|
||||||
|
render(html`${this.renderName(data.item)}`, root)
|
||||||
|
}}></vaadin-grid-column>
|
||||||
|
<vaadin-grid-column header="Status" .renderer=${(root, column, data) => {
|
||||||
|
render(html`${this.renderStatus(data.item)}`, root)
|
||||||
|
}}></vaadin-grid-column>
|
||||||
|
<vaadin-grid-column width="10rem" flex-grow="0" header="" .renderer=${(root, column, data) => {
|
||||||
|
render(html`${this.renderFollowUnfollowButton(data.item)}`, root);
|
||||||
|
}}></vaadin-grid-column>
|
||||||
|
<vaadin-grid-column width="10rem" flex-grow="0" header="" .renderer=${(root, column, data) => {
|
||||||
|
render(html`${this.renderBlockUnblockButton(data.item)}`, root);
|
||||||
|
}}></vaadin-grid-column>
|
||||||
|
</vaadin-grid>
|
||||||
|
</vaadin-grid>
|
||||||
|
${this.isEmptyArray(this.resources) ? html`
|
||||||
|
No websites available
|
||||||
|
`: ''}
|
||||||
|
</div>
|
||||||
|
${this.renderRelayModeText()}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
renderRelayModeText() {
|
||||||
|
if (this.relayMode === true) {
|
||||||
|
return html`<div class="relay-mode-notice">Relay mode is enabled. This means that your node will help to transport encrypted data around the network when a peer requests it. You can opt out by setting <strong>"relayModeEnabled": false</strong> in settings.json</div>`;
|
||||||
|
}
|
||||||
|
else if (this.relayMode === false) {
|
||||||
|
return html`<div class="relay-mode-notice">Relay mode is disabled. You can enable it by setting <strong>"relayModeEnabled": true</strong> in settings.json</div>`;
|
||||||
|
}
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPublishButton() {
|
||||||
|
// Only show the publish button if we have admin permissions on this node
|
||||||
|
// We can check the followed names array to achieve this
|
||||||
|
if (this.followedNames == null || !Array.isArray(this.followedNames)) {
|
||||||
|
return html``
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`<mwc-button style="float:right;" @click=${() => this.publishWebsite()}><mwc-icon>add</mwc-icon>Publish Website</mwc-button>`
|
||||||
|
}
|
||||||
|
|
||||||
|
publishWebsite() {
|
||||||
|
window.location.href = `publish/index.html?service=${this.service}&identifier=${this.identifier}&uploadType=zip&category=Website&showName=true&showService=false&showIdentifier=false`
|
||||||
|
}
|
||||||
|
|
||||||
|
async followName(websiteObj) {
|
||||||
|
let name = websiteObj.name
|
||||||
|
let items = [
|
||||||
|
name
|
||||||
|
]
|
||||||
|
let namesJsonString = JSON.stringify({"items": items})
|
||||||
|
|
||||||
|
let ret = await parentEpml.request('apiCall', {
|
||||||
|
url: `/lists/followedNames?apiKey=${this.getApiKey()}`,
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: `${namesJsonString}`
|
||||||
|
})
|
||||||
|
|
||||||
|
if (ret === true) {
|
||||||
|
// Successfully followed - add to local list
|
||||||
|
// Remove it first by filtering the list - doing it this way ensures the UI updates
|
||||||
|
// immediately, as apposed to only adding if it doesn't already exist
|
||||||
|
this.followedNames = this.followedNames.filter(item => item != name);
|
||||||
|
this.followedNames.push(name)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parentEpml.request('showSnackBar', 'Error occurred when trying to follow this registered name. Please try again')
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
async unfollowName(websiteObj) {
|
||||||
|
let name = websiteObj.name
|
||||||
|
let items = [
|
||||||
|
name
|
||||||
|
]
|
||||||
|
let namesJsonString = JSON.stringify({"items": items})
|
||||||
|
|
||||||
|
let ret = await parentEpml.request('apiCall', {
|
||||||
|
url: `/lists/followedNames?apiKey=${this.getApiKey()}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: `${namesJsonString}`
|
||||||
|
})
|
||||||
|
|
||||||
|
if (ret === true) {
|
||||||
|
// Successfully unfollowed - remove from local list
|
||||||
|
this.followedNames = this.followedNames.filter(item => item != name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parentEpml.request('showSnackBar', 'Error occurred when trying to unfollow this registered name. Please try again')
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
async blockName(websiteObj) {
|
||||||
|
let name = websiteObj.name
|
||||||
|
let items = [
|
||||||
|
name
|
||||||
|
]
|
||||||
|
let namesJsonString = JSON.stringify({"items": items})
|
||||||
|
|
||||||
|
let ret = await parentEpml.request('apiCall', {
|
||||||
|
url: `/lists/blockedNames?apiKey=${this.getApiKey()}`,
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: `${namesJsonString}`
|
||||||
|
})
|
||||||
|
|
||||||
|
if (ret === true) {
|
||||||
|
// Successfully blocked - add to local list
|
||||||
|
// Remove it first by filtering the list - doing it this way ensures the UI updates
|
||||||
|
// immediately, as apposed to only adding if it doesn't already exist
|
||||||
|
this.blockedNames = this.blockedNames.filter(item => item != name);
|
||||||
|
this.blockedNames.push(name)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parentEpml.request('showSnackBar', 'Error occurred when trying to block this registered name. Please try again')
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
async unblockName(websiteObj) {
|
||||||
|
let name = websiteObj.name
|
||||||
|
let items = [
|
||||||
|
name
|
||||||
|
]
|
||||||
|
let namesJsonString = JSON.stringify({"items": items})
|
||||||
|
|
||||||
|
let ret = await parentEpml.request('apiCall', {
|
||||||
|
url: `/lists/blockedNames?apiKey=${this.getApiKey()}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: `${namesJsonString}`
|
||||||
|
})
|
||||||
|
|
||||||
|
if (ret === true) {
|
||||||
|
// Successfully unblocked - remove from local list
|
||||||
|
this.blockedNames = this.blockedNames.filter(item => item != name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
parentEpml.request('showSnackBar', 'Error occurred when trying to unblock this registered name. Please try again')
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
renderRole(groupObj) {
|
||||||
|
if (groupObj.owner === this.selectedAddress.address) {
|
||||||
|
return "Owner"
|
||||||
|
} else if (groupObj.isAdmin === true) {
|
||||||
|
return "Admin"
|
||||||
|
} else {
|
||||||
|
return "Member"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderName(websiteObj) {
|
||||||
|
let name = websiteObj.name
|
||||||
|
|
||||||
|
return html`<a class="visitSite" href="browser/index.html?name=${name}&service=${this.service}">${name}</a>`
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStatus(websiteObj) {
|
||||||
|
return html`<span title="${websiteObj.status.description}">${websiteObj.status.title}</span>`
|
||||||
|
}
|
||||||
|
|
||||||
|
renderFollowUnfollowButton(websiteObj) {
|
||||||
|
let name = websiteObj.name
|
||||||
|
|
||||||
|
// Only show the follow/unfollow button if we have permission to modify the list on this node
|
||||||
|
if (this.followedNames == null || !Array.isArray(this.followedNames)) {
|
||||||
|
return html``
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.followedNames.indexOf(name) === -1) {
|
||||||
|
// render follow button
|
||||||
|
return html`<mwc-button @click=${() => this.followName(websiteObj)}><mwc-icon>add_to_queue</mwc-icon> Follow</mwc-button>`
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// render unfollow button
|
||||||
|
return html`<mwc-button @click=${() => this.unfollowName(websiteObj)}><mwc-icon>remove_from_queue</mwc-icon> Unfollow</mwc-button>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBlockUnblockButton(websiteObj) {
|
||||||
|
let name = websiteObj.name
|
||||||
|
|
||||||
|
// Only show the block/unblock button if we have permission to modify the list on this node
|
||||||
|
if (this.blockedNames == null || !Array.isArray(this.blockedNames)) {
|
||||||
|
return html``
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.blockedNames.indexOf(name) === -1) {
|
||||||
|
// render block button
|
||||||
|
return html`<mwc-button @click=${() => this.blockName(websiteObj)}><mwc-icon>block</mwc-icon> Block</mwc-button>`
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// render unblock button
|
||||||
|
return html`<mwc-button @click=${() => this.unblockName(websiteObj)}><mwc-icon>radio_button_unchecked</mwc-icon> Unblock</mwc-button>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_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()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
|
||||||
|
const getArbitraryResources = async () => {
|
||||||
|
// this.resources = []
|
||||||
|
|
||||||
|
let resources = await parentEpml.request('apiCall', {
|
||||||
|
url: `/arbitrary/resources?service=${this.service}&default=true&limit=0&reverse=true&includestatus=true`
|
||||||
|
})
|
||||||
|
|
||||||
|
this.resources = resources
|
||||||
|
setTimeout(getArbitraryResources, this.config.user.nodeSettings.pingInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFollowedNames = async () => {
|
||||||
|
// this.followedNames = []
|
||||||
|
|
||||||
|
let followedNames = await parentEpml.request('apiCall', {
|
||||||
|
url: `/lists/followedNames?apiKey=${this.getApiKey()}`
|
||||||
|
})
|
||||||
|
|
||||||
|
this.followedNames = followedNames
|
||||||
|
setTimeout(getFollowedNames, this.config.user.nodeSettings.pingInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getBlockedNames = async () => {
|
||||||
|
// this.blockedNames = []
|
||||||
|
|
||||||
|
let blockedNames = await parentEpml.request('apiCall', {
|
||||||
|
url: `/lists/blockedNames?apiKey=${this.getApiKey()}`
|
||||||
|
})
|
||||||
|
|
||||||
|
this.blockedNames = blockedNames
|
||||||
|
setTimeout(getBlockedNames, this.config.user.nodeSettings.pingInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRelayMode = async () => {
|
||||||
|
|
||||||
|
let relayMode = await parentEpml.request('apiCall', {
|
||||||
|
url: `/arbitrary/relaymode?apiKey=${this.getApiKey()}`
|
||||||
|
})
|
||||||
|
this.relayMode = relayMode;
|
||||||
|
|
||||||
|
setTimeout(getRelayMode, this.config.user.nodeSettings.pingInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
window.addEventListener("contextmenu", (event) => {
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
this._textMenu(event)
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("click", () => {
|
||||||
|
|
||||||
|
parentEpml.request('closeCopyTextMenu', null)
|
||||||
|
});
|
||||||
|
|
||||||
|
window.onkeyup = (e) => {
|
||||||
|
if (e.keyCode === 27) {
|
||||||
|
|
||||||
|
parentEpml.request('closeCopyTextMenu', null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.subscribe('config', c => {
|
||||||
|
if (!configLoaded) {
|
||||||
|
setTimeout(getArbitraryResources, 1)
|
||||||
|
setTimeout(getFollowedNames, 1)
|
||||||
|
setTimeout(getBlockedNames, 1)
|
||||||
|
setTimeout(getRelayMode, 1)
|
||||||
|
configLoaded = true
|
||||||
|
}
|
||||||
|
this.config = JSON.parse(c)
|
||||||
|
})
|
||||||
|
parentEpml.subscribe('copy_menu_switch', async value => {
|
||||||
|
|
||||||
|
if (value === 'false' && window.getSelection().toString().length !== 0) {
|
||||||
|
|
||||||
|
this.clearSelection()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
parentEpml.imReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
getApiKey() {
|
||||||
|
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
|
||||||
|
let apiKey = myNode.apiKey;
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearSelection() {
|
||||||
|
|
||||||
|
window.getSelection().removeAllRanges()
|
||||||
|
window.parent.getSelection().removeAllRanges()
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmptyArray(arr) {
|
||||||
|
if (!arr) { return true }
|
||||||
|
return arr.length === 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.customElements.define('websites-list', Websites)
|
@ -174,12 +174,7 @@ class SendMoneyPage extends LitElement {
|
|||||||
</mwc-textfield>
|
</mwc-textfield>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<mwc-textfield
|
<mwc-textfield style="width:100%;" label="To (address or name)" id="recipient" type="text" value="${this.recipient}"></mwc-textfield>
|
||||||
style="width:100%;"
|
|
||||||
label="To (address or name)"
|
|
||||||
id="recipient" type="text" value="${this.recipient}"
|
|
||||||
>
|
|
||||||
</mwc-textfield>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div style="${this.selectedCoin === 'invalid' || this.selectedCoin === 'qort' ? 'visibility: hidden; margin-bottom: -5em;' : 'visibility: visible; margin-bottom: 0;'}">
|
<div style="${this.selectedCoin === 'invalid' || this.selectedCoin === 'qort' ? 'visibility: hidden; margin-bottom: -5em;' : 'visibility: visible; margin-bottom: 0;'}">
|
||||||
@ -608,7 +603,7 @@ class SendMoneyPage extends LitElement {
|
|||||||
clearTimeout(this.updateAccountBalanceTimeout)
|
clearTimeout(this.updateAccountBalanceTimeout)
|
||||||
parentEpml
|
parentEpml
|
||||||
.request('apiCall', {
|
.request('apiCall', {
|
||||||
url: `/addresses/balance/${this.selectedAddress.address}`,
|
url: `/addresses/balance/${this.selectedAddress.address}?apiKey=${this.getApiKey()}`,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
this.qortBalance = res
|
this.qortBalance = res
|
||||||
@ -807,7 +802,7 @@ class SendMoneyPage extends LitElement {
|
|||||||
updateBTCAccountBalance() {
|
updateBTCAccountBalance() {
|
||||||
parentEpml
|
parentEpml
|
||||||
.request('apiCall', {
|
.request('apiCall', {
|
||||||
url: `/crosschain/btc/walletbalance`,
|
url: `/crosschain/btc/walletbalance?apiKey=${this.getApiKey()}`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: window.parent.reduxStore.getState().app.selectedAddress.btcWallet.derivedMasterPublicKey,
|
body: window.parent.reduxStore.getState().app.selectedAddress.btcWallet.derivedMasterPublicKey,
|
||||||
})
|
})
|
||||||
@ -823,7 +818,7 @@ class SendMoneyPage extends LitElement {
|
|||||||
updateLTCAccountBalance() {
|
updateLTCAccountBalance() {
|
||||||
parentEpml
|
parentEpml
|
||||||
.request('apiCall', {
|
.request('apiCall', {
|
||||||
url: `/crosschain/ltc/walletbalance`,
|
url: `/crosschain/ltc/walletbalance?apiKey=${this.getApiKey()}`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: window.parent.reduxStore.getState().app.selectedAddress.ltcWallet.derivedMasterPublicKey,
|
body: window.parent.reduxStore.getState().app.selectedAddress.ltcWallet.derivedMasterPublicKey,
|
||||||
})
|
})
|
||||||
@ -839,7 +834,7 @@ class SendMoneyPage extends LitElement {
|
|||||||
updateDOGEAccountBalance() {
|
updateDOGEAccountBalance() {
|
||||||
parentEpml
|
parentEpml
|
||||||
.request('apiCall', {
|
.request('apiCall', {
|
||||||
url: `/crosschain/doge/walletbalance`,
|
url: `/crosschain/doge/walletbalance?apiKey=${this.getApiKey()}`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: window.parent.reduxStore.getState().app.selectedAddress.dogeWallet.derivedMasterPublicKey,
|
body: window.parent.reduxStore.getState().app.selectedAddress.dogeWallet.derivedMasterPublicKey,
|
||||||
})
|
})
|
||||||
@ -852,6 +847,12 @@ class SendMoneyPage extends LitElement {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getApiKey() {
|
||||||
|
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
|
||||||
|
let apiKey = myNode.apiKey;
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
clearSelection() {
|
clearSelection() {
|
||||||
window.getSelection().removeAllRanges()
|
window.getSelection().removeAllRanges()
|
||||||
window.parent.getSelection().removeAllRanges()
|
window.parent.getSelection().removeAllRanges()
|
||||||
|
63
qortal-ui-plugins/plugins/core/streams/onNewBlock.js.old
Normal file
63
qortal-ui-plugins/plugins/core/streams/onNewBlock.js.old
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { parentEpml } from '../connect.js'
|
||||||
|
import { EpmlStream } from 'epml'
|
||||||
|
|
||||||
|
const BLOCK_CHECK_INTERVAL = 3000 // You should be runn off config.user.nodeSettings.pingInterval...
|
||||||
|
const BLOCK_CHECK_TIMEOUT = 3000
|
||||||
|
|
||||||
|
export const BLOCK_STREAM_NAME = 'new_block'
|
||||||
|
|
||||||
|
const onNewBlockFunctions = []
|
||||||
|
|
||||||
|
let mostRecentBlock = {
|
||||||
|
height: -1
|
||||||
|
}
|
||||||
|
|
||||||
|
export const onNewBlock = newBlockFn => onNewBlockFunctions.push(newBlockFn)
|
||||||
|
|
||||||
|
export const check = () => {
|
||||||
|
const c = doCheck()
|
||||||
|
c.then(() => {
|
||||||
|
setTimeout(() => check(), BLOCK_CHECK_INTERVAL)
|
||||||
|
})
|
||||||
|
c.catch(() => {
|
||||||
|
setTimeout(() => check(), BLOCK_CHECK_INTERVAL)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const doCheck = async () => {
|
||||||
|
let timeout = setTimeout(() => {
|
||||||
|
throw new Error('Block check timed out')
|
||||||
|
}, BLOCK_CHECK_TIMEOUT)
|
||||||
|
|
||||||
|
let _nodeStatus = {}
|
||||||
|
|
||||||
|
const block = await parentEpml.request('apiCall', {
|
||||||
|
url: '/blocks/last'
|
||||||
|
})
|
||||||
|
|
||||||
|
const _nodeInfo = await parentEpml.request('apiCall', {
|
||||||
|
url: '/admin/info'
|
||||||
|
})
|
||||||
|
|
||||||
|
let nodeConfig = await parentEpml.request('getNodeConfig')
|
||||||
|
|
||||||
|
if (nodeConfig.node === 0 || nodeConfig.node === 1) {
|
||||||
|
_nodeStatus = await parentEpml.request('apiCall', {
|
||||||
|
url: '/admin/status'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let appInfo = {
|
||||||
|
block: block,
|
||||||
|
nodeInfo: _nodeInfo,
|
||||||
|
nodeStatus: _nodeStatus
|
||||||
|
}
|
||||||
|
parentEpml.request('updateAppInfo', appInfo)
|
||||||
|
|
||||||
|
clearTimeout(timeout)
|
||||||
|
|
||||||
|
if (block.height > mostRecentBlock.height) {
|
||||||
|
mostRecentBlock = block
|
||||||
|
onNewBlockFunctions.forEach(fn => fn(mostRecentBlock))
|
||||||
|
}
|
||||||
|
}
|
@ -532,14 +532,14 @@ class TradePortal extends LitElement {
|
|||||||
<div class="open-market-container">
|
<div class="open-market-container">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<mwc-tab-bar id="tabs-1" activeIndex="0">
|
<mwc-tab-bar id="tabs-1" activeIndex="0">
|
||||||
<mwc-tab id="tab-buy" label="Buy" @click=${(e) => this.displayTabContent('buy')}></mwc-tab>
|
<mwc-tab id="tab-buy" label="Buy" @click="${(e) => this.displayTabContent('buy')}"></mwc-tab>
|
||||||
<mwc-tab id="tab-sell" label="Sell" @click=${(e) => this.displayTabContent('sell')}></mwc-tab>
|
<mwc-tab id="tab-sell" label="Sell" @click="${(e) => this.displayTabContent('sell')}"></mwc-tab>
|
||||||
</mwc-tab-bar>
|
</mwc-tab-bar>
|
||||||
<z id="tabs-1-content">
|
<z id="tabs-1-content">
|
||||||
<div id="tab-buy-content">
|
<div id="tab-buy-content">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div style="margin-left: auto">
|
<div style="margin-left: auto">
|
||||||
<mwc-icon-button class="btn-clear" title="Clear Form" icon="clear_all" @click=${() => this.clearBuyForm()}></mwc-icon-button>
|
<mwc-icon-button class="btn-clear" title="Clear Form" icon="clear_all" @click="${() => this.clearBuyForm()}"></mwc-icon-button>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
<mwc-textfield
|
<mwc-textfield
|
||||||
@ -592,7 +592,7 @@ class TradePortal extends LitElement {
|
|||||||
<span class="you-have">You have: ${this.listedCoins.get(this.selectedCoin).balance} ${this.listedCoins.get(this.selectedCoin).coinCode}</span>
|
<span class="you-have">You have: ${this.listedCoins.get(this.selectedCoin).balance} ${this.listedCoins.get(this.selectedCoin).coinCode}</span>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<div>
|
<div>
|
||||||
<mwc-button class="buy-button" ?disabled=${this.buyBtnDisable} style="width:100%;" raised @click=${(e) => this.buyAction(e)}>
|
<mwc-button class="buy-button" ?disabled="${this.buyBtnDisable}" style="width:100%;" raised @click="${(e) => this.buyAction(e)}">
|
||||||
${this.isBuyLoading === false ? 'BUY' : html`<paper-spinner-lite active></paper-spinner-lite>`}
|
${this.isBuyLoading === false ? 'BUY' : html`<paper-spinner-lite active></paper-spinner-lite>`}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</div>
|
</div>
|
||||||
@ -602,7 +602,7 @@ class TradePortal extends LitElement {
|
|||||||
<div id="tab-sell-content">
|
<div id="tab-sell-content">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div style="margin-left: auto">
|
<div style="margin-left: auto">
|
||||||
<mwc-icon-button class="btn-clear" title="Clear Form" icon="clear_all" @click=${() => this.clearSellForm()}></mwc-icon-button>
|
<mwc-icon-button class="btn-clear" title="Clear Form" icon="clear_all" @click="${() => this.clearSellForm()}"></mwc-icon-button>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
<mwc-textfield
|
<mwc-textfield
|
||||||
@ -646,7 +646,7 @@ class TradePortal extends LitElement {
|
|||||||
<span class="you-have">You have: ${this.listedCoins.get("QORTAL").balance} QORT</span>
|
<span class="you-have">You have: ${this.listedCoins.get("QORTAL").balance} QORT</span>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<div>
|
<div>
|
||||||
<mwc-button class="sell-button" ?disabled=${this.sellBtnDisable} style="width:100%;" raised @click=${(e) => this.sellAction()}>
|
<mwc-button class="sell-button" ?disabled="${this.sellBtnDisable}" style="width:100%;" raised @click="${(e) => this.sellAction()}">
|
||||||
${this.isSellLoading === false ? 'SELL' : html`<paper-spinner-lite active></paper-spinner-lite>`}
|
${this.isSellLoading === false ? 'SELL' : html`<paper-spinner-lite active></paper-spinner-lite>`}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</div>
|
</div>
|
||||||
@ -1644,7 +1644,7 @@ class TradePortal extends LitElement {
|
|||||||
updateAccountBalance() {
|
updateAccountBalance() {
|
||||||
clearTimeout(this.updateAccountBalanceTimeout)
|
clearTimeout(this.updateAccountBalanceTimeout)
|
||||||
parentEpml.request('apiCall', {
|
parentEpml.request('apiCall', {
|
||||||
url: `/addresses/balance/${this.selectedAddress.address}`,
|
url: `/addresses/balance/${this.selectedAddress.address}?apiKey=${this.getApiKey()}`,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
this.listedCoins.get("QORTAL").balance = res
|
this.listedCoins.get("QORTAL").balance = res
|
||||||
@ -1658,11 +1658,11 @@ class TradePortal extends LitElement {
|
|||||||
|
|
||||||
switch (this.selectedCoin) {
|
switch (this.selectedCoin) {
|
||||||
case 'LITECOIN':
|
case 'LITECOIN':
|
||||||
_url = `/crosschain/ltc/walletbalance`
|
_url = `/crosschain/ltc/walletbalance?apiKey=${this.getApiKey()}`
|
||||||
_body = window.parent.reduxStore.getState().app.selectedAddress.ltcWallet.derivedMasterPublicKey
|
_body = window.parent.reduxStore.getState().app.selectedAddress.ltcWallet.derivedMasterPublicKey
|
||||||
break
|
break
|
||||||
case 'DOGECOIN':
|
case 'DOGECOIN':
|
||||||
_url = `/crosschain/doge/walletbalance`
|
_url = `/crosschain/doge/walletbalance?apiKey=${this.getApiKey()}`
|
||||||
_body = window.parent.reduxStore.getState().app.selectedAddress.dogeWallet.derivedMasterPublicKey
|
_body = window.parent.reduxStore.getState().app.selectedAddress.dogeWallet.derivedMasterPublicKey
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
@ -1824,6 +1824,12 @@ class TradePortal extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getApiKey() {
|
||||||
|
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
|
||||||
|
let apiKey = myNode.apiKey;
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
clearSelection() {
|
clearSelection() {
|
||||||
window.getSelection().removeAllRanges()
|
window.getSelection().removeAllRanges()
|
||||||
window.parent.getSelection().removeAllRanges()
|
window.parent.getSelection().removeAllRanges()
|
||||||
|
@ -1,40 +1,40 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<link rel="stylesheet" href="/font/material-icons.css">
|
<link rel="stylesheet" href="/font/material-icons.css" />
|
||||||
<style>
|
<style>
|
||||||
html {
|
html {
|
||||||
--scrollbarBG: #a1a1a1;
|
--scrollbarBG: #a1a1a1;
|
||||||
--thumbBG: #6a6c75;
|
--thumbBG: #6a6c75;
|
||||||
}
|
}
|
||||||
|
|
||||||
*::-webkit-scrollbar {
|
*::-webkit-scrollbar {
|
||||||
width: 11px;
|
width: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: var(--thumbBG) var(--scrollbarBG);
|
scrollbar-color: var(--thumbBG) var(--scrollbarBG);
|
||||||
}
|
}
|
||||||
|
|
||||||
*::-webkit-scrollbar-track {
|
*::-webkit-scrollbar-track {
|
||||||
background: var(--scrollbarBG);
|
background: var(--scrollbarBG);
|
||||||
}
|
}
|
||||||
|
|
||||||
*::-webkit-scrollbar-thumb {
|
*::-webkit-scrollbar-thumb {
|
||||||
background-color: var(--thumbBG);
|
background-color: var(--thumbBG);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
border: 3px solid var(--scrollbarBG);
|
border: 3px solid var(--scrollbarBG);
|
||||||
}
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: "Roboto", sans-serif;
|
background: white;
|
||||||
background: #fff;
|
font-family: 'Roboto', sans-serif;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<multi-wallet></multi-wallet>
|
<multi-wallet></multi-wallet>
|
||||||
|
@ -852,6 +852,12 @@ class MultiWallet extends LitElement {
|
|||||||
checkSelectedTextAndShowMenu()
|
checkSelectedTextAndShowMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getApiKey() {
|
||||||
|
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
|
||||||
|
let apiKey = myNode.apiKey;
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
clearSelection() {
|
clearSelection() {
|
||||||
window.getSelection().removeAllRanges()
|
window.getSelection().removeAllRanges()
|
||||||
window.parent.getSelection().removeAllRanges()
|
window.parent.getSelection().removeAllRanges()
|
||||||
@ -946,7 +952,7 @@ async showWallet(){
|
|||||||
//fetching the qort balance
|
//fetching the qort balance
|
||||||
parentEpml
|
parentEpml
|
||||||
.request('apiCall', {
|
.request('apiCall', {
|
||||||
url: `/addresses/balance/${this.wallets.get('qort').wallet.address}`,
|
url: `/addresses/balance/${this.wallets.get('qort').wallet.address}?apiKey=${this.getApiKey()}`,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (isNaN(Number(res))) {
|
if (isNaN(Number(res))) {
|
||||||
@ -973,7 +979,7 @@ async showWallet(){
|
|||||||
const walletName = `${coin}Wallet`
|
const walletName = `${coin}Wallet`
|
||||||
parentEpml
|
parentEpml
|
||||||
.request('apiCall', {
|
.request('apiCall', {
|
||||||
url: `/crosschain/${coin}/walletbalance`,
|
url: `/crosschain/${coin}/walletbalance?apiKey=${this.getApiKey()}`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: `${window.parent.reduxStore.getState().app.selectedAddress[walletName].derivedMasterPublicKey}`,
|
body: `${window.parent.reduxStore.getState().app.selectedAddress[walletName].derivedMasterPublicKey}`,
|
||||||
})
|
})
|
||||||
@ -989,7 +995,7 @@ async showWallet(){
|
|||||||
})
|
})
|
||||||
//fetching transactions
|
//fetching transactions
|
||||||
const txs = await parentEpml.request('apiCall', {
|
const txs = await parentEpml.request('apiCall', {
|
||||||
url: `/crosschain/${coin}/wallettransactions`,
|
url: `/crosschain/${coin}/wallettransactions?apiKey=${this.getApiKey()}`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: `${window.parent.reduxStore.getState().app.selectedAddress[walletName].derivedMasterPublicKey}`,
|
body: `${window.parent.reduxStore.getState().app.selectedAddress[walletName].derivedMasterPublicKey}`,
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user