added custom select input for port selection

This commit is contained in:
PhilReact 2023-09-17 22:57:25 -05:00
parent cb886e29e1
commit 28c58cb249
6 changed files with 801 additions and 390 deletions

View File

@ -200,6 +200,8 @@
"snack3": "Successfully added and saved custom node", "snack3": "Successfully added and saved custom node",
"snack4": "Nodes successfully saved as", "snack4": "Nodes successfully saved as",
"snack5": "Nodes successfully imported", "snack5": "Nodes successfully imported",
"snack6": "Successfully removed custom node",
"snack7": "Successfully edited custom node",
"exp1": "Export Private Master Key", "exp1": "Export Private Master Key",
"exp2": "Export Master Key", "exp2": "Export Master Key",
"exp3": "Export", "exp3": "Export",

View File

@ -1,34 +1,46 @@
import { LitElement, html, css } from 'lit' import { LitElement, html, css } from 'lit';
import { connect } from 'pwa-helpers' import { connect } from 'pwa-helpers';
import { store } from '../store.js' import { store } from '../store.js';
import { doAddNode, doSetNode, doLoadNodeConfig } from '../redux/app/app-actions.js' import {
import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate' doAddNode,
import snackbar from './snackbar.js' doSetNode,
import '../components/language-selector.js' doLoadNodeConfig,
import '../custom-elements/frag-file-input.js' doRemoveNode,
import FileSaver from 'file-saver' doEditNode,
} from '../redux/app/app-actions.js';
import {
use,
get,
translate,
translateUnsafeHTML,
registerTranslateConfig,
} from 'lit-translate';
import snackbar from './snackbar.js';
import '../components/language-selector.js';
import '../custom-elements/frag-file-input.js';
import FileSaver from 'file-saver';
import '@material/mwc-dialog' import '@material/mwc-dialog';
import '@material/mwc-button' import '@material/mwc-button';
import '@material/mwc-select' import '@material/mwc-select';
import '@material/mwc-textfield' import '@material/mwc-textfield';
import '@material/mwc-icon' import '@material/mwc-icon';
import '@material/mwc-list/mwc-list-item.js' import '@material/mwc-list/mwc-list-item.js';
registerTranslateConfig({ registerTranslateConfig({
loader: lang => fetch(`/language/${lang}.json`).then(res => res.json()) loader: (lang) => fetch(`/language/${lang}.json`).then((res) => res.json()),
}) });
const checkLanguage = localStorage.getItem('qortalLanguage') const checkLanguage = localStorage.getItem('qortalLanguage');
if (checkLanguage === null || checkLanguage.length === 0) { if (checkLanguage === null || checkLanguage.length === 0) {
localStorage.setItem('qortalLanguage', 'us') localStorage.setItem('qortalLanguage', 'us');
use('us') use('us');
} else { } else {
use(checkLanguage) use(checkLanguage);
} }
let settingsDialog let settingsDialog;
class SettingsPage extends connect(store)(LitElement) { class SettingsPage extends connect(store)(LitElement) {
static get properties() { static get properties() {
@ -36,8 +48,10 @@ class SettingsPage extends connect(store)(LitElement) {
lastSelected: { type: Number }, lastSelected: { type: Number },
nodeConfig: { type: Object }, nodeConfig: { type: Object },
theme: { type: String, reflect: true }, theme: { type: String, reflect: true },
nodeIndex: { type: Number } nodeIndex: { type: Number },e
} isBeingEdited: { type: Boolean },
dropdownOpen: { type: Boolean },
};
} }
static get styles() { static get styles() {
@ -51,6 +65,7 @@ class SettingsPage extends connect(store)(LitElement) {
--mdc-dialog-min-width: 300px; --mdc-dialog-min-width: 300px;
--mdc-dialog-max-width: 650px; --mdc-dialog-max-width: 650px;
--mdc-dialog-max-height: 700px; --mdc-dialog-max-height: 700px;
--mdc-list-item-text-width: 100%;
} }
#main { #main {
@ -83,7 +98,9 @@ class SettingsPage extends connect(store)(LitElement) {
.buttongreen { .buttongreen {
color: #03c851; color: #03c851;
} }
.buttonBlue {
color: #03a9f4;
}
.floatleft { .floatleft {
float: left; float: left;
} }
@ -91,49 +108,249 @@ class SettingsPage extends connect(store)(LitElement) {
.floatright { .floatright {
float: right; float: right;
} }
` .list-parent {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
#customSelect {
position: relative;
border: 1px solid #ccc;
cursor: pointer;
background: var(--plugback);
}
#customSelect .selected {
padding: 10px;
display: flex;
align-items: center;
justify-content: space-between;
}
#customSelect ul {
position: absolute;
top: 100%;
left: 0;
list-style: none;
margin: 0;
padding: 0;
border: 1px solid #ccc;
display: none;
background: var(--plugback);
width: 100%;
box-sizing: border-box;
z-index: 10;
}
#customSelect ul.open {
display: block;
}
#customSelect ul li {
padding: 10px;
transition: 0.2s all;
}
#customSelect ul li:hover {
background-color: var(--graylight);
}
.selected-left-side{
display: flex;
align-items: center;
}
`;
} }
constructor() { constructor() {
super() super();
this.nodeConfig = {} this.nodeConfig = {};
this.nodeIndex = localStorage.getItem('mySelectedNode') this.nodeIndex = localStorage.getItem('mySelectedNode');
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' this.theme = localStorage.getItem('qortalTheme')
? localStorage.getItem('qortalTheme')
: 'light';
this.isBeingEdited = false;
this.isBeingEditedIndex = null;
this.dropdownOpen = false;
} }
render() { render() {
console.log('this.dropdownOpen', this.dropdownOpen);
return html` return html`
<mwc-dialog id="settingsDialog" opened=false> <mwc-dialog id="settingsDialog" opened="false">
<div style="display: inline; text-align: center;"> <div style="display: inline; text-align: center;">
<h1>${translate("settings.settings")}</h1> <h1>${translate('settings.settings')}</h1>
<hr> <hr />
</div> </div>
<br> <br />
<div style="min-height: 250px; min-width: 500px; box-sizing: border-box; position: relative;"> <div
<mwc-select icon="link" id="nodeSelect" label="${translate("settings.nodeurl")}" index="${this.nodeIndex}" @selected="${(e) => this.nodeSelected(e)}" style="min-width: 130px; max-width:100%; width:100%;"> style="min-height: 250px; min-width: 500px; box-sizing: border-box; position: relative;"
${this.nodeConfig.knownNodes.map((n, index) => html` >
<mwc-list-item value="${index}"> <div id="customSelect" @click="${this.toggleDropdown}" @blur="${
<span class="name">${n.name}</span> this.handleBlur
<span>${n.protocol + '://' + n.domain + ':' + n.port}</span> }" tabindex="0">
</mwc-list-item> <div class="selected">
`)} <div class="selected-left-side">
</mwc-select> <mwc-icon style="margin-right: 10px"
<p style="margin-top: 30px; text-align: center;">${translate("settings.nodehint")}</p> >link</mwc-icon
>
${
this.selectedItem
? html`
<div>
<span class="name"
>${this.selectedItem
.name}</span
>
<span
>${this.selectedItem
.protocol +
'://' +
this.selectedItem.domain +
':' +
this.selectedItem
.port}</span
>
</div>
`
: 'Please select an option'
}
</div>
<mwc-icon
>expand_more</mwc-icon
>
</div>
<ul class="${this.dropdownOpen ? 'open' : ''}">
${this.nodeConfig.knownNodes.map(
(n, index) => html`
<li
@click="${(e) =>
this.handleSelection(e, n, index)}"
>
<div class="list-parent">
<div>
<span class="name"
>${n.name}</span
>
<span
>${n.protocol +
'://' +
n.domain +
':' +
n.port}</span
>
</div>
<div>
<mwc-button
outlined
@click="${(e) => {
e.stopPropagation();
const currentValues =
this.nodeConfig
.knownNodes[
index
];
const dialog =
this.shadowRoot.querySelector(
'#addNodeDialog'
);
// Set the value for mwc-textfield elements
dialog.querySelector(
'#nameInput'
).value =
currentValues.name;
dialog.querySelector(
'#domainInput'
).value =
currentValues.domain;
dialog.querySelector(
'#portInput'
).value =
currentValues.port;
// Set the selected value for mwc-select
const protocolList =
dialog.querySelector(
'#protocolList'
);
const desiredProtocol =
currentValues.protocol;
protocolList.value =
desiredProtocol;
this.isBeingEdited = true;
this.isBeingEditedIndex =
index;
this.shadowRoot
.querySelector(
'#addNodeDialog'
)
.show();
}}"
><mwc-icon class="buttonBlue"
>edit</mwc-icon
></mwc-button
>
<mwc-button
outlined
@click="${(e) =>
this.removeNode(
e,
index
)}"
><mwc-icon class="buttonred"
>remove</mwc-icon
></mwc-button
>
</div>
</div>
</li>
`
)}
</ul>
</div>
<p style="margin-top: 30px; text-align: center;">
${translate('settings.nodehint')}
</p>
<center> <center>
<mwc-button outlined @click="${() => this.shadowRoot.querySelector('#addNodeDialog').show()}"><mwc-icon class="buttongreen">add</mwc-icon>${translate("settings.addcustomnode")}</mwc-button> <mwc-button
outlined
@click="${() => this.shadowRoot.querySelector('#addNodeDialog').show()}"
><mwc-icon class="buttongreen">add</mwc-icon
>${translate('settings.addcustomnode')}</mwc-button
>
</center> </center>
<center> <center>
<mwc-button outlined @click="${() => this.removeList()}"><mwc-icon class="buttonred">remove</mwc-icon>${translate("settings.deletecustomnode")}</mwc-button> <mwc-button outlined @click="${() => this.removeList()}"
><mwc-icon class="buttonred">remove</mwc-icon
>${translate('settings.deletecustomnode')}</mwc-button
>
</center> </center>
<br> <br />
<div class="floatleft">${this.renderExportNodesListButton()}</div><div class="floatright">${this.renderImportNodesListButton()}</div> <div class="floatleft">
<br><br> ${this.renderExportNodesListButton()}
</div> </div>
<div style="min-height:100px; min-width: 300px; box-sizing: border-box; position: relative;"> <div class="floatright">
<hr><br> ${this.renderImportNodesListButton()}
</div>
<br /><br />
</div>
<div
style="min-height:100px; min-width: 300px; box-sizing: border-box; position: relative;"
>
<hr />
<br />
<center> <center>
<div id="main"> <div id="main">
<mwc-icon class="globe">language</mwc-icon>&nbsp;<language-selector></language-selector> <mwc-icon class="globe">language</mwc-icon
>&nbsp;<language-selector></language-selector>
</div> </div>
</center> </center>
</div> </div>
@ -142,278 +359,419 @@ class SettingsPage extends connect(store)(LitElement) {
dialogAction="close" dialogAction="close"
class="red" class="red"
> >
${translate("general.close")} ${translate('general.close')}
</mwc-button> </mwc-button>
</mwc-dialog> </mwc-dialog>
<mwc-dialog id="addNodeDialog"> <mwc-dialog id="addNodeDialog">
<div style="text-align: center;"> <div style="text-align: center;">
<h2>${translate("settings.addcustomnode")}</h2> <h2>${translate('settings.addcustomnode')}</h2>
<hr> <hr />
</div> </div>
<br> <br />
<mwc-textfield id="nameInput" style="width:100%;" label="${translate("login.name")}"></mwc-textfield> <mwc-textfield
<br> id="nameInput"
<mwc-select id="protocolList" style="width:100%;" label="${translate("settings.protocol")}"> style="width:100%;"
label="${translate('login.name')}"
></mwc-textfield>
<br />
<mwc-select
id="protocolList"
style="width:100%;"
label="${translate('settings.protocol')}"
>
<mwc-list-item value="http">http</mwc-list-item> <mwc-list-item value="http">http</mwc-list-item>
<mwc-list-item value="https">https</mwc-list-item> <mwc-list-item value="https">https</mwc-list-item>
</mwc-select> </mwc-select>
<br> <br />
<mwc-textfield id="domainInput" style="width:100%;" label="${translate("settings.domain")}"></mwc-textfield> <mwc-textfield
<mwc-textfield id="portInput" style="width:100%;" label="${translate("settings.port")}"></mwc-textfield> id="domainInput"
style="width:100%;"
label="${translate('settings.domain')}"
></mwc-textfield>
<mwc-textfield
id="portInput"
style="width:100%;"
label="${translate('settings.port')}"
></mwc-textfield>
<mwc-button <mwc-button
slot="secondaryAction" slot="secondaryAction"
dialogAction="close" dialogAction="close"
class="red" class="red"
> >
${translate("general.close")} ${translate('general.close')}
</mwc-button> </mwc-button>
<mwc-button <mwc-button slot="primaryAction" @click="${this.addNode}">
slot="primaryAction" ${translate('settings.addandsave')}
@click="${this.addNode}"
>
${translate("settings.addandsave")}
</mwc-button> </mwc-button>
</mwc-dialog> </mwc-dialog>
<mwc-dialog id="importQortalNodesListDialog"> <mwc-dialog id="importQortalNodesListDialog">
<div style="text-align:center"> <div style="text-align:center">
<h2>${translate("settings.import")}</h2> <h2>${translate('settings.import')}</h2>
<hr> <hr />
<br> <br />
</div> </div>
<div style="min-height: 150px; min-width: 500px; box-sizing: border-box; position: relative;"> <div
<frag-file-input accept=".nodes" @file-read-success="${(e) => this.importQortalNodesList(e.detail.result)}"></frag-file-input> style="min-height: 150px; min-width: 500px; box-sizing: border-box; position: relative;"
<h4 style="color: #F44336; text-align: center;">${translate("walletpage.wchange56")}</h4> >
<h5 style="text-align: center;">${translate("settings.warning")}</h5> <frag-file-input
accept=".nodes"handleBlur(event) {
if (!this.shadowRoot.querySelector("#customSelect").contains(event.relatedTarget)) {
this.dropdownOpen = false;
}
}
@file-read-success="${(e) => this.importQortalNodesList(e.detail.result)}"
></frag-file-input>
<h4 style="color: #F44336; text-align: center;">
${translate('walletpage.wchange56')}
</h4>
<h5 style="text-align: center;">
${translate('settings.warning')}
</h5>
</div> </div>
<mwc-button <mwc-button
slot="primaryAction" slot="primaryAction"
dialogAction="cancel" dialogAction="cancel"
class="red" class="red"
> >
${translate("general.close")} ${translate('general.close')}
</mwc-button> </mwc-button>
</mwc-dialog> </mwc-dialog>
` `;
} }
firstUpdated() { firstUpdated() {
const checkNode = localStorage.getItem('mySelectedNode') const checkNode = localStorage.getItem('mySelectedNode');
if (checkNode === null || checkNode.length === 0) { if (checkNode === null || checkNode.length === 0) {
localStorage.setItem('mySelectedNode', 0) localStorage.setItem('mySelectedNode', 0);
} else { } else {
} }
} }
toggleDropdown() {
this.dropdownOpen = !this.dropdownOpen;
}
handleBlur(event) {
if (
!this.shadowRoot
.querySelector('#customSelect')
.contains(event.relatedTarget)
) {
this.dropdownOpen = false;
}
}
focusOnCustomSelect() {
const customSelect = this.shadowRoot.querySelector('#customSelect');
if (customSelect) {
customSelect.focus();
}
}
handleSelection(event, node, index) {
event.stopPropagation();
this.selectedItem = node;
this.dropdownOpen = false;
this.requestUpdate();
this.nodeSelected(index);
}
show() { show() {
this.shadowRoot.getElementById('settingsDialog').show() this.shadowRoot.getElementById('settingsDialog').show();
} }
close() { close() {
this.shadowRoot.getElementById('settingsDialog').close() this.shadowRoot.getElementById('settingsDialog').close();
} }
removeList() { removeList() {
localStorage.removeItem("myQortalNodes") localStorage.removeItem('myQortalNodes');
const obj1 = { const obj1 = {
name: 'Local Node', name: 'Local Node',
protocol: 'http', protocol: 'http',
domain: '127.0.0.1', domain: '127.0.0.1',
port: 12391, port: 12391,
enableManagement: true enableManagement: true,
} };
const obj2 = { const obj2 = {
name: 'Local Testnet', name: 'Local Testnet',
protocol: 'http', protocol: 'http',
domain: '127.0.0.1', domain: '127.0.0.1',
port: 62391, port: 62391,
enableManagement: true enableManagement: true,
} };
var renewNodes = []; var renewNodes = [];
renewNodes.push(obj1,obj2) renewNodes.push(obj1, obj2);
localStorage.setItem('myQortalNodes', JSON.stringify(renewNodes)) localStorage.setItem('myQortalNodes', JSON.stringify(renewNodes));
let snack1string = get("settings.snack1") let snack1string = get('settings.snack1');
snackbar.add({ snackbar.add({
labelText: `${snack1string}`, labelText: `${snack1string}`,
dismiss: true dismiss: true,
}) });
localStorage.removeItem('mySelectedNode') localStorage.removeItem('mySelectedNode');
localStorage.setItem('mySelectedNode', 0) localStorage.setItem('mySelectedNode', 0);
store.dispatch(doLoadNodeConfig()) store.dispatch(doLoadNodeConfig());
} }
nodeSelected(e) { nodeSelected(selectedNodeIndex) {
const selectedNodeIndex = this.shadowRoot.getElementById('nodeSelect').value const selectedNode = this.nodeConfig.knownNodes[selectedNodeIndex];
const selectedNode = this.nodeConfig.knownNodes[selectedNodeIndex] const selectedNodeUrl = `${
const selectedNodeUrl = `${selectedNode.protocol + '://' + selectedNode.domain + ':' + selectedNode.port}` selectedNode.protocol +
'://' +
selectedNode.domain +
':' +
selectedNode.port
}`;
const index = parseInt(selectedNodeIndex) const index = parseInt(selectedNodeIndex);
if (isNaN(index)) return if (isNaN(index)) return;
store.dispatch(doSetNode(selectedNodeIndex)) store.dispatch(doSetNode(selectedNodeIndex));
localStorage.removeItem('mySelectedNode') localStorage.removeItem('mySelectedNode');
localStorage.setItem('mySelectedNode', selectedNodeIndex) localStorage.setItem('mySelectedNode', selectedNodeIndex);
let snack2string = get("settings.snack2") let snack2string = get('settings.snack2');
snackbar.add({ snackbar.add({
labelText: `${snack2string} : ${selectedNodeUrl}`, labelText: `${snack2string} : ${selectedNodeUrl}`,
dismiss: true dismiss: true,
}) });
this.shadowRoot.querySelector('#settingsDialog').close() // this.shadowRoot.querySelector('#settingsDialog').close();
} }
addNode() { addNode(e) {
const nameInput = this.shadowRoot.getElementById('nameInput').value e.stopPropagation();
const protocolList = this.shadowRoot.getElementById('protocolList').value if (this.isBeingEdited) {
const domainInput = this.shadowRoot.getElementById('domainInput').value this.editNode(this.isBeingEditedIndex);
const portInput = this.shadowRoot.getElementById('portInput').value return;
}
const nameInput = this.shadowRoot.getElementById('nameInput').value;
const protocolList =
this.shadowRoot.getElementById('protocolList').value;
const domainInput = this.shadowRoot.getElementById('domainInput').value;
const portInput = this.shadowRoot.getElementById('portInput').value;
if (protocolList.length >= 4 && domainInput.length >= 3 && portInput.length >= 2) { if (
protocolList.length >= 4 &&
domainInput.length >= 3 &&
portInput.length >= 2
) {
const nodeObject = { const nodeObject = {
name: nameInput, name: nameInput,
protocol: protocolList, protocol: protocolList,
domain: domainInput, domain: domainInput,
port: portInput, port: portInput,
enableManagement: true enableManagement: true,
} };
store.dispatch(doAddNode(nodeObject)) store.dispatch(doAddNode(nodeObject));
const haveNodes = JSON.parse(localStorage.getItem('myQortalNodes')) const haveNodes = JSON.parse(localStorage.getItem('myQortalNodes'));
if (haveNodes === null || haveNodes.length === 0) { if (haveNodes === null || haveNodes.length === 0) {
var savedNodes = []; var savedNodes = [];
savedNodes.push(nodeObject); savedNodes.push(nodeObject);
localStorage.setItem('myQortalNodes', JSON.stringify(savedNodes)) localStorage.setItem(
'myQortalNodes',
JSON.stringify(savedNodes)
);
let snack3string = get("settings.snack3") let snack3string = get('settings.snack3');
snackbar.add({ snackbar.add({
labelText: `${snack3string}`, labelText: `${snack3string}`,
dismiss: true dismiss: true,
}) });
this.shadowRoot.getElementById('nameInput').value = '' this.shadowRoot.getElementById('nameInput').value = '';
this.shadowRoot.getElementById('protocolList').value = '' this.shadowRoot.getElementById('protocolList').value = '';
this.shadowRoot.getElementById('domainInput').value = '' this.shadowRoot.getElementById('domainInput').value = '';
this.shadowRoot.getElementById('portInput').value = '' this.shadowRoot.getElementById('portInput').value = '';
this.shadowRoot.querySelector('#addNodeDialog').close()
this.shadowRoot.querySelector('#addNodeDialog').close();
} else { } else {
var stored = JSON.parse(localStorage.getItem('myQortalNodes')); var stored = JSON.parse(localStorage.getItem('myQortalNodes'));
stored.push(nodeObject); stored.push(nodeObject);
localStorage.setItem('myQortalNodes', JSON.stringify(stored)); localStorage.setItem('myQortalNodes', JSON.stringify(stored));
let snack3string = get("settings.snack3") let snack3string = get('settings.snack3');
snackbar.add({ snackbar.add({
labelText: `${snack3string}`, labelText: `${snack3string}`,
dismiss: true dismiss: true,
}) });
this.shadowRoot.getElementById('nameInput').value = '' this.shadowRoot.getElementById('nameInput').value = '';
this.shadowRoot.getElementById('protocolList').value = '' this.shadowRoot.getElementById('protocolList').value = '';
this.shadowRoot.getElementById('domainInput').value = '' this.shadowRoot.getElementById('domainInput').value = '';
this.shadowRoot.getElementById('portInput').value = '' this.shadowRoot.getElementById('portInput').value = '';
this.shadowRoot.querySelector('#addNodeDialog').close() this.shadowRoot.querySelector('#addNodeDialog').close();
} }
} }
} }
removeNode(event, index) {
event.stopPropagation();
let stored = JSON.parse(localStorage.getItem('myQortalNodes'));
stored.splice(index, 1);
localStorage.setItem('myQortalNodes', JSON.stringify(stored));
store.dispatch(doRemoveNode(index));
let snack3string = get('settings.snack6');
snackbar.add({
labelText: `${snack3string}`,
dismiss: true,
});
this.shadowRoot.querySelector('#addNodeDialog').close();
}
editNode(index) {
const nameInput = this.shadowRoot.getElementById('nameInput').value;
const protocolList =
this.shadowRoot.getElementById('protocolList').value;
const domainInput = this.shadowRoot.getElementById('domainInput').value;
const portInput = this.shadowRoot.getElementById('portInput').value;
if (
protocolList.length >= 4 &&
domainInput.length >= 3 &&
portInput.length >= 2
) {
const nodeObject = {
name: nameInput,
protocol: protocolList,
domain: domainInput,
port: portInput,
enableManagement: true,
};
let stored = JSON.parse(localStorage.getItem('myQortalNodes'));
const copyStored = [...stored];
copyStored[index] = nodeObject;
localStorage.setItem('myQortalNodes', JSON.stringify(copyStored));
store.dispatch(doEditNode(index, nodeObject));
let snack3string = get('settings.snack7');
snackbar.add({
labelText: `${snack3string}`,
dismiss: true,
});
this.shadowRoot.getElementById('nameInput').value = '';
this.shadowRoot.getElementById('protocolList').value = '';
this.shadowRoot.getElementById('domainInput').value = '';
this.shadowRoot.getElementById('portInput').value = '';
this.isBeingEdited = false;
this.isBeingEditedIndex = null;
this.shadowRoot.querySelector('#addNodeDialog').close();
}
}
openImportNodesDialog() { openImportNodesDialog() {
this.shadowRoot.querySelector("#importQortalNodesListDialog").show() this.shadowRoot.querySelector('#importQortalNodesListDialog').show();
} }
closeImportNodesDialog() { closeImportNodesDialog() {
this.shadowRoot.querySelector("#importQortalNodesListDialog").close() this.shadowRoot.querySelector('#importQortalNodesListDialog').close();
} }
renderExportNodesListButton() { renderExportNodesListButton() {
return html` return html`
<mwc-button dense unelevated label="${translate("settings.export")}" @click="${() => this.exportQortalNodesList()}"></mwc-button> <mwc-button
` dense
unelevated
label="${translate('settings.export')}"
@click="${() => this.exportQortalNodesList()}"
></mwc-button>
`;
} }
exportQortalNodesList() { exportQortalNodesList() {
let nodelist = "" let nodelist = '';
const qortalNodesList = JSON.stringify(localStorage.getItem("myQortalNodes")) const qortalNodesList = JSON.stringify(
const qortalNodesListSave = JSON.parse((qortalNodesList) || "[]") localStorage.getItem('myQortalNodes')
const blob = new Blob([qortalNodesListSave], { type: 'text/plain;charset=utf-8' }) );
nodelist = "qortal.nodes" const qortalNodesListSave = JSON.parse(qortalNodesList || '[]');
this.saveFileToDisk(blob, nodelist) const blob = new Blob([qortalNodesListSave], {
type: 'text/plain;charset=utf-8',
});
nodelist = 'qortal.nodes';
this.saveFileToDisk(blob, nodelist);
} }
async saveFileToDisk(blob, fileName) { async saveFileToDisk(blob, fileName) {
try { try {
const fileHandle = await self.showSaveFilePicker({ const fileHandle = await self.showSaveFilePicker({
suggestedName: fileName, suggestedName: fileName,
types: [{ types: [
description: "File", {
}] description: 'File',
}) },
],
});
const writeFile = async (fileHandle, contents) => { const writeFile = async (fileHandle, contents) => {
const writable = await fileHandle.createWritable() const writable = await fileHandle.createWritable();
await writable.write(contents) await writable.write(contents);
await writable.close() await writable.close();
} };
writeFile(fileHandle, blob).then(() => console.log("FILE SAVED")) writeFile(fileHandle, blob).then(() => console.log('FILE SAVED'));
let snack4string = get("settings.snack4") let snack4string = get('settings.snack4');
snackbar.add({ snackbar.add({
labelText: `${snack4string} qortal.nodes`, labelText: `${snack4string} qortal.nodes`,
dismiss: true dismiss: true,
}) });
} catch (error) { } catch (error) {
if (error.name === 'AbortError') { if (error.name === 'AbortError') {
return return;
} }
FileSaver.saveAs(blob, fileName) FileSaver.saveAs(blob, fileName);
} }
} }
renderImportNodesListButton() { renderImportNodesListButton() {
return html` return html`
<mwc-button dense unelevated label="${translate("settings.import")}" @click="${() => this.openImportNodesDialog()}"></mwc-button> <mwc-button
` dense
unelevated
label="${translate('settings.import')}"
@click="${() => this.openImportNodesDialog()}"
></mwc-button>
`;
} }
async importQortalNodesList(file) { async importQortalNodesList(file) {
localStorage.removeItem("myQortalNodes") localStorage.removeItem('myQortalNodes');
const newItems = JSON.parse((file) || "[]") const newItems = JSON.parse(file || '[]');
localStorage.setItem("myQortalNodes", JSON.stringify(newItems)) localStorage.setItem('myQortalNodes', JSON.stringify(newItems));
this.shadowRoot.querySelector('#importQortalNodesListDialog').close() this.shadowRoot.querySelector('#importQortalNodesListDialog').close();
let snack5string = get("settings.snack5") let snack5string = get('settings.snack5');
snackbar.add({ snackbar.add({
labelText: `${snack5string}`, labelText: `${snack5string}`,
dismiss: true dismiss: true,
}) });
localStorage.removeItem('mySelectedNode') localStorage.removeItem('mySelectedNode');
localStorage.setItem('mySelectedNode', 0) localStorage.setItem('mySelectedNode', 0);
store.dispatch(doLoadNodeConfig()) store.dispatch(doLoadNodeConfig());
} }
stateChanged(state) { stateChanged(state) {
this.config = state.config this.config = state.config;
this.nodeConfig = state.app.nodeConfig this.nodeConfig = state.app.nodeConfig;
} }
} }
window.customElements.define('settings-page', SettingsPage) window.customElements.define('settings-page', SettingsPage);
const settings = document.createElement('settings-page') const settings = document.createElement('settings-page');
settingsDialog = document.body.appendChild(settings) settingsDialog = document.body.appendChild(settings);
export default settingsDialog export default settingsDialog;

View File

@ -1,5 +1,5 @@
// Node Config Actions here... // Node Config Actions here...
import { LOAD_NODE_CONFIG, SET_NODE, ADD_NODE } from '../app-action-types.js' import { LOAD_NODE_CONFIG, SET_NODE, ADD_NODE, REMOVE_NODE, EDIT_NODE } from '../app-action-types.js'
import { UI_VERSION } from '../version.js' import { UI_VERSION } from '../version.js'
const nodeConfigUrl = '/getConfig' const nodeConfigUrl = '/getConfig'
@ -72,6 +72,16 @@ export const doAddNode = (nodeObject) => {
return dispatch(addNode(nodeObject)) return dispatch(addNode(nodeObject))
} }
} }
export const doRemoveNode = (index) => {
return (dispatch, getState) => {
return dispatch(removeNode(index))
}
}
export const doEditNode = (index, nodeObject) => {
return (dispatch, getState) => {
return dispatch(editNode({index, nodeObject}))
}
}
const addNode = (payload) => { const addNode = (payload) => {
return { return {
@ -80,6 +90,18 @@ const addNode = (payload) => {
} }
} }
const editNode = (payload) => {
return {
type: EDIT_NODE,
payload
}
}
const removeNode = (payload) => {
return {
type: REMOVE_NODE,
payload
}
}
const obj1 = { const obj1 = {
name: 'Local Node', name: 'Local Node',
protocol: 'http', protocol: 'http',

View File

@ -12,6 +12,8 @@ export const UPDATE_NODE_STATUS = 'UPDATE_NODE_STATUS'
export const UPDATE_NODE_INFO = 'UPDATE_NODE_INFO' export const UPDATE_NODE_INFO = 'UPDATE_NODE_INFO'
export const SET_NODE = 'SET_NODE' export const SET_NODE = 'SET_NODE'
export const ADD_NODE = 'ADD_NODE' export const ADD_NODE = 'ADD_NODE'
export const EDIT_NODE = 'EDIT_NODE'
export const REMOVE_NODE = 'REMOVE_NODE'
export const LOAD_NODE_CONFIG = 'LOAD_NODE_CONFIG' export const LOAD_NODE_CONFIG = 'LOAD_NODE_CONFIG'
export const PAGE_URL = 'PAGE_URL' export const PAGE_URL = 'PAGE_URL'
export const CHAT_HEADS = 'CHAT_HEADS' export const CHAT_HEADS = 'CHAT_HEADS'

View File

@ -1,9 +1,9 @@
// Loading state, login state, isNavDrawOpen state etc. None of this needs to be saved to localstorage. // Loading state, login state, isNavDrawOpen state etc. None of this needs to be saved to localstorage.
import { loadStateFromLocalStorage, saveStateToLocalStorage } from '../../localStorageHelpers.js' import { loadStateFromLocalStorage, saveStateToLocalStorage } from '../../localStorageHelpers.js'
import { LOG_IN, LOG_OUT, NETWORK_CONNECTION_STATUS, INIT_WORKERS, ADD_PLUGIN_URL, ADD_PLUGIN, ADD_NEW_PLUGIN_URL, NAVIGATE, SELECT_ADDRESS, ACCOUNT_INFO, CHAT_HEADS, UPDATE_BLOCK_INFO, UPDATE_NODE_STATUS, UPDATE_NODE_INFO, LOAD_NODE_CONFIG, SET_NODE, ADD_NODE, PAGE_URL, ADD_AUTO_LOAD_IMAGES_CHAT, REMOVE_AUTO_LOAD_IMAGES_CHAT, ALLOW_QAPP_AUTO_AUTH, REMOVE_QAPP_AUTO_AUTH, SET_CHAT_LAST_SEEN, ADD_CHAT_LAST_SEEN, ALLOW_QAPP_AUTO_LISTS, REMOVE_QAPP_AUTO_LISTS, SET_NEW_TAB, ADD_TAB_INFO, SET_TAB_NOTIFICATIONS, IS_OPEN_DEV_DIALOG } from './app-action-types.js' import { LOG_IN, LOG_OUT, NETWORK_CONNECTION_STATUS, INIT_WORKERS, ADD_PLUGIN_URL, ADD_PLUGIN, ADD_NEW_PLUGIN_URL, NAVIGATE, SELECT_ADDRESS, ACCOUNT_INFO, CHAT_HEADS, UPDATE_BLOCK_INFO, UPDATE_NODE_STATUS, UPDATE_NODE_INFO, LOAD_NODE_CONFIG, SET_NODE, ADD_NODE, PAGE_URL, ADD_AUTO_LOAD_IMAGES_CHAT, REMOVE_AUTO_LOAD_IMAGES_CHAT, ALLOW_QAPP_AUTO_AUTH, REMOVE_QAPP_AUTO_AUTH, SET_CHAT_LAST_SEEN, ADD_CHAT_LAST_SEEN, ALLOW_QAPP_AUTO_LISTS, REMOVE_QAPP_AUTO_LISTS, SET_NEW_TAB, ADD_TAB_INFO, SET_TAB_NOTIFICATIONS, IS_OPEN_DEV_DIALOG, REMOVE_NODE, EDIT_NODE } from './app-action-types.js'
import { initWorkersReducer } from './reducers/init-workers.js' import { initWorkersReducer } from './reducers/init-workers.js'
import { loginReducer } from './reducers/login-reducer.js' import { loginReducer } from './reducers/login-reducer.js'
import { setNode, addNode } from './reducers/manage-node.js' import { setNode, addNode, removeNode, editNode } from './reducers/manage-node.js'
import localForage from "localforage"; import localForage from "localforage";
const chatLastSeen = localForage.createInstance({ const chatLastSeen = localForage.createInstance({
name: "chat-last-seen", name: "chat-last-seen",
@ -120,6 +120,10 @@ export default (state = INITIAL_STATE, action) => {
return setNode(state, action) return setNode(state, action)
case ADD_NODE: case ADD_NODE:
return addNode(state, action) return addNode(state, action)
case EDIT_NODE:
return editNode(state, action)
case REMOVE_NODE:
return removeNode(state, action)
case PAGE_URL: case PAGE_URL:
return { return {
...state, ...state,

View File

@ -20,3 +20,26 @@ export const addNode = (state, action) => {
} }
} }
} }
export const editNode = (state, action) => {
const copyKnownNodes = [...state.nodeConfig.knownNodes]
copyKnownNodes[action.payload.index] = action.payload.nodeObject
return {
...state,
nodeConfig: {
...state.nodeConfig,
knownNodes: copyKnownNodes
}
}
}
export const removeNode = (state, action) => {
const copyKnownNodes = [...state.nodeConfig.knownNodes]
copyKnownNodes.splice(action.payload, 1);
return {
...state,
nodeConfig: {
...state.nodeConfig,
knownNodes: copyKnownNodes
}
}
}