diff --git a/src/backend/wallet_connect.nim b/src/backend/wallet_connect.nim index 28414f9a26..d3863da3ba 100644 --- a/src/backend/wallet_connect.nim +++ b/src/backend/wallet_connect.nim @@ -57,8 +57,7 @@ proc getActiveSessions*(validAtTimestamp: int): JsonNode = return nil let jsonResultStr = rpcRes.result.getStr() - if jsonResultStr == "null": - # nil means error + if jsonResultStr == "null" or jsonResultStr == "": return newJArray() if rpcRes.result.kind != JArray: diff --git a/storybook/pages/DAppsWorkflowPage.qml b/storybook/pages/DAppsWorkflowPage.qml index c281a253b3..6e54e05b74 100644 --- a/storybook/pages/DAppsWorkflowPage.qml +++ b/storybook/pages/DAppsWorkflowPage.qml @@ -83,6 +83,15 @@ Item { font.bold: true } } + RowLayout { + StatusBaseText { text: "SDK status:" } + Rectangle { + Layout.preferredWidth: 20 + Layout.preferredHeight: Layout.preferredWidth + radius: Layout.preferredWidth / 2 + color: walletConnectService.wcSDK.sdkReady ? "green" : "red" + } + } CheckBox { text: "Testnet Mode" @@ -289,7 +298,7 @@ Item { id: walletConnectService wcSDK: WalletConnectSDK { - active: settings.enableSDK + enableSdk: settings.enableSDK projectId: projectIdText.projectId } diff --git a/ui/StatusQ/CMakeLists.txt b/ui/StatusQ/CMakeLists.txt index 3cd2f83c6c..5734c5d2fc 100644 --- a/ui/StatusQ/CMakeLists.txt +++ b/ui/StatusQ/CMakeLists.txt @@ -108,6 +108,7 @@ add_library(StatusQ SHARED include/StatusQ/modelsyncedcontainer.h include/StatusQ/modelutilsinternal.h include/StatusQ/movablemodel.h + include/StatusQ/networkchecker.h include/StatusQ/objectproxymodel.h include/StatusQ/permissionutilsinternal.h include/StatusQ/rolesrenamingmodel.h @@ -137,6 +138,7 @@ add_library(StatusQ SHARED src/modelentry.cpp src/modelutilsinternal.cpp src/movablemodel.cpp + src/networkchecker.cpp src/objectproxymodel.cpp src/permissionutilsinternal.cpp src/plugin.cpp diff --git a/ui/StatusQ/include/StatusQ/networkchecker.h b/ui/StatusQ/include/StatusQ/networkchecker.h new file mode 100644 index 0000000000..39e4f91c3f --- /dev/null +++ b/ui/StatusQ/include/StatusQ/networkchecker.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include +#include + +#include + +using namespace std::chrono_literals; + +/// Checks if the internet connection is available, when active. +/// It checks the connection every 30 seconds as long as the \c active property is \c true. +class NetworkChecker : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool isOnline READ isOnline NOTIFY isOnlineChanged) + Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged) + +public: + explicit NetworkChecker(QObject* parent = nullptr); + bool isOnline() const; + + bool isActive() const; + void setActive(bool active); + +signals: + void isOnlineChanged(bool online); + void activeChanged(bool active); + +private: + QNetworkAccessManager manager; + QTimer timer; + bool online = false; + bool active = true; + constexpr static std::chrono::milliseconds checkInterval = 30s; + + void checkNetwork(); + void onFinished(QNetworkReply* reply); + void updateRegularCheck(bool active); +}; \ No newline at end of file diff --git a/ui/StatusQ/src/StatusQ/Components/WebEngineLoader.qml b/ui/StatusQ/src/StatusQ/Components/WebEngineLoader.qml index afd84415a5..a7fe2b6a91 100644 --- a/ui/StatusQ/src/StatusQ/Components/WebEngineLoader.qml +++ b/ui/StatusQ/src/StatusQ/Components/WebEngineLoader.qml @@ -3,6 +3,8 @@ import QtQuick 2.15 import QtWebEngine 1.10 import QtWebChannel 1.15 +import StatusQ 0.1 + // Helper to load and setup an instance of \c WebEngineView // // The \c webChannelObjects property is used to register specific objects @@ -11,34 +13,33 @@ import QtWebChannel 1.15 // qrc:/StatusQ/Components/private/qwebchannel/helpers.js will provide // access to window.statusq APIs used to exchange data between the internal // web engine and the QML application +// +// It doesn't load the web engine until NetworkChecker detects and active internet +// connection to avoid the corner case of initializing the web engine without +// network connectivity. If the web engine is initialized without network connectivity +// it won't restore the connectivity when it's available on Mac OS Item { id: root required property url url required property var webChannelObjects - property alias active: loader.active + // Used to control the loading of the web engine + property bool active: false + // Useful to monitor the loading state of the web engine (depends on active and internet connectivity) + readonly property bool isActive: loader.active property alias instance: loader.item + property bool waitForInternet: true signal engineLoaded(WebEngineView instance) signal engineUnloaded() signal pageLoaded() signal pageLoadingError(string errorString) - Loader { - id: loader + Component { + id: webEngineViewComponent - active: false - - onStatusChanged: function() { - if (status === Loader.Ready) { - root.engineLoaded(loader.item) - } else if (status === Loader.Null) { - root.engineUnloaded() - } - } - - sourceComponent: WebEngineView { + WebEngineView { id: webEngineView anchors.fill: parent @@ -65,4 +66,35 @@ Item { } } } + + Loader { + id: loader + + active: root.active && (!root.waitForInternet || (d.passedFirstTimeInitialization || networkChecker.isOnline)) + + onStatusChanged: function() { + if (status === Loader.Ready) { + root.engineLoaded(loader.item) + d.passedFirstTimeInitialization = true + } else if (status === Loader.Null) { + root.engineUnloaded() + } + } + + sourceComponent: webEngineViewComponent + } + + NetworkChecker { + id: networkChecker + + // Deactivate searching for network connectivity after the web engine is loaded + active: !d.passedFirstTimeInitialization + } + + QtObject { + id: d + + // Used to hold the loading of the web engine until internet connectivity is available + property bool passedFirstTimeInitialization: false + } } \ No newline at end of file diff --git a/ui/StatusQ/src/networkchecker.cpp b/ui/StatusQ/src/networkchecker.cpp new file mode 100644 index 0000000000..9a8fea3136 --- /dev/null +++ b/ui/StatusQ/src/networkchecker.cpp @@ -0,0 +1,61 @@ +#include "StatusQ/networkchecker.h" + +NetworkChecker::NetworkChecker(QObject* parent) + : QObject(parent) +{ + connect(&manager, &QNetworkAccessManager::finished, this, &NetworkChecker::onFinished); + connect(&timer, &QTimer::timeout, this, &NetworkChecker::checkNetwork); + + updateRegularCheck(active); +} + +bool NetworkChecker::isOnline() const +{ + return online; +} + +void NetworkChecker::checkNetwork() +{ + QNetworkRequest request(QUrl("https://fedoraproject.org/static/hotspot.txt")); + manager.get(request); +} + +void NetworkChecker::onFinished(QNetworkReply* reply) +{ + bool wasOnline = online; + online = (reply->error() == QNetworkReply::NoError); + reply->deleteLater(); + + if(wasOnline != online) + { + emit isOnlineChanged(online); + } +} + +bool NetworkChecker::isActive() const +{ + return active; +} + +void NetworkChecker::setActive(bool active) +{ + if(active == this->active) return; + + this->active = active; + emit activeChanged(active); + + updateRegularCheck(active); +} + +void NetworkChecker::updateRegularCheck(bool active) +{ + if(active) + { + checkNetwork(); + timer.start(checkInterval); + } + else + { + timer.stop(); + } +} \ No newline at end of file diff --git a/ui/StatusQ/src/plugin.cpp b/ui/StatusQ/src/plugin.cpp index 93526ac313..dcc0bf9a82 100644 --- a/ui/StatusQ/src/plugin.cpp +++ b/ui/StatusQ/src/plugin.cpp @@ -17,6 +17,7 @@ #include "StatusQ/modelentry.h" #include "StatusQ/modelutilsinternal.h" #include "StatusQ/movablemodel.h" +#include "StatusQ/networkchecker.h" #include "StatusQ/objectproxymodel.h" #include "StatusQ/permissionutilsinternal.h" #include "StatusQ/rolesrenamingmodel.h" @@ -58,6 +59,7 @@ public: qmlRegisterType("StatusQ", 0, 1, "SourceModel"); qmlRegisterType("StatusQ", 0, 1, "ConcatModel"); qmlRegisterType("StatusQ", 0, 1, "MovableModel"); + qmlRegisterType("StatusQ", 0, 1, "NetworkChecker"); qmlRegisterType("StatusQ", 0, 1, "FastExpressionFilter"); qmlRegisterType("StatusQ", 0, 1, "FastExpressionRole"); diff --git a/ui/StatusQ/tests/TestComponents/tst_WebEngineLoader.qml b/ui/StatusQ/tests/TestComponents/tst_WebEngineLoader.qml index 339db1bd80..b14c1126b7 100644 --- a/ui/StatusQ/tests/TestComponents/tst_WebEngineLoader.qml +++ b/ui/StatusQ/tests/TestComponents/tst_WebEngineLoader.qml @@ -38,6 +38,8 @@ TestCase { sourceComponent: WebEngineLoader { url: "./WebEngineLoader/test.html" webChannelObjects: [testObject] + + waitForInternet: false } } SignalSpy { id: loadedSpy; target: loader; signalName: "loaded" } @@ -67,13 +69,13 @@ TestCase { compare(webEngine.instance, null, "By default the engine is not loaded") webEngine.active = true - webEngineLoadedSpy.wait(1000); + webEngineLoadedSpy.wait(1000) verify(webEngine.instance !== null , "The WebEngineView should be available") if (Qt.platform.os === "linux") { skip("fails to load page on linux") } - pageLoadedSpy.wait(1000); + pageLoadedSpy.wait(1000) webEngine.active = false engineUnloadedSpy.wait(1000); diff --git a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectSDK.qml b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectSDK.qml index 9f24366b32..149bcd5539 100644 --- a/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectSDK.qml +++ b/ui/app/AppLayouts/Wallet/services/dapps/WalletConnectSDK.qml @@ -14,10 +14,10 @@ WalletConnectSDKBase { id: root readonly property alias sdkReady: d.sdkReady - readonly property alias webEngineLoader: loader - property alias active: loader.active - property alias url: loader.url + // Enable the WalletConnect SDK + property alias enableSdk: loader.active + readonly property alias url: loader.url implicitWidth: 1 implicitHeight: 1 @@ -81,15 +81,15 @@ WalletConnectSDKBase { function init() { console.debug(`WC WalletConnectSDK.wcCall.init; root.projectId: ${root.projectId}`) - d.engine.runJavaScript(`wc.init("${root.projectId}").catch((error) => {wc.statusObject.sdkInitialized("SDK init error: "+error);})`, function(result) { - - console.debug(`WC WalletConnectSDK.wcCall.init; response: ${JSON.stringify(result)}`) - - if (result && !!result.error) - { - console.error("init: ", result.error) - } - }) + d.engine.runJavaScript(` + wc.init("${root.projectId}") + .then(()=> { + wc.statusObject.sdkInitialized(""); + }) + .catch((error) => { + wc.statusObject.sdkInitialized("SDK init error: "+error) + }) + `) } function getPairings(callback) { @@ -259,7 +259,7 @@ WalletConnectSDKBase { WebChannel.id: "statusObject" - function bubbleConsoleMessage(type, message) { + function echo(type, message) { if (type === "warn") { console.warn(message) } else if (type === "debug") { @@ -272,7 +272,7 @@ WalletConnectSDKBase { } function sdkInitialized(error) { - console.debug(`WC WalletConnectSDK.sdkInitialized; error: ${error}`) + console.debug(`WC WalletConnectSDK.sdkInitialized: ${!!error ? "error: " + error : "success"}`) d.sdkReady = !error root.sdkInit(d.sdkReady, error) } diff --git a/ui/app/AppLayouts/Wallet/services/dapps/helpers.js b/ui/app/AppLayouts/Wallet/services/dapps/helpers.js index 85b2fbc930..b67d98730c 100644 --- a/ui/app/AppLayouts/Wallet/services/dapps/helpers.js +++ b/ui/app/AppLayouts/Wallet/services/dapps/helpers.js @@ -54,8 +54,8 @@ function buildSupportedNamespacesFromModels(chainsModel, accountsModel, methods) } function buildSupportedNamespaces(chainIds, addresses, methods) { - var eipChainIds = [] - var eipAddresses = [] + let eipChainIds = [] + let eipAddresses = [] for (let i = 0; i < chainIds.length; i++) { let chainId = chainIds[i] eipChainIds.push(`"eip155:${chainId}"`) @@ -65,7 +65,13 @@ function buildSupportedNamespaces(chainIds, addresses, methods) { } let methodsStr = methods.map(method => `"${method}"`).join(',') return `{ - "eip155":{"chains": [${eipChainIds.join(',')}],"methods": [${methodsStr}],"events": ["accountsChanged", "chainChanged"],"accounts": [${eipAddresses.join(',')}]}}` + "eip155":{ + "chains": [${eipChainIds.join(',')}], + "methods": [${methodsStr}], + "events": ["accountsChanged", "chainChanged"], + "accounts": [${eipAddresses.join(',')}] + } + }` } function validURI(uri) { diff --git a/ui/app/mainui/AppMain.qml b/ui/app/mainui/AppMain.qml index e975fd9c82..da9da15b0f 100644 --- a/ui/app/mainui/AppMain.qml +++ b/ui/app/mainui/AppMain.qml @@ -2191,7 +2191,7 @@ Item { id: walletConnectService wcSDK: WalletConnectSDK { - active: WalletStore.RootStore.walletSectionInst.walletReady + enableSdk: WalletStore.RootStore.walletSectionInst.walletReady projectId: WalletStore.RootStore.appSettings.walletConnectProjectID }