mirror of
https://github.com/monero-project/monero-gui.git
synced 2024-12-23 03:59:38 +00:00
Merge pull request #3373
6bc9627
SettingsNode: implement multiple remote nodes support (xiphon)
This commit is contained in:
commit
183585653f
7 changed files with 435 additions and 89 deletions
66
components/Dialog.qml
Normal file
66
components/Dialog.qml
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
// Copyright (c) 2021, The Monero Project
|
||||||
|
//
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification, are
|
||||||
|
// permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
// conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||||
|
// of conditions and the following disclaimer in the documentation and/or other
|
||||||
|
// materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without specific
|
||||||
|
// prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||||
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
|
||||||
|
import "." as MoneroComponents
|
||||||
|
|
||||||
|
Popup {
|
||||||
|
id: dialog
|
||||||
|
|
||||||
|
default property alias content: mainLayout.children
|
||||||
|
property alias title: header.text
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: MoneroComponents.Style.blackTheme ? Qt.rgba(255, 255, 255, 0.25) : Qt.rgba(0, 0, 0, 0.25)
|
||||||
|
border.width: 1
|
||||||
|
color: MoneroComponents.Style.blackTheme ? "black" : "white"
|
||||||
|
radius: 10
|
||||||
|
}
|
||||||
|
closePolicy: Popup.CloseOnEscape
|
||||||
|
focus: true
|
||||||
|
padding: 20
|
||||||
|
x: (appWindow.width - width) / 2
|
||||||
|
y: (appWindow.height - height) / 2
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: mainLayout
|
||||||
|
spacing: dialog.padding
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: header
|
||||||
|
color: MoneroComponents.Style.defaultFontColor
|
||||||
|
font.bold: true
|
||||||
|
font.family: MoneroComponents.Style.fontRegular.name
|
||||||
|
font.pixelSize: 18
|
||||||
|
visible: text != ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
153
components/RemoteNodeDialog.qml
Normal file
153
components/RemoteNodeDialog.qml
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
// Copyright (c) 2021, The Monero Project
|
||||||
|
//
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification, are
|
||||||
|
// permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
// conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||||
|
// of conditions and the following disclaimer in the documentation and/or other
|
||||||
|
// materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||||
|
// used to endorse or promote products derived from this software without specific
|
||||||
|
// prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||||
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.2
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
|
||||||
|
import "." as MoneroComponents
|
||||||
|
|
||||||
|
MoneroComponents.Dialog {
|
||||||
|
id: root
|
||||||
|
title: (editMode ? qsTr("Edit remote node") : qsTr("Add remote node")) + translationManager.emptyString
|
||||||
|
|
||||||
|
property var callbackOnSuccess: null
|
||||||
|
property bool editMode: false
|
||||||
|
property bool success: false
|
||||||
|
|
||||||
|
onActiveFocusChanged: activeFocus && remoteNodeAddress.forceActiveFocus()
|
||||||
|
|
||||||
|
function add(callbackOnSuccess) {
|
||||||
|
root.editMode = false;
|
||||||
|
root.callbackOnSuccess = callbackOnSuccess;
|
||||||
|
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
|
||||||
|
function edit(remoteNode, callbackOnSuccess) {
|
||||||
|
const hostPort = remoteNode.address.match(/^(.*?)(?:\:?(\d*))$/);
|
||||||
|
if (hostPort) {
|
||||||
|
remoteNodeAddress.daemonAddrText = hostPort[1];
|
||||||
|
remoteNodeAddress.daemonPortText = hostPort[2];
|
||||||
|
}
|
||||||
|
daemonUsername.text = remoteNode.username;
|
||||||
|
daemonPassword.text = remoteNode.password;
|
||||||
|
setTrustedDaemonCheckBox.checked = remoteNode.trusted;
|
||||||
|
root.callbackOnSuccess = callbackOnSuccess;
|
||||||
|
root.editMode = true;
|
||||||
|
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
|
||||||
|
onClosed: {
|
||||||
|
if (root.success && callbackOnSuccess) {
|
||||||
|
callbackOnSuccess({
|
||||||
|
address: remoteNodeAddress.getAddress(),
|
||||||
|
username: daemonUsername.text,
|
||||||
|
password: daemonPassword.text,
|
||||||
|
trusted: setTrustedDaemonCheckBox.checked,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteNodeAddress.daemonAddrText = "";
|
||||||
|
remoteNodeAddress.daemonPortText = "";
|
||||||
|
daemonUsername.text = "";
|
||||||
|
daemonPassword.text = "";
|
||||||
|
setTrustedDaemonCheckBox.checked = false;
|
||||||
|
root.success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MoneroComponents.RemoteNodeEdit {
|
||||||
|
id: remoteNodeAddress
|
||||||
|
Layout.fillWidth: true
|
||||||
|
placeholderFontSize: 15
|
||||||
|
|
||||||
|
daemonAddrLabelText: qsTr("Address") + translationManager.emptyString
|
||||||
|
daemonPortLabelText: qsTr("Port") + translationManager.emptyString
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 32
|
||||||
|
|
||||||
|
MoneroComponents.LineEdit {
|
||||||
|
id: daemonUsername
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumWidth: 220
|
||||||
|
labelText: qsTr("Daemon username") + translationManager.emptyString
|
||||||
|
placeholderText: qsTr("(optional)") + translationManager.emptyString
|
||||||
|
placeholderFontSize: 15
|
||||||
|
labelFontSize: 14
|
||||||
|
fontSize: 15
|
||||||
|
}
|
||||||
|
|
||||||
|
MoneroComponents.LineEdit {
|
||||||
|
id: daemonPassword
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumWidth: 220
|
||||||
|
labelText: qsTr("Daemon password") + translationManager.emptyString
|
||||||
|
placeholderText: qsTr("Password") + translationManager.emptyString
|
||||||
|
password: true
|
||||||
|
placeholderFontSize: 15
|
||||||
|
labelFontSize: 14
|
||||||
|
fontSize: 15
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MoneroComponents.CheckBox {
|
||||||
|
id: setTrustedDaemonCheckBox
|
||||||
|
activeFocusOnTab: true
|
||||||
|
text: qsTr("Mark as Trusted Daemon") + translationManager.emptyString
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
spacing: parent.spacing
|
||||||
|
|
||||||
|
MoneroComponents.StandardButton {
|
||||||
|
activeFocusOnTab: true
|
||||||
|
fontBold: false
|
||||||
|
primary: false
|
||||||
|
text: qsTr("Cancel") + translationManager.emptyString
|
||||||
|
|
||||||
|
onClicked: root.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
MoneroComponents.StandardButton {
|
||||||
|
activeFocusOnTab: true
|
||||||
|
fontBold: false
|
||||||
|
enabled: remoteNodeAddress.getAddress() != ""
|
||||||
|
text: qsTr("Ok") + translationManager.emptyString
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.success = true;
|
||||||
|
root.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
134
main.qml
134
main.qml
|
@ -26,6 +26,7 @@
|
||||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
import QtQml.Models 2.12
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Window 2.0
|
import QtQuick.Window 2.0
|
||||||
import QtQuick.Controls 1.1
|
import QtQuick.Controls 1.1
|
||||||
|
@ -364,13 +365,13 @@ ApplicationWindow {
|
||||||
console.log("Recovering from seed: ", persistentSettings.is_recovering)
|
console.log("Recovering from seed: ", persistentSettings.is_recovering)
|
||||||
console.log("restore Height", persistentSettings.restore_height)
|
console.log("restore Height", persistentSettings.restore_height)
|
||||||
|
|
||||||
// Use saved daemon rpc login settings
|
if (persistentSettings.useRemoteNode) {
|
||||||
currentWallet.setDaemonLogin(persistentSettings.daemonUsername, persistentSettings.daemonPassword)
|
const remoteNode = remoteNodesModel.currentRemoteNode();
|
||||||
|
currentDaemonAddress = remoteNode.address;
|
||||||
if(persistentSettings.useRemoteNode)
|
currentWallet.setDaemonLogin(remoteNode.username, remoteNode.password);
|
||||||
currentDaemonAddress = persistentSettings.remoteNodeAddress
|
} else {
|
||||||
else
|
currentDaemonAddress = localDaemonAddress;
|
||||||
currentDaemonAddress = localDaemonAddress
|
}
|
||||||
|
|
||||||
console.log("initializing with daemon address: ", currentDaemonAddress)
|
console.log("initializing with daemon address: ", currentDaemonAddress)
|
||||||
currentWallet.initAsync(
|
currentWallet.initAsync(
|
||||||
|
@ -387,7 +388,7 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isTrustedDaemon() {
|
function isTrustedDaemon() {
|
||||||
return !persistentSettings.useRemoteNode || persistentSettings.is_trusted_daemon;
|
return !persistentSettings.useRemoteNode || remoteNodesModel.currentRemoteNode().trusted;
|
||||||
}
|
}
|
||||||
|
|
||||||
function usefulName(path) {
|
function usefulName(path) {
|
||||||
|
@ -616,7 +617,9 @@ ApplicationWindow {
|
||||||
|
|
||||||
const callback = function() {
|
const callback = function() {
|
||||||
persistentSettings.useRemoteNode = true;
|
persistentSettings.useRemoteNode = true;
|
||||||
currentDaemonAddress = persistentSettings.remoteNodeAddress;
|
const remoteNode = remoteNodesModel.currentRemoteNode();
|
||||||
|
currentDaemonAddress = remoteNode.address;
|
||||||
|
currentWallet.setDaemonLogin(remoteNode.username, remoteNode.password);
|
||||||
currentWallet.initAsync(
|
currentWallet.initAsync(
|
||||||
currentDaemonAddress,
|
currentDaemonAddress,
|
||||||
isTrustedDaemon(),
|
isTrustedDaemon(),
|
||||||
|
@ -642,6 +645,7 @@ ApplicationWindow {
|
||||||
console.log("disconnecting remote node");
|
console.log("disconnecting remote node");
|
||||||
persistentSettings.useRemoteNode = false;
|
persistentSettings.useRemoteNode = false;
|
||||||
currentDaemonAddress = localDaemonAddress
|
currentDaemonAddress = localDaemonAddress
|
||||||
|
currentWallet.setDaemonLogin("", "");
|
||||||
currentWallet.initAsync(
|
currentWallet.initAsync(
|
||||||
currentDaemonAddress,
|
currentDaemonAddress,
|
||||||
isTrustedDaemon(),
|
isTrustedDaemon(),
|
||||||
|
@ -1350,6 +1354,8 @@ ApplicationWindow {
|
||||||
confirmationDialog.open();
|
confirmationDialog.open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
remoteNodesModel.initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
MoneroSettings {
|
MoneroSettings {
|
||||||
|
@ -1370,22 +1376,33 @@ ApplicationWindow {
|
||||||
property bool miningIgnoreBattery : true
|
property bool miningIgnoreBattery : true
|
||||||
property var nettype: NetworkType.MAINNET
|
property var nettype: NetworkType.MAINNET
|
||||||
property int restore_height : 0
|
property int restore_height : 0
|
||||||
property bool is_trusted_daemon : false
|
property bool is_trusted_daemon : false // TODO: drop after v0.17.2.0 release
|
||||||
property bool is_recovering : false
|
property bool is_recovering : false
|
||||||
property bool is_recovering_from_device : false
|
property bool is_recovering_from_device : false
|
||||||
property bool customDecorations : true
|
property bool customDecorations : true
|
||||||
property string daemonFlags
|
property string daemonFlags
|
||||||
property int logLevel: 0
|
property int logLevel: 0
|
||||||
property string logCategories: ""
|
property string logCategories: ""
|
||||||
property string daemonUsername: ""
|
property string daemonUsername: "" // TODO: drop after v0.17.2.0 release
|
||||||
property string daemonPassword: ""
|
property string daemonPassword: "" // TODO: drop after v0.17.2.0 release
|
||||||
property bool transferShowAdvanced: false
|
property bool transferShowAdvanced: false
|
||||||
property bool receiveShowAdvanced: false
|
property bool receiveShowAdvanced: false
|
||||||
property bool historyShowAdvanced: false
|
property bool historyShowAdvanced: false
|
||||||
property bool historyHumanDates: true
|
property bool historyHumanDates: true
|
||||||
property string blockchainDataDir: ""
|
property string blockchainDataDir: ""
|
||||||
property bool useRemoteNode: false
|
property bool useRemoteNode: false
|
||||||
property string remoteNodeAddress: ""
|
property string remoteNodeAddress: "" // TODO: drop after v0.17.2.0 release
|
||||||
|
property string remoteNodesSerialized: JSON.stringify({
|
||||||
|
selected: 0,
|
||||||
|
nodes: remoteNodeAddress != ""
|
||||||
|
? [{
|
||||||
|
address: remoteNodeAddress,
|
||||||
|
username: daemonUsername,
|
||||||
|
password: daemonPassword,
|
||||||
|
trusted: is_trusted_daemon,
|
||||||
|
}]
|
||||||
|
: [],
|
||||||
|
})
|
||||||
property string bootstrapNodeAddress: ""
|
property string bootstrapNodeAddress: ""
|
||||||
property bool segregatePreForkOutputs: true
|
property bool segregatePreForkOutputs: true
|
||||||
property bool keyReuseMitigation2: true
|
property bool keyReuseMitigation2: true
|
||||||
|
@ -1432,6 +1449,88 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: remoteNodesModel
|
||||||
|
|
||||||
|
property int selected: 0
|
||||||
|
|
||||||
|
signal store()
|
||||||
|
|
||||||
|
function initialize() {
|
||||||
|
try {
|
||||||
|
const remoteNodes = JSON.parse(persistentSettings.remoteNodesSerialized);
|
||||||
|
for (var index = 0; index < remoteNodes.nodes.length; ++index) {
|
||||||
|
const remoteNode = remoteNodes.nodes[index];
|
||||||
|
remoteNodesModel.append(remoteNode);
|
||||||
|
}
|
||||||
|
selected = remoteNodes.selected % remoteNodesModel.count || 0;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('failed to parse remoteNodesSerialized', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
store.connect(function() {
|
||||||
|
var remoteNodes = [];
|
||||||
|
for (var index = 0; index < remoteNodesModel.count; ++index) {
|
||||||
|
remoteNodes.push(remoteNodesModel.get(index));
|
||||||
|
}
|
||||||
|
persistentSettings.remoteNodesSerialized = JSON.stringify({
|
||||||
|
selected: selected,
|
||||||
|
nodes: remoteNodes
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendIfNotExists(newRemoteNode) {
|
||||||
|
for (var index = 0; index < remoteNodesModel.count; ++index) {
|
||||||
|
const remoteNode = remoteNodesModel.get(index);
|
||||||
|
if (remoteNode.address == newRemoteNode.address &&
|
||||||
|
remoteNode.username == newRemoteNode.username &&
|
||||||
|
remoteNode.password == newRemoteNode.password &&
|
||||||
|
remoteNode.trusted == newRemoteNode.trusted) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
remoteNodesModel.append(newRemoteNode);
|
||||||
|
return remoteNodesModel.count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyRemoteNode(index) {
|
||||||
|
selected = index;
|
||||||
|
const remoteNode = currentRemoteNode();
|
||||||
|
persistentSettings.useRemoteNode = true;
|
||||||
|
if (currentWallet) {
|
||||||
|
currentWallet.setDaemonLogin(remoteNode.username, remoteNode.password);
|
||||||
|
currentWallet.setTrustedDaemon(remoteNode.trusted);
|
||||||
|
appWindow.connectRemoteNode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function currentRemoteNode() {
|
||||||
|
if (selected < remoteNodesModel.count) {
|
||||||
|
return remoteNodesModel.get(selected);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
address: "",
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
trusted: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeSelectNextIfNeeded(index) {
|
||||||
|
remoteNodesModel.remove(index);
|
||||||
|
if (selected == index) {
|
||||||
|
applyRemoteNode(selected % remoteNodesModel.count || 0);
|
||||||
|
} else if (selected > index) {
|
||||||
|
selected = selected - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCountChanged: store()
|
||||||
|
onDataChanged: store()
|
||||||
|
onSelectedChanged: store()
|
||||||
|
}
|
||||||
|
|
||||||
// Information dialog
|
// Information dialog
|
||||||
StandardDialog {
|
StandardDialog {
|
||||||
// dynamically change onclose handler
|
// dynamically change onclose handler
|
||||||
|
@ -1512,6 +1611,10 @@ ApplicationWindow {
|
||||||
y: (parent.height - height) / 2
|
y: (parent.height - height) / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MoneroComponents.RemoteNodeDialog {
|
||||||
|
id: remoteNodeDialog
|
||||||
|
}
|
||||||
|
|
||||||
// Choose blockchain folder
|
// Choose blockchain folder
|
||||||
FileDialog {
|
FileDialog {
|
||||||
id: blockchainFileDialog
|
id: blockchainFileDialog
|
||||||
|
@ -1739,7 +1842,9 @@ ApplicationWindow {
|
||||||
anchors.fill: blurredArea
|
anchors.fill: blurredArea
|
||||||
source: blurredArea
|
source: blurredArea
|
||||||
radius: 64
|
radius: 64
|
||||||
visible: passwordDialog.visible || inputDialog.visible || splash.visible || updateDialog.visible || devicePassphraseDialog.visible || txConfirmationPopup.visible || successfulTxPopup.visible
|
visible: passwordDialog.visible || inputDialog.visible || splash.visible || updateDialog.visible ||
|
||||||
|
devicePassphraseDialog.visible || txConfirmationPopup.visible || successfulTxPopup.visible ||
|
||||||
|
remoteNodeDialog.visible
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2129,6 +2234,7 @@ ApplicationWindow {
|
||||||
|
|
||||||
passwordDialog.onRejectedCallback = function() { appWindow.showWizard(); }
|
passwordDialog.onRejectedCallback = function() { appWindow.showWizard(); }
|
||||||
if (inputDialogVisible) inputDialog.close()
|
if (inputDialogVisible) inputDialog.close()
|
||||||
|
remoteNodeDialog.close();
|
||||||
passwordDialog.open();
|
passwordDialog.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -262,80 +262,93 @@ Rectangle{
|
||||||
text: qsTr("To find a remote node, type 'Monero remote node' into your favorite search engine. Please ensure the node is run by a trusted third-party.") + translationManager.emptyString
|
text: qsTr("To find a remote node, type 'Monero remote node' into your favorite search engine. Please ensure the node is run by a trusted third-party.") + translationManager.emptyString
|
||||||
}
|
}
|
||||||
|
|
||||||
MoneroComponents.RemoteNodeEdit {
|
|
||||||
id: remoteNodeEdit
|
|
||||||
Layout.minimumWidth: 100
|
|
||||||
placeholderFontSize: 15
|
|
||||||
|
|
||||||
daemonAddrLabelText: qsTr("Address") + translationManager.emptyString
|
|
||||||
daemonPortLabelText: qsTr("Port") + translationManager.emptyString
|
|
||||||
|
|
||||||
initialAddress: persistentSettings.remoteNodeAddress
|
|
||||||
onEditingFinished: {
|
|
||||||
persistentSettings.remoteNodeAddress = remoteNodeEdit.getAddress();
|
|
||||||
console.log("setting remote node to " + persistentSettings.remoteNodeAddress);
|
|
||||||
if (persistentSettings.is_trusted_daemon) {
|
|
||||||
persistentSettings.is_trusted_daemon = !persistentSettings.is_trusted_daemon
|
|
||||||
currentWallet.setTrustedDaemon(persistentSettings.is_trusted_daemon)
|
|
||||||
setTrustedDaemonCheckBox.checked = !setTrustedDaemonCheckBox.checked
|
|
||||||
appWindow.showStatusMessage(qsTr("Remote node updated. Trusted daemon has been reset. Mark again, if desired."), 8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GridLayout {
|
|
||||||
columns: 2
|
|
||||||
columnSpacing: 32
|
|
||||||
|
|
||||||
MoneroComponents.LineEdit {
|
|
||||||
id: daemonUsername
|
|
||||||
Layout.fillWidth: true
|
|
||||||
labelText: qsTr("Daemon username") + translationManager.emptyString
|
|
||||||
text: persistentSettings.daemonUsername
|
|
||||||
placeholderText: qsTr("(optional)") + translationManager.emptyString
|
|
||||||
placeholderFontSize: 15
|
|
||||||
labelFontSize: 14
|
|
||||||
fontSize: 15
|
|
||||||
}
|
|
||||||
|
|
||||||
MoneroComponents.LineEdit {
|
|
||||||
id: daemonPassword
|
|
||||||
Layout.fillWidth: true
|
|
||||||
labelText: qsTr("Daemon password") + translationManager.emptyString
|
|
||||||
text: persistentSettings.daemonPassword
|
|
||||||
placeholderText: qsTr("Password") + translationManager.emptyString
|
|
||||||
password: true
|
|
||||||
placeholderFontSize: 15
|
|
||||||
labelFontSize: 14
|
|
||||||
fontSize: 15
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MoneroComponents.CheckBox {
|
MoneroComponents.CheckBox {
|
||||||
id: setTrustedDaemonCheckBox
|
border: false
|
||||||
checked: persistentSettings.is_trusted_daemon
|
checkedIcon: FontAwesome.minusCircle
|
||||||
onClicked: {
|
uncheckedIcon: FontAwesome.plusCircle
|
||||||
persistentSettings.is_trusted_daemon = !persistentSettings.is_trusted_daemon
|
fontAwesomeIcons: true
|
||||||
currentWallet.setTrustedDaemon(persistentSettings.is_trusted_daemon)
|
fontSize: 16
|
||||||
}
|
iconOnTheLeft: true
|
||||||
text: qsTr("Mark as Trusted Daemon") + translationManager.emptyString
|
text: qsTr("Add remote node") + translationManager.emptyString
|
||||||
|
toggleOnClick: false
|
||||||
|
onClicked: remoteNodeDialog.add(remoteNodesModel.append)
|
||||||
}
|
}
|
||||||
|
|
||||||
MoneroComponents.StandardButton {
|
ColumnLayout {
|
||||||
id: btnConnectRemote
|
spacing: 0
|
||||||
enabled: remoteNodeEdit.isValid()
|
|
||||||
small: true
|
|
||||||
text: qsTr("Connect") + translationManager.emptyString
|
|
||||||
onClicked: {
|
|
||||||
// Update daemon login
|
|
||||||
persistentSettings.remoteNodeAddress = remoteNodeEdit.getAddress();
|
|
||||||
persistentSettings.daemonUsername = daemonUsername.text;
|
|
||||||
persistentSettings.daemonPassword = daemonPassword.text;
|
|
||||||
persistentSettings.useRemoteNode = true
|
|
||||||
|
|
||||||
currentWallet.setDaemonLogin(persistentSettings.daemonUsername, persistentSettings.daemonPassword);
|
Repeater {
|
||||||
|
model: remoteNodesModel
|
||||||
|
|
||||||
appWindow.connectRemoteNode()
|
Rectangle {
|
||||||
|
height: 30
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: itemMouseArea.containsMouse || index === remoteNodesModel.selected ? MoneroComponents.Style.titleBarButtonHoverColor : "transparent"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: MoneroComponents.Style.appWindowBorderColor
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.top: parent.top
|
||||||
|
height: 1
|
||||||
|
visible: index > 0
|
||||||
|
|
||||||
|
MoneroEffects.ColorTransition {
|
||||||
|
targetObj: parent
|
||||||
|
blackColor: MoneroComponents.Style._b_appWindowBorderColor
|
||||||
|
whiteColor: MoneroComponents.Style._w_appWindowBorderColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.rightMargin: 80
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
MoneroComponents.TextPlain {
|
||||||
|
color: index === remoteNodesModel.selected ? MoneroComponents.Style.defaultFontColor : MoneroComponents.Style.dimmedFontColor
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 6
|
||||||
|
font.pixelSize: 16
|
||||||
|
text: address
|
||||||
|
themeTransition: false
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: itemMouseArea
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: remoteNodesModel.applyRemoteNode(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 6
|
||||||
|
height: 30
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
MoneroComponents.InlineButton {
|
||||||
|
buttonColor: "transparent"
|
||||||
|
fontFamily: FontAwesome.fontFamily
|
||||||
|
text: FontAwesome.edit
|
||||||
|
onClicked: remoteNodeDialog.edit(remoteNodesModel.get(index), function (remoteNode) {
|
||||||
|
remoteNodesModel.set(index, remoteNode)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
MoneroComponents.InlineButton {
|
||||||
|
buttonColor: "transparent"
|
||||||
|
fontFamily: FontAwesome.fontFamily
|
||||||
|
text: FontAwesome.times
|
||||||
|
visible: remoteNodesModel.count > 1
|
||||||
|
onClicked: remoteNodesModel.removeSelectNextIfNeeded(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2
qml.qrc
2
qml.qrc
|
@ -3,10 +3,12 @@
|
||||||
<file>main.qml</file>
|
<file>main.qml</file>
|
||||||
<file>LeftPanel.qml</file>
|
<file>LeftPanel.qml</file>
|
||||||
<file>MiddlePanel.qml</file>
|
<file>MiddlePanel.qml</file>
|
||||||
|
<file>components/Dialog.qml</file>
|
||||||
<file>components/Label.qml</file>
|
<file>components/Label.qml</file>
|
||||||
<file>components/LanguageButton.qml</file>
|
<file>components/LanguageButton.qml</file>
|
||||||
<file>components/Navbar.qml</file>
|
<file>components/Navbar.qml</file>
|
||||||
<file>components/NavbarItem.qml</file>
|
<file>components/NavbarItem.qml</file>
|
||||||
|
<file>components/RemoteNodeDialog.qml</file>
|
||||||
<file>components/SettingsListItem.qml</file>
|
<file>components/SettingsListItem.qml</file>
|
||||||
<file>components/Slider.qml</file>
|
<file>components/Slider.qml</file>
|
||||||
<file>components/UpdateDialog.qml</file>
|
<file>components/UpdateDialog.qml</file>
|
||||||
|
|
|
@ -42,7 +42,13 @@ ColumnLayout {
|
||||||
|
|
||||||
function save(){
|
function save(){
|
||||||
persistentSettings.useRemoteNode = remoteNode.checked
|
persistentSettings.useRemoteNode = remoteNode.checked
|
||||||
persistentSettings.remoteNodeAddress = remoteNodeEdit.getAddress();
|
const index = remoteNodesModel.appendIfNotExists({
|
||||||
|
address: remoteNodeEdit.getAddress(),
|
||||||
|
username: "",
|
||||||
|
password: "",
|
||||||
|
trusted: false,
|
||||||
|
});
|
||||||
|
remoteNodesModel.applyRemoteNode(index);
|
||||||
if (bootstrapNodeEdit.daemonAddrText == "auto") {
|
if (bootstrapNodeEdit.daemonAddrText == "auto") {
|
||||||
persistentSettings.bootstrapNodeAddress = "auto";
|
persistentSettings.bootstrapNodeAddress = "auto";
|
||||||
} else {
|
} else {
|
||||||
|
@ -201,7 +207,7 @@ ColumnLayout {
|
||||||
id: remoteNodeEdit
|
id: remoteNodeEdit
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
initialAddress: persistentSettings.remoteNodeAddress
|
initialAddress: remoteNodesModel.currentRemoteNode().address
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,10 +65,10 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
WizardSummaryItem {
|
WizardSummaryItem {
|
||||||
visible: persistentSettings.remoteNodeAddress !== "" && appWindow.walletMode == 0
|
visible: remoteNodesModel.currentRemoteNode().address !== "" && appWindow.walletMode == 0
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
header: qsTr("Daemon address") + translationManager.emptyString
|
header: qsTr("Daemon address") + translationManager.emptyString
|
||||||
value: persistentSettings.remoteNodeAddress
|
value: remoteNodesModel.currentRemoteNode().address
|
||||||
}
|
}
|
||||||
|
|
||||||
WizardSummaryItem {
|
WizardSummaryItem {
|
||||||
|
|
Loading…
Reference in a new issue