From fd3ee6909df31c8e8e4312c6a2aa86c8a20f4b31 Mon Sep 17 00:00:00 2001 From: jpk68 Date: Sat, 7 Mar 2026 21:39:55 -0500 Subject: [PATCH 1/3] settings: add config page for i2p --- images/itoopie.svg | 1997 +++++++++++++++++++++++++++++ main.qml | 12 +- pages/settings/SettingsNode.qml | 129 ++ qml.qrc | 1 + src/libwalletqt/WalletManager.cpp | 12 + src/libwalletqt/WalletManager.h | 2 + 6 files changed, 2152 insertions(+), 1 deletion(-) create mode 100644 images/itoopie.svg diff --git a/images/itoopie.svg b/images/itoopie.svg new file mode 100644 index 0000000000..d0a42a59b5 --- /dev/null +++ b/images/itoopie.svg @@ -0,0 +1,1997 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/main.qml b/main.qml index 679195746f..d67f321406 100644 --- a/main.qml +++ b/main.qml @@ -787,7 +787,13 @@ ApplicationWindow { const noSync = appWindow.walletMode === 0; const bootstrapNodeAddress = persistentSettings.walletMode < 2 ? "auto" : persistentSettings.bootstrapNodeAddress - daemonManager.start(flags, persistentSettings.nettype, persistentSettings.blockchainDataDir, bootstrapNodeAddress, noSync, persistentSettings.pruneBlockchain); + + var allFlags = flags; + if (persistentSettings.anonymityNetworkType === 1) { + allFlags = (allFlags + " --i2p-sam " + persistentSettings.i2pRouterAddress + ":" + persistentSettings.i2pSamPort).trim(); + } + + daemonManager.start(allFlags, persistentSettings.nettype, persistentSettings.blockchainDataDir, bootstrapNodeAddress, noSync, persistentSettings.pruneBlockchain); } function stopDaemon(callback, splash){ @@ -1514,6 +1520,10 @@ ApplicationWindow { property int autosaveMinutes: 10 property bool pruneBlockchain: false + property int anonymityNetworkType: 0 + property string i2pRouterAddress: "127.0.0.1" + property string i2pSamPort: "7656" + property bool fiatPriceEnabled: false property bool fiatPriceToggle: false property string fiatPriceProvider: "kraken" diff --git a/pages/settings/SettingsNode.qml b/pages/settings/SettingsNode.qml index 28480419ff..3a2d98d4b7 100644 --- a/pages/settings/SettingsNode.qml +++ b/pages/settings/SettingsNode.qml @@ -352,6 +352,135 @@ Rectangle{ } } } + + Timer { + id: i2pStatusTimer + interval: 5000 + running: appWindow.persistentSettings.anonymityNetworkType === 1 + repeat: true + triggeredOnStart: true + onTriggered: { + var host = appWindow.persistentSettings.i2pRouterAddress || "127.0.0.1"; + var port = parseInt(appWindow.persistentSettings.i2pSamPort) || 7656; + walletManager.checkI2PSamAsync(host, port, function(reachable) { + var color = reachable ? (MoneroComponents.Style.blackTheme ? "lime" : "green") : MoneroComponents.Style.errorColor; + var label = reachable ? qsTr("Router is online") : qsTr("Router is offline"); + i2pStatus.text = "" + label + ""; + }); + } + } + + MoneroComponents.Label { + id: soloTitleLabel + fontSize: 24 + text: qsTr("Anonymity network") + translationManager.emptyString + } + + ColumnLayout { + spacing: 20 + Layout.fillWidth: true + id: networkColumn + z: parent.z + 1 + + ColumnLayout { + spacing: 10 + + MoneroComponents.RadioButton { + id: noneRadioButton + text: qsTr("None") + translationManager.emptyString + fontSize: 16 + checked: appWindow.persistentSettings.anonymityNetworkType === 0 + onClicked: { + checked = true; + i2pRadioButton.checked = false; + appWindow.persistentSettings.anonymityNetworkType = 0; + } + } + + RowLayout { + spacing: 8 + + MoneroComponents.RadioButton { + id: i2pRadioButton + text: qsTr("I2P") + translationManager.emptyString + fontSize: 16 + checked: appWindow.persistentSettings.anonymityNetworkType === 1 + onClicked: { + checked = true; + noneRadioButton.checked = false; + appWindow.persistentSettings.anonymityNetworkType = 1; + } + } + + Image { + source: "../../images/itoopie.svg" + sourceSize.width: 22 + sourceSize.height: 22 + fillMode: Image.PreserveAspectFit + } + } + } + + ColumnLayout { + id: i2pSettings + spacing: 20 + Layout.fillWidth: true + visible: appWindow.persistentSettings.anonymityNetworkType === 1 + + MoneroComponents.TextPlain { + id: soloMainLabel + text: qsTr("Configure your I2P connection settings. Most users should stick to the default settings.") + translationManager.emptyString + wrapMode: Text.Wrap + Layout.fillWidth: true + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 14 + color: MoneroComponents.Style.defaultFontColor + } + + RowLayout { + spacing: 32 + Layout.fillWidth: true + + MoneroComponents.LineEdit { + id: i2pRouterAddress + Layout.fillWidth: true + Layout.preferredWidth: 300 + labelFontSize: 14 + labelText: qsTr("Router address") + translationManager.emptyString + text: appWindow.persistentSettings.i2pRouterAddress || "127.0.0.1" + placeholderText: "127.0.0.1" + placeholderFontSize: 14 + fontSize: 14 + onEditingFinished: { + appWindow.persistentSettings.i2pRouterAddress = text + } + } + + MoneroComponents.LineEdit { + id: i2pSamPort + Layout.fillWidth: true + Layout.preferredWidth: 100 + labelFontSize: 14 + labelText: qsTr("SAM port") + translationManager.emptyString + text: appWindow.persistentSettings.i2pSamPort || "7656" + placeholderText: "7656" + placeholderFontSize: 14 + fontSize: 14 + onEditingFinished: { + appWindow.persistentSettings.i2pSamPort = text + } + } + } + + Text { + id: i2pStatus + textFormat: Text.RichText + font.family: MoneroComponents.Style.fontRegular.name + font.pixelSize: 14 + text: "" + qsTr("Checking router status...") + "" + } + } + } } } } diff --git a/qml.qrc b/qml.qrc index 9223423dc8..b2af7bfd35 100644 --- a/qml.qrc +++ b/qml.qrc @@ -36,6 +36,7 @@ images/tip.png components/MenuButtonDivider.qml images/monero-vector.svg + images/itoopie.svg components/StandardDropdown.qml images/whiteDropIndicator.png images/whiteDropIndicator@2x.png diff --git a/src/libwalletqt/WalletManager.cpp b/src/libwalletqt/WalletManager.cpp index ae03357b5e..b326f26a0a 100644 --- a/src/libwalletqt/WalletManager.cpp +++ b/src/libwalletqt/WalletManager.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include "qt/updater.h" #include "qt/ScopeGuard.h" @@ -378,6 +379,17 @@ bool WalletManager::stopMining() return m_pimpl->stopMining(); } +void WalletManager::checkI2PSamAsync(const QString &host, int port, const QJSValue &callback) +{ + m_scheduler.run([host, port] { + QTcpSocket socket; + socket.connectToHost(host, static_cast(port)); + bool reachable = socket.waitForConnected(2000); + if (reachable) socket.disconnectFromHost(); + return QJSValueList({reachable}); + }, callback); +} + bool WalletManager::localDaemonSynced() const { return blockchainHeight() > 1 && blockchainHeight() >= blockchainTargetHeight(); diff --git a/src/libwalletqt/WalletManager.h b/src/libwalletqt/WalletManager.h index e98648c120..6dd384f9ce 100644 --- a/src/libwalletqt/WalletManager.h +++ b/src/libwalletqt/WalletManager.h @@ -162,6 +162,8 @@ class WalletManager : public QObject, public PassprasePrompter Q_INVOKABLE bool startMining(const QString &address, quint32 threads, bool backgroundMining, bool ignoreBattery); Q_INVOKABLE bool stopMining(); + Q_INVOKABLE void checkI2PSamAsync(const QString &host, int port, const QJSValue &callback); + // QML missing such functionality, implementing these helpers here Q_INVOKABLE QString urlToLocalPath(const QUrl &url) const; Q_INVOKABLE QUrl localPathToUrl(const QString &path) const; From 3cf7e78eb71bda92ab84f6aaf4c44810218c5e75 Mon Sep 17 00:00:00 2001 From: jpk68 Date: Tue, 14 Jul 2026 15:16:03 -0400 Subject: [PATCH 2/3] use epee for router status checking (again) --- src/libwalletqt/WalletManager.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/libwalletqt/WalletManager.cpp b/src/libwalletqt/WalletManager.cpp index b326f26a0a..eaf590a207 100644 --- a/src/libwalletqt/WalletManager.cpp +++ b/src/libwalletqt/WalletManager.cpp @@ -42,7 +42,7 @@ #include #include #include -#include +#include "net/net_helper.h" #include "qt/updater.h" #include "qt/ScopeGuard.h" @@ -382,11 +382,13 @@ bool WalletManager::stopMining() void WalletManager::checkI2PSamAsync(const QString &host, int port, const QJSValue &callback) { m_scheduler.run([host, port] { - QTcpSocket socket; - socket.connectToHost(host, static_cast(port)); - bool reachable = socket.waitForConnected(2000); - if (reachable) socket.disconnectFromHost(); - return QJSValueList({reachable}); + epee::net_utils::blocked_mode_client client; + client.set_ssl(epee::net_utils::ssl_support_t::e_ssl_support_disabled); + const bool reachable = client.connect( + host.toStdString(), + std::to_string(port), + std::chrono::milliseconds{2000}); + return QJSValueList{reachable}; }, callback); } From 248b8b76e15268bb1b998e7c808d76bb0134d5e1 Mon Sep 17 00:00:00 2001 From: jpk68 Date: Tue, 14 Jul 2026 21:42:28 -0400 Subject: [PATCH 3/3] remove image, restore dropdown, change font size --- images/itoopie.svg | 1997 ------------------------------- pages/settings/SettingsNode.qml | 55 +- qml.qrc | 1 - 3 files changed, 18 insertions(+), 2035 deletions(-) delete mode 100644 images/itoopie.svg diff --git a/images/itoopie.svg b/images/itoopie.svg deleted file mode 100644 index d0a42a59b5..0000000000 --- a/images/itoopie.svg +++ /dev/null @@ -1,1997 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pages/settings/SettingsNode.qml b/pages/settings/SettingsNode.qml index 3a2d98d4b7..5e90c55ef6 100644 --- a/pages/settings/SettingsNode.qml +++ b/pages/settings/SettingsNode.qml @@ -372,7 +372,7 @@ Rectangle{ MoneroComponents.Label { id: soloTitleLabel - fontSize: 24 + fontSize: 18 text: qsTr("Anonymity network") + translationManager.emptyString } @@ -382,42 +382,13 @@ Rectangle{ id: networkColumn z: parent.z + 1 - ColumnLayout { - spacing: 10 - - MoneroComponents.RadioButton { - id: noneRadioButton - text: qsTr("None") + translationManager.emptyString - fontSize: 16 - checked: appWindow.persistentSettings.anonymityNetworkType === 0 - onClicked: { - checked = true; - i2pRadioButton.checked = false; - appWindow.persistentSettings.anonymityNetworkType = 0; - } - } - - RowLayout { - spacing: 8 - - MoneroComponents.RadioButton { - id: i2pRadioButton - text: qsTr("I2P") + translationManager.emptyString - fontSize: 16 - checked: appWindow.persistentSettings.anonymityNetworkType === 1 - onClicked: { - checked = true; - noneRadioButton.checked = false; - appWindow.persistentSettings.anonymityNetworkType = 1; - } - } - - Image { - source: "../../images/itoopie.svg" - sourceSize.width: 22 - sourceSize.height: 22 - fillMode: Image.PreserveAspectFit - } + MoneroComponents.StandardDropdown { + id: anonymityNetworkDropdown + Layout.maximumWidth: 200 + dataModel: anonymityNetworkModel + currentIndex: appWindow.persistentSettings.anonymityNetworkType + onChanged: { + appWindow.persistentSettings.anonymityNetworkType = currentIndex; } } @@ -481,6 +452,16 @@ Rectangle{ } } } + + ListModel { + id: anonymityNetworkModel + ListElement { + column1: "None" + } + ListElement { + column1: "I2P" + } + } } } } diff --git a/qml.qrc b/qml.qrc index b2af7bfd35..9223423dc8 100644 --- a/qml.qrc +++ b/qml.qrc @@ -36,7 +36,6 @@ images/tip.png components/MenuButtonDivider.qml images/monero-vector.svg - images/itoopie.svg components/StandardDropdown.qml images/whiteDropIndicator.png images/whiteDropIndicator@2x.png