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..5e90c55ef6 100644 --- a/pages/settings/SettingsNode.qml +++ b/pages/settings/SettingsNode.qml @@ -352,6 +352,116 @@ 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: 18 + text: qsTr("Anonymity network") + translationManager.emptyString + } + + ColumnLayout { + spacing: 20 + Layout.fillWidth: true + id: networkColumn + z: parent.z + 1 + + MoneroComponents.StandardDropdown { + id: anonymityNetworkDropdown + Layout.maximumWidth: 200 + dataModel: anonymityNetworkModel + currentIndex: appWindow.persistentSettings.anonymityNetworkType + onChanged: { + appWindow.persistentSettings.anonymityNetworkType = currentIndex; + } + } + + 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...") + "" + } + } + } + + ListModel { + id: anonymityNetworkModel + ListElement { + column1: "None" + } + ListElement { + column1: "I2P" + } + } } } } diff --git a/src/libwalletqt/WalletManager.cpp b/src/libwalletqt/WalletManager.cpp index ae03357b5e..eaf590a207 100644 --- a/src/libwalletqt/WalletManager.cpp +++ b/src/libwalletqt/WalletManager.cpp @@ -42,6 +42,7 @@ #include #include #include +#include "net/net_helper.h" #include "qt/updater.h" #include "qt/ScopeGuard.h" @@ -378,6 +379,19 @@ bool WalletManager::stopMining() return m_pimpl->stopMining(); } +void WalletManager::checkI2PSamAsync(const QString &host, int port, const QJSValue &callback) +{ + m_scheduler.run([host, port] { + 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); +} + 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;