Skip to content

Commit

Permalink
fix(dapps) Wallet Connect internet connection reestablishing issue
Browse files Browse the repository at this point in the history
Add a new NetworkChecker QObject to StatusQ to be used in checking
internet connection status. This is used by the WebEngineLoader
to only allow loading of web pages when there is an active internet
to cover for a corner case on MacOS where the internet connection is
not reestablished if the WebEngineView was loaded without an active
internet connection.

Closes: #15598, #15806
  • Loading branch information
stefandunca committed Jul 31, 2024
1 parent f226b47 commit 721f1bb
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 37 deletions.
3 changes: 1 addition & 2 deletions src/backend/wallet_connect.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
11 changes: 10 additions & 1 deletion storybook/pages/DAppsWorkflowPage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -289,7 +298,7 @@ Item {
id: walletConnectService

wcSDK: WalletConnectSDK {
active: settings.enableSDK
enableSdk: settings.enableSDK

projectId: projectIdText.projectId
}
Expand Down
2 changes: 2 additions & 0 deletions ui/StatusQ/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
41 changes: 41 additions & 0 deletions ui/StatusQ/include/StatusQ/networkchecker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#pragma once

#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QObject>
#include <QTimer>

#include <chrono>

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);
};
60 changes: 46 additions & 14 deletions ui/StatusQ/src/StatusQ/Components/WebEngineLoader.qml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

active: false
Component {
id: webEngineViewComponent

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
Expand All @@ -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
}
}
61 changes: 61 additions & 0 deletions ui/StatusQ/src/networkchecker.cpp
Original file line number Diff line number Diff line change
@@ -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();
}
}
2 changes: 2 additions & 0 deletions ui/StatusQ/src/plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -58,6 +59,7 @@ class StatusQPlugin : public QQmlExtensionPlugin
qmlRegisterType<SourceModel>("StatusQ", 0, 1, "SourceModel");
qmlRegisterType<ConcatModel>("StatusQ", 0, 1, "ConcatModel");
qmlRegisterType<MovableModel>("StatusQ", 0, 1, "MovableModel");
qmlRegisterType<NetworkChecker>("StatusQ", 0, 1, "NetworkChecker");

qmlRegisterType<FastExpressionFilter>("StatusQ", 0, 1, "FastExpressionFilter");
qmlRegisterType<FastExpressionRole>("StatusQ", 0, 1, "FastExpressionRole");
Expand Down
6 changes: 4 additions & 2 deletions ui/StatusQ/tests/TestComponents/tst_WebEngineLoader.qml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ TestCase {
sourceComponent: WebEngineLoader {
url: "./WebEngineLoader/test.html"
webChannelObjects: [testObject]

waitForInternet: false
}
}
SignalSpy { id: loadedSpy; target: loader; signalName: "loaded" }
Expand Down Expand Up @@ -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);

Expand Down
28 changes: 14 additions & 14 deletions ui/app/AppLayouts/Wallet/services/dapps/WalletConnectSDK.qml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -97,15 +97,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) {
Expand Down Expand Up @@ -330,7 +330,7 @@ WalletConnectSDKBase {

WebChannel.id: "statusObject"

function bubbleConsoleMessage(type, message) {
function echo(type, message) {
if (type === "warn") {
console.warn(message)
} else if (type === "debug") {
Expand All @@ -343,7 +343,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)
}
Expand Down
12 changes: 9 additions & 3 deletions ui/app/AppLayouts/Wallet/services/dapps/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}"`)
Expand All @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion ui/app/mainui/AppMain.qml
Original file line number Diff line number Diff line change
Expand Up @@ -2185,7 +2185,7 @@ Item {
id: walletConnectService

wcSDK: WalletConnectSDK {
active: WalletStore.RootStore.walletSectionInst.walletReady
enableSdk: WalletStore.RootStore.walletSectionInst.walletReady

projectId: WalletStore.RootStore.appSettings.walletConnectProjectID
}
Expand Down

0 comments on commit 721f1bb

Please sign in to comment.